From 9b72340868b9e7db8826f41611b498c07f8d2e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Fri, 5 Dec 2025 12:27:23 +0100 Subject: [PATCH 1/3] Convert e2e test mock server to Typescript I noticed multiple tiny issues in the e2e test server, e.g. app.serviceId was used instead of app.id. This PR fixes this and also converts it to TypeScript so we notice things like this in the future. --- end2end/server/Dockerfile | 10 +- end2end/server/app.js | 35 - end2end/server/app.ts | 38 + end2end/server/package-lock.json | 942 ++++++++++++++++++ end2end/server/package.json | 12 +- .../{captureEvent.js => captureEvent.ts} | 22 +- .../handlers/{createApp.js => createApp.ts} | 14 +- end2end/server/src/handlers/getConfig.js | 12 - end2end/server/src/handlers/getConfig.ts | 14 + end2end/server/src/handlers/listEvents.js | 11 - end2end/server/src/handlers/listEvents.ts | 10 + .../src/handlers/{lists.js => lists.ts} | 26 +- end2end/server/src/handlers/realtimeConfig.js | 14 - end2end/server/src/handlers/realtimeConfig.ts | 16 + .../{updateConfig.js => updateConfig.ts} | 12 +- .../{updateLists.js => updateLists.ts} | 24 +- end2end/server/src/middleware/checkToken.js | 23 - end2end/server/src/middleware/checkToken.ts | 24 + end2end/server/src/types.ts | 6 + end2end/server/src/zen/{apps.js => apps.ts} | 33 +- .../server/src/zen/{config.js => config.ts} | 121 ++- .../server/src/zen/{events.js => events.ts} | 11 +- end2end/server/tsconfig.json | 11 + 23 files changed, 1209 insertions(+), 232 deletions(-) delete mode 100644 end2end/server/app.js create mode 100644 end2end/server/app.ts create mode 100644 end2end/server/package-lock.json rename end2end/server/src/handlers/{captureEvent.js => captureEvent.ts} (54%) rename end2end/server/src/handlers/{createApp.js => createApp.ts} (60%) delete mode 100644 end2end/server/src/handlers/getConfig.js create mode 100644 end2end/server/src/handlers/getConfig.ts delete mode 100644 end2end/server/src/handlers/listEvents.js create mode 100644 end2end/server/src/handlers/listEvents.ts rename end2end/server/src/handlers/{lists.js => lists.ts} (71%) delete mode 100644 end2end/server/src/handlers/realtimeConfig.js create mode 100644 end2end/server/src/handlers/realtimeConfig.ts rename end2end/server/src/handlers/{updateConfig.js => updateConfig.ts} (53%) rename end2end/server/src/handlers/{updateLists.js => updateLists.ts} (66%) delete mode 100644 end2end/server/src/middleware/checkToken.js create mode 100644 end2end/server/src/middleware/checkToken.ts create mode 100644 end2end/server/src/types.ts rename end2end/server/src/zen/{apps.js => apps.ts} (63%) rename end2end/server/src/zen/{config.js => config.ts} (55%) rename end2end/server/src/zen/{events.js => events.ts} (55%) create mode 100644 end2end/server/tsconfig.json diff --git a/end2end/server/Dockerfile b/end2end/server/Dockerfile index fdc5e895c..44b48d62e 100644 --- a/end2end/server/Dockerfile +++ b/end2end/server/Dockerfile @@ -1,11 +1,11 @@ -FROM node:22-slim +FROM node:24-slim WORKDIR /app -COPY package.json /app +COPY package.json package-lock.json /app/ -RUN npm install +RUN npm ci -COPY . /app +COPY . /app/ -CMD ["node" , "/app/app.js"] +CMD ["node" , "/app/app.ts"] diff --git a/end2end/server/app.js b/end2end/server/app.js deleted file mode 100644 index d0fc91d41..000000000 --- a/end2end/server/app.js +++ /dev/null @@ -1,35 +0,0 @@ -// This is an insecure mock server for testing purposes -const express = require("express"); -const config = require("./src/handlers/getConfig"); -const captureEvent = require("./src/handlers/captureEvent"); -const listEvents = require("./src/handlers/listEvents"); -const createApp = require("./src/handlers/createApp"); -const checkToken = require("./src/middleware/checkToken"); -const updateConfig = require("./src/handlers/updateConfig"); -const lists = require("./src/handlers/lists"); -const updateLists = require("./src/handlers/updateLists"); -const realtimeConfig = require("./src/handlers/realtimeConfig"); - -const app = express(); - -const port = process.env.PORT || 3000; - -app.use(express.json()); - -app.get("/api/runtime/config", checkToken, config); -app.post("/api/runtime/config", checkToken, updateConfig); - -// Realtime polling endpoint -app.get("/config", checkToken, realtimeConfig); - -app.get("/api/runtime/events", checkToken, listEvents); -app.post("/api/runtime/events", checkToken, captureEvent); - -app.get("/api/runtime/firewall/lists", checkToken, lists); -app.post("/api/runtime/firewall/lists", checkToken, updateLists); - -app.post("/api/runtime/apps", createApp); - -app.listen(port, () => { - console.log(`Server is running on port ${port}`); -}); diff --git a/end2end/server/app.ts b/end2end/server/app.ts new file mode 100644 index 000000000..da888d0a0 --- /dev/null +++ b/end2end/server/app.ts @@ -0,0 +1,38 @@ +// This is an insecure mock server for testing purposes + +import express from "express"; +import { getConfig } from "./src/handlers/getConfig.ts"; +import { captureEvent } from "./src/handlers/captureEvent.ts"; +import { listEvents } from "./src/handlers/listEvents.ts"; +import { createApp } from "./src/handlers/createApp.ts"; +import { checkToken } from "./src/middleware/checkToken.ts"; +import { updateConfig } from "./src/handlers/updateConfig.ts"; +import { lists } from "./src/handlers/lists.ts"; +import { updateIPLists } from "./src/handlers/updateLists.ts"; +import { realtimeConfig } from "./src/handlers/realtimeConfig.ts"; + +const app = express(); +app.set("trust proxy", false); +app.set("x-powered-by", false); + +const port = process.env.PORT || 3000; + +app.use(express.json()); + +app.get("/api/runtime/config", checkToken, getConfig); +app.post("/api/runtime/config", checkToken, updateConfig); + +// Realtime polling endpoint +app.get("/config", checkToken, realtimeConfig); + +app.get("/api/runtime/events", checkToken, listEvents); +app.post("/api/runtime/events", checkToken, captureEvent); + +app.get("/api/runtime/firewall/lists", checkToken, lists); +app.post("/api/runtime/firewall/lists", checkToken, updateIPLists); + +app.post("/api/runtime/apps", createApp); + +app.listen(port, () => { + console.log(`Server is running on port ${port}`); +}); diff --git a/end2end/server/package-lock.json b/end2end/server/package-lock.json new file mode 100644 index 000000000..9cb5d43d4 --- /dev/null +++ b/end2end/server/package-lock.json @@ -0,0 +1,942 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "dependencies": { + "express": "^5.1.0" + }, + "devDependencies": { + "@types/express": "^5.0.6", + "@types/node": "^24.10.1", + "typescript": "^5.9.3" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/end2end/server/package.json b/end2end/server/package.json index 9c6a98f5d..4e38b55e3 100644 --- a/end2end/server/package.json +++ b/end2end/server/package.json @@ -1,9 +1,19 @@ { "name": "server", "version": "1.0.0", - "main": "index.js", + "main": "app.ts", "private": true, + "type": "module", "dependencies": { "express": "^5.1.0" + }, + "devDependencies": { + "@types/express": "^5.0.6", + "@types/node": "^24.10.1", + "typescript": "^5.9.3" + }, + "scripts": { + "start": "node ./app.ts", + "check:types": "tsc --noEmit" } } diff --git a/end2end/server/src/handlers/captureEvent.js b/end2end/server/src/handlers/captureEvent.ts similarity index 54% rename from end2end/server/src/handlers/captureEvent.js rename to end2end/server/src/handlers/captureEvent.ts index 549f4e944..ff2b5bc35 100644 --- a/end2end/server/src/handlers/captureEvent.js +++ b/end2end/server/src/handlers/captureEvent.ts @@ -1,16 +1,18 @@ -const { getAppConfig } = require("../zen/config"); -const { captureEvent: capture } = require("../zen/events"); -const { setTimeout } = require("timers/promises"); -const { randomInt } = require("crypto"); +import type { Response } from "express"; +import { getAppConfig } from "../zen/config.ts"; +import { captureEvent as capture } from "../zen/events.ts"; +import { setTimeout } from "node:timers/promises"; +import { randomInt } from "node:crypto"; +import type { ZenRequest } from "../types.ts"; -module.exports = async function captureEvent(req, res) { - if (!req.app) { +export async function captureEvent(req: ZenRequest, res: Response) { + if (!req.zenApp) { throw new Error("App is missing"); } // For testing: allow simulating API failures and delays for attack events if (req.body.type === "detected_attack") { - const config = getAppConfig(req.app); + const config = getAppConfig(req.zenApp); if (typeof config.failureRate === "number" && config.failureRate > 0) { if (Math.random() < config.failureRate) { @@ -24,7 +26,7 @@ module.exports = async function captureEvent(req, res) { } } - capture(req.body, req.app); + capture(req.body, req.zenApp); if (req.body.type === "detected_attack") { return res.json({ @@ -32,5 +34,5 @@ module.exports = async function captureEvent(req, res) { }); } - return res.json(getAppConfig(req.app)); -}; + return res.json(getAppConfig(req.zenApp)); +} diff --git a/end2end/server/src/handlers/createApp.js b/end2end/server/src/handlers/createApp.ts similarity index 60% rename from end2end/server/src/handlers/createApp.js rename to end2end/server/src/handlers/createApp.ts index 788ed7f49..bfbceb410 100644 --- a/end2end/server/src/handlers/createApp.js +++ b/end2end/server/src/handlers/createApp.ts @@ -1,14 +1,16 @@ -const { createApp: create, getByToken } = require("../zen/apps"); -const { updateAppConfig } = require("../zen/config"); +import type { Response } from "express"; +import { createApp as create, getByToken } from "../zen/apps.ts"; +import { updateAppConfig } from "../zen/config.ts"; +import type { ZenRequest } from "../types.ts"; -module.exports = function createApp(req, res) { +export function createApp(req: ZenRequest, res: Response) { const token = create(); // Support optional config parameters for testing if (req.body) { const app = getByToken(token); if (app) { - const testConfig = {}; + const testConfig: Record = {}; if (typeof req.body.failureRate === "number") { testConfig.failureRate = req.body.failureRate; @@ -25,6 +27,6 @@ module.exports = function createApp(req, res) { } res.json({ - token: token, + token, }); -}; +} diff --git a/end2end/server/src/handlers/getConfig.js b/end2end/server/src/handlers/getConfig.js deleted file mode 100644 index 2ffb32df3..000000000 --- a/end2end/server/src/handlers/getConfig.js +++ /dev/null @@ -1,12 +0,0 @@ -const { getAppConfig } = require("../zen/config"); - -module.exports = function getConfig(req, res) { - if (!req.app) { - throw new Error("App is missing"); - } - - const config = getAppConfig(req.app); - delete config.failureRate; - delete config.timeout; - res.json(config); -}; diff --git a/end2end/server/src/handlers/getConfig.ts b/end2end/server/src/handlers/getConfig.ts new file mode 100644 index 000000000..246739a43 --- /dev/null +++ b/end2end/server/src/handlers/getConfig.ts @@ -0,0 +1,14 @@ +import type { Response } from "express"; +import { getAppConfig } from "../zen/config.ts"; +import type { ZenRequest } from "../types.ts"; + +export function getConfig(req: ZenRequest, res: Response) { + if (!req.zenApp) { + throw new Error("App is missing"); + } + + const config = getAppConfig(req.zenApp); + delete config.failureRate; + delete config.timeout; + res.json(config); +} diff --git a/end2end/server/src/handlers/listEvents.js b/end2end/server/src/handlers/listEvents.js deleted file mode 100644 index 33f703e17..000000000 --- a/end2end/server/src/handlers/listEvents.js +++ /dev/null @@ -1,11 +0,0 @@ -const { listEvents: list } = require("../zen/events"); - -function listEvents(req, res) { - if (!req.app) { - throw new Error("App is missing"); - } - - res.json(list(req.app)); -} - -module.exports = listEvents; diff --git a/end2end/server/src/handlers/listEvents.ts b/end2end/server/src/handlers/listEvents.ts new file mode 100644 index 000000000..7a01b3bc4 --- /dev/null +++ b/end2end/server/src/handlers/listEvents.ts @@ -0,0 +1,10 @@ +import type { Response } from "express"; +import { listEvents as list } from "../zen/events.ts"; +import type { ZenRequest } from "../types.ts"; + +export function listEvents(req: ZenRequest, res: Response) { + if (!req.zenApp) { + throw new Error("App is missing"); + } + res.json(list(req.zenApp)); +} diff --git a/end2end/server/src/handlers/lists.js b/end2end/server/src/handlers/lists.ts similarity index 71% rename from end2end/server/src/handlers/lists.js rename to end2end/server/src/handlers/lists.ts index b25ec4229..07a2473ec 100644 --- a/end2end/server/src/handlers/lists.js +++ b/end2end/server/src/handlers/lists.ts @@ -1,14 +1,16 @@ -const { +import { getBlockedIPAddresses, getBlockedUserAgents, getAllowedIPAddresses, getMonitoredUserAgents, getMonitoredIPAddresses, getUserAgentDetails, -} = require("../zen/config"); +} from "../zen/config.ts"; +import type { Response } from "express"; +import type { ZenRequest } from "../types.ts"; -module.exports = function lists(req, res) { - if (!req.app) { +export function lists(req: ZenRequest, res: Response) { + if (!req.zenApp) { throw new Error("App is missing"); } @@ -22,16 +24,16 @@ module.exports = function lists(req, res) { }); } - const blockedIps = getBlockedIPAddresses(req.app); - const blockedUserAgents = getBlockedUserAgents(req.app); - const allowedIps = getAllowedIPAddresses(req.app); - const monitoredUserAgents = getMonitoredUserAgents(req.app); - const monitoredIps = getMonitoredIPAddresses(req.app); - const userAgentDetails = getUserAgentDetails(req.app); + const blockedIps = getBlockedIPAddresses(req.zenApp); + const blockedUserAgents = getBlockedUserAgents(req.zenApp); + const allowedIps = getAllowedIPAddresses(req.zenApp); + const monitoredUserAgents = getMonitoredUserAgents(req.zenApp); + const monitoredIps = getMonitoredIPAddresses(req.zenApp); + const userAgentDetails = getUserAgentDetails(req.zenApp); res.json({ success: true, - serviceId: req.app.id, + serviceId: req.zenApp.id, blockedIPAddresses: blockedIps.length > 0 ? [ @@ -69,4 +71,4 @@ module.exports = function lists(req, res) { }, ], }); -}; +} diff --git a/end2end/server/src/handlers/realtimeConfig.js b/end2end/server/src/handlers/realtimeConfig.js deleted file mode 100644 index 70dad8ec3..000000000 --- a/end2end/server/src/handlers/realtimeConfig.js +++ /dev/null @@ -1,14 +0,0 @@ -const { getAppConfig } = require("../zen/config"); - -module.exports = function realtimeConfig(req, res) { - if (!req.app) { - throw new Error("App is missing"); - } - - const config = getAppConfig(req.app); - - res.json({ - serviceId: req.app.serviceId, - configUpdatedAt: config.configUpdatedAt, - }); -}; diff --git a/end2end/server/src/handlers/realtimeConfig.ts b/end2end/server/src/handlers/realtimeConfig.ts new file mode 100644 index 000000000..0db5e72e8 --- /dev/null +++ b/end2end/server/src/handlers/realtimeConfig.ts @@ -0,0 +1,16 @@ +import type { Response } from "express"; +import { getAppConfig } from "../zen/config.ts"; +import type { ZenRequest } from "../types.ts"; + +export function realtimeConfig(req: ZenRequest, res: Response) { + if (!req.zenApp) { + throw new Error("App is missing"); + } + + const config = getAppConfig(req.zenApp); + + res.json({ + serviceId: req.zenApp.id, + configUpdatedAt: config.configUpdatedAt, + }); +} diff --git a/end2end/server/src/handlers/updateConfig.js b/end2end/server/src/handlers/updateConfig.ts similarity index 53% rename from end2end/server/src/handlers/updateConfig.js rename to end2end/server/src/handlers/updateConfig.ts index d97475942..6bac393f7 100644 --- a/end2end/server/src/handlers/updateConfig.js +++ b/end2end/server/src/handlers/updateConfig.ts @@ -1,7 +1,9 @@ -const { updateAppConfig } = require("../zen/config"); +import type { Response } from "express"; +import { updateAppConfig } from "../zen/config.ts"; +import type { ZenRequest } from "../types.ts"; -module.exports = function updateConfig(req, res) { - if (!req.app) { +export function updateConfig(req: ZenRequest, res: Response) { + if (!req.zenApp) { throw new Error("App is missing"); } @@ -16,5 +18,5 @@ module.exports = function updateConfig(req, res) { message: "Request body is missing or invalid", }); } - res.json({ success: updateAppConfig(req.app, req.body) }); -}; + res.json({ success: updateAppConfig(req.zenApp, req.body) }); +} diff --git a/end2end/server/src/handlers/updateLists.js b/end2end/server/src/handlers/updateLists.ts similarity index 66% rename from end2end/server/src/handlers/updateLists.js rename to end2end/server/src/handlers/updateLists.ts index 0b7885763..ee3ab689a 100644 --- a/end2end/server/src/handlers/updateLists.js +++ b/end2end/server/src/handlers/updateLists.ts @@ -1,14 +1,16 @@ -const { +import type { Response } from "express"; +import { updateBlockedIPAddresses, updateBlockedUserAgents, updateAllowedIPAddresses, updateMonitoredUserAgents, updateMonitoredIPAddresses, updateUserAgentDetails, -} = require("../zen/config"); +} from "../zen/config.ts"; +import type { ZenRequest } from "../types.ts"; -module.exports = function updateIPLists(req, res) { - if (!req.app) { +export function updateIPLists(req: ZenRequest, res: Response) { + if (!req.zenApp) { throw new Error("App is missing"); } @@ -33,39 +35,39 @@ module.exports = function updateIPLists(req, res) { }); } - updateBlockedIPAddresses(req.app, req.body.blockedIPAddresses); + updateBlockedIPAddresses(req.zenApp, req.body.blockedIPAddresses); if ( req.body.blockedUserAgents && typeof req.body.blockedUserAgents === "string" ) { - updateBlockedUserAgents(req.app, req.body.blockedUserAgents); + updateBlockedUserAgents(req.zenApp, req.body.blockedUserAgents); } if ( req.body.allowedIPAddresses && Array.isArray(req.body.allowedIPAddresses) ) { - updateAllowedIPAddresses(req.app, req.body.allowedIPAddresses); + updateAllowedIPAddresses(req.zenApp, req.body.allowedIPAddresses); } if ( req.body.monitoredUserAgents && typeof req.body.monitoredUserAgents === "string" ) { - updateMonitoredUserAgents(req.app, req.body.monitoredUserAgents); + updateMonitoredUserAgents(req.zenApp, req.body.monitoredUserAgents); } if ( req.body.monitoredIPAddresses && Array.isArray(req.body.monitoredIPAddresses) ) { - updateMonitoredIPAddresses(req.app, req.body.monitoredIPAddresses); + updateMonitoredIPAddresses(req.zenApp, req.body.monitoredIPAddresses); } if (req.body.userAgentDetails && Array.isArray(req.body.userAgentDetails)) { - updateUserAgentDetails(req.app, req.body.userAgentDetails); + updateUserAgentDetails(req.zenApp, req.body.userAgentDetails); } res.json({ success: true }); -}; +} diff --git a/end2end/server/src/middleware/checkToken.js b/end2end/server/src/middleware/checkToken.js deleted file mode 100644 index 2e35d891f..000000000 --- a/end2end/server/src/middleware/checkToken.js +++ /dev/null @@ -1,23 +0,0 @@ -const { getByToken } = require("../zen/apps"); - -module.exports = function checkToken(req, res, next) { - const token = req.headers["authorization"]; - - if (!token) { - return res.status(401).json({ - message: "Token is required", - }); - } - - const app = getByToken(token); - - if (!app) { - return res.status(401).json({ - message: "Invalid token", - }); - } - - req.app = app; - - next(); -}; diff --git a/end2end/server/src/middleware/checkToken.ts b/end2end/server/src/middleware/checkToken.ts new file mode 100644 index 000000000..9afc75b1e --- /dev/null +++ b/end2end/server/src/middleware/checkToken.ts @@ -0,0 +1,24 @@ +import type { Response, NextFunction } from "express"; +import { getByToken } from "../zen/apps.ts"; +import type { ZenRequest } from "../types.ts"; + +export function checkToken(req: ZenRequest, res: Response, next: NextFunction) { + const token = req.headers["authorization"] as string | undefined; + + if (!token) { + return res.status(401).json({ + message: "Token is required", + }); + } + + const app = getByToken(token); + if (!app) { + return res.status(401).json({ + message: "Invalid token", + }); + } + + req.zenApp = app; + + next(); +} diff --git a/end2end/server/src/types.ts b/end2end/server/src/types.ts new file mode 100644 index 000000000..cdeff08d6 --- /dev/null +++ b/end2end/server/src/types.ts @@ -0,0 +1,6 @@ +import type { App } from "./zen/apps.ts"; +import type { Request } from "express"; + +export interface ZenRequest extends Request { + zenApp?: App; +} diff --git a/end2end/server/src/zen/apps.js b/end2end/server/src/zen/apps.ts similarity index 63% rename from end2end/server/src/zen/apps.js rename to end2end/server/src/zen/apps.ts index a3a102277..221fd5f4c 100644 --- a/end2end/server/src/zen/apps.js +++ b/end2end/server/src/zen/apps.ts @@ -1,47 +1,42 @@ -const { randomInt, timingSafeEqual } = require("crypto"); +import { randomInt, timingSafeEqual } from "node:crypto"; -const apps = []; +export interface App { + id: number; + token: string; + configUpdatedAt: number; +} + +const apps: App[] = []; let id = 1; -function createApp() { +export function createApp(): string { const appId = id++; const token = `AIK_RUNTIME_1_${appId}_${generateRandomString(48)}`; - const app = { + apps.push({ id: appId, - token: token, + token, configUpdatedAt: Date.now(), - }; - - apps.push(app); - + }); return token; } -function getByToken(token) { +export function getByToken(token: string): App | undefined { return apps.find((app) => { if (app.token.length !== token.length) { return false; } - return timingSafeEqual(Buffer.from(app.token), Buffer.from(token)); }); } -function generateRandomString(length) { +function generateRandomString(length: number): string { const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const size = chars.length; let str = ""; - for (let i = 0; i < length; i++) { const randomIndex = randomInt(0, size); str += chars[randomIndex]; } - return str; } - -module.exports = { - createApp, - getByToken, -}; diff --git a/end2end/server/src/zen/config.js b/end2end/server/src/zen/config.ts similarity index 55% rename from end2end/server/src/zen/config.js rename to end2end/server/src/zen/config.ts index 1eda00e82..b719ed3ea 100644 --- a/end2end/server/src/zen/config.js +++ b/end2end/server/src/zen/config.ts @@ -1,6 +1,23 @@ -const configs = []; +import type { App } from "./apps.ts"; + +type AppConfig = { + success: boolean; + serviceId: number; + configUpdatedAt: number; + heartbeatIntervalInMS: number; + endpoints: any[]; + blockedUserIds: number[]; + allowedIPAddresses: string[]; + receivedAnyStats: boolean; + blockNewOutgoingRequests: boolean; + domains: any[]; + failureRate?: number; + timeout?: number; +}; + +const configs: AppConfig[] = []; -function generateConfig(app) { +export function generateConfig(app: App): AppConfig { return { success: true, serviceId: app.id, @@ -15,7 +32,7 @@ function generateConfig(app) { }; } -function getAppConfig(app) { +export function getAppConfig(app: App) { const existingConf = configs.find((config) => config.serviceId === app.id); if (existingConf) { return existingConf; @@ -25,8 +42,8 @@ function getAppConfig(app) { return newConf; } -function updateAppConfig(app, newConfig) { - let index = configs.findIndex((config) => config.serviceId === app.serviceId); +export function updateAppConfig(app: App, newConfig: Partial) { + let index = configs.findIndex((config) => config.serviceId === app.id); if (index === -1) { getAppConfig(app); index = configs.length - 1; @@ -39,20 +56,20 @@ function updateAppConfig(app, newConfig) { return true; } -const blockedIPAddresses = []; -const blockedUserAgents = []; -const allowedIPAddresses = []; -const monitoredUserAgents = []; -const monitoredIPAddresses = []; -const userAgentDetails = []; +const blockedIPAddresses: { serviceId: number; ipAddresses: string[] }[] = []; +const blockedUserAgents: { serviceId: number; userAgents: string[] }[] = []; +const allowedIPAddresses: { serviceId: number; ipAddresses: string[] }[] = []; +const monitoredUserAgents: { serviceId: number; userAgents: string[] }[] = []; +const monitoredIPAddresses: { serviceId: number; ipAddresses: string[] }[] = []; +const userAgentDetails: { serviceId: number; userAgents: string[] }[] = []; -function updateBlockedIPAddresses(app, ips) { - let entry = blockedIPAddresses.find((ip) => ip.serviceId === app.serviceId); +export function updateBlockedIPAddresses(app: App, ips: string[]) { + let entry = blockedIPAddresses.find((ip) => ip.serviceId === app.id); if (entry) { entry.ipAddresses = ips; } else { - entry = { serviceId: app.serviceId, ipAddresses: ips }; + entry = { serviceId: app.id, ipAddresses: ips }; blockedIPAddresses.push(entry); } @@ -60,23 +77,23 @@ function updateBlockedIPAddresses(app, ips) { updateAppConfig(app, {}); } -function getBlockedIPAddresses(app) { - const entry = blockedIPAddresses.find((ip) => ip.serviceId === app.serviceId); +export function getBlockedIPAddresses(app: App) { + const entry = blockedIPAddresses.find((ip) => ip.serviceId === app.id); if (entry) { return entry.ipAddresses; } - return { serviceId: app.serviceId, ipAddresses: [] }; + return []; } -function updateAllowedIPAddresses(app, ips) { - let entry = allowedIPAddresses.find((ip) => ip.serviceId === app.serviceId); +export function updateAllowedIPAddresses(app: App, ips: string[]) { + let entry = allowedIPAddresses.find((ip) => ip.serviceId === app.id); if (entry) { entry.ipAddresses = ips; } else { - entry = { serviceId: app.serviceId, ipAddresses: ips }; + entry = { serviceId: app.id, ipAddresses: ips }; allowedIPAddresses.push(entry); } @@ -84,23 +101,22 @@ function updateAllowedIPAddresses(app, ips) { updateAppConfig(app, {}); } -function getAllowedIPAddresses(app) { - const entry = allowedIPAddresses.find((ip) => ip.serviceId === app.serviceId); +export function getAllowedIPAddresses(app: App) { + const entry = allowedIPAddresses.find((ip) => ip.serviceId === app.id); if (entry) { return entry.ipAddresses; } - return { serviceId: app.serviceId, ipAddresses: [] }; + return []; } - -function updateBlockedUserAgents(app, uas) { - let entry = blockedUserAgents.find((e) => e.serviceId === app.serviceId); +export function updateBlockedUserAgents(app: App, uas: string[]) { + let entry = blockedUserAgents.find((e) => e.serviceId === app.id); if (entry) { entry.userAgents = uas; } else { - entry = { serviceId: app.serviceId, userAgents: uas }; + entry = { serviceId: app.id, userAgents: uas }; blockedUserAgents.push(entry); } @@ -108,8 +124,8 @@ function updateBlockedUserAgents(app, uas) { updateAppConfig(app, {}); } -function getBlockedUserAgents(app) { - const entry = blockedUserAgents.find((e) => e.serviceId === app.serviceId); +export function getBlockedUserAgents(app: App) { + const entry = blockedUserAgents.find((e) => e.serviceId === app.id); if (entry) { return entry.userAgents; @@ -118,13 +134,13 @@ function getBlockedUserAgents(app) { return ""; } -function updateMonitoredUserAgents(app, uas) { - let entry = monitoredUserAgents.find((e) => e.serviceId === app.serviceId); +export function updateMonitoredUserAgents(app: App, uas: string[]) { + let entry = monitoredUserAgents.find((e) => e.serviceId === app.id); if (entry) { entry.userAgents = uas; } else { - entry = { serviceId: app.serviceId, userAgents: uas }; + entry = { serviceId: app.id, userAgents: uas }; monitoredUserAgents.push(entry); } @@ -132,8 +148,8 @@ function updateMonitoredUserAgents(app, uas) { updateAppConfig(app, {}); } -function getMonitoredUserAgents(app) { - const entry = monitoredUserAgents.find((e) => e.serviceId === app.serviceId); +export function getMonitoredUserAgents(app: App) { + const entry = monitoredUserAgents.find((e) => e.serviceId === app.id); if (entry) { return entry.userAgents; @@ -142,13 +158,13 @@ function getMonitoredUserAgents(app) { return ""; } -function updateMonitoredIPAddresses(app, ips) { - let entry = monitoredIPAddresses.find((e) => e.serviceId === app.serviceId); +export function updateMonitoredIPAddresses(app: App, ips: string[]) { + let entry = monitoredIPAddresses.find((e) => e.serviceId === app.id); if (entry) { entry.ipAddresses = ips; } else { - entry = { serviceId: app.serviceId, ipAddresses: ips }; + entry = { serviceId: app.id, ipAddresses: ips }; monitoredIPAddresses.push(entry); } @@ -156,8 +172,8 @@ function updateMonitoredIPAddresses(app, ips) { updateAppConfig(app, {}); } -function getMonitoredIPAddresses(app) { - const entry = monitoredIPAddresses.find((e) => e.serviceId === app.serviceId); +export function getMonitoredIPAddresses(app: App) { + const entry = monitoredIPAddresses.find((e) => e.serviceId === app.id); if (entry) { return entry.ipAddresses; @@ -166,13 +182,13 @@ function getMonitoredIPAddresses(app) { return []; } -function updateUserAgentDetails(app, uas) { - let entry = userAgentDetails.find((e) => e.serviceId === app.serviceId); +export function updateUserAgentDetails(app: App, uas: string[]) { + let entry = userAgentDetails.find((e) => e.serviceId === app.id); if (entry) { entry.userAgents = uas; } else { - entry = { serviceId: app.serviceId, userAgents: uas }; + entry = { serviceId: app.id, userAgents: uas }; userAgentDetails.push(entry); } @@ -180,8 +196,8 @@ function updateUserAgentDetails(app, uas) { updateAppConfig(app, {}); } -function getUserAgentDetails(app) { - const entry = userAgentDetails.find((e) => e.serviceId === app.serviceId); +export function getUserAgentDetails(app: App) { + const entry = userAgentDetails.find((e) => e.serviceId === app.id); if (entry) { return entry.userAgents; @@ -189,20 +205,3 @@ function getUserAgentDetails(app) { return []; } - -module.exports = { - getAppConfig, - updateAppConfig, - updateBlockedIPAddresses, - getBlockedIPAddresses, - updateBlockedUserAgents, - getBlockedUserAgents, - getAllowedIPAddresses, - updateAllowedIPAddresses, - updateMonitoredUserAgents, - getMonitoredUserAgents, - updateMonitoredIPAddresses, - getMonitoredIPAddresses, - updateUserAgentDetails, - getUserAgentDetails, -}; diff --git a/end2end/server/src/zen/events.js b/end2end/server/src/zen/events.ts similarity index 55% rename from end2end/server/src/zen/events.js rename to end2end/server/src/zen/events.ts index 74d1e80e8..34f65e15c 100644 --- a/end2end/server/src/zen/events.js +++ b/end2end/server/src/zen/events.ts @@ -1,6 +1,8 @@ +import type { App } from "./apps.ts"; + const events = new Map(); -function captureEvent(event, app) { +export function captureEvent(event: unknown, app: App) { if (!events.has(app.id)) { events.set(app.id, []); } @@ -8,11 +10,6 @@ function captureEvent(event, app) { events.get(app.id).push(event); } -function listEvents(app) { +export function listEvents(app: App) { return events.get(app.id) || []; } - -module.exports = { - captureEvent, - listEvents, -}; diff --git a/end2end/server/tsconfig.json b/end2end/server/tsconfig.json new file mode 100644 index 000000000..b9bc8054f --- /dev/null +++ b/end2end/server/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "nodenext", + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} From 39e261fd42eff9c5e55ba74f17ee8f51e34cd16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 11 Dec 2025 10:17:02 +0100 Subject: [PATCH 2/3] Update end2end/server/src/zen/apps.ts Co-authored-by: Hans Ott --- end2end/server/src/zen/apps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/end2end/server/src/zen/apps.ts b/end2end/server/src/zen/apps.ts index 221fd5f4c..2ada91914 100644 --- a/end2end/server/src/zen/apps.ts +++ b/end2end/server/src/zen/apps.ts @@ -1,6 +1,6 @@ import { randomInt, timingSafeEqual } from "node:crypto"; -export interface App { +export type App = { id: number; token: string; configUpdatedAt: number; From 90d1f250f39ae203e3b838b10b1a948207b06030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6ssler?= Date: Thu, 11 Dec 2025 10:26:57 +0100 Subject: [PATCH 3/3] Improve Dockerfile --- end2end/server/.dockerignore | 1 + end2end/server/Dockerfile | 11 ++++++----- end2end/server/src/zen/apps.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 end2end/server/.dockerignore diff --git a/end2end/server/.dockerignore b/end2end/server/.dockerignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/end2end/server/.dockerignore @@ -0,0 +1 @@ +node_modules/ diff --git a/end2end/server/Dockerfile b/end2end/server/Dockerfile index 44b48d62e..aaf4d3aca 100644 --- a/end2end/server/Dockerfile +++ b/end2end/server/Dockerfile @@ -1,11 +1,12 @@ FROM node:24-slim +USER node WORKDIR /app -COPY package.json package-lock.json /app/ +COPY --chown=node:node package.json package-lock.json ./ +RUN npm ci --ignore-scripts -RUN npm ci +COPY --chown=node:node . ./ +RUN node --run check:types -COPY . /app/ - -CMD ["node" , "/app/app.ts"] +CMD ["node" , "app.ts"] diff --git a/end2end/server/src/zen/apps.ts b/end2end/server/src/zen/apps.ts index 2ada91914..e11e2cd99 100644 --- a/end2end/server/src/zen/apps.ts +++ b/end2end/server/src/zen/apps.ts @@ -4,7 +4,7 @@ export type App = { id: number; token: string; configUpdatedAt: number; -} +}; const apps: App[] = [];