@c9up/ream 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/Application.d.ts.map +1 -1
- package/dist/Application.js +6 -1
- package/dist/Application.js.map +1 -1
- package/dist/Ignitor.d.ts +9 -0
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +29 -3
- package/dist/Ignitor.js.map +1 -1
- package/dist/Provider.d.ts +20 -2
- package/dist/Provider.d.ts.map +1 -1
- package/dist/Provider.js.map +1 -1
- package/dist/bodyparser/BodyParserMiddleware.d.ts +1 -1
- package/dist/bodyparser/BodyParserMiddleware.d.ts.map +1 -1
- package/dist/bodyparser/BodyParserMiddleware.js +12 -1
- package/dist/bodyparser/BodyParserMiddleware.js.map +1 -1
- package/dist/bodyparser/MultipartFile.d.ts.map +1 -1
- package/dist/bodyparser/MultipartFile.js +7 -3
- package/dist/bodyparser/MultipartFile.js.map +1 -1
- package/dist/bodyparser/config.d.ts +15 -0
- package/dist/bodyparser/config.d.ts.map +1 -0
- package/dist/bodyparser/config.js +16 -0
- package/dist/bodyparser/config.js.map +1 -0
- package/dist/bodyparser/parseSize.d.ts.map +1 -1
- package/dist/bodyparser/parseSize.js +3 -2
- package/dist/bodyparser/parseSize.js.map +1 -1
- package/dist/commands/MigrateBase.d.ts.map +1 -1
- package/dist/commands/MigrateBase.js +4 -1
- package/dist/commands/MigrateBase.js.map +1 -1
- package/dist/config/moduleConfig.d.ts +15 -0
- package/dist/config/moduleConfig.d.ts.map +1 -0
- package/dist/config/moduleConfig.js +24 -0
- package/dist/config/moduleConfig.js.map +1 -0
- package/dist/console/Kernel.d.ts.map +1 -1
- package/dist/console/Kernel.js +7 -0
- package/dist/console/Kernel.js.map +1 -1
- package/dist/console/cliui.d.ts +2 -0
- package/dist/console/cliui.d.ts.map +1 -1
- package/dist/console/cliui.js +2 -1
- package/dist/console/cliui.js.map +1 -1
- package/dist/console/types.d.ts +14 -0
- package/dist/console/types.d.ts.map +1 -1
- package/dist/console/types.js.map +1 -1
- package/dist/container/Container.d.ts.map +1 -1
- package/dist/container/Container.js +6 -1
- package/dist/container/Container.js.map +1 -1
- package/dist/container/types.d.ts +11 -1
- package/dist/container/types.d.ts.map +1 -1
- package/dist/decorators/Service.d.ts +1 -1
- package/dist/decorators/Service.d.ts.map +1 -1
- package/dist/env/Env.d.ts.map +1 -1
- package/dist/env/Env.js +4 -2
- package/dist/env/Env.js.map +1 -1
- package/dist/env/interpolate.d.ts.map +1 -1
- package/dist/env/interpolate.js +5 -3
- package/dist/env/interpolate.js.map +1 -1
- package/dist/errors/FuzzyMatcher.d.ts.map +1 -1
- package/dist/errors/FuzzyMatcher.js +30 -13
- package/dist/errors/FuzzyMatcher.js.map +1 -1
- package/dist/errors/PipelineStageError.d.ts.map +1 -1
- package/dist/errors/PipelineStageError.js +4 -2
- package/dist/errors/PipelineStageError.js.map +1 -1
- package/dist/events/Emitter.d.ts +21 -4
- package/dist/events/Emitter.d.ts.map +1 -1
- package/dist/events/Emitter.js +6 -2
- package/dist/events/Emitter.js.map +1 -1
- package/dist/events/testing/FakeBus.d.ts +1 -1
- package/dist/events/testing/FakeBus.d.ts.map +1 -1
- package/dist/events/testing/FakeBus.js.map +1 -1
- package/dist/graphql/GraphQLEngine.d.ts +2 -2
- package/dist/graphql/GraphQLEngine.d.ts.map +1 -1
- package/dist/graphql/GraphQLEngine.js +4 -2
- package/dist/graphql/GraphQLEngine.js.map +1 -1
- package/dist/helpers/bytes.d.ts.map +1 -1
- package/dist/helpers/bytes.js +13 -2
- package/dist/helpers/bytes.js.map +1 -1
- package/dist/helpers/string.js +3 -3
- package/dist/helpers/string.js.map +1 -1
- package/dist/http/Exception.d.ts +8 -4
- package/dist/http/Exception.d.ts.map +1 -1
- package/dist/http/Exception.js +14 -8
- package/dist/http/Exception.js.map +1 -1
- package/dist/http/Request.d.ts.map +1 -1
- package/dist/http/Request.js +10 -7
- package/dist/http/Request.js.map +1 -1
- package/dist/http/mime.d.ts.map +1 -1
- package/dist/http/mime.js +2 -1
- package/dist/http/mime.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/native/generated.d.ts +12 -8
- package/dist/native/generated.d.ts.map +1 -1
- package/dist/openapi/OpenApiGenerator.d.ts.map +1 -1
- package/dist/openapi/OpenApiGenerator.js +6 -4
- package/dist/openapi/OpenApiGenerator.js.map +1 -1
- package/dist/router/Router.d.ts +10 -1
- package/dist/router/Router.d.ts.map +1 -1
- package/dist/router/Router.js +22 -10
- package/dist/router/Router.js.map +1 -1
- package/dist/rpc/RpcRouter.d.ts +16 -3
- package/dist/rpc/RpcRouter.d.ts.map +1 -1
- package/dist/rpc/RpcRouter.js +34 -3
- package/dist/rpc/RpcRouter.js.map +1 -1
- package/dist/scheduler/Schedule.d.ts +6 -1
- package/dist/scheduler/Schedule.d.ts.map +1 -1
- package/dist/scheduler/Schedule.js +5 -0
- package/dist/scheduler/Schedule.js.map +1 -1
- package/dist/scheduler/ScheduleProvider.d.ts +15 -11
- package/dist/scheduler/ScheduleProvider.d.ts.map +1 -1
- package/dist/scheduler/ScheduleProvider.js +21 -12
- package/dist/scheduler/ScheduleProvider.js.map +1 -1
- package/dist/security/CookieSigner.d.ts.map +1 -1
- package/dist/security/CookieSigner.js +12 -5
- package/dist/security/CookieSigner.js.map +1 -1
- package/dist/security/SignedUrl.js +3 -2
- package/dist/security/SignedUrl.js.map +1 -1
- package/dist/session/SessionMiddleware.d.ts +2 -4
- package/dist/session/SessionMiddleware.d.ts.map +1 -1
- package/dist/session/SessionMiddleware.js +13 -1
- package/dist/session/SessionMiddleware.js.map +1 -1
- package/dist/session/config.d.ts +25 -0
- package/dist/session/config.d.ts.map +1 -0
- package/dist/session/config.js +18 -0
- package/dist/session/config.js.map +1 -0
- package/dist/session/drivers/DatabaseDriver.d.ts +13 -1
- package/dist/session/drivers/DatabaseDriver.d.ts.map +1 -1
- package/dist/session/drivers/DatabaseDriver.js +23 -3
- package/dist/session/drivers/DatabaseDriver.js.map +1 -1
- package/dist/session/drivers/RedisDriver.d.ts.map +1 -1
- package/dist/session/drivers/RedisDriver.js +10 -2
- package/dist/session/drivers/RedisDriver.js.map +1 -1
- package/dist/testing/ApiResponse.d.ts.map +1 -1
- package/dist/testing/ApiResponse.js +10 -11
- package/dist/testing/ApiResponse.js.map +1 -1
- package/dist/testing/TestClient.js +6 -6
- package/dist/testing/TestClient.js.map +1 -1
- package/dist/utils/Macroable.d.ts +6 -2
- package/dist/utils/Macroable.d.ts.map +1 -1
- package/dist/utils/Macroable.js +4 -0
- package/dist/utils/Macroable.js.map +1 -1
- package/dist/utils/objectPath.d.ts.map +1 -1
- package/dist/utils/objectPath.js +14 -6
- package/dist/utils/objectPath.js.map +1 -1
- package/events.darwin-arm64.node +0 -0
- package/events.darwin-x64.node +0 -0
- package/events.linux-arm64-gnu.node +0 -0
- package/events.linux-x64-gnu.node +0 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +13 -4
- package/scheduler.darwin-arm64.node +0 -0
- package/scheduler.darwin-x64.node +0 -0
- package/scheduler.linux-arm64-gnu.node +0 -0
- package/scheduler.linux-x64-gnu.node +0 -0
- package/scheduler.win32-x64-msvc.node +0 -0
- package/scripts/build-napi-types.mjs +4 -3
- package/scripts/generate-napi-types.mjs +28 -12
- package/src/Application.ts +6 -1
- package/src/Ignitor.ts +42 -3
- package/src/Provider.ts +21 -2
- package/src/bodyparser/BodyParserMiddleware.ts +12 -1
- package/src/bodyparser/MultipartFile.ts +7 -3
- package/src/bodyparser/config.ts +18 -0
- package/src/bodyparser/parseSize.ts +3 -2
- package/src/commands/MigrateBase.ts +4 -1
- package/src/config/moduleConfig.ts +24 -0
- package/src/console/BaseCommand.ts +1 -1
- package/src/console/Kernel.ts +8 -0
- package/src/console/cliui.ts +2 -1
- package/src/console/types.ts +15 -0
- package/src/container/Container.ts +15 -6
- package/src/container/types.ts +11 -1
- package/src/decorators/Service.ts +1 -1
- package/src/env/Env.ts +4 -2
- package/src/env/interpolate.ts +5 -3
- package/src/errors/FuzzyMatcher.ts +34 -13
- package/src/errors/PipelineStageError.ts +4 -2
- package/src/events/Emitter.ts +34 -8
- package/src/events/testing/FakeBus.ts +12 -5
- package/src/graphql/GraphQLEngine.ts +11 -7
- package/src/helpers/bytes.ts +13 -2
- package/src/helpers/string.ts +3 -3
- package/src/http/Exception.ts +14 -8
- package/src/http/Request.ts +8 -5
- package/src/http/mime.ts +2 -1
- package/src/index.ts +2 -2
- package/src/native/generated.ts +12 -8
- package/src/openapi/OpenApiGenerator.ts +6 -4
- package/src/router/Router.ts +31 -11
- package/src/rpc/RpcRouter.ts +38 -8
- package/src/scheduler/Schedule.ts +8 -1
- package/src/scheduler/ScheduleProvider.ts +22 -12
- package/src/security/CookieSigner.ts +15 -5
- package/src/security/SignedUrl.ts +3 -2
- package/src/session/SessionMiddleware.ts +15 -2
- package/src/session/config.ts +29 -0
- package/src/session/drivers/DatabaseDriver.ts +36 -8
- package/src/session/drivers/RedisDriver.ts +13 -5
- package/src/testing/ApiResponse.ts +9 -10
- package/src/testing/TestClient.ts +6 -6
- package/src/utils/Macroable.ts +6 -2
- package/src/utils/objectPath.ts +12 -6
package/dist/utils/objectPath.js
CHANGED
|
@@ -72,9 +72,14 @@ export function hasPath(source, path) {
|
|
|
72
72
|
*/
|
|
73
73
|
export function setPath(target, path, value) {
|
|
74
74
|
const segments = toSegments(path);
|
|
75
|
+
// The last segment is the one written; everything before it is walked. Split
|
|
76
|
+
// off by name rather than by index, so neither has to be read back out of
|
|
77
|
+
// the array afterwards and re-checked for existence.
|
|
78
|
+
const last = segments.pop();
|
|
79
|
+
if (last === undefined)
|
|
80
|
+
return target;
|
|
75
81
|
let current = target;
|
|
76
|
-
for (
|
|
77
|
-
const key = segments[i];
|
|
82
|
+
for (const key of segments) {
|
|
78
83
|
const next = current[key];
|
|
79
84
|
if (!isIndexable(next)) {
|
|
80
85
|
const created = {};
|
|
@@ -85,7 +90,7 @@ export function setPath(target, path, value) {
|
|
|
85
90
|
current = next;
|
|
86
91
|
}
|
|
87
92
|
}
|
|
88
|
-
current[
|
|
93
|
+
current[last] = value;
|
|
89
94
|
return target;
|
|
90
95
|
}
|
|
91
96
|
/**
|
|
@@ -110,14 +115,17 @@ export function omitPaths(source, paths) {
|
|
|
110
115
|
const result = structuredClone(source);
|
|
111
116
|
for (const path of paths) {
|
|
112
117
|
const segments = toSegments(path);
|
|
118
|
+
const last = segments.pop();
|
|
119
|
+
if (last === undefined)
|
|
120
|
+
continue;
|
|
113
121
|
let current = result;
|
|
114
|
-
for (
|
|
122
|
+
for (const key of segments) {
|
|
115
123
|
if (!isIndexable(current))
|
|
116
124
|
break;
|
|
117
|
-
current = current[
|
|
125
|
+
current = current[key];
|
|
118
126
|
}
|
|
119
127
|
if (isIndexable(current))
|
|
120
|
-
delete current[
|
|
128
|
+
delete current[last];
|
|
121
129
|
}
|
|
122
130
|
return result;
|
|
123
131
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectPath.js","sourceRoot":"","sources":["../../src/utils/objectPath.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,gFAAgF;AAChF,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAA;AACpD,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACvE,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,MAAM,OAAO,GAAG,WAAW,CAAA;IAC3B,IAAI,KAAK,GAA2B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,OAAO,KAAK,KAAK,IAAI,EAAE,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QACvB,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,MAAe,EAAE,IAAY,EAAE,YAAsB;IAC3E,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,OAAO,GAAY,MAAM,CAAA;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAAE,OAAO,YAAY,CAAA;QAC9C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;IACD,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAA;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,MAAe,EAAE,IAAY;IACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IACjC,IAAI,OAAO,GAAY,MAAM,CAAA;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAA;QAC3E,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,MAAqB,EAAE,IAAY,EAAE,KAAc;IACzE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;IACjC,
|
|
1
|
+
{"version":3,"file":"objectPath.js","sourceRoot":"","sources":["../../src/utils/objectPath.ts"],"names":[],"mappings":"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"}
|
package/events.darwin-arm64.node
CHANGED
|
Binary file
|
package/events.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/ream",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "Ream — Rust-powered Node.js application framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -187,6 +187,14 @@
|
|
|
187
187
|
"./swcrc.app.json": "./swcrc.app.json",
|
|
188
188
|
"./tsconfig.package.json": "./tsconfig.package.json",
|
|
189
189
|
"./tsconfig.client.json": "./tsconfig.client.json",
|
|
190
|
+
"./session/config": {
|
|
191
|
+
"types": "./dist/session/config.d.ts",
|
|
192
|
+
"import": "./dist/session/config.js"
|
|
193
|
+
},
|
|
194
|
+
"./bodyparser/config": {
|
|
195
|
+
"types": "./dist/bodyparser/config.d.ts",
|
|
196
|
+
"import": "./dist/bodyparser/config.js"
|
|
197
|
+
},
|
|
190
198
|
"./session_middleware": {
|
|
191
199
|
"types": "./dist/session/SessionMiddleware.d.ts",
|
|
192
200
|
"import": "./dist/session/SessionMiddleware.js"
|
|
@@ -228,9 +236,10 @@
|
|
|
228
236
|
}
|
|
229
237
|
},
|
|
230
238
|
"devDependencies": {
|
|
231
|
-
"@biomejs/biome": "
|
|
239
|
+
"@biomejs/biome": "2.5.12",
|
|
232
240
|
"@c9up/comet": "^0.1.0",
|
|
233
241
|
"@types/node": "^22.19.15",
|
|
242
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
234
243
|
"mime-db": "^1.54.0",
|
|
235
244
|
"mime-types": "^3.0.2",
|
|
236
245
|
"typescript": "^6.0.2",
|
|
@@ -254,9 +263,9 @@
|
|
|
254
263
|
"build:napi": "pnpm build:rust && pnpm build:napi-types && node scripts/copy-napi.mjs",
|
|
255
264
|
"test": "vitest run",
|
|
256
265
|
"test:rust": "cargo test --all",
|
|
257
|
-
"lint": "biome check src/",
|
|
266
|
+
"lint": "biome check src/ tests/",
|
|
258
267
|
"test:coverage": "vitest run --coverage",
|
|
259
268
|
"typecheck": "tsc --noEmit",
|
|
260
|
-
"typecheck:
|
|
269
|
+
"typecheck:tests": "tsc --noEmit -p tsconfig.tests.json"
|
|
261
270
|
}
|
|
262
271
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* cargo a `/tmp/...` path the native proc-macro cannot write to, so the
|
|
8
8
|
* type-def file comes back empty and the build fails for no visible reason.
|
|
9
9
|
*
|
|
10
|
-
* One crate at a time on purpose: napi-derive
|
|
10
|
+
* One crate at a time on purpose: with napi-derive 2 the writes all went to a
|
|
11
|
+
* single `TYPE_DEF_TMP_PATH` and a parallel build interleaved them
|
|
11
12
|
* while cargo compiles, and a parallel build interleaves the writes —
|
|
12
13
|
* definitions go missing, silently, and the generated file comes out short.
|
|
13
14
|
*/
|
|
@@ -28,11 +29,11 @@ const scratch = mkdtempSync(join(tmpdir(), 'napi-types-'))
|
|
|
28
29
|
try {
|
|
29
30
|
const lines = []
|
|
30
31
|
for (const crate of CRATES) {
|
|
31
|
-
const perCrate = join(scratch,
|
|
32
|
+
const perCrate = join(scratch, crate)
|
|
32
33
|
writeFileSync(perCrate, '')
|
|
33
34
|
execFileSync('cargo', ['build', '-p', crate], {
|
|
34
35
|
cwd: packageRoot,
|
|
35
|
-
env: { ...process.env,
|
|
36
|
+
env: { ...process.env, NAPI_TYPE_DEF_TMP_FOLDER: scratch },
|
|
36
37
|
stdio: ['ignore', 'ignore', 'inherit'],
|
|
37
38
|
})
|
|
38
39
|
const emitted = readFileSync(perCrate, 'utf8').split('\n').filter(Boolean)
|
|
@@ -29,27 +29,40 @@ import { readFileSync, writeFileSync } from 'node:fs'
|
|
|
29
29
|
* entry must match, or the script fails: a stale refinement is how a hand
|
|
30
30
|
* annotation quietly stops describing the Rust.
|
|
31
31
|
*/
|
|
32
|
+
/**
|
|
33
|
+
* napi-derive 3 emits `callback: (arg: unknown) => unknown` where 2 emitted
|
|
34
|
+
* `(...args: any[]) => any` — better, since it no longer reaches for `any`,
|
|
35
|
+
* but a different string to match on. These refinements exist because the
|
|
36
|
+
* generated shape says nothing about what the callback actually receives.
|
|
37
|
+
*/
|
|
32
38
|
const CALLBACK_REFINEMENTS = {
|
|
33
39
|
'EventBus.subscribe': {
|
|
34
|
-
from: 'callback: (
|
|
40
|
+
from: 'callback: (arg: unknown) => unknown',
|
|
35
41
|
to: 'callback: (eventJson: string) => void',
|
|
36
42
|
},
|
|
37
43
|
'EventBus.onRequest': {
|
|
38
|
-
from: 'callback: (
|
|
44
|
+
from: 'callback: (arg: unknown) => unknown',
|
|
39
45
|
to: 'callback: (eventJson: string, reply: (response: string) => void) => void',
|
|
40
46
|
},
|
|
41
47
|
'HyperServer.onRequest': {
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
// Only the argument is named. The `to` used to say
|
|
49
|
+
// `(request: string) => Promise<string> | string`, which described the
|
|
50
|
+
// boundary as it was BEFORE it stopped serialising: the Rust hands the
|
|
51
|
+
// handler a JsObject and awaits a `NapiResponse`, and napi's own output
|
|
52
|
+
// already said so. The refinement was overwriting a correct shape with a
|
|
53
|
+
// stale one, and nothing noticed because every caller of `onRequest` was
|
|
54
|
+
// outside the typecheck.
|
|
55
|
+
from: 'callback: (arg: unknown) => Promise<NapiResponse>',
|
|
56
|
+
to: 'callback: (request: Record<string, unknown>) => Promise<NapiResponse>',
|
|
44
57
|
},
|
|
45
58
|
'HyperServer.onStreamDisconnect': {
|
|
46
|
-
from: 'callback: (
|
|
59
|
+
from: 'callback: (arg: unknown) => unknown',
|
|
47
60
|
to: 'callback: (streamId: string) => void',
|
|
48
61
|
},
|
|
49
62
|
// The Rust hands over a serialised JsTaskPayload — `{ task_name,
|
|
50
63
|
// scheduled_for_ms }` renamed to camelCase by serde.
|
|
51
64
|
'RustScheduler.register': {
|
|
52
|
-
from: 'callback: (
|
|
65
|
+
from: 'callback: (arg: unknown) => unknown',
|
|
53
66
|
to: 'callback: (invocation: { taskName: string; scheduledForMs: number }) => void',
|
|
54
67
|
},
|
|
55
68
|
}
|
|
@@ -150,12 +163,15 @@ for (const fn of fns) {
|
|
|
150
163
|
// napi-derive emits the whole declaration for a function, unlike a struct
|
|
151
164
|
// where `def` holds only the members.
|
|
152
165
|
const declaration = fn.def.trim()
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
// napi-derive 3 emits a bare `function name(...)` where 2 emitted the
|
|
167
|
+
// signature after the name; concatenating onto the former produced
|
|
168
|
+
// `function xfunction x(...)`.
|
|
169
|
+
const rendered = declaration.startsWith('export declare function')
|
|
170
|
+
? declaration
|
|
171
|
+
: declaration.startsWith('function ')
|
|
172
|
+
? `export declare ${declaration}`
|
|
173
|
+
: `export declare function ${fn.name}${declaration}`
|
|
174
|
+
out.push(`${rendered};`, '')
|
|
159
175
|
}
|
|
160
176
|
|
|
161
177
|
const stale = Object.keys(CALLBACK_REFINEMENTS).filter((k) => !used.has(k))
|
package/src/Application.ts
CHANGED
|
@@ -181,7 +181,12 @@ export class Application implements AppContext {
|
|
|
181
181
|
|
|
182
182
|
/** Resolve inside one of the conventional directories. */
|
|
183
183
|
#inDirectory(key: keyof DirectoriesNode, paths: string[]): string {
|
|
184
|
-
|
|
184
|
+
// `DirectoriesNode` is an index signature, so a key nobody registered
|
|
185
|
+
// reads as undefined rather than failing to compile. Falling back to the
|
|
186
|
+
// key itself keeps the convention: `configPath()` under a node with no
|
|
187
|
+
// `config` entry resolves to `<root>/config`, which is where it would
|
|
188
|
+
// have been anyway.
|
|
189
|
+
return this.makePath(this.#directories[key] ?? String(key), ...paths)
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
/** Absolute path inside the `config/` directory. */
|
package/src/Ignitor.ts
CHANGED
|
@@ -210,6 +210,15 @@ export interface HyperServerLike {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
export interface IgnitorConfig {
|
|
213
|
+
/**
|
|
214
|
+
* Bind port. Falls back to `process.env.PORT`, then to `3000`. Prefer the
|
|
215
|
+
* environment: the fallback is read when the server binds, AFTER `booting()`
|
|
216
|
+
* has loaded `#start/env`, whereas a value passed here is fixed at
|
|
217
|
+
* construction — before `.env` has been read at all.
|
|
218
|
+
*
|
|
219
|
+
* It stays available because a test harness booting several applications in
|
|
220
|
+
* one process needs to hand each one an ephemeral port it has already picked.
|
|
221
|
+
*/
|
|
213
222
|
port?: number
|
|
214
223
|
/**
|
|
215
224
|
* Bind address of the HTTP server. Falls back to `process.env.HOST`, then to
|
|
@@ -690,12 +699,31 @@ export class Ignitor {
|
|
|
690
699
|
this.#phase = 'started'
|
|
691
700
|
}
|
|
692
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Whether this process should bind an HTTP server.
|
|
704
|
+
*
|
|
705
|
+
* Not the same question as "is the environment 'web'". A test harness needs
|
|
706
|
+
* the HTTP surface — it passes a `serverFactory` precisely to get one — but
|
|
707
|
+
* it also needs to declare itself as `test`, because that is what an
|
|
708
|
+
* environment-scoped provider entry reads. Tying the two together forced a
|
|
709
|
+
* test bootstrap to call `httpServer()`, which set the environment to
|
|
710
|
+
* `'web'`, so `{ environment: ['web'] }` excluded nothing from a test run and
|
|
711
|
+
* a scheduled task fired in the middle of the suite.
|
|
712
|
+
*
|
|
713
|
+
* A test process that passes no factory still gets no server, and no error.
|
|
714
|
+
*/
|
|
715
|
+
#servesHttp(): boolean {
|
|
716
|
+
const environment = this.#app.getEnvironment()
|
|
717
|
+
if (environment === 'web') return true
|
|
718
|
+
return environment === 'test' && this.#config.serverFactory !== undefined
|
|
719
|
+
}
|
|
720
|
+
|
|
693
721
|
async #phaseReady(): Promise<void> {
|
|
694
722
|
// Boot the Server (resolves lazy error handler etc.)
|
|
695
723
|
await this.#server.boot()
|
|
696
724
|
|
|
697
|
-
// Start HTTP server if
|
|
698
|
-
if (this.#
|
|
725
|
+
// Start HTTP server if this process serves one
|
|
726
|
+
if (this.#servesHttp() && this.#config.serverFactory) {
|
|
699
727
|
// Build the HttpKernel with server middleware + router middleware.
|
|
700
728
|
// The streaming backend is resolved lazily — the HyperServer is
|
|
701
729
|
// created a few lines down, after the kernel — so the kernel
|
|
@@ -737,7 +765,18 @@ export class Ignitor {
|
|
|
737
765
|
},
|
|
738
766
|
})
|
|
739
767
|
|
|
740
|
-
|
|
768
|
+
// Read HERE, not at construction: `#start/env` runs in `booting()`, so a
|
|
769
|
+
// PORT living in `.env` does not exist in `process.env` until this point.
|
|
770
|
+
// Reading it early bound 3000 while every later reader — the boot banner
|
|
771
|
+
// included — saw 3007 and said so. Same lazy rule as `host` below.
|
|
772
|
+
const desiredPort = this.#config.port ?? Number(process.env.PORT || 3000)
|
|
773
|
+
if (!Number.isInteger(desiredPort) || desiredPort < 0 || desiredPort > 65535) {
|
|
774
|
+
throw new ReamError(
|
|
775
|
+
'E_IGNITOR_INVALID_PORT',
|
|
776
|
+
`PORT must be an integer between 0 and 65535, received '${process.env.PORT}'.`,
|
|
777
|
+
{ hint: 'Set PORT in .env, or pass { port } to the Ignitor.' },
|
|
778
|
+
)
|
|
779
|
+
}
|
|
741
780
|
// Port-scan fallback (+1..+19) is a DEV convenience only. In production
|
|
742
781
|
// a silent drift to :3001 while the LB targets :3000 is an outage —
|
|
743
782
|
// bind the configured port and let EADDRINUSE fail loudly. The probe
|
package/src/Provider.ts
CHANGED
|
@@ -7,12 +7,31 @@
|
|
|
7
7
|
* register() → boot() → start() → ready() → ... → shutdown()
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { ConfigStore } from './ConfigLoader.js'
|
|
11
10
|
import type { Container } from './container/Container.js'
|
|
12
11
|
|
|
12
|
+
/**
|
|
13
|
+
* The config a provider reads, as a CONTRACT rather than as the class.
|
|
14
|
+
*
|
|
15
|
+
* `ConfigStore` satisfies it, so nothing that holds a real one changes. But
|
|
16
|
+
* typed as the class, `AppContext` demanded a private `#tree` no one outside
|
|
17
|
+
* `ConfigLoader` can produce — so a test could not substitute a config, and an
|
|
18
|
+
* independently-published package could not describe an `AppContext` at all
|
|
19
|
+
* without importing `@c9up/ream`, which is the coupling the comment below this
|
|
20
|
+
* one exists to avoid. bay, station and blackhole each re-declared their own
|
|
21
|
+
* for exactly that reason.
|
|
22
|
+
*
|
|
23
|
+
* `get` and `set` are what providers actually use; a `ConfigStore`'s other
|
|
24
|
+
* methods stay reachable on the concrete type.
|
|
25
|
+
*/
|
|
26
|
+
export interface ConfigReader {
|
|
27
|
+
get<T = unknown>(key: string): T | undefined
|
|
28
|
+
get<T = unknown>(key: string, defaultValue: T): T
|
|
29
|
+
set(key: string, value: unknown): void
|
|
30
|
+
}
|
|
31
|
+
|
|
13
32
|
export interface AppContext {
|
|
14
33
|
container: Container
|
|
15
|
-
config:
|
|
34
|
+
config: ConfigReader
|
|
16
35
|
}
|
|
17
36
|
|
|
18
37
|
/**
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Configured via config/bodyparser.ts.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { readModuleConfig } from '../config/moduleConfig.js'
|
|
9
10
|
import { Exception } from '../http/Exception.js'
|
|
10
11
|
import type { HttpContext } from '../http/HttpContext.js'
|
|
11
12
|
import { hydrateMultipartPayload } from './MultipartFile.js'
|
|
@@ -98,7 +99,17 @@ const DEFAULT_CONFIG: ResolvedBodyParserConfig = {
|
|
|
98
99
|
export default class BodyParserMiddleware {
|
|
99
100
|
#config: ResolvedBodyParserConfig
|
|
100
101
|
|
|
101
|
-
constructor(
|
|
102
|
+
constructor(explicitConfig: BodyParserConfig | undefined = undefined) {
|
|
103
|
+
// The default value is load-bearing: an OPTIONAL parameter still counts
|
|
104
|
+
// toward `Function.length`, and the container refuses to auto-construct a
|
|
105
|
+
// class that declares parameters it has no metadata for. A parameter with a
|
|
106
|
+
// default counts for nothing, which is what lets
|
|
107
|
+
// `router.use([() => import('…')])` build this middleware.
|
|
108
|
+
// Nothing is passed when the container builds this middleware from
|
|
109
|
+
// `router.use([() => import('@c9up/ream/bodyparser_middleware')])`, so the
|
|
110
|
+
// settings come from `config/bodyparser.ts` — the file this class's own
|
|
111
|
+
// documentation has always named.
|
|
112
|
+
const config = explicitConfig ?? readModuleConfig<BodyParserConfig>('bodyparser')
|
|
102
113
|
assertNoEnabledFlag(config)
|
|
103
114
|
this.#config = {
|
|
104
115
|
allowedMethods: config?.allowedMethods ?? DEFAULT_CONFIG.allowedMethods,
|
|
@@ -125,7 +125,10 @@ export class MultipartFile {
|
|
|
125
125
|
#mimeParts(): [string | undefined, string | undefined] {
|
|
126
126
|
const source = this.#detected?.mime ?? this.#headerMime
|
|
127
127
|
if (!source) return [undefined, undefined]
|
|
128
|
-
|
|
128
|
+
// `split` always yields a first element; naming it is how that stops
|
|
129
|
+
// being something the compiler has to be told.
|
|
130
|
+
const [head = ''] = source.split(';')
|
|
131
|
+
const [type, subtype] = head.trim().split('/')
|
|
129
132
|
return [type || undefined, subtype || undefined]
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -432,8 +435,9 @@ function isSafeFilename(name: string): boolean {
|
|
|
432
435
|
function parseFileSize(size: string): number {
|
|
433
436
|
const match = size.match(/^(\d+)(kb|mb|gb)?$/i)
|
|
434
437
|
if (!match) return 1024 * 1024
|
|
435
|
-
const
|
|
436
|
-
|
|
438
|
+
const [, digits, unit] = match
|
|
439
|
+
const num = parseInt(digits ?? '0', 10)
|
|
440
|
+
switch (unit?.toLowerCase()) {
|
|
437
441
|
case 'kb':
|
|
438
442
|
return num * 1024
|
|
439
443
|
case 'mb':
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `config/bodyparser.ts` — body-parsing limits and types, in the file the
|
|
3
|
+
* config loader reads (`config/<name>.ts` → `config.get('<name>')`).
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* // config/bodyparser.ts
|
|
7
|
+
* import { defineConfig } from '@c9up/ream/bodyparser/config'
|
|
8
|
+
*
|
|
9
|
+
* export default defineConfig({
|
|
10
|
+
* multipart: { limit: '20mb' },
|
|
11
|
+
* })
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { BodyParserConfig } from './BodyParserMiddleware.js'
|
|
15
|
+
|
|
16
|
+
export function defineConfig(config: BodyParserConfig): BodyParserConfig {
|
|
17
|
+
return config
|
|
18
|
+
}
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
export function parseSize(size: string): number {
|
|
7
7
|
const match = size.match(/^(\d+)(kb|mb|gb)?$/i)
|
|
8
8
|
if (!match) return 1024 * 1024 // default 1mb
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const [, digits, unit] = match
|
|
10
|
+
const num = parseInt(digits ?? '0', 10)
|
|
11
|
+
switch (unit?.toLowerCase()) {
|
|
11
12
|
case 'kb':
|
|
12
13
|
return num * 1024
|
|
13
14
|
case 'mb':
|
|
@@ -19,7 +19,10 @@ import type { MigrationRegistry } from '../migrations/MigrationRegistry.js'
|
|
|
19
19
|
import type { RegisteredMigrationSource } from '../migrations/types.js'
|
|
20
20
|
|
|
21
21
|
export abstract class MigrateBase extends BaseCommand {
|
|
22
|
-
|
|
22
|
+
// `drivesMigrations` because these three ARE the migration run: without it the
|
|
23
|
+
// boot applies everything first, so `migrate` says "nothing to migrate" after
|
|
24
|
+
// the work is done, and `migrate:status` mutates the schema it reports on.
|
|
25
|
+
static override options: CommandOptions = { startApp: true, drivesMigrations: true }
|
|
23
26
|
|
|
24
27
|
@flags.string({
|
|
25
28
|
description: 'Only this migration source, by the name its package registered',
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a module's own `config/<name>.ts` entry without demanding one.
|
|
3
|
+
*
|
|
4
|
+
* A middleware constructed by the container gets nothing passed to it — the
|
|
5
|
+
* resolver calls `make(Class)`, which is what makes `router.use([() =>
|
|
6
|
+
* import('…/session_middleware')])` possible. The configuration therefore has
|
|
7
|
+
* to come from the config store, the way it does everywhere else.
|
|
8
|
+
*
|
|
9
|
+
* The read is deliberately forgiving. The same classes are also constructed
|
|
10
|
+
* directly (`new BodyParserMiddleware()`) in tests and in embedding hosts that
|
|
11
|
+
* never boot an Application, and there the store does not exist. Throwing there
|
|
12
|
+
* would trade a working default for a crash.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getApp } from '../services/app.js'
|
|
16
|
+
|
|
17
|
+
export function readModuleConfig<T extends object>(key: string): T | undefined {
|
|
18
|
+
const store = getApp()?.config
|
|
19
|
+
if (store === undefined || !store.has(key)) return undefined
|
|
20
|
+
const value = store.get<T>(key)
|
|
21
|
+
// A config file exporting something that is not an object (a stray
|
|
22
|
+
// `export default 42`) is ignored rather than spread over the defaults.
|
|
23
|
+
return typeof value === 'object' && value !== null ? value : undefined
|
|
24
|
+
}
|
|
@@ -117,7 +117,7 @@ export abstract class BaseCommand {
|
|
|
117
117
|
error?: unknown
|
|
118
118
|
|
|
119
119
|
/** Assigned by the kernel — see {@link terminate}. */
|
|
120
|
-
protected
|
|
120
|
+
declare protected onTerminate?: () => void | Promise<void>
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Lifecycle hooks, run in this order around `run()`: `prepare`, `interact`,
|
package/src/console/Kernel.ts
CHANGED
|
@@ -999,6 +999,14 @@ export class Kernel {
|
|
|
999
999
|
)
|
|
1000
1000
|
}
|
|
1001
1001
|
|
|
1002
|
+
// Before the boot, not after: a data package that migrates on boot for the
|
|
1003
|
+
// convenience of `dev` would otherwise apply everything a moment before the
|
|
1004
|
+
// command that exists to apply it runs — leaving `migrate` with nothing to
|
|
1005
|
+
// report and `migrate:status` having changed what it was asked to look at.
|
|
1006
|
+
if (Command.options?.drivesMigrations === true) {
|
|
1007
|
+
process.env.REAM_SKIP_BOOT_MIGRATE = '1'
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1002
1010
|
return this.#startApp()
|
|
1003
1011
|
}
|
|
1004
1012
|
|
package/src/console/cliui.ts
CHANGED
|
@@ -750,7 +750,8 @@ function pad(text: string, width: number): string {
|
|
|
750
750
|
return text + ' '.repeat(Math.max(0, width - visibleWidth(text)))
|
|
751
751
|
}
|
|
752
752
|
|
|
753
|
-
|
|
753
|
+
/** Removes the SGR escape sequences `colors` writes, leaving the visible text. */
|
|
754
|
+
export function stripAnsi(text: string): string {
|
|
754
755
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching ANSI escapes is the point
|
|
755
756
|
return text.replace(/\u001B\[[0-9;]*m/g, '')
|
|
756
757
|
}
|
package/src/console/types.ts
CHANGED
|
@@ -37,6 +37,21 @@ export interface CommandOptions {
|
|
|
37
37
|
* default so a typo (`--warm` for `--warn`) is reported, not swallowed.
|
|
38
38
|
*/
|
|
39
39
|
allowUnknownFlags?: boolean
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* This command drives migrations itself, so booting must not run them.
|
|
43
|
+
*
|
|
44
|
+
* A data package may migrate on boot for the convenience of `dev` — atlas
|
|
45
|
+
* does, outside production. For the commands that exist to migrate, that
|
|
46
|
+
* convenience is the bug: the boot applies everything, and then `migrate`
|
|
47
|
+
* reports "nothing to migrate" while `migrate:status`, asked only to look,
|
|
48
|
+
* has changed the schema it is reporting on.
|
|
49
|
+
*
|
|
50
|
+
* Declaring this sets `REAM_SKIP_BOOT_MIGRATE=1` before the application
|
|
51
|
+
* starts, which is the flag a data package watches for. Any package's own
|
|
52
|
+
* migration command opts in the same way; the framework names none of them.
|
|
53
|
+
*/
|
|
54
|
+
drivesMigrations?: boolean
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
/** Metadata recorded by an `@args.*` decorator. */
|
|
@@ -63,7 +63,7 @@ interface ResolutionChain {
|
|
|
63
63
|
* down. Saved and restored around each construction, so a dependency's own
|
|
64
64
|
* dependencies see IT as their parent, not its parent.
|
|
65
65
|
*/
|
|
66
|
-
parent?: new (
|
|
66
|
+
parent?: abstract new (
|
|
67
67
|
...args: never[]
|
|
68
68
|
) => unknown
|
|
69
69
|
}
|
|
@@ -91,8 +91,12 @@ export class Container {
|
|
|
91
91
|
/** Alias key → the token it forwards to (AdonisJS `container.alias`). */
|
|
92
92
|
#aliases: Map<ServiceToken, ServiceToken> = new Map()
|
|
93
93
|
/** parent class → (binding key → the factory that class gets instead). */
|
|
94
|
-
#contextualBindings: Map<
|
|
95
|
-
new
|
|
94
|
+
#contextualBindings: Map<
|
|
95
|
+
abstract new (
|
|
96
|
+
...args: never[]
|
|
97
|
+
) => unknown,
|
|
98
|
+
Map<ServiceToken, ServiceFactory>
|
|
99
|
+
> = new Map()
|
|
96
100
|
/** token key → post-resolution callbacks (AdonisJS `container.resolving`). */
|
|
97
101
|
#resolvingHooks: Map<ServiceToken, Array<(value: unknown) => void | Promise<void>>> = new Map()
|
|
98
102
|
/**
|
|
@@ -625,7 +629,7 @@ export class Container {
|
|
|
625
629
|
* 3. No params → plain `new Class()`
|
|
626
630
|
*/
|
|
627
631
|
async #autoConstruct(
|
|
628
|
-
target: new (...args: never[]) => unknown,
|
|
632
|
+
target: abstract new (...args: never[]) => unknown,
|
|
629
633
|
runtimeValues?: unknown[],
|
|
630
634
|
): Promise<unknown> {
|
|
631
635
|
// Everything resolved inside is a dependency OF `target`, which is what a
|
|
@@ -642,7 +646,7 @@ export class Container {
|
|
|
642
646
|
}
|
|
643
647
|
|
|
644
648
|
async #constructWithDeps(
|
|
645
|
-
target: new (...args: never[]) => unknown,
|
|
649
|
+
target: abstract new (...args: never[]) => unknown,
|
|
646
650
|
runtimeValues?: unknown[],
|
|
647
651
|
): Promise<unknown> {
|
|
648
652
|
const metadata = getServiceMetadata(target)
|
|
@@ -719,7 +723,12 @@ export class Container {
|
|
|
719
723
|
)
|
|
720
724
|
}
|
|
721
725
|
// (d) Genuine zero-argument class.
|
|
722
|
-
|
|
726
|
+
//
|
|
727
|
+
// `Reflect.construct`, not `new target()`: a token may be an abstract
|
|
728
|
+
// class (that is what a contextual binding binds against), and `new` on
|
|
729
|
+
// one is a compile error even though the runtime constructs it happily —
|
|
730
|
+
// `abstract` exists only at compile time.
|
|
731
|
+
const instance = Reflect.construct(target, [])
|
|
723
732
|
this.#cacheIfAppWide(scope, key, instance, store, readsBefore)
|
|
724
733
|
return instance
|
|
725
734
|
}
|
package/src/container/types.ts
CHANGED
|
@@ -16,7 +16,17 @@ export interface ServiceMetadata {
|
|
|
16
16
|
/** Token used to resolve a service — class constructor, string name, or registered symbol. */
|
|
17
17
|
// `never[]` accepts every constructor shape — parameters are contravariant, so
|
|
18
18
|
// a rest of `never` is assignable from any concrete list.
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* What the container accepts as a token.
|
|
21
|
+
*
|
|
22
|
+
* `abstract new`, not `new`: binding an implementation against an abstract base
|
|
23
|
+
* is the whole point of a contextual binding — `@Inject(Hash)` where `Hash` is
|
|
24
|
+
* the abstract class and `Bcrypt` is what the container provides. An abstract
|
|
25
|
+
* class has no construct signature, so spelt `new (...)` the type rejected
|
|
26
|
+
* exactly the pattern the feature exists for. `abstract new` admits concrete
|
|
27
|
+
* classes too.
|
|
28
|
+
*/
|
|
29
|
+
export type ServiceToken = (abstract new (...args: never[]) => unknown) | string | symbol
|
|
20
30
|
|
|
21
31
|
/**
|
|
22
32
|
* What a binding factory is handed so it can resolve its own dependencies
|
|
@@ -14,7 +14,7 @@ const INJECT_METADATA_KEY = Symbol('ream:inject')
|
|
|
14
14
|
// `never[]` accepts every constructor shape: parameters are contravariant, so a
|
|
15
15
|
// rest of `never` is assignable from any concrete list. Same spelling as the
|
|
16
16
|
// router's, so a class flows between the two without an assertion.
|
|
17
|
-
type AnyConstructor = new (...args: never[]) => unknown
|
|
17
|
+
type AnyConstructor = abstract new (...args: never[]) => unknown
|
|
18
18
|
|
|
19
19
|
/** Registry of all decorated services. */
|
|
20
20
|
const serviceRegistry: Map<AnyConstructor, ServiceMetadata> = new Map()
|