mirror of
https://github.com/devkitshq/notifkit.git
synced 2026-10-19 18:24:52 +02:00
refactor(profiling): name service containers and update dependencies
- Upgrade zod to ^4.6.5 along with compatible dependency updates across ai, dotenv, drizzle-orm, ioredis, pino, postgres, and dev tools. - Adjust tests/workers-ai.test.ts mock UUIDs to adhere to RFC 4122 v4 validation in Zod 4. - Update tests/http.test.ts validation message expectations for Zod 4. - Replace generic server container names with role-based definitions (api-server-1, api-server-2, enricher-worker, engine-worker, delivery-worker). - Add serverNodes option in distributed profiling harness to route and health-check designated worker and API nodes. - Add multi-run statistical metrics (mean, median, min, max, stdDev) reporting to money-scaling benchmark runner.
This commit is contained in:
Generated
+217
-181
@@ -13,16 +13,16 @@
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"ai": "^7.0.37",
|
||||
"dotenv": "^16.4.5",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"ai": "^7.0.116",
|
||||
"dotenv": "^18.0.4",
|
||||
"drizzle-orm": "^0.45.3",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"pino": "^9.2.0",
|
||||
"pino-pretty": "^11.0.0",
|
||||
"postgres": "^3.4.4",
|
||||
"ioredis": "^6.0.0",
|
||||
"pino": "^10.3.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"postgres": "^3.4.9",
|
||||
"prom-client": "^15.1.3",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "^4.6.5"
|
||||
},
|
||||
"bin": {
|
||||
"notifkit-create-admin": "scripts/create-admin.mjs",
|
||||
@@ -35,15 +35,15 @@
|
||||
"@testcontainers/redis": "^12.0.4",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/supertest": "^7.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.70.1",
|
||||
"@typescript-eslint/parser": "^8.70.1",
|
||||
"@vitest/coverage-v8": "^2.0.0",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"drizzle-kit": "^0.31.11",
|
||||
"eslint": "^9.0.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.5",
|
||||
"prettier": "^3.3.0",
|
||||
"supertest": "^7.2.2",
|
||||
"lint-staged": "^15.5.2",
|
||||
"prettier": "^3.9.9",
|
||||
"supertest": "^7.3.0",
|
||||
"testcontainers": "^12.0.4",
|
||||
"tsdown": "^0.22.14",
|
||||
"typescript": "^5.8.3",
|
||||
@@ -55,13 +55,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ai-sdk/gateway": {
|
||||
"version": "4.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.28.tgz",
|
||||
"integrity": "sha512-ee9TsNO3mkgHDWmTmJ8Fvltr6PFh52zhrV2+FaKJY3F3iu7kWZi5tCRJCR1HVhhlpj6lHniUb28nLQdPHkA/1Q==",
|
||||
"version": "4.0.94",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.94.tgz",
|
||||
"integrity": "sha512-hcp7GLnMynH6NfMnemgvW9acn7j3WsUe/dHFzX3/fhPit08xhm1aYmDGHMiIT6YwsXVBxkit9TriKb2SGAOg+Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "4.0.3",
|
||||
"@ai-sdk/provider-utils": "5.0.12",
|
||||
"@ai-sdk/provider": "4.0.18",
|
||||
"@ai-sdk/provider-utils": "5.0.49",
|
||||
"@vercel/oidc": "3.2.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -72,9 +72,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ai-sdk/provider": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz",
|
||||
"integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.18.tgz",
|
||||
"integrity": "sha512-+GZJIgz1jk86pwEbb3f1BD2bdoSKyWE4Jg4YUc7NMnMozbWemSKYZcw2F4nMaO5qwsL5A8RmioAKW81YktRpIQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"json-schema": "^0.4.0"
|
||||
@@ -84,15 +84,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ai-sdk/provider-utils": {
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.12.tgz",
|
||||
"integrity": "sha512-bbhlOgHeYwrIGheLkM6fhS8hVger8uFPmcOLg+kxc9EFh7y30XYorWhthlYAgpadO3SJhFZrIcEknN7qEqEVvA==",
|
||||
"version": "5.0.49",
|
||||
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.49.tgz",
|
||||
"integrity": "sha512-T+/H8DCvqJoCqLhltVatS7ffA779Iyk/ZZrgNXkrgv51E0n2Cd6eIdCmtwhCGIKqtbTtwCvTmQdAMv5Z8rOGHQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "4.0.3",
|
||||
"@ai-sdk/provider": "4.0.18",
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@workflow/serde": "4.1.0",
|
||||
"eventsource-parser": "^3.0.8"
|
||||
"eventsource-parser": "^3.0.8",
|
||||
"undici": "^7.29.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
@@ -1534,9 +1535,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ioredis/commands": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
|
||||
"integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-2.0.0.tgz",
|
||||
"integrity": "sha512-vrx0AE/T0h7cRZwfo1M39Cr+ZhZrkf0V8mQN75wucKCxCLD9l/VX6no3gFvrLqD1IlG/1LtzWovqEw3t0Vr9zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
@@ -2775,17 +2776,17 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz",
|
||||
"integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.1.tgz",
|
||||
"integrity": "sha512-nDNrUQ/4ruSNYbu749TRY7cfrzPtoLHEXSNBI8aaNY32LlZCajixqRf3FqcKC4p5Cam4VOHYx/t+i5+nKXvrqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/type-utils": "8.64.0",
|
||||
"@typescript-eslint/utils": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/scope-manager": "8.70.1",
|
||||
"@typescript-eslint/type-utils": "8.70.1",
|
||||
"@typescript-eslint/utils": "8.70.1",
|
||||
"@typescript-eslint/visitor-keys": "8.70.1",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -2798,22 +2799,22 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.64.0",
|
||||
"@typescript-eslint/parser": "^8.70.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz",
|
||||
"integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.1.tgz",
|
||||
"integrity": "sha512-nO974WLllwhSFWQXnMLj6nDGa8f0khKEz1JzpPJ1u7Vm/4X1X6ZHajpoknU4bb41vJyMB0HHVyS2GqdhWfIXZw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/scope-manager": "8.70.1",
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"@typescript-eslint/typescript-estree": "8.70.1",
|
||||
"@typescript-eslint/visitor-keys": "8.70.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2829,14 +2830,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz",
|
||||
"integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.1.tgz",
|
||||
"integrity": "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.64.0",
|
||||
"@typescript-eslint/types": "^8.64.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.70.1",
|
||||
"@typescript-eslint/types": "^8.70.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2851,14 +2852,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz",
|
||||
"integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.1.tgz",
|
||||
"integrity": "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0"
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"@typescript-eslint/visitor-keys": "8.70.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2869,9 +2870,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz",
|
||||
"integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.1.tgz",
|
||||
"integrity": "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2886,15 +2887,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz",
|
||||
"integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.1.tgz",
|
||||
"integrity": "sha512-7zKTnyvaVWqzLZHPFQtX1hVHqgkMC+WebPWakNCSyrQVbIP1AM0L0TlBZtACldIRb6PptI8Odk+jyZ5kP3B1VA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0",
|
||||
"@typescript-eslint/utils": "8.64.0",
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"@typescript-eslint/typescript-estree": "8.70.1",
|
||||
"@typescript-eslint/utils": "8.70.1",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -2911,9 +2912,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz",
|
||||
"integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.1.tgz",
|
||||
"integrity": "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2925,16 +2926,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz",
|
||||
"integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.1.tgz",
|
||||
"integrity": "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.64.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/visitor-keys": "8.64.0",
|
||||
"@typescript-eslint/project-service": "8.70.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.70.1",
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"@typescript-eslint/visitor-keys": "8.70.1",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -2953,16 +2954,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz",
|
||||
"integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.1.tgz",
|
||||
"integrity": "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.64.0",
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/typescript-estree": "8.64.0"
|
||||
"@typescript-eslint/scope-manager": "8.70.1",
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"@typescript-eslint/typescript-estree": "8.70.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2977,13 +2978,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.64.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz",
|
||||
"integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==",
|
||||
"version": "8.70.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.1.tgz",
|
||||
"integrity": "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.64.0",
|
||||
"@typescript-eslint/types": "8.70.1",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3501,6 +3502,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
@@ -3580,14 +3582,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ai": {
|
||||
"version": "7.0.37",
|
||||
"resolved": "https://registry.npmjs.org/ai/-/ai-7.0.37.tgz",
|
||||
"integrity": "sha512-stF+SEQJgKY3Qfe3FwNzqUrehHviOp2l7LemoI8YMOa0Zk5PxKsRNgUk3cHXz0RAue9RRyCAis2fz6LSCP6EKw==",
|
||||
"version": "7.0.116",
|
||||
"resolved": "https://registry.npmjs.org/ai/-/ai-7.0.116.tgz",
|
||||
"integrity": "sha512-gmkVGPzTNPcJixBiG9zUvP3gmvwko85dHiKrxFKK0zcVJIIu1FP6YDo3jna+ZrC2twMAsUPLr5htCwZjCINH3Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/gateway": "4.0.28",
|
||||
"@ai-sdk/provider": "4.0.3",
|
||||
"@ai-sdk/provider-utils": "5.0.12"
|
||||
"@ai-sdk/gateway": "4.0.94",
|
||||
"@ai-sdk/provider": "4.0.18",
|
||||
"@ai-sdk/provider-utils": "5.0.49"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
@@ -4168,6 +4170,7 @@
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5033,10 +5036,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-18.0.4.tgz",
|
||||
"integrity": "sha512-jGt9uI5BIxpVO15kvy+iEFqNAwxYClKWnkptLjSNPnFLC003Imd0ofr3aEVsbpHdSBdSsbcApNIlz4nbyGMwdA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"dotenv": "dist/index.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -5045,9 +5051,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-kit": {
|
||||
"version": "0.31.10",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.10.tgz",
|
||||
"integrity": "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==",
|
||||
"version": "0.31.11",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.11.tgz",
|
||||
"integrity": "sha512-YCYqxTLIB2OCqf6w9/Vef13baBVbwQIPcbT4Y56AfO6B9ajZhDhD8Jkveg/hJvT/iuMloKD/IYYkyMMNhr7Kqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5545,9 +5551,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-orm": {
|
||||
"version": "0.45.2",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz",
|
||||
"integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==",
|
||||
"version": "0.45.3",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.3.tgz",
|
||||
"integrity": "sha512-CAloER21cDdcgZ1OmjrZX82EeRNsdP+y0onX/eyoYDaYzki/adrAa91lzM5jWh7zZPK4qUbw/6LGv9J+A+uizA==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-rds-data": ">=3",
|
||||
@@ -5556,6 +5562,7 @@
|
||||
"@libsql/client": ">=0.10.0",
|
||||
"@libsql/client-wasm": ">=0.10.0",
|
||||
"@neondatabase/serverless": ">=0.10.0",
|
||||
"@netlify/db": ">=0.4.0",
|
||||
"@op-engineering/op-sqlite": ">=2",
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"@planetscale/database": ">=1.13",
|
||||
@@ -5598,6 +5605,9 @@
|
||||
"@neondatabase/serverless": {
|
||||
"optional": true
|
||||
},
|
||||
"@netlify/db": {
|
||||
"optional": true
|
||||
},
|
||||
"@op-engineering/op-sqlite": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -6258,6 +6268,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -6274,6 +6285,7 @@
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
@@ -6451,9 +6463,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-copy": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
|
||||
"integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.1.1.tgz",
|
||||
"integrity": "sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
@@ -6928,9 +6940,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
|
||||
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz",
|
||||
"integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -7584,6 +7596,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -7688,21 +7701,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ioredis": {
|
||||
"version": "5.11.1",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz",
|
||||
"integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-6.0.0.tgz",
|
||||
"integrity": "sha512-f+Dtubxfpf6KYFq7WVXJoOLn0bk4TJrMrN9SzeE+jrWrCWj7XX3fA6vkryafhADX+GMymRxgDJDOI33COkJc0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ioredis/commands": "1.10.0",
|
||||
"@ioredis/commands": "2.0.0",
|
||||
"cluster-key-slot": "1.1.1",
|
||||
"debug": "4.4.3",
|
||||
"denque": "2.1.0",
|
||||
"redis-errors": "1.2.0",
|
||||
"redis-parser": "3.0.0",
|
||||
"standard-as-callback": "2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0"
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -9150,9 +9162,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -9176,75 +9188,70 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "9.14.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz",
|
||||
"integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==",
|
||||
"version": "10.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
|
||||
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pinojs/redact": "^0.4.0",
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-abstract-transport": "^3.0.0",
|
||||
"pino-std-serializers": "^7.0.0",
|
||||
"process-warning": "^5.0.0",
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
"real-require": "^0.2.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"thread-stream": "^3.0.0"
|
||||
"thread-stream": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pino": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-abstract-transport": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
|
||||
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
|
||||
"integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty": {
|
||||
"version": "11.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz",
|
||||
"integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==",
|
||||
"version": "13.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz",
|
||||
"integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.7",
|
||||
"dateformat": "^4.6.3",
|
||||
"fast-copy": "^3.0.2",
|
||||
"fast-copy": "^4.0.0",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"help-me": "^5.0.0",
|
||||
"joycon": "^3.1.1",
|
||||
"minimist": "^1.2.6",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-abstract-transport": "^3.0.0",
|
||||
"pump": "^3.0.0",
|
||||
"readable-stream": "^4.0.0",
|
||||
"secure-json-parse": "^2.4.0",
|
||||
"secure-json-parse": "^4.0.0",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
"strip-json-comments": "^5.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"pino-pretty": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty/node_modules/readable-stream": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||
"node_modules/pino-pretty/node_modules/strip-json-comments": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
|
||||
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"process": "^0.11.10",
|
||||
"string_decoder": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-std-serializers": {
|
||||
@@ -9321,9 +9328,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.9.5",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz",
|
||||
"integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==",
|
||||
"version": "3.9.9",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz",
|
||||
"integrity": "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -9340,6 +9347,7 @@
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
@@ -9372,6 +9380,7 @@
|
||||
"version": "15.1.3",
|
||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz",
|
||||
"integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==",
|
||||
"deprecated": "prom-client has been replaced by @prometheus-io/client",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.4.0",
|
||||
@@ -9647,18 +9656,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/redis-parser": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"redis-errors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
@@ -9996,9 +9993,19 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
||||
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
@@ -10387,6 +10394,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
@@ -10533,6 +10541,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -10616,9 +10625,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/supertest": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz",
|
||||
"integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==",
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.3.0.tgz",
|
||||
"integrity": "sha512-UwwmWq3xLhyU96c521wYNaBg7IqX78Wpj4FKs6Katp6vdklX42zU5ESvX3KeZRLFyO44RwFp4yvxYMNZzCcX9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10773,6 +10782,16 @@
|
||||
"undici": "^8.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/testcontainers/node_modules/undici": {
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz",
|
||||
"integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/text-decoder": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
|
||||
@@ -10797,14 +10816,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz",
|
||||
"integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz",
|
||||
"integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"real-require": "^0.2.0"
|
||||
"real-require": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream/node_modules/real-require": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz",
|
||||
"integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
@@ -10820,9 +10848,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
|
||||
"integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
|
||||
"integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -11678,13 +11706,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-8.7.0.tgz",
|
||||
"integrity": "sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ==",
|
||||
"dev": true,
|
||||
"version": "7.30.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz",
|
||||
"integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=22.19.0"
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
@@ -12611,9 +12638,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
||||
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
|
||||
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@@ -12813,9 +12840,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"version": "4.6.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz",
|
||||
"integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
@@ -12850,6 +12877,15 @@
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
},
|
||||
"packages/mcp/node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"packages/provider-console": {
|
||||
"name": "@notifkit/provider-console",
|
||||
"version": "0.1.1",
|
||||
|
||||
+14
-14
@@ -72,16 +72,16 @@
|
||||
"prepublishOnly": "npm run typecheck && npm run build && npm run build:dashboard"
|
||||
},
|
||||
"dependencies": {
|
||||
"ai": "^7.0.37",
|
||||
"dotenv": "^16.4.5",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"ai": "^7.0.116",
|
||||
"dotenv": "^18.0.4",
|
||||
"drizzle-orm": "^0.45.3",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"pino": "^9.2.0",
|
||||
"pino-pretty": "^11.0.0",
|
||||
"postgres": "^3.4.4",
|
||||
"ioredis": "^6.0.0",
|
||||
"pino": "^10.3.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"postgres": "^3.4.9",
|
||||
"prom-client": "^15.1.3",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "^4.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
@@ -90,15 +90,15 @@
|
||||
"@testcontainers/redis": "^12.0.4",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/supertest": "^7.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.70.1",
|
||||
"@typescript-eslint/parser": "^8.70.1",
|
||||
"@vitest/coverage-v8": "^2.0.0",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"drizzle-kit": "^0.31.11",
|
||||
"eslint": "^9.0.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.5",
|
||||
"prettier": "^3.3.0",
|
||||
"supertest": "^7.2.2",
|
||||
"lint-staged": "^15.5.2",
|
||||
"prettier": "^3.9.9",
|
||||
"supertest": "^7.3.0",
|
||||
"testcontainers": "^12.0.4",
|
||||
"tsdown": "^0.22.14",
|
||||
"typescript": "^5.8.3",
|
||||
|
||||
@@ -23,6 +23,244 @@ services:
|
||||
cpus: "${REDIS_CPUS:-1.5}"
|
||||
memory: "${REDIS_MEMORY:-1G}"
|
||||
|
||||
# ── Descriptive Role-Based Services ──────────────────────────────────────────
|
||||
|
||||
api-server-1:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${API_SERVER_1_SERVICES:-api}
|
||||
ports:
|
||||
- "35678:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
api-server-2:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${API_SERVER_2_SERVICES:-api}
|
||||
ports:
|
||||
- "35679:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
pipeline-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${PIPELINE_WORKER_SERVICES:-enricher,engine,scheduler}
|
||||
ports:
|
||||
- "35680:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
delivery-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${DELIVERY_WORKER_SERVICES:-delivery}
|
||||
ports:
|
||||
- "35681:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
scheduler-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SCHEDULER_WORKER_SERVICES:-scheduler}
|
||||
ports:
|
||||
- "35682:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
enricher-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${ENRICHER_WORKER_SERVICES:-enricher}
|
||||
ports:
|
||||
- "35683:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
engine-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${ENGINE_WORKER_SERVICES:-engine}
|
||||
ports:
|
||||
- "35684:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
monolith-server:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${MONOLITH_SERVER_SERVICES:-all}
|
||||
ports:
|
||||
- "35678:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
worker-node:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${WORKER_NODE_SERVICES:-enricher,engine,delivery,scheduler}
|
||||
ports:
|
||||
- "35679:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
# ── Generic Server Definitions (Backwards Compatibility) ─────────────────────
|
||||
|
||||
server1:
|
||||
build:
|
||||
context: ..
|
||||
@@ -36,6 +274,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER1_SERVICES:-all}
|
||||
ports:
|
||||
- "35678:3000"
|
||||
@@ -45,8 +284,8 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-1.0}"
|
||||
memory: "${SERVER_MEMORY:-1G}"
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server2:
|
||||
build:
|
||||
@@ -61,6 +300,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER2_SERVICES:-all}
|
||||
ports:
|
||||
- "35679:3000"
|
||||
@@ -70,8 +310,8 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-1.0}"
|
||||
memory: "${SERVER_MEMORY:-1G}"
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server3:
|
||||
build:
|
||||
@@ -86,6 +326,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER3_SERVICES:-all}
|
||||
ports:
|
||||
- "35680:3000"
|
||||
@@ -95,8 +336,8 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-1.0}"
|
||||
memory: "${SERVER_MEMORY:-1G}"
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server4:
|
||||
build:
|
||||
@@ -111,6 +352,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER4_SERVICES:-all}
|
||||
ports:
|
||||
- "35681:3000"
|
||||
@@ -120,8 +362,8 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-1.0}"
|
||||
memory: "${SERVER_MEMORY:-1G}"
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server5:
|
||||
build:
|
||||
@@ -136,6 +378,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER5_SERVICES:-all}
|
||||
ports:
|
||||
- "35682:3000"
|
||||
@@ -145,5 +388,57 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-1.0}"
|
||||
memory: "${SERVER_MEMORY:-1G}"
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server6:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER6_SERVICES:-all}
|
||||
ports:
|
||||
- "35683:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
server7:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: profiling/Dockerfile
|
||||
environment:
|
||||
DATABASE_URL: postgres://notifkit:password@db:5432/notifkit
|
||||
REDIS_URL: redis://redis:6379
|
||||
ADMIN_API_KEY: perf-admin-key
|
||||
LOG_LEVEL: warn
|
||||
HOST: 0.0.0.0
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-100}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-5}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
SERVICES: ${SERVER7_SERVICES:-all}
|
||||
ports:
|
||||
- "35684:3000"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: "${SERVER_CPUS:-2.0}"
|
||||
memory: "${SERVER_MEMORY:-2G}"
|
||||
|
||||
@@ -36,6 +36,7 @@ services:
|
||||
WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-50}
|
||||
DB_MAX_CONNECTIONS: ${DB_MAX_CONNECTIONS:-20}
|
||||
PROVIDER_LATENCY_MS: ${PROVIDER_LATENCY_MS:-0}
|
||||
RATE_LIMIT_PER_HOUR: ${RATE_LIMIT_PER_HOUR:-10000000}
|
||||
ports:
|
||||
- "35678:3000"
|
||||
depends_on:
|
||||
|
||||
+49
-34
@@ -58,6 +58,7 @@ class ProfilingTransport implements Transport {
|
||||
}
|
||||
|
||||
import http from "node:http";
|
||||
import cluster from "node:cluster";
|
||||
|
||||
const rawServices = process.env.SERVICES?.trim();
|
||||
const services: (
|
||||
@@ -69,39 +70,53 @@ const services: (
|
||||
)[])
|
||||
: ["api", "enricher", "engine", "delivery", "scheduler"];
|
||||
|
||||
const server = new NotifkitServer({
|
||||
services,
|
||||
redisUrl: process.env.REDIS_URL || "redis://localhost:6379",
|
||||
databaseUrl: process.env.DATABASE_URL || "postgres://notifkit:password@localhost:5432/notifkit",
|
||||
logLevel: (process.env.LOG_LEVEL as "info" | "warn" | "error" | "debug") || "info",
|
||||
autoMigrate: false,
|
||||
port: PORT,
|
||||
workerConcurrency,
|
||||
providers: [new ProfilingTransport()],
|
||||
});
|
||||
const isApiOnly = services.length === 1 && services[0] === "api";
|
||||
const clusterWorkers = isApiOnly ? 2 : 1;
|
||||
|
||||
server
|
||||
.start()
|
||||
.then(() => {
|
||||
console.log(
|
||||
`🚀 Profiling Server started on port ${PORT} [services: ${services.join(",")}] (concurrency: ${workerConcurrency})`,
|
||||
);
|
||||
|
||||
// If API is not enabled on this worker node, expose a lightweight health responder so orchestration succeeds
|
||||
if (!services.includes("api")) {
|
||||
const healthServer = http.createServer((req, res) => {
|
||||
if (req.url === "/health") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok", services, isWorkerOnly: true }));
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
healthServer.listen(PORT, "0.0.0.0");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to start profiling server:", err);
|
||||
process.exit(1);
|
||||
if (clusterWorkers > 1 && cluster.isPrimary) {
|
||||
console.log(`⚡ Forking ${clusterWorkers} API worker processes on port ${PORT}...`);
|
||||
for (let i = 0; i < clusterWorkers; i++) {
|
||||
cluster.fork();
|
||||
}
|
||||
cluster.on("exit", (w) => {
|
||||
console.warn(`Worker ${w.process.pid} exited, spawning replacement...`);
|
||||
cluster.fork();
|
||||
});
|
||||
} else {
|
||||
const server = new NotifkitServer({
|
||||
services,
|
||||
redisUrl: process.env.REDIS_URL || "redis://localhost:6379",
|
||||
databaseUrl: process.env.DATABASE_URL || "postgres://notifkit:password@localhost:5432/notifkit",
|
||||
logLevel: (process.env.LOG_LEVEL as "info" | "warn" | "error" | "debug") || "info",
|
||||
autoMigrate: false,
|
||||
port: PORT,
|
||||
workerConcurrency,
|
||||
providers: [new ProfilingTransport()],
|
||||
});
|
||||
|
||||
server
|
||||
.start()
|
||||
.then(() => {
|
||||
console.log(
|
||||
`🚀 Profiling Server started on port ${PORT} [services: ${services.join(",")}] (pid: ${process.pid})`,
|
||||
);
|
||||
|
||||
// If API is not enabled on this worker node, expose a lightweight health responder so orchestration succeeds
|
||||
if (!services.includes("api")) {
|
||||
const healthServer = http.createServer((req, res) => {
|
||||
if (req.url === "/health") {
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ status: "ok", services, isWorkerOnly: true }));
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
healthServer.listen(PORT, "0.0.0.0");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to start profiling server:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { setGlobalDispatcher, Agent } from "undici";
|
||||
// Configure high-concurrency connection pool for client benchmark
|
||||
setGlobalDispatcher(
|
||||
new Agent({
|
||||
connections: 300,
|
||||
connections: 2000,
|
||||
pipelining: 1,
|
||||
keepAliveTimeout: 30000,
|
||||
keepAliveMaxTimeout: 60000,
|
||||
@@ -16,6 +16,7 @@ export interface DistributedLoadTestOptions {
|
||||
serverCount?: number;
|
||||
durationSeconds?: number;
|
||||
concurrency?: number;
|
||||
servicesCount?: number;
|
||||
adminApiKey?: string;
|
||||
dbPort?: number;
|
||||
serverCpus?: string;
|
||||
@@ -28,6 +29,7 @@ export interface DistributedLoadTestOptions {
|
||||
dbMaxConnections?: number;
|
||||
providerLatencyMs?: number | string;
|
||||
serverServices?: string[];
|
||||
serverNodes?: Array<{ name: string; port: number; services?: string }>;
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
@@ -38,6 +40,7 @@ export interface DistributedBenchmarkResult {
|
||||
workerConcurrency: number;
|
||||
durationSec: number;
|
||||
concurrency: number;
|
||||
servicesCount?: number;
|
||||
requestsSent: number;
|
||||
successCount: number;
|
||||
failCount: number;
|
||||
@@ -87,14 +90,29 @@ export async function runDistributedBenchmark(
|
||||
});
|
||||
}
|
||||
|
||||
const services = [
|
||||
"db",
|
||||
"redis",
|
||||
...Array.from({ length: serverCount }, (_, i) => `server${i + 1}`),
|
||||
];
|
||||
// If explicit serverNodes are provided, use their service names and custom env vars
|
||||
const nodes =
|
||||
options.serverNodes && options.serverNodes.length > 0
|
||||
? options.serverNodes
|
||||
: Array.from({ length: serverCount }, (_, i) => ({
|
||||
name: `server${i + 1}`,
|
||||
port: 35678 + i,
|
||||
services: options.serverServices?.[i],
|
||||
}));
|
||||
|
||||
for (const node of nodes) {
|
||||
if (node.services) {
|
||||
const envKey = `${node.name.toUpperCase().replace(/-/g, "_")}_SERVICES`;
|
||||
composeEnv[envKey] = node.services;
|
||||
}
|
||||
}
|
||||
|
||||
const effectiveServerCount = nodes.length;
|
||||
|
||||
const services = ["db", "redis", ...nodes.map((n) => n.name)];
|
||||
|
||||
console.log(
|
||||
`🚀 Booting distributed profiling environment (${serverCount} Server Instances: ${serverCpus} vCPU each, ${serverMemory} RAM | DB: ${composeEnv.DB_CPUS ?? "2.0"} vCPU | Redis: ${composeEnv.REDIS_CPUS ?? "1.5"} vCPU | Provider Latency: ${providerLatency}ms)...`,
|
||||
`🚀 Booting distributed profiling environment (${effectiveServerCount} Server Instances: [${nodes.map((n) => n.name).join(", ")}] | ${serverCpus} vCPU each, ${serverMemory} RAM | DB: ${composeEnv.DB_CPUS ?? "2.0"} vCPU | Redis: ${composeEnv.REDIS_CPUS ?? "1.5"} vCPU | Provider Latency: ${providerLatency}ms)...`,
|
||||
);
|
||||
|
||||
const environment = await new DockerComposeEnvironment(".", "docker-compose.distributed.yml")
|
||||
@@ -102,14 +120,13 @@ export async function runDistributedBenchmark(
|
||||
.withBuild()
|
||||
.up(services);
|
||||
|
||||
const serverPorts = Array.from({ length: serverCount }, (_, i) => 35678 + i);
|
||||
const serverUrls = serverPorts.map((p) => `http://localhost:${p}`);
|
||||
const serverUrls = nodes.map((n) => `http://localhost:${n.port}`);
|
||||
const dbUrl = `postgres://notifkit:password@localhost:${dbPort}/notifkit`;
|
||||
|
||||
const apiLatencies: number[] = [];
|
||||
const deliveryLatencies: number[] = [];
|
||||
|
||||
console.log(`⏳ Waiting for ${serverCount} server instances health checks...`);
|
||||
console.log(`⏳ Waiting for ${effectiveServerCount} server instances health checks...`);
|
||||
const apiUrls: string[] = [];
|
||||
for (const url of serverUrls) {
|
||||
let ready = false;
|
||||
@@ -135,8 +152,8 @@ export async function runDistributedBenchmark(
|
||||
let lastDeliveryTimestamp: number | null = null;
|
||||
|
||||
// Hook into container logs of all active servers
|
||||
for (let i = 1; i <= serverCount; i++) {
|
||||
const serverContainer = environment.getContainer(`server${i}-1`);
|
||||
for (const node of nodes) {
|
||||
const serverContainer = environment.getContainer(`${node.name}-1`);
|
||||
const stream = await serverContainer.logs();
|
||||
let logBuffer = "";
|
||||
stream.on("data", (chunk) => {
|
||||
@@ -213,8 +230,28 @@ export async function runDistributedBenchmark(
|
||||
}),
|
||||
});
|
||||
|
||||
const MICROSERVICES = [
|
||||
{ name: "order-service", priority: "critical", event: "order_confirmation" },
|
||||
{ name: "payments-service", priority: "critical", event: "payment_receipt" },
|
||||
{ name: "billing-service", priority: "normal", event: "invoice_ready" },
|
||||
{ name: "auth-service", priority: "critical", event: "password_reset" },
|
||||
{ name: "shipping-service", priority: "normal", event: "tracking_update" },
|
||||
{ name: "account-service", priority: "normal", event: "security_alert" },
|
||||
{ name: "inventory-service", priority: "low", event: "stock_replenished" },
|
||||
{ name: "fraud-service", priority: "critical", event: "suspicious_login" },
|
||||
{ name: "marketing-service", priority: "low", event: "promotional_blast" },
|
||||
{ name: "support-service", priority: "normal", event: "ticket_reply" },
|
||||
];
|
||||
|
||||
const activeServicesCount = options.servicesCount ?? (concurrency <= 10 ? concurrency : 10);
|
||||
const activeServices = MICROSERVICES.slice(
|
||||
0,
|
||||
Math.min(activeServicesCount, MICROSERVICES.length),
|
||||
);
|
||||
const workersPerService = Math.max(1, Math.round(concurrency / activeServices.length));
|
||||
|
||||
console.log(
|
||||
`\n🔥 Starting Distributed Load Generation: ${concurrency} virtual users across ${targetApiUrls.length} API ingress node(s) for ${durationSec}s`,
|
||||
`\n🔥 Starting Distributed Load Generation: ${activeServices.length} microservices blasting concurrently (${concurrency} parallel workers, ~${workersPerService} workers/service) across ${targetApiUrls.length} API ingress node(s) for ${durationSec}s`,
|
||||
);
|
||||
|
||||
let requestsSent = 0;
|
||||
@@ -224,8 +261,19 @@ export async function runDistributedBenchmark(
|
||||
const startTime = Date.now();
|
||||
const endTime = startTime + durationSec * 1000;
|
||||
|
||||
const progressInterval = setInterval(() => {
|
||||
const elapsedSec = ((Date.now() - startTime) / 1000).toFixed(0);
|
||||
const remainingSec = Math.max(0, Math.round((endTime - Date.now()) / 1000));
|
||||
const curReqRate = (requestsSent / Math.max(1, Number(elapsedSec))).toFixed(1);
|
||||
const curDeliv = deliveryLatencies.length;
|
||||
console.log(
|
||||
`⚡ Blasting: ${elapsedSec}s elapsed (${remainingSec}s remaining) | Sent: ${requestsSent} (${curReqRate} req/s) | Delivered so far: ${curDeliv} msgs`,
|
||||
);
|
||||
}, 10000);
|
||||
|
||||
async function worker(workerIndex: number) {
|
||||
const targetUrl = targetApiUrls[workerIndex % targetApiUrls.length]!;
|
||||
const svc = activeServices[workerIndex % activeServices.length]!;
|
||||
while (Date.now() < endTime) {
|
||||
requestsSent++;
|
||||
try {
|
||||
@@ -240,8 +288,13 @@ export async function runDistributedBenchmark(
|
||||
user: "perf-user-1",
|
||||
template: "perf-email",
|
||||
channels: ["email"],
|
||||
priority: "critical",
|
||||
data: { name: "Load Tester", requestTime: reqStart },
|
||||
priority: svc.priority,
|
||||
data: {
|
||||
name: "Load Tester",
|
||||
service: svc.name,
|
||||
event: svc.event,
|
||||
requestTime: reqStart,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -260,6 +313,7 @@ export async function runDistributedBenchmark(
|
||||
|
||||
const workers = Array.from({ length: concurrency }).map((_, i) => worker(i));
|
||||
await Promise.all(workers);
|
||||
clearInterval(progressInterval);
|
||||
|
||||
const activeWindowSec = durationSec;
|
||||
const ingestionThroughput = (requestsSent / activeWindowSec).toFixed(2);
|
||||
@@ -270,9 +324,10 @@ export async function runDistributedBenchmark(
|
||||
|
||||
const flushStart = Date.now();
|
||||
let lastLogged = 0;
|
||||
const flushTimeoutMs = Math.max(1800000, durationSec * 3000);
|
||||
|
||||
while (deliveryLatencies.length < successCount) {
|
||||
if (Date.now() - flushStart > 600000) {
|
||||
if (Date.now() - flushStart > flushTimeoutMs) {
|
||||
console.log(
|
||||
`⚠️ Flush timeout! Delivered ${deliveryLatencies.length}/${successCount} messages.`,
|
||||
);
|
||||
@@ -315,7 +370,7 @@ export async function runDistributedBenchmark(
|
||||
console.log(" 📊 LOAD TEST RESULTS 📊 ");
|
||||
console.log("=================================");
|
||||
console.log(`Topology: ${serverCount} Distributed Servers`);
|
||||
console.log(`Generation Window: ${activeWindowSec}s (${concurrency} virtual users)`);
|
||||
console.log(`Generation Window: ${activeWindowSec}s (${concurrency} microservice callers)`);
|
||||
console.log(`Total Ingestion: ${requestsSent} requests (${ingestionThroughput} req/sec)`);
|
||||
console.log(
|
||||
`Accepted (202): ${successCount} (${((successCount / requestsSent) * 100).toFixed(1)}%)`,
|
||||
@@ -356,6 +411,7 @@ export async function runDistributedBenchmark(
|
||||
workerConcurrency,
|
||||
durationSec: activeWindowSec,
|
||||
concurrency,
|
||||
servicesCount: activeServices.length,
|
||||
requestsSent,
|
||||
successCount,
|
||||
failCount,
|
||||
|
||||
@@ -16,8 +16,10 @@ export interface BudgetTier {
|
||||
workerConcurrency: number;
|
||||
dbMaxConnections: number;
|
||||
concurrency: number;
|
||||
servicesCount?: number;
|
||||
description: string;
|
||||
serverServices?: string[];
|
||||
serverNodes?: Array<{ name: string; port: number; services?: string }>;
|
||||
awsTopology?: string;
|
||||
awsCostDetails?: string;
|
||||
}
|
||||
@@ -27,110 +29,191 @@ const DEFAULT_BUDGET_TIERS: BudgetTier[] = [
|
||||
budget: "$20/mo",
|
||||
monthlyCost: 20,
|
||||
serverCount: 1,
|
||||
serverCpus: "1.0",
|
||||
serverMemory: "1G",
|
||||
serverCpus: "1.5",
|
||||
serverMemory: "1.5G",
|
||||
dbCpus: "0.5",
|
||||
dbMemory: "512M",
|
||||
redisCpus: "0.5",
|
||||
redisMemory: "256M",
|
||||
workerConcurrency: 100,
|
||||
workerConcurrency: 80,
|
||||
dbMaxConnections: 5,
|
||||
concurrency: 150,
|
||||
concurrency: 100,
|
||||
servicesCount: 5,
|
||||
description: "1x Monolith on EC2 t4g.small",
|
||||
awsTopology: "1x EC2 t4g.small (Monolith)",
|
||||
awsCostDetails:
|
||||
"EC2 t4g.small (2 vCPU/2G: $12.26) + 30GB gp3 ($2.40) + IPv4/Net ($5.34) = ~$20.00/mo",
|
||||
serverServices: ["api,enricher,engine,delivery,scheduler"],
|
||||
serverNodes: [
|
||||
{ name: "monolith-server", port: 35678, services: "api,enricher,engine,delivery,scheduler" },
|
||||
],
|
||||
},
|
||||
{
|
||||
budget: "$40/mo",
|
||||
monthlyCost: 40,
|
||||
serverCount: 2,
|
||||
serverCpus: "1.0",
|
||||
serverMemory: "1G",
|
||||
serverCpus: "2.0",
|
||||
serverMemory: "2G",
|
||||
dbCpus: "1.0",
|
||||
dbMemory: "1G",
|
||||
redisCpus: "0.5",
|
||||
redisMemory: "512M",
|
||||
workerConcurrency: 200,
|
||||
workerConcurrency: 160,
|
||||
dbMaxConnections: 8,
|
||||
concurrency: 300,
|
||||
concurrency: 180,
|
||||
servicesCount: 6,
|
||||
description: "1x API + 1x Worker (RDS db.t4g.micro + ElastiCache)",
|
||||
awsTopology: "1x EC2 t4g.small + RDS db.t4g.micro + ElastiCache t4g.micro",
|
||||
awsCostDetails:
|
||||
"EC2 t4g.small ($13.86) + RDS db.t4g.micro ($13.98) + ElastiCache Valkey ($9.34) + Net ($2.82) = ~$40.00/mo",
|
||||
serverServices: ["api", "enricher,engine,delivery,scheduler"],
|
||||
serverNodes: [
|
||||
{ name: "api-server-1", port: 35678, services: "api" },
|
||||
{ name: "worker-node", port: 35679, services: "enricher,engine,delivery,scheduler" },
|
||||
],
|
||||
},
|
||||
{
|
||||
budget: "$60/mo",
|
||||
monthlyCost: 60,
|
||||
serverCount: 3,
|
||||
serverCpus: "1.0",
|
||||
serverMemory: "1G",
|
||||
serverCpus: "2.0",
|
||||
serverMemory: "2G",
|
||||
dbCpus: "1.0",
|
||||
dbMemory: "1G",
|
||||
redisCpus: "1.0",
|
||||
redisMemory: "1G",
|
||||
workerConcurrency: 300,
|
||||
workerConcurrency: 220,
|
||||
dbMaxConnections: 10,
|
||||
concurrency: 300,
|
||||
concurrency: 240,
|
||||
servicesCount: 8,
|
||||
description: "1x API + 1x Pipeline + 1x Delivery (Decoupled Compute)",
|
||||
awsTopology: "2x EC2 t4g.small + RDS db.t4g.micro + ElastiCache t4g.micro + ALB",
|
||||
awsCostDetails:
|
||||
"2x EC2 t4g.small ($27.72) + RDS db.t4g.micro ($13.98) + ElastiCache Valkey ($9.34) + ALB/Net ($8.96) = ~$60.00/mo",
|
||||
serverServices: ["api", "enricher,engine,scheduler", "delivery"],
|
||||
serverNodes: [
|
||||
{ name: "api-server-1", port: 35678, services: "api" },
|
||||
{ name: "pipeline-worker", port: 35680, services: "enricher,engine,scheduler" },
|
||||
{ name: "delivery-worker", port: 35681, services: "delivery" },
|
||||
],
|
||||
},
|
||||
{
|
||||
budget: "$80/mo",
|
||||
monthlyCost: 80,
|
||||
serverCount: 3,
|
||||
serverCpus: "1.0",
|
||||
serverMemory: "1G",
|
||||
serverCpus: "2.0",
|
||||
serverMemory: "2G",
|
||||
dbCpus: "2.0",
|
||||
dbMemory: "2G",
|
||||
redisCpus: "1.0",
|
||||
redisMemory: "1G",
|
||||
workerConcurrency: 400,
|
||||
workerConcurrency: 280,
|
||||
dbMaxConnections: 15,
|
||||
concurrency: 400,
|
||||
concurrency: 280,
|
||||
servicesCount: 8,
|
||||
description: "1x API + 1x Pipeline + 1x Delivery (RDS db.t4g.small 2G)",
|
||||
awsTopology: "3x EC2 t4g.small + RDS db.t4g.small (2G) + ElastiCache t4g.micro",
|
||||
awsCostDetails:
|
||||
"3x EC2 t4g.small ($41.58) + RDS db.t4g.small ($27.70) + ElastiCache Valkey ($9.34) + Net ($1.38) = ~$80.00/mo",
|
||||
serverServices: ["api", "enricher,engine,scheduler", "delivery"],
|
||||
serverNodes: [
|
||||
{ name: "api-server-1", port: 35678, services: "api" },
|
||||
{ name: "pipeline-worker", port: 35680, services: "enricher,engine,scheduler" },
|
||||
{ name: "delivery-worker", port: 35681, services: "delivery" },
|
||||
],
|
||||
},
|
||||
{
|
||||
budget: "$100/mo",
|
||||
monthlyCost: 100,
|
||||
serverCount: 4,
|
||||
serverCpus: "1.0",
|
||||
serverMemory: "1G",
|
||||
serverCpus: "2.0",
|
||||
serverMemory: "2G",
|
||||
dbCpus: "2.0",
|
||||
dbMemory: "2G",
|
||||
redisCpus: "1.5",
|
||||
redisMemory: "1.5G",
|
||||
workerConcurrency: 450,
|
||||
workerConcurrency: 400,
|
||||
dbMaxConnections: 20,
|
||||
concurrency: 500,
|
||||
description: "1x API + 1x Pipeline + 2x Delivery (2x Scaled Workers)",
|
||||
concurrency: 350,
|
||||
servicesCount: 10,
|
||||
description: "1x API + 1x Enricher + 1x Engine + 1x Delivery (Decoupled Pipeline)",
|
||||
awsTopology: "4x EC2 t4g.small + RDS db.t4g.small + ElastiCache cache.t4g.small",
|
||||
awsCostDetails:
|
||||
"4x EC2 t4g.small ($53.84) + RDS db.t4g.small ($28.27) + ElastiCache t4g.small ($18.69) = ~$100.80/mo",
|
||||
serverServices: ["api", "enricher,scheduler", "engine", "delivery"],
|
||||
serverNodes: [
|
||||
{ name: "api-server-1", port: 35678, services: "api" },
|
||||
{ name: "enricher-worker", port: 35683, services: "enricher,scheduler" },
|
||||
{ name: "engine-worker", port: 35684, services: "engine" },
|
||||
{ name: "delivery-worker", port: 35681, services: "delivery" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export interface MetricStats {
|
||||
mean: number;
|
||||
median: number;
|
||||
min: number;
|
||||
max: number;
|
||||
stdDev: number;
|
||||
}
|
||||
|
||||
export interface TierAggregatedStats {
|
||||
tier: BudgetTier;
|
||||
runs: DistributedBenchmarkResult[];
|
||||
ingestionThroughput: MetricStats;
|
||||
activeDeliveryRate: MetricStats;
|
||||
totalDeliveryRate: MetricStats;
|
||||
apiP50: MetricStats;
|
||||
apiP95: MetricStats;
|
||||
apiP99: MetricStats;
|
||||
deliveryP50: MetricStats;
|
||||
deliveryP95: MetricStats;
|
||||
deliveryP99: MetricStats;
|
||||
costEfficiency: MetricStats;
|
||||
}
|
||||
|
||||
export function calculateStats(values: number[]): MetricStats {
|
||||
if (values.length === 0) {
|
||||
return { mean: 0, median: 0, min: 0, max: 0, stdDev: 0 };
|
||||
}
|
||||
const sorted = [...values].sort((a, b) => a - b);
|
||||
const sum = sorted.reduce((acc, v) => acc + v, 0);
|
||||
const mean = sum / sorted.length;
|
||||
const mid = Math.floor(sorted.length / 2);
|
||||
const median = sorted.length % 2 !== 0 ? sorted[mid]! : (sorted[mid - 1]! + sorted[mid]!) / 2;
|
||||
const min = sorted[0]!;
|
||||
const max = sorted[sorted.length - 1]!;
|
||||
const variance = sorted.reduce((acc, v) => acc + Math.pow(v - mean, 2), 0) / sorted.length;
|
||||
const stdDev = Math.sqrt(variance);
|
||||
|
||||
return {
|
||||
mean: Number(mean.toFixed(2)),
|
||||
median: Number(median.toFixed(2)),
|
||||
min: Number(min.toFixed(2)),
|
||||
max: Number(max.toFixed(2)),
|
||||
stdDev: Number(stdDev.toFixed(2)),
|
||||
};
|
||||
}
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
let durationSeconds = 10;
|
||||
let providerLatencyMs: number | string | undefined = process.env.PROVIDER_LATENCY_MS || "60-150";
|
||||
let durationSeconds = 600; // 10 minutes default
|
||||
let runs = 3; // 3 runs default
|
||||
let providerLatencyMs: number | string | undefined = process.env.PROVIDER_LATENCY_MS || "150-250";
|
||||
let tiers = DEFAULT_BUDGET_TIERS;
|
||||
|
||||
for (const arg of args) {
|
||||
if (arg.startsWith("--duration=")) {
|
||||
durationSeconds = parseInt(arg.split("=")[1]!, 10);
|
||||
} else if (arg.startsWith("--runs=") || arg.startsWith("--iterations=")) {
|
||||
runs = parseInt(arg.split("=")[1]!, 10);
|
||||
} else if (arg === "--quick") {
|
||||
durationSeconds = 5;
|
||||
runs = 1;
|
||||
} else if (arg.startsWith("--latency=")) {
|
||||
const val = arg.split("=")[1]!;
|
||||
providerLatencyMs = val.includes("-") ? val : parseInt(val, 10);
|
||||
@@ -150,67 +233,173 @@ function parseArgs() {
|
||||
}
|
||||
}
|
||||
|
||||
return { durationSeconds, providerLatencyMs, tiers };
|
||||
return { durationSeconds, runs, providerLatencyMs, tiers };
|
||||
}
|
||||
|
||||
function printSummaryTable(results: Array<{ tier: BudgetTier; res: DistributedBenchmarkResult }>) {
|
||||
function printTierBreakdown(stat: TierAggregatedStats, runsCount: number) {
|
||||
console.log("\n" + "=".repeat(170));
|
||||
console.log(
|
||||
" 💰 NOTIFKIT AWS BUDGET SCALING BENCHMARK SUMMARY 💰",
|
||||
` 📊 TIER STATISTICAL BREAKDOWN: ${stat.tier.budget} [${stat.tier.description}] across ${runsCount} Runs (${stat.runs[0]?.durationSec ?? 600}s each)`,
|
||||
);
|
||||
console.log("=".repeat(170));
|
||||
|
||||
const headers = [
|
||||
"Budget Tier",
|
||||
"AWS Architecture & Topology",
|
||||
"Clients",
|
||||
"Ingestion",
|
||||
"Active Deliv",
|
||||
"Wall Drain",
|
||||
"API p95",
|
||||
"Deliv p95",
|
||||
"Cost Efficiency",
|
||||
"Metric",
|
||||
...stat.runs.map((_, i) => `Run ${i + 1}`),
|
||||
"Avg (Mean)",
|
||||
"Median",
|
||||
"Min",
|
||||
"Max",
|
||||
"StdDev",
|
||||
];
|
||||
|
||||
const colWidths = [13, 62, 9, 14, 15, 14, 10, 11, 15];
|
||||
const colWidths = [32, ...stat.runs.map(() => 14), 14, 14, 14, 14, 12];
|
||||
|
||||
const formatRow = (cols: string[]) =>
|
||||
cols.map((col, idx) => col.padEnd(colWidths[idx] ?? 14)).join(" | ");
|
||||
|
||||
console.log(formatRow(headers));
|
||||
console.log("-".repeat(170));
|
||||
|
||||
const rows: Array<{ label: string; values: number[]; s: MetricStats; unit: string }> = [
|
||||
{
|
||||
label: "Ingestion Throughput",
|
||||
values: stat.runs.map((r) => r.ingestionThroughput),
|
||||
s: stat.ingestionThroughput,
|
||||
unit: " req/s",
|
||||
},
|
||||
{
|
||||
label: "Active Delivery Rate",
|
||||
values: stat.runs.map((r) => r.activeDeliveryRate),
|
||||
s: stat.activeDeliveryRate,
|
||||
unit: " msg/s",
|
||||
},
|
||||
{
|
||||
label: "Wall Drain Rate",
|
||||
values: stat.runs.map((r) => r.totalDeliveryRate),
|
||||
s: stat.totalDeliveryRate,
|
||||
unit: " msg/s",
|
||||
},
|
||||
{
|
||||
label: "API Latency p50",
|
||||
values: stat.runs.map((r) => r.apiP50),
|
||||
s: stat.apiP50,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "API Latency p95",
|
||||
values: stat.runs.map((r) => r.apiP95),
|
||||
s: stat.apiP95,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "API Latency p99",
|
||||
values: stat.runs.map((r) => r.apiP99),
|
||||
s: stat.apiP99,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "Delivery Latency p50",
|
||||
values: stat.runs.map((r) => r.deliveryP50),
|
||||
s: stat.deliveryP50,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "Delivery Latency p95",
|
||||
values: stat.runs.map((r) => r.deliveryP95),
|
||||
s: stat.deliveryP95,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "Delivery Latency p99",
|
||||
values: stat.runs.map((r) => r.deliveryP99),
|
||||
s: stat.deliveryP99,
|
||||
unit: "ms",
|
||||
},
|
||||
{
|
||||
label: "Cost Efficiency",
|
||||
values: stat.runs.map((r) =>
|
||||
Number((r.activeDeliveryRate / stat.tier.monthlyCost).toFixed(2)),
|
||||
),
|
||||
s: stat.costEfficiency,
|
||||
unit: " msg/s/$",
|
||||
},
|
||||
];
|
||||
|
||||
for (const row of rows) {
|
||||
const cols = [
|
||||
row.label,
|
||||
...row.values.map((v) => `${v}${row.unit}`),
|
||||
`${row.s.mean}${row.unit}`,
|
||||
`${row.s.median}${row.unit}`,
|
||||
`${row.s.min}${row.unit}`,
|
||||
`${row.s.max}${row.unit}`,
|
||||
`${row.s.stdDev}`,
|
||||
];
|
||||
console.log(formatRow(cols));
|
||||
}
|
||||
|
||||
console.log("=".repeat(170) + "\n");
|
||||
}
|
||||
|
||||
function printSummaryTable(allStats: TierAggregatedStats[], runsCount: number) {
|
||||
console.log("\n" + "=".repeat(215));
|
||||
console.log(
|
||||
` 💰 NOTIFKIT AWS BUDGET SCALING BENCHMARK SUMMARY (${runsCount} RUNS AGGREGATED) 💰`,
|
||||
);
|
||||
console.log("=".repeat(215));
|
||||
|
||||
const headers = [
|
||||
"Budget Tier",
|
||||
"AWS Architecture & Topology",
|
||||
"Ingestion (Avg ± Std)",
|
||||
"Active Deliv (Avg / Med / Max)",
|
||||
"Wall Drain (Avg)",
|
||||
"API p95 (Avg / Med)",
|
||||
"Deliv p95 (Avg / Med)",
|
||||
"Cost Eff (Avg)",
|
||||
];
|
||||
|
||||
const colWidths = [13, 56, 26, 34, 18, 22, 24, 16];
|
||||
|
||||
const formatRow = (cols: string[]) =>
|
||||
cols.map((col, idx) => col.padEnd(colWidths[idx]!)).join(" | ");
|
||||
|
||||
console.log(formatRow(headers));
|
||||
console.log("-".repeat(170));
|
||||
|
||||
for (const { tier, res } of results) {
|
||||
const msgsPerDollar = (res.activeDeliveryRate / tier.monthlyCost).toFixed(1) + " msg/s/$";
|
||||
console.log("-".repeat(215));
|
||||
|
||||
for (const stat of allStats) {
|
||||
const row = [
|
||||
tier.budget,
|
||||
tier.description,
|
||||
`${res.concurrency} VUs`,
|
||||
`${res.ingestionThroughput} req/s`,
|
||||
`${res.activeDeliveryRate} msg/s`,
|
||||
`${res.totalDeliveryRate} msg/s`,
|
||||
`${res.apiP95}ms`,
|
||||
`${res.deliveryP95}ms`,
|
||||
msgsPerDollar,
|
||||
stat.tier.budget,
|
||||
stat.tier.description,
|
||||
`${stat.ingestionThroughput.mean} ± ${stat.ingestionThroughput.stdDev} r/s`,
|
||||
`${stat.activeDeliveryRate.mean} / ${stat.activeDeliveryRate.median} / ${stat.activeDeliveryRate.max} m/s`,
|
||||
`${stat.totalDeliveryRate.mean} msg/s`,
|
||||
`${stat.apiP95.mean}ms / ${stat.apiP95.median}ms`,
|
||||
`${stat.deliveryP95.mean}ms / ${stat.deliveryP95.median}ms`,
|
||||
`${stat.costEfficiency.mean} msg/s/$`,
|
||||
];
|
||||
console.log(formatRow(row));
|
||||
}
|
||||
|
||||
console.log("=".repeat(170));
|
||||
console.log("=".repeat(215));
|
||||
|
||||
console.log("\n" + "-".repeat(170));
|
||||
console.log(" 📋 AWS MONTHLY INFRASTRUCTURE PRICING BREAKDOWN");
|
||||
console.log("-".repeat(170));
|
||||
for (const { tier } of results) {
|
||||
console.log(` • ${tier.budget.padEnd(8)} [${tier.awsTopology ?? tier.description}]:`);
|
||||
console.log(` ${tier.awsCostDetails ?? "On-demand AWS components"}`);
|
||||
console.log("\n" + "-".repeat(215));
|
||||
console.log(
|
||||
" 📋 AWS MONTHLY INFRASTRUCTURE PRICING BREAKDOWN",
|
||||
);
|
||||
console.log("-".repeat(215));
|
||||
for (const stat of allStats) {
|
||||
console.log(
|
||||
` • ${stat.tier.budget.padEnd(8)} [${stat.tier.awsTopology ?? stat.tier.description}]:`,
|
||||
);
|
||||
console.log(` ${stat.tier.awsCostDetails ?? "On-demand AWS components"}`);
|
||||
}
|
||||
console.log("-".repeat(170) + "\n");
|
||||
console.log("-".repeat(215) + "\n");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { durationSeconds, providerLatencyMs, tiers } = parseArgs();
|
||||
const { durationSeconds, runs, providerLatencyMs, tiers } = parseArgs();
|
||||
|
||||
console.log(
|
||||
"==========================================================================================",
|
||||
@@ -222,7 +411,7 @@ async function main() {
|
||||
"==========================================================================================",
|
||||
);
|
||||
console.log(
|
||||
`Window: ${durationSeconds}s per tier | Measuring: ${DEFAULT_BUDGET_TIERS.map((t) => t.budget).join(" ➜ ")} (AWS Actual Pricing)`,
|
||||
`Window: ${durationSeconds}s (${(durationSeconds / 60).toFixed(1)} mins) per run | Runs: ${runs} | Total Runs: ${tiers.length * runs}`,
|
||||
);
|
||||
console.log(`Tiers to test: ${tiers.map((t) => t.budget).join(" ➜ ")}`);
|
||||
if (providerLatencyMs && providerLatencyMs !== "0") {
|
||||
@@ -232,44 +421,96 @@ async function main() {
|
||||
"==========================================================================================\n",
|
||||
);
|
||||
|
||||
const results: Array<{ tier: BudgetTier; res: DistributedBenchmarkResult }> = [];
|
||||
const allStats: TierAggregatedStats[] = [];
|
||||
|
||||
for (let tIdx = 0; tIdx < tiers.length; tIdx++) {
|
||||
const tier = tiers[tIdx]!;
|
||||
const tierRuns: DistributedBenchmarkResult[] = [];
|
||||
|
||||
for (let i = 0; i < tiers.length; i++) {
|
||||
const tier = tiers[i]!;
|
||||
console.log(
|
||||
`\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`,
|
||||
);
|
||||
console.log(`[Tier ${i + 1}/${tiers.length}] Benchmarking Budget Tier: ${tier.budget}`);
|
||||
console.log(`Topology: ${tier.description} (${tier.concurrency} Virtual Users)`);
|
||||
console.log(
|
||||
`[Tier ${tIdx + 1}/${tiers.length}] Budget Tier: ${tier.budget} — Running ${runs} iteration(s)`,
|
||||
);
|
||||
console.log(
|
||||
`Topology: ${tier.description} (${tier.servicesCount ?? 10} Microservices, ${tier.concurrency} In-Flight Workers)`,
|
||||
);
|
||||
console.log(
|
||||
`>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n`,
|
||||
);
|
||||
|
||||
const res = await runDistributedBenchmark({
|
||||
serverCount: tier.serverCount,
|
||||
serverCpus: tier.serverCpus,
|
||||
serverMemory: tier.serverMemory,
|
||||
dbCpus: tier.dbCpus,
|
||||
dbMemory: tier.dbMemory,
|
||||
redisCpus: tier.redisCpus,
|
||||
redisMemory: tier.redisMemory,
|
||||
workerConcurrency: tier.workerConcurrency,
|
||||
dbMaxConnections: tier.dbMaxConnections,
|
||||
serverServices: tier.serverServices,
|
||||
providerLatencyMs,
|
||||
durationSeconds,
|
||||
concurrency: tier.concurrency,
|
||||
});
|
||||
for (let rIdx = 0; rIdx < runs; rIdx++) {
|
||||
console.log(
|
||||
`\n--- [Tier ${tier.budget}] Run ${rIdx + 1} of ${runs} (Duration: ${durationSeconds}s) ---`,
|
||||
);
|
||||
|
||||
results.push({ tier, res });
|
||||
const res = await runDistributedBenchmark({
|
||||
serverCount: tier.serverCount,
|
||||
serverCpus: tier.serverCpus,
|
||||
serverMemory: tier.serverMemory,
|
||||
dbCpus: tier.dbCpus,
|
||||
dbMemory: tier.dbMemory,
|
||||
redisCpus: tier.redisCpus,
|
||||
redisMemory: tier.redisMemory,
|
||||
workerConcurrency: tier.workerConcurrency,
|
||||
dbMaxConnections: tier.dbMaxConnections,
|
||||
serverServices: tier.serverServices,
|
||||
serverNodes: tier.serverNodes,
|
||||
providerLatencyMs,
|
||||
durationSeconds,
|
||||
concurrency: tier.concurrency,
|
||||
servicesCount: tier.servicesCount,
|
||||
});
|
||||
|
||||
if (i < tiers.length - 1) {
|
||||
console.log("⏳ Cooling down for 4s before next tier...");
|
||||
await new Promise((r) => setTimeout(r, 4000));
|
||||
tierRuns.push(res);
|
||||
|
||||
if (rIdx < runs - 1) {
|
||||
console.log("⏳ Cooling down for 5s before next run...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
}
|
||||
}
|
||||
|
||||
const tierStats: TierAggregatedStats = {
|
||||
tier,
|
||||
runs: tierRuns,
|
||||
ingestionThroughput: calculateStats(tierRuns.map((r) => r.ingestionThroughput)),
|
||||
activeDeliveryRate: calculateStats(tierRuns.map((r) => r.activeDeliveryRate)),
|
||||
totalDeliveryRate: calculateStats(tierRuns.map((r) => r.totalDeliveryRate)),
|
||||
apiP50: calculateStats(tierRuns.map((r) => r.apiP50)),
|
||||
apiP95: calculateStats(tierRuns.map((r) => r.apiP95)),
|
||||
apiP99: calculateStats(tierRuns.map((r) => r.apiP99)),
|
||||
deliveryP50: calculateStats(tierRuns.map((r) => r.deliveryP50)),
|
||||
deliveryP95: calculateStats(tierRuns.map((r) => r.deliveryP95)),
|
||||
deliveryP99: calculateStats(tierRuns.map((r) => r.deliveryP99)),
|
||||
costEfficiency: calculateStats(
|
||||
tierRuns.map((r) => Number((r.activeDeliveryRate / tier.monthlyCost).toFixed(2))),
|
||||
),
|
||||
};
|
||||
|
||||
allStats.push(tierStats);
|
||||
printTierBreakdown(tierStats, runs);
|
||||
|
||||
if (tIdx < tiers.length - 1) {
|
||||
console.log("⏳ Cooling down for 6s before next tier...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 6000));
|
||||
}
|
||||
}
|
||||
|
||||
printSummaryTable(results);
|
||||
printSummaryTable(allStats, runs);
|
||||
|
||||
// Persist results to JSON
|
||||
try {
|
||||
const resultsDir = path.resolve(process.cwd(), "results");
|
||||
if (!fs.existsSync(resultsDir)) {
|
||||
fs.mkdirSync(resultsDir, { recursive: true });
|
||||
}
|
||||
const outputPath = path.join(resultsDir, `money-scaling-${Date.now()}.json`);
|
||||
fs.writeFileSync(outputPath, JSON.stringify(allStats, null, 2), "utf-8");
|
||||
console.log(`📁 Detailed statistical benchmark results saved to: ${outputPath}`);
|
||||
} catch (err) {
|
||||
console.warn("⚠️ Failed to persist results to JSON:", err);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
|
||||
+2
-2
@@ -171,8 +171,8 @@ describe("HTTP Helpers (src/services/api/http.ts)", () => {
|
||||
const parsedBody = JSON.parse(res.body);
|
||||
expect(parsedBody.error).toBe("validation_error");
|
||||
expect(parsedBody.issues).toEqual([
|
||||
{ path: "user.email", message: "Invalid email" },
|
||||
{ path: "user.age", message: "Number must be greater than or equal to 18" },
|
||||
{ path: "user.email", message: "Invalid email address" },
|
||||
{ path: "user.age", message: "Too small: expected number to be >=18" },
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
+15
-15
@@ -81,8 +81,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "email",
|
||||
priority: "normal",
|
||||
@@ -105,8 +105,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "email",
|
||||
priority: "normal",
|
||||
@@ -130,8 +130,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "email",
|
||||
priority: "normal",
|
||||
@@ -157,8 +157,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "email",
|
||||
priority: "normal",
|
||||
@@ -174,7 +174,7 @@ describe("AiWorker Edge Cases", () => {
|
||||
await worker.process(msg);
|
||||
expect(globalEmitter.emit).toHaveBeenCalledWith(
|
||||
"notification:failed",
|
||||
"00000000-0000-0000-0000-000000000002",
|
||||
"b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
"Invalid prompt",
|
||||
"email",
|
||||
);
|
||||
@@ -191,8 +191,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "email",
|
||||
priority: "normal",
|
||||
@@ -215,8 +215,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "push",
|
||||
priority: "normal",
|
||||
@@ -241,8 +241,8 @@ describe("AiWorker Edge Cases", () => {
|
||||
event: buildStreamEvent(
|
||||
"notification.ai_pending",
|
||||
{
|
||||
projectId: "00000000-0000-0000-0000-000000000001",
|
||||
enrichedEventId: "00000000-0000-0000-0000-000000000002",
|
||||
projectId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
|
||||
enrichedEventId: "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22",
|
||||
recipientId: "usr1",
|
||||
channel: "push",
|
||||
priority: "normal",
|
||||
|
||||
Reference in New Issue
Block a user