@velajs/vela 1.10.0 → 1.13.0
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/CHANGELOG.md +157 -0
- package/dist/application.d.ts +25 -0
- package/dist/application.js +103 -2
- package/dist/container/container.d.ts +46 -1
- package/dist/container/container.js +156 -13
- package/dist/container/disposable.js +1 -1
- package/dist/container/types.d.ts +27 -0
- package/dist/cors/cors.module.d.ts +5 -5
- package/dist/cors/cors.module.js +26 -25
- package/dist/discovery/discoverable.decorator.d.ts +38 -0
- package/dist/discovery/discoverable.decorator.js +42 -0
- package/dist/discovery/discovery.service.d.ts +102 -0
- package/dist/discovery/discovery.service.js +177 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.js +2 -0
- package/dist/entrypoint/entrypoint.registry.d.ts +42 -0
- package/dist/entrypoint/entrypoint.registry.js +112 -0
- package/dist/entrypoint/entrypoint.types.d.ts +41 -0
- package/dist/entrypoint/entrypoint.types.js +3 -0
- package/dist/entrypoint/execution-context.d.ts +15 -0
- package/dist/entrypoint/execution-context.js +21 -0
- package/dist/entrypoint/execution-scope.d.ts +21 -0
- package/dist/entrypoint/execution-scope.js +28 -0
- package/dist/entrypoint/index.d.ts +4 -0
- package/dist/entrypoint/index.js +4 -0
- package/dist/event-emitter/event-emitter.module.js +1 -0
- package/dist/event-emitter/event-emitter.subscriber.d.ts +3 -3
- package/dist/event-emitter/event-emitter.subscriber.js +12 -29
- package/dist/factory/adapter.d.ts +36 -0
- package/dist/factory/adapter.js +18 -0
- package/dist/factory/bootstrap.js +14 -2
- package/dist/factory.d.ts +11 -1
- package/dist/factory.js +23 -2
- package/dist/http/handler-executor.js +22 -19
- package/dist/http/route-contributor.d.ts +68 -0
- package/dist/http/route-contributor.js +22 -0
- package/dist/http/route.manager.js +33 -19
- package/dist/i18n/i18n.module.js +9 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +9 -2
- package/dist/internal.d.ts +0 -2
- package/dist/internal.js +0 -7
- package/dist/module/configurable-module.builder.d.ts +12 -0
- package/dist/module/configurable-module.builder.js +21 -114
- package/dist/module/decorators.js +4 -2
- package/dist/module/define-module.d.ts +112 -0
- package/dist/module/define-module.js +232 -0
- package/dist/module/index.d.ts +3 -1
- package/dist/module/index.js +3 -1
- package/dist/module/lazy-modules.d.ts +82 -0
- package/dist/module/lazy-modules.js +231 -0
- package/dist/module/lazy-provider.d.ts +65 -0
- package/dist/module/lazy-provider.js +111 -0
- package/dist/module/module-loader.d.ts +21 -0
- package/dist/module/module-loader.js +99 -24
- package/dist/openapi/document.js +16 -15
- package/dist/pipeline/component.manager.d.ts +15 -9
- package/dist/pipeline/component.manager.js +32 -46
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +31 -0
- package/dist/pipeline/pipeline-runner.js +40 -0
- package/dist/registry/metadata.registry.d.ts +9 -3
- package/dist/registry/metadata.registry.js +55 -25
- package/dist/registry/types.d.ts +10 -0
- package/dist/schedule/schedule.module.d.ts +5 -0
- package/dist/schedule/schedule.module.js +21 -8
- package/dist/schedule/schedule.registry.d.ts +3 -3
- package/dist/schedule/schedule.registry.js +23 -44
- package/dist/schedule-node/schedule-node.module.d.ts +5 -0
- package/dist/schedule-node/schedule-node.module.js +20 -10
- package/dist/seeder/seeder.module.d.ts +8 -10
- package/dist/seeder/seeder.module.js +19 -15
- package/dist/websocket/index.d.ts +1 -1
- package/dist/websocket/index.js +1 -1
- package/dist/websocket/websocket.module.d.ts +20 -3
- package/dist/websocket/websocket.module.js +50 -39
- package/dist/websocket/websocket.tokens.d.ts +2 -1
- package/dist/websocket/websocket.tokens.js +3 -2
- package/dist/websocket/ws-dispatcher.d.ts +21 -9
- package/dist/websocket/ws-dispatcher.js +44 -38
- package/dist/websocket-node/register-gateways.js +3 -3
- package/package.json +1 -1
- package/dist/http/crud-bridge.d.ts +0 -90
- package/dist/http/crud-bridge.js +0 -25
|
@@ -16,7 +16,8 @@ export function Module(options = {}) {
|
|
|
16
16
|
providers: options.providers,
|
|
17
17
|
controllers: options.controllers,
|
|
18
18
|
exports: options.exports,
|
|
19
|
-
isGlobal: options.isGlobal
|
|
19
|
+
isGlobal: options.isGlobal,
|
|
20
|
+
lazy: options.lazy
|
|
20
21
|
});
|
|
21
22
|
};
|
|
22
23
|
}
|
|
@@ -40,6 +41,7 @@ export function getModuleMetadata(target) {
|
|
|
40
41
|
controllers: options.controllers ?? [],
|
|
41
42
|
imports: options.imports ?? [],
|
|
42
43
|
exports: options.exports ?? [],
|
|
43
|
-
isGlobal: options.isGlobal === true
|
|
44
|
+
isGlobal: options.isGlobal === true,
|
|
45
|
+
lazy: options.lazy === true
|
|
44
46
|
};
|
|
45
47
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { InjectionToken, type ProviderOptions, type Token, type Type } from '../container/types';
|
|
2
|
+
import type { ComponentInstance, ModuleImport } from '../registry/types';
|
|
3
|
+
import type { ConfigurableModuleAsyncOptions, ConfigurableModuleExtras, ConfigurableModuleExtrasTransform, ConfigurableModuleHost } from './configurable-module.types';
|
|
4
|
+
/** The `global:` slot's component groups, lowered to `APP_*` registrations. */
|
|
5
|
+
export interface GlobalComponentSlot {
|
|
6
|
+
guards?: Array<Type | ComponentInstance>;
|
|
7
|
+
pipes?: Array<Type | ComponentInstance>;
|
|
8
|
+
interceptors?: Array<Type | ComponentInstance>;
|
|
9
|
+
filters?: Array<Type | ComponentInstance>;
|
|
10
|
+
middleware?: Array<Type | ComponentInstance>;
|
|
11
|
+
}
|
|
12
|
+
/** What a module instance contributes, computed from its call-time options. */
|
|
13
|
+
export interface ModuleContributions {
|
|
14
|
+
providers?: Array<Type | ProviderOptions>;
|
|
15
|
+
controllers?: Type[];
|
|
16
|
+
imports?: ModuleImport[];
|
|
17
|
+
exports?: Array<Type | InjectionToken>;
|
|
18
|
+
/**
|
|
19
|
+
* Standardized global-component registration — the one idiom replacing both
|
|
20
|
+
* the `@Module({ providers: [{ provide: APP_GUARD, useExisting: X }] })`
|
|
21
|
+
* pattern and the conditional-push-into-forRoot pattern. Class entries are
|
|
22
|
+
* registered as providers and wired via `useExisting`; instances via `useValue`.
|
|
23
|
+
*/
|
|
24
|
+
global?: GlobalComponentSlot;
|
|
25
|
+
}
|
|
26
|
+
export interface ModuleSetupContext<Opts> {
|
|
27
|
+
/** The options token — derived providers do `inject: [OPTIONS]`. */
|
|
28
|
+
readonly OPTIONS: InjectionToken<Opts>;
|
|
29
|
+
/**
|
|
30
|
+
* Structural options known at call time. For `forRoot` this is the full
|
|
31
|
+
* bag; for `forRootAsync` it is only the structural fields passed alongside
|
|
32
|
+
* the factory (the DI-resolved options exist only at resolution time —
|
|
33
|
+
* read them through `OPTIONS`, never here).
|
|
34
|
+
*/
|
|
35
|
+
readonly options: Partial<Opts>;
|
|
36
|
+
/** The instance key in effect (for deriving per-instance token names). */
|
|
37
|
+
readonly key: string;
|
|
38
|
+
}
|
|
39
|
+
export interface DefineModuleSpec<Opts, Extras extends ConfigurableModuleExtras = {
|
|
40
|
+
isGlobal?: boolean;
|
|
41
|
+
}, MethodKey extends string = 'forRoot', FactoryMethodKey extends string = 'create'> {
|
|
42
|
+
/** Names the generated base class, the minted options token, and diagnostics. */
|
|
43
|
+
name: string;
|
|
44
|
+
/**
|
|
45
|
+
* Reuse an existing options token instead of minting one — critical for
|
|
46
|
+
* migrations so the module's public token keeps its identity.
|
|
47
|
+
*/
|
|
48
|
+
optionsToken?: InjectionToken<Opts>;
|
|
49
|
+
/**
|
|
50
|
+
* Structural contributions as a function of the call-time options. Runs at
|
|
51
|
+
* `forRoot`/`forRootAsync` call time, once per module instance.
|
|
52
|
+
*/
|
|
53
|
+
setup?: (ctx: ModuleSetupContext<Opts>) => ModuleContributions;
|
|
54
|
+
/**
|
|
55
|
+
* Dedup key from the structural options. Defaults to `stableHash(options)`.
|
|
56
|
+
* Modules whose options carry stateful instances (drivers, registries)
|
|
57
|
+
* should derive from the stable identifying subset — or document that
|
|
58
|
+
* callers pass an explicit `key` for multi-instance setups.
|
|
59
|
+
*/
|
|
60
|
+
key?: (options: Partial<Opts>) => string;
|
|
61
|
+
/** Call-site extras defaults (default `{ isGlobal: false }`). */
|
|
62
|
+
extras?: Extras;
|
|
63
|
+
/** Reshape the definition from resolved extras (default: `isGlobal` → `global: true`). */
|
|
64
|
+
transform?: ConfigurableModuleExtrasTransform<Extras>;
|
|
65
|
+
/** Rename the sync static (default `forRoot`); the async static becomes `<name>Async`. */
|
|
66
|
+
methodName?: MethodKey;
|
|
67
|
+
/** Method a `useClass`/`useExisting` options factory must implement (default `create`). */
|
|
68
|
+
factoryMethodName?: FactoryMethodKey;
|
|
69
|
+
/**
|
|
70
|
+
* Default every generated module instance to deferred (first-use)
|
|
71
|
+
* materialization. Call sites can also opt in per instance by passing
|
|
72
|
+
* `lazy: true` alongside the options (recognized like `isGlobal`).
|
|
73
|
+
* See MODULE_AUTHORING.md "Lazy modules".
|
|
74
|
+
*/
|
|
75
|
+
lazy?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The one blessed module-authoring engine. Generates `forRoot` AND
|
|
79
|
+
* `forRootAsync` statics with: `stableHash` key derivation (multi-instance
|
|
80
|
+
* dedup that survives HMR), typed `inject` tuple inference on the async
|
|
81
|
+
* factory, an `isGlobal` extra, and — the piece `ConfigurableModuleBuilder`
|
|
82
|
+
* could not express — providers/controllers/imports/exports/global components
|
|
83
|
+
* computed **as functions of the options**.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = defineModule<CorsOptions>({
|
|
87
|
+
* name: 'Cors',
|
|
88
|
+
* setup: ({ OPTIONS }) => ({
|
|
89
|
+
* providers: [{
|
|
90
|
+
* provide: APP_MIDDLEWARE,
|
|
91
|
+
* useFactory: (o: CorsOptions) => buildCorsMiddleware(o),
|
|
92
|
+
* inject: [OPTIONS],
|
|
93
|
+
* }],
|
|
94
|
+
* exports: [OPTIONS],
|
|
95
|
+
* }),
|
|
96
|
+
* });
|
|
97
|
+
* export class CorsModule extends ConfigurableModuleClass {}
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* `ConfigurableModuleBuilder` remains supported (NestJS parity) and is a thin
|
|
101
|
+
* adapter over this engine.
|
|
102
|
+
*/
|
|
103
|
+
export declare function defineModule<Opts, Extras extends ConfigurableModuleExtras = {
|
|
104
|
+
isGlobal?: boolean;
|
|
105
|
+
}, MethodKey extends string = 'forRoot', FactoryMethodKey extends string = 'create'>(spec: DefineModuleSpec<Opts, Extras, MethodKey, FactoryMethodKey>): ConfigurableModuleHost<Opts, MethodKey, FactoryMethodKey, Extras>;
|
|
106
|
+
/**
|
|
107
|
+
* Lower `useFactory`/`useClass`/`useExisting` async options into provider
|
|
108
|
+
* registrations. Structural fields from the call site merge UNDER the resolved
|
|
109
|
+
* options (`{ ...structural, ...resolved }`) so sync-declared fields act as
|
|
110
|
+
* defaults and the factory stays authoritative.
|
|
111
|
+
*/
|
|
112
|
+
export declare function buildAsyncOptionsProviders<Opts>(optionsToken: Token, factoryMethodName: string, async: Pick<ConfigurableModuleAsyncOptions<Opts>, 'inject' | 'useFactory' | 'useClass' | 'useExisting'>, structural?: Record<string, unknown>): Array<ProviderOptions>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { InjectionToken } from "../container/types.js";
|
|
2
|
+
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "../pipeline/tokens.js";
|
|
3
|
+
import { stableHash } from "./stable-hash.js";
|
|
4
|
+
const DEFAULT_EXTRAS = {
|
|
5
|
+
isGlobal: false
|
|
6
|
+
};
|
|
7
|
+
const DEFAULT_TRANSFORM = (def, extras)=>extras.isGlobal ? {
|
|
8
|
+
...def,
|
|
9
|
+
global: true
|
|
10
|
+
} : def;
|
|
11
|
+
const GLOBAL_SLOT_TOKENS = {
|
|
12
|
+
guards: APP_GUARD,
|
|
13
|
+
pipes: APP_PIPE,
|
|
14
|
+
interceptors: APP_INTERCEPTOR,
|
|
15
|
+
filters: APP_FILTER,
|
|
16
|
+
middleware: APP_MIDDLEWARE
|
|
17
|
+
};
|
|
18
|
+
/** Lower a `global:` slot to `APP_*` provider registrations. */ function lowerGlobalSlot(slot) {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const kind of Object.keys(GLOBAL_SLOT_TOKENS)){
|
|
21
|
+
for (const component of slot[kind] ?? []){
|
|
22
|
+
const token = GLOBAL_SLOT_TOKENS[kind];
|
|
23
|
+
if (typeof component === 'function') {
|
|
24
|
+
out.push(component, {
|
|
25
|
+
provide: token,
|
|
26
|
+
useExisting: component
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
out.push({
|
|
30
|
+
provide: token,
|
|
31
|
+
useValue: component
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
const ASYNC_OPTION_KEYS = new Set([
|
|
39
|
+
'key',
|
|
40
|
+
'imports',
|
|
41
|
+
'inject',
|
|
42
|
+
'useFactory',
|
|
43
|
+
'useClass',
|
|
44
|
+
'useExisting'
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* The one blessed module-authoring engine. Generates `forRoot` AND
|
|
48
|
+
* `forRootAsync` statics with: `stableHash` key derivation (multi-instance
|
|
49
|
+
* dedup that survives HMR), typed `inject` tuple inference on the async
|
|
50
|
+
* factory, an `isGlobal` extra, and — the piece `ConfigurableModuleBuilder`
|
|
51
|
+
* could not express — providers/controllers/imports/exports/global components
|
|
52
|
+
* computed **as functions of the options**.
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } = defineModule<CorsOptions>({
|
|
56
|
+
* name: 'Cors',
|
|
57
|
+
* setup: ({ OPTIONS }) => ({
|
|
58
|
+
* providers: [{
|
|
59
|
+
* provide: APP_MIDDLEWARE,
|
|
60
|
+
* useFactory: (o: CorsOptions) => buildCorsMiddleware(o),
|
|
61
|
+
* inject: [OPTIONS],
|
|
62
|
+
* }],
|
|
63
|
+
* exports: [OPTIONS],
|
|
64
|
+
* }),
|
|
65
|
+
* });
|
|
66
|
+
* export class CorsModule extends ConfigurableModuleClass {}
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* `ConfigurableModuleBuilder` remains supported (NestJS parity) and is a thin
|
|
70
|
+
* adapter over this engine.
|
|
71
|
+
*/ export function defineModule(spec) {
|
|
72
|
+
const optionsToken = spec.optionsToken ?? new InjectionToken(`${spec.name}_MODULE_OPTIONS`);
|
|
73
|
+
const syncName = spec.methodName ?? 'forRoot';
|
|
74
|
+
const asyncName = `${syncName}Async`;
|
|
75
|
+
const factoryMethodName = spec.factoryMethodName ?? 'create';
|
|
76
|
+
const extrasDefaults = spec.extras ?? DEFAULT_EXTRAS;
|
|
77
|
+
const transform = spec.transform ?? DEFAULT_TRANSFORM;
|
|
78
|
+
const deriveKey = (explicit, structural)=>explicit ?? spec.key?.(structural) ?? stableHash(structural);
|
|
79
|
+
// Laziness is OR-composed: the spec defaults it, a call site can add it.
|
|
80
|
+
const applyLazy = (definition, callSiteLazy)=>spec.lazy === true || callSiteLazy === true ? {
|
|
81
|
+
...definition,
|
|
82
|
+
lazy: true
|
|
83
|
+
} : definition;
|
|
84
|
+
const applyContributions = (definition, structural, key)=>{
|
|
85
|
+
if (!spec.setup) return definition;
|
|
86
|
+
const contributions = spec.setup({
|
|
87
|
+
OPTIONS: optionsToken,
|
|
88
|
+
options: structural,
|
|
89
|
+
key
|
|
90
|
+
});
|
|
91
|
+
const providers = [
|
|
92
|
+
...definition.providers ?? [],
|
|
93
|
+
...contributions.providers ?? [],
|
|
94
|
+
...contributions.global ? lowerGlobalSlot(contributions.global) : []
|
|
95
|
+
];
|
|
96
|
+
return {
|
|
97
|
+
...definition,
|
|
98
|
+
providers,
|
|
99
|
+
controllers: [
|
|
100
|
+
...definition.controllers ?? [],
|
|
101
|
+
...contributions.controllers ?? []
|
|
102
|
+
],
|
|
103
|
+
imports: [
|
|
104
|
+
...definition.imports ?? [],
|
|
105
|
+
...contributions.imports ?? []
|
|
106
|
+
],
|
|
107
|
+
exports: [
|
|
108
|
+
...definition.exports ?? [],
|
|
109
|
+
...contributions.exports ?? []
|
|
110
|
+
]
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
let GeneratedModuleClass = class GeneratedModuleClass {
|
|
114
|
+
};
|
|
115
|
+
Object.defineProperty(GeneratedModuleClass, 'name', {
|
|
116
|
+
value: `${spec.name}ModuleHost`
|
|
117
|
+
});
|
|
118
|
+
Object.defineProperty(GeneratedModuleClass, syncName, {
|
|
119
|
+
configurable: true,
|
|
120
|
+
writable: true,
|
|
121
|
+
enumerable: false,
|
|
122
|
+
value (options = {}) {
|
|
123
|
+
const { key: explicitKey, ...rest } = options;
|
|
124
|
+
const key = deriveKey(explicitKey, rest);
|
|
125
|
+
const definition = {
|
|
126
|
+
module: this,
|
|
127
|
+
key,
|
|
128
|
+
providers: [
|
|
129
|
+
{
|
|
130
|
+
provide: optionsToken,
|
|
131
|
+
useValue: rest
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
return applyLazy(transform(applyContributions(definition, rest, key), {
|
|
136
|
+
...extrasDefaults,
|
|
137
|
+
...rest
|
|
138
|
+
}), rest.lazy);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
Object.defineProperty(GeneratedModuleClass, asyncName, {
|
|
142
|
+
configurable: true,
|
|
143
|
+
writable: true,
|
|
144
|
+
enumerable: false,
|
|
145
|
+
value (options = {}) {
|
|
146
|
+
const bag = options;
|
|
147
|
+
const structural = {};
|
|
148
|
+
for (const [k, v] of Object.entries(bag)){
|
|
149
|
+
if (!ASYNC_OPTION_KEYS.has(k)) structural[k] = v;
|
|
150
|
+
}
|
|
151
|
+
const key = deriveKey(bag.key, // Async factories aren't structurally hashable in a useful way, so the
|
|
152
|
+
// default key hashes the async wiring + structural fields — same
|
|
153
|
+
// instance-identity semantics ConfigurableModuleBuilder always had.
|
|
154
|
+
spec.key ? structural : {
|
|
155
|
+
inject: bag.inject,
|
|
156
|
+
useFactory: bag.useFactory,
|
|
157
|
+
useClass: bag.useClass,
|
|
158
|
+
useExisting: bag.useExisting,
|
|
159
|
+
...structural
|
|
160
|
+
});
|
|
161
|
+
const definition = {
|
|
162
|
+
module: this,
|
|
163
|
+
key,
|
|
164
|
+
imports: bag.imports ?? [],
|
|
165
|
+
providers: buildAsyncOptionsProviders(optionsToken, factoryMethodName, bag, structural)
|
|
166
|
+
};
|
|
167
|
+
return applyLazy(transform(applyContributions(definition, structural, key), {
|
|
168
|
+
...extrasDefaults,
|
|
169
|
+
...structural
|
|
170
|
+
}), structural.lazy);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
ConfigurableModuleClass: GeneratedModuleClass,
|
|
175
|
+
MODULE_OPTIONS_TOKEN: optionsToken,
|
|
176
|
+
// Type-only sentinels — never read at runtime.
|
|
177
|
+
OPTIONS_TYPE: undefined,
|
|
178
|
+
ASYNC_OPTIONS_TYPE: undefined
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Lower `useFactory`/`useClass`/`useExisting` async options into provider
|
|
183
|
+
* registrations. Structural fields from the call site merge UNDER the resolved
|
|
184
|
+
* options (`{ ...structural, ...resolved }`) so sync-declared fields act as
|
|
185
|
+
* defaults and the factory stays authoritative.
|
|
186
|
+
*/ export function buildAsyncOptionsProviders(optionsToken, factoryMethodName, async, structural = {}) {
|
|
187
|
+
const hasStructural = Object.keys(structural).length > 0;
|
|
188
|
+
const merge = (resolved)=>resolved instanceof Promise ? resolved.then((o)=>({
|
|
189
|
+
...structural,
|
|
190
|
+
...o
|
|
191
|
+
})) : {
|
|
192
|
+
...structural,
|
|
193
|
+
...resolved
|
|
194
|
+
};
|
|
195
|
+
if (async.useFactory) {
|
|
196
|
+
const factory = async.useFactory;
|
|
197
|
+
return [
|
|
198
|
+
{
|
|
199
|
+
provide: optionsToken,
|
|
200
|
+
// No structural fields → pass the caller's factory through untouched
|
|
201
|
+
// (function identity preserved; nothing to merge).
|
|
202
|
+
useFactory: hasStructural ? (...deps)=>merge(factory(...deps)) : factory,
|
|
203
|
+
inject: async.inject ?? []
|
|
204
|
+
}
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
if (async.useClass) {
|
|
208
|
+
const factoryClass = async.useClass;
|
|
209
|
+
return [
|
|
210
|
+
factoryClass,
|
|
211
|
+
{
|
|
212
|
+
provide: optionsToken,
|
|
213
|
+
useFactory: (instance)=>merge(instance[factoryMethodName]()),
|
|
214
|
+
inject: [
|
|
215
|
+
factoryClass
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
if (async.useExisting) {
|
|
221
|
+
return [
|
|
222
|
+
{
|
|
223
|
+
provide: optionsToken,
|
|
224
|
+
useFactory: (instance)=>merge(instance[factoryMethodName]()),
|
|
225
|
+
inject: [
|
|
226
|
+
async.useExisting
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
];
|
|
230
|
+
}
|
|
231
|
+
throw new Error('Async module options require one of `useFactory`, `useClass`, or `useExisting`.');
|
|
232
|
+
}
|
package/dist/module/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { Global, Module, isModule, getModuleMetadata, defineDynamicModule, } from './decorators';
|
|
2
2
|
export { stableHash } from './stable-hash';
|
|
3
|
-
export { ConfigurableModuleBuilder, defineConfigurableModule, } from './configurable-module.builder';
|
|
3
|
+
export { ConfigurableModuleBuilder, defineConfigurableModule, moduleKey, } from './configurable-module.builder';
|
|
4
|
+
export { defineModule, buildAsyncOptionsProviders, type DefineModuleSpec, type GlobalComponentSlot, type ModuleContributions, type ModuleSetupContext, } from './define-module';
|
|
5
|
+
export { lazyProvider, moduleToken, provideGlobal, sideEffectModule, type LazyProviderSpec, } from './lazy-provider';
|
|
4
6
|
export type { ConfigurableModuleAsyncOptions, ConfigurableModuleBuilderOptions, ConfigurableModuleClassType, ConfigurableModuleExtras, ConfigurableModuleExtrasTransform, ConfigurableModuleHost, ConfigurableModuleOptionsFactory, DefineConfigurableModuleSpec, } from './configurable-module.types';
|
|
5
7
|
export { ModuleLoader } from './module-loader';
|
|
6
8
|
export { MiddlewareBuilder } from './middleware';
|
package/dist/module/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Global, Module, isModule, getModuleMetadata, defineDynamicModule } from "./decorators.js";
|
|
2
2
|
export { stableHash } from "./stable-hash.js";
|
|
3
|
-
export { ConfigurableModuleBuilder, defineConfigurableModule } from "./configurable-module.builder.js";
|
|
3
|
+
export { ConfigurableModuleBuilder, defineConfigurableModule, moduleKey } from "./configurable-module.builder.js";
|
|
4
|
+
export { defineModule, buildAsyncOptionsProviders } from "./define-module.js";
|
|
5
|
+
export { lazyProvider, moduleToken, provideGlobal, sideEffectModule } from "./lazy-provider.js";
|
|
4
6
|
export { ModuleLoader } from "./module-loader.js";
|
|
5
7
|
export { MiddlewareBuilder } from "./middleware.js";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Container } from '../container/container';
|
|
2
|
+
import type { LazyResolutionHook, Token } from '../container/types';
|
|
3
|
+
/** One lazy module instance's deferral unit: its own tokens, in registration order. */
|
|
4
|
+
export interface LazyModuleGroup {
|
|
5
|
+
moduleId: string;
|
|
6
|
+
tokens: Token[];
|
|
7
|
+
/**
|
|
8
|
+
* A class provider's prototype declares `collectEntrypoints` — the module
|
|
9
|
+
* must be materialized before the entrypoint registry snapshot or its
|
|
10
|
+
* computed contributions would be silently absent.
|
|
11
|
+
*/
|
|
12
|
+
hasEntrypointContributor: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Owns deferred-module state for one application: which module instances are
|
|
16
|
+
* still pending, which have been claimed by a container trigger, and how
|
|
17
|
+
* claimed groups get completed (construction + lifecycle-hook replay).
|
|
18
|
+
*
|
|
19
|
+
* Phases:
|
|
20
|
+
* - `'bootstrap'` (until the end of `callOnApplicationBootstrap`): completed
|
|
21
|
+
* groups are *absorbed* — their instances queue up for the application's
|
|
22
|
+
* normal hook phases, so a lazy module dragged in by an eager consumer gets
|
|
23
|
+
* today-identical semantics.
|
|
24
|
+
* - `'live'`: hooks replay at drain time (memoized, exactly once), then the
|
|
25
|
+
* instances are handed to the application for shutdown-hook symmetry.
|
|
26
|
+
*
|
|
27
|
+
* Sync/async duality: sync drains throw a descriptive error when a member
|
|
28
|
+
* factory or hook yields a thenable — lazy modules with async providers or
|
|
29
|
+
* hooks must be reached through an async seam (`materializeLazyModules()`,
|
|
30
|
+
* `resolveAsync`) or stay eager.
|
|
31
|
+
*/
|
|
32
|
+
export declare class LazyModuleManager implements LazyResolutionHook {
|
|
33
|
+
private readonly container;
|
|
34
|
+
private readonly pending;
|
|
35
|
+
private readonly claimed;
|
|
36
|
+
private readonly absorbed;
|
|
37
|
+
private phase;
|
|
38
|
+
private draining;
|
|
39
|
+
private drainPromise?;
|
|
40
|
+
private onMaterialized?;
|
|
41
|
+
constructor(container: Container);
|
|
42
|
+
registerGroup(group: LazyModuleGroup): void;
|
|
43
|
+
/** Application sink for live-phase materializations (shutdown symmetry). */
|
|
44
|
+
setOnMaterialized(sink: (instances: unknown[]) => void): void;
|
|
45
|
+
setPhaseLive(): void;
|
|
46
|
+
isPending(moduleId: string): boolean;
|
|
47
|
+
/** Tokens whose owning modules are ALL still pending are deferred. */
|
|
48
|
+
hasPendingModules(): boolean;
|
|
49
|
+
claim(moduleId: string): void;
|
|
50
|
+
hasClaimed(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* A drain loop is running. The container consults this before its
|
|
53
|
+
* post-resolution drain: `constructGroupAsync` resolves members through
|
|
54
|
+
* `resolveAsync`, whose end-of-cascade check would otherwise re-enter and
|
|
55
|
+
* AWAIT the very drain promise it is executing inside — a self-referential
|
|
56
|
+
* await that never settles (with ≥2 claimed groups). Claims made while
|
|
57
|
+
* draining are picked up by the running loop instead.
|
|
58
|
+
*/
|
|
59
|
+
isDraining(): boolean;
|
|
60
|
+
/** Instances constructed during the bootstrap phase, owed their hooks. */
|
|
61
|
+
takeAbsorbed(): unknown[];
|
|
62
|
+
drainSync(): void;
|
|
63
|
+
drainAsync(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Materialize every still-pending group whose providers compute entrypoints
|
|
66
|
+
* (`collectEntrypoints` on a class prototype). Called by the application
|
|
67
|
+
* right before the entrypoint-registry snapshot so computed contributions
|
|
68
|
+
* are never silently absent. The cost — those modules are effectively
|
|
69
|
+
* eager — is the documented price of contributing computed entrypoints.
|
|
70
|
+
*/
|
|
71
|
+
materializeContributors(): Promise<void>;
|
|
72
|
+
/** Materialize everything still pending (warmup / tests / node runtimes). */
|
|
73
|
+
materializeAll(): Promise<void>;
|
|
74
|
+
private groupTokensToConstruct;
|
|
75
|
+
private constructGroupSync;
|
|
76
|
+
private constructGroupAsync;
|
|
77
|
+
/** Reversed batch = dependency-before-consumer (see drainSync comment). */
|
|
78
|
+
private orderBatch;
|
|
79
|
+
private finishBatchSync;
|
|
80
|
+
private finishBatchAsync;
|
|
81
|
+
private describeSyncFailure;
|
|
82
|
+
}
|