@velajs/vela 1.9.0 → 1.12.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 +122 -0
- package/dist/__tests__/workers-als/entry-als.d.ts +9 -0
- package/dist/application.d.ts +18 -0
- package/dist/application.js +45 -0
- package/dist/cache/cache.module.d.ts +5 -7
- package/dist/cache/cache.module.js +44 -66
- package/dist/cache/cache.types.d.ts +26 -0
- package/dist/cache/cache.types.js +1 -1
- package/dist/cache/index.d.ts +2 -1
- package/dist/cache/index.js +1 -0
- package/dist/cache/tiered-cache.store.d.ts +19 -0
- package/dist/cache/tiered-cache.store.js +40 -0
- package/dist/config/config.module.d.ts +6 -5
- package/dist/config/config.module.js +46 -49
- package/dist/container/container.d.ts +58 -0
- package/dist/container/container.js +223 -18
- package/dist/container/disposable.d.ts +5 -0
- package/dist/container/disposable.js +33 -0
- package/dist/container/types.d.ts +8 -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 +94 -0
- package/dist/discovery/discovery.service.js +166 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.js +2 -0
- package/dist/entrypoint/entrypoint.registry.d.ts +40 -0
- package/dist/entrypoint/entrypoint.registry.js +106 -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.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 +20 -2
- package/dist/factory.d.ts +11 -1
- package/dist/factory.js +23 -2
- package/dist/fetch/fetch.module.d.ts +5 -6
- package/dist/fetch/fetch.module.js +9 -42
- package/dist/http/ambient.d.ts +17 -0
- package/dist/http/ambient.js +44 -0
- package/dist/http/execution-context.js +4 -1
- package/dist/http/handler-executor.js +22 -19
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/route-contributor.d.ts +68 -0
- package/dist/http/route-contributor.js +22 -0
- package/dist/http/route.manager.d.ts +19 -0
- package/dist/http/route.manager.js +112 -19
- package/dist/i18n/deep-merge.d.ts +2 -0
- package/dist/i18n/deep-merge.js +15 -0
- package/dist/i18n/i18n.middleware.d.ts +15 -0
- package/dist/i18n/i18n.middleware.js +94 -0
- package/dist/i18n/i18n.module.d.ts +13 -0
- package/dist/i18n/i18n.module.js +64 -0
- package/dist/i18n/i18n.options.d.ts +30 -0
- package/dist/i18n/i18n.options.js +16 -0
- package/dist/i18n/i18n.service.d.ts +20 -0
- package/dist/i18n/i18n.service.js +49 -0
- package/dist/i18n/i18n.tokens.d.ts +6 -0
- package/dist/i18n/i18n.tokens.js +3 -0
- package/dist/i18n/i18n.types.d.ts +6 -0
- package/dist/i18n/i18n.types.js +1 -0
- package/dist/i18n/index.d.ts +10 -0
- package/dist/i18n/index.js +8 -0
- package/dist/i18n/message-loader.service.d.ts +21 -0
- package/dist/i18n/message-loader.service.js +90 -0
- package/dist/i18n/message-registry.d.ts +16 -0
- package/dist/i18n/message-registry.js +49 -0
- package/dist/index.d.ts +17 -5
- package/dist/index.js +14 -3
- package/dist/internal.d.ts +0 -2
- package/dist/internal.js +0 -7
- package/dist/module/configurable-module.builder.d.ts +56 -0
- package/dist/module/configurable-module.builder.js +84 -0
- package/dist/module/configurable-module.types.d.ts +93 -0
- package/dist/module/configurable-module.types.js +5 -0
- package/dist/module/define-module.d.ts +105 -0
- package/dist/module/define-module.js +227 -0
- package/dist/module/index.d.ts +4 -0
- package/dist/module/index.js +3 -0
- package/dist/module/lazy-provider.d.ts +65 -0
- package/dist/module/lazy-provider.js +111 -0
- 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/pipeline/types.d.ts +21 -4
- package/dist/registry/metadata.registry.d.ts +24 -18
- package/dist/registry/metadata.registry.js +137 -49
- package/dist/schedule/schedule.module.d.ts +5 -0
- package/dist/schedule/schedule.module.js +18 -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/index.d.ts +5 -0
- package/dist/seeder/index.js +4 -0
- package/dist/seeder/seeder.decorator.d.ts +7 -0
- package/dist/seeder/seeder.decorator.js +15 -0
- package/dist/seeder/seeder.module.d.ts +16 -0
- package/dist/seeder/seeder.module.js +41 -0
- package/dist/seeder/seeder.registry.d.ts +23 -0
- package/dist/seeder/seeder.registry.js +83 -0
- package/dist/seeder/seeder.tokens.d.ts +2 -0
- package/dist/seeder/seeder.tokens.js +1 -0
- package/dist/seeder/seeder.types.d.ts +20 -0
- package/dist/seeder/seeder.types.js +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/path-template.d.ts +9 -0
- package/dist/storage/path-template.js +20 -0
- package/dist/storage/signed-url.d.ts +12 -0
- package/dist/storage/signed-url.js +55 -0
- package/dist/storage/storage.types.d.ts +41 -0
- package/dist/storage/storage.types.js +5 -0
- package/dist/throttler/throttler.module.d.ts +5 -8
- package/dist/throttler/throttler.module.js +27 -55
- package/dist/websocket/index.d.ts +12 -0
- package/dist/websocket/index.js +16 -0
- package/dist/websocket/websocket.decorators.d.ts +33 -0
- package/dist/websocket/websocket.decorators.js +58 -0
- package/dist/websocket/websocket.module.d.ts +35 -0
- package/dist/websocket/websocket.module.js +63 -0
- package/dist/websocket/websocket.tokens.d.ts +23 -0
- package/dist/websocket/websocket.tokens.js +21 -0
- package/dist/websocket/websocket.types.d.ts +86 -0
- package/dist/websocket/websocket.types.js +4 -0
- package/dist/websocket/ws-argument-resolver.d.ts +13 -0
- package/dist/websocket/ws-argument-resolver.js +40 -0
- package/dist/websocket/ws-dispatcher.d.ts +45 -0
- package/dist/websocket/ws-dispatcher.js +259 -0
- package/dist/websocket/ws-exception.d.ts +16 -0
- package/dist/websocket/ws-exception.js +32 -0
- package/dist/websocket/ws-execution-context.d.ts +9 -0
- package/dist/websocket/ws-execution-context.js +24 -0
- package/dist/websocket/ws-server.d.ts +31 -0
- package/dist/websocket/ws-server.js +74 -0
- package/dist/websocket/ws-sync.d.ts +47 -0
- package/dist/websocket/ws-sync.js +74 -0
- package/dist/websocket-node/index.d.ts +4 -0
- package/dist/websocket-node/index.js +5 -0
- package/dist/websocket-node/node-ws-client.d.ts +24 -0
- package/dist/websocket-node/node-ws-client.js +49 -0
- package/dist/websocket-node/redis-sync.d.ts +33 -0
- package/dist/websocket-node/redis-sync.js +56 -0
- package/dist/websocket-node/register-gateways.d.ts +20 -0
- package/dist/websocket-node/register-gateways.js +61 -0
- package/package.json +31 -1
- package/dist/http/crud-bridge.d.ts +0 -90
- package/dist/http/crud-bridge.js +0 -25
|
@@ -0,0 +1,227 @@
|
|
|
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
|
+
const applyContributions = (definition, structural, key)=>{
|
|
80
|
+
if (!spec.setup) return definition;
|
|
81
|
+
const contributions = spec.setup({
|
|
82
|
+
OPTIONS: optionsToken,
|
|
83
|
+
options: structural,
|
|
84
|
+
key
|
|
85
|
+
});
|
|
86
|
+
const providers = [
|
|
87
|
+
...definition.providers ?? [],
|
|
88
|
+
...contributions.providers ?? [],
|
|
89
|
+
...contributions.global ? lowerGlobalSlot(contributions.global) : []
|
|
90
|
+
];
|
|
91
|
+
return {
|
|
92
|
+
...definition,
|
|
93
|
+
providers,
|
|
94
|
+
controllers: [
|
|
95
|
+
...definition.controllers ?? [],
|
|
96
|
+
...contributions.controllers ?? []
|
|
97
|
+
],
|
|
98
|
+
imports: [
|
|
99
|
+
...definition.imports ?? [],
|
|
100
|
+
...contributions.imports ?? []
|
|
101
|
+
],
|
|
102
|
+
exports: [
|
|
103
|
+
...definition.exports ?? [],
|
|
104
|
+
...contributions.exports ?? []
|
|
105
|
+
]
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
let GeneratedModuleClass = class GeneratedModuleClass {
|
|
109
|
+
};
|
|
110
|
+
Object.defineProperty(GeneratedModuleClass, 'name', {
|
|
111
|
+
value: `${spec.name}ModuleHost`
|
|
112
|
+
});
|
|
113
|
+
Object.defineProperty(GeneratedModuleClass, syncName, {
|
|
114
|
+
configurable: true,
|
|
115
|
+
writable: true,
|
|
116
|
+
enumerable: false,
|
|
117
|
+
value (options = {}) {
|
|
118
|
+
const { key: explicitKey, ...rest } = options;
|
|
119
|
+
const key = deriveKey(explicitKey, rest);
|
|
120
|
+
const definition = {
|
|
121
|
+
module: this,
|
|
122
|
+
key,
|
|
123
|
+
providers: [
|
|
124
|
+
{
|
|
125
|
+
provide: optionsToken,
|
|
126
|
+
useValue: rest
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
};
|
|
130
|
+
return transform(applyContributions(definition, rest, key), {
|
|
131
|
+
...extrasDefaults,
|
|
132
|
+
...rest
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
Object.defineProperty(GeneratedModuleClass, asyncName, {
|
|
137
|
+
configurable: true,
|
|
138
|
+
writable: true,
|
|
139
|
+
enumerable: false,
|
|
140
|
+
value (options = {}) {
|
|
141
|
+
const bag = options;
|
|
142
|
+
const structural = {};
|
|
143
|
+
for (const [k, v] of Object.entries(bag)){
|
|
144
|
+
if (!ASYNC_OPTION_KEYS.has(k)) structural[k] = v;
|
|
145
|
+
}
|
|
146
|
+
const key = deriveKey(bag.key, // Async factories aren't structurally hashable in a useful way, so the
|
|
147
|
+
// default key hashes the async wiring + structural fields — same
|
|
148
|
+
// instance-identity semantics ConfigurableModuleBuilder always had.
|
|
149
|
+
spec.key ? structural : {
|
|
150
|
+
inject: bag.inject,
|
|
151
|
+
useFactory: bag.useFactory,
|
|
152
|
+
useClass: bag.useClass,
|
|
153
|
+
useExisting: bag.useExisting,
|
|
154
|
+
...structural
|
|
155
|
+
});
|
|
156
|
+
const definition = {
|
|
157
|
+
module: this,
|
|
158
|
+
key,
|
|
159
|
+
imports: bag.imports ?? [],
|
|
160
|
+
providers: buildAsyncOptionsProviders(optionsToken, factoryMethodName, bag, structural)
|
|
161
|
+
};
|
|
162
|
+
return transform(applyContributions(definition, structural, key), {
|
|
163
|
+
...extrasDefaults,
|
|
164
|
+
...structural
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
ConfigurableModuleClass: GeneratedModuleClass,
|
|
170
|
+
MODULE_OPTIONS_TOKEN: optionsToken,
|
|
171
|
+
// Type-only sentinels — never read at runtime.
|
|
172
|
+
OPTIONS_TYPE: undefined,
|
|
173
|
+
ASYNC_OPTIONS_TYPE: undefined
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Lower `useFactory`/`useClass`/`useExisting` async options into provider
|
|
178
|
+
* registrations. Structural fields from the call site merge UNDER the resolved
|
|
179
|
+
* options (`{ ...structural, ...resolved }`) so sync-declared fields act as
|
|
180
|
+
* defaults and the factory stays authoritative.
|
|
181
|
+
*/ export function buildAsyncOptionsProviders(optionsToken, factoryMethodName, async, structural = {}) {
|
|
182
|
+
const hasStructural = Object.keys(structural).length > 0;
|
|
183
|
+
const merge = (resolved)=>resolved instanceof Promise ? resolved.then((o)=>({
|
|
184
|
+
...structural,
|
|
185
|
+
...o
|
|
186
|
+
})) : {
|
|
187
|
+
...structural,
|
|
188
|
+
...resolved
|
|
189
|
+
};
|
|
190
|
+
if (async.useFactory) {
|
|
191
|
+
const factory = async.useFactory;
|
|
192
|
+
return [
|
|
193
|
+
{
|
|
194
|
+
provide: optionsToken,
|
|
195
|
+
// No structural fields → pass the caller's factory through untouched
|
|
196
|
+
// (function identity preserved; nothing to merge).
|
|
197
|
+
useFactory: hasStructural ? (...deps)=>merge(factory(...deps)) : factory,
|
|
198
|
+
inject: async.inject ?? []
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
if (async.useClass) {
|
|
203
|
+
const factoryClass = async.useClass;
|
|
204
|
+
return [
|
|
205
|
+
factoryClass,
|
|
206
|
+
{
|
|
207
|
+
provide: optionsToken,
|
|
208
|
+
useFactory: (instance)=>merge(instance[factoryMethodName]()),
|
|
209
|
+
inject: [
|
|
210
|
+
factoryClass
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
];
|
|
214
|
+
}
|
|
215
|
+
if (async.useExisting) {
|
|
216
|
+
return [
|
|
217
|
+
{
|
|
218
|
+
provide: optionsToken,
|
|
219
|
+
useFactory: (instance)=>merge(instance[factoryMethodName]()),
|
|
220
|
+
inject: [
|
|
221
|
+
async.useExisting
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
throw new Error('Async module options require one of `useFactory`, `useClass`, or `useExisting`.');
|
|
227
|
+
}
|
package/dist/module/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { Global, Module, isModule, getModuleMetadata, defineDynamicModule, } from './decorators';
|
|
2
2
|
export { stableHash } from './stable-hash';
|
|
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';
|
|
6
|
+
export type { ConfigurableModuleAsyncOptions, ConfigurableModuleBuilderOptions, ConfigurableModuleClassType, ConfigurableModuleExtras, ConfigurableModuleExtrasTransform, ConfigurableModuleHost, ConfigurableModuleOptionsFactory, DefineConfigurableModuleSpec, } from './configurable-module.types';
|
|
3
7
|
export { ModuleLoader } from './module-loader';
|
|
4
8
|
export { MiddlewareBuilder } from './middleware';
|
|
5
9
|
export type { MiddlewareConsumer, MiddlewareConfigProxy, MiddlewareRouteDefinition, NestModule, RouteInfo, } from './middleware';
|
package/dist/module/index.js
CHANGED
|
@@ -1,4 +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, moduleKey } from "./configurable-module.builder.js";
|
|
4
|
+
export { defineModule, buildAsyncOptionsProviders } from "./define-module.js";
|
|
5
|
+
export { lazyProvider, moduleToken, provideGlobal, sideEffectModule } from "./lazy-provider.js";
|
|
3
6
|
export { ModuleLoader } from "./module-loader.js";
|
|
4
7
|
export { MiddlewareBuilder } from "./middleware.js";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { InjectionToken, type InferTokens, type ProviderOptions, type Token, type Type } from '../container/types';
|
|
2
|
+
import type { ComponentType, DynamicModule } from '../registry/types';
|
|
3
|
+
import type { ModuleContributions } from './define-module';
|
|
4
|
+
export interface LazyProviderSpec<T, Inject extends readonly Token<unknown>[]> {
|
|
5
|
+
/** Token under which the memoized thunk `() => T` is provided. */
|
|
6
|
+
provide: Token<() => T>;
|
|
7
|
+
inject?: Inject;
|
|
8
|
+
useFactory: (...deps: InferTokens<Inject>) => T;
|
|
9
|
+
/** Memoize the first call's result (default true). */
|
|
10
|
+
memoize?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Provide a zero-arg thunk `() => T` whose factory runs on FIRST CALL, not at
|
|
14
|
+
* provider construction — for values that don't exist yet when the module
|
|
15
|
+
* graph is built (Cloudflare bindings are only live at request time).
|
|
16
|
+
*
|
|
17
|
+
* The shared primitive replacing the hand-rolled
|
|
18
|
+
* `useFactory: (...deps) => () => build(...deps)` closure that auth and
|
|
19
|
+
* storage each copy-pasted.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* lazyProvider({
|
|
23
|
+
* provide: STORAGE_DRIVER_BUILDER,
|
|
24
|
+
* inject: [MODULE_OPTIONS_TOKEN],
|
|
25
|
+
* useFactory: (options) => options.driver(),
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function lazyProvider<T, const Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]>(spec: LazyProviderSpec<T, Inject>): ProviderOptions;
|
|
30
|
+
/**
|
|
31
|
+
* The one idiom for registering an app-wide component from a module's
|
|
32
|
+
* providers. Returns registrations to spread:
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* providers: [MyService, ...provideGlobal('guard', AuthGuard)]
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* Class components are registered as providers and wired via `useExisting`
|
|
39
|
+
* (so DI constructs them with their dependencies); instances via `useValue`.
|
|
40
|
+
* Inside `defineModule`, prefer the equivalent `global:` contribution slot.
|
|
41
|
+
*/
|
|
42
|
+
export declare function provideGlobal(kind: ComponentType, component: Type | object): Array<Type | ProviderOptions>;
|
|
43
|
+
/**
|
|
44
|
+
* A first-class side-effect-only module: contributes providers/exports without
|
|
45
|
+
* being a configurable module — the supported form of the "empty marker
|
|
46
|
+
* module" trick (i18n's `registerMessages`). Content-derived `key` makes
|
|
47
|
+
* identical contributions dedup (HMR-idempotent) while distinct ones coexist.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* export function registerMessages(messages: Messages): DynamicModule {
|
|
51
|
+
* return sideEffectModule('I18nMessages', {
|
|
52
|
+
* providers: [{ provide: I18N_MESSAGES, useValue: messages }],
|
|
53
|
+
* exports: [I18N_MESSAGES],
|
|
54
|
+
* });
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function sideEffectModule(name: string, contributions?: Omit<ModuleContributions, 'global'> & {
|
|
59
|
+
key?: string;
|
|
60
|
+
}): DynamicModule;
|
|
61
|
+
/**
|
|
62
|
+
* Blessed token-minting convention: always an `InjectionToken` (never a raw
|
|
63
|
+
* string), named for diagnostics. Namespace it `'<pkg>:<area>:<thing>'`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function moduleToken<T>(name: string): InjectionToken<T>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { InjectionToken } from "../container/types.js";
|
|
2
|
+
import { Module } from "./decorators.js";
|
|
3
|
+
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "../pipeline/tokens.js";
|
|
4
|
+
import { stableHash } from "./stable-hash.js";
|
|
5
|
+
/**
|
|
6
|
+
* Provide a zero-arg thunk `() => T` whose factory runs on FIRST CALL, not at
|
|
7
|
+
* provider construction — for values that don't exist yet when the module
|
|
8
|
+
* graph is built (Cloudflare bindings are only live at request time).
|
|
9
|
+
*
|
|
10
|
+
* The shared primitive replacing the hand-rolled
|
|
11
|
+
* `useFactory: (...deps) => () => build(...deps)` closure that auth and
|
|
12
|
+
* storage each copy-pasted.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* lazyProvider({
|
|
16
|
+
* provide: STORAGE_DRIVER_BUILDER,
|
|
17
|
+
* inject: [MODULE_OPTIONS_TOKEN],
|
|
18
|
+
* useFactory: (options) => options.driver(),
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/ export function lazyProvider(spec) {
|
|
22
|
+
const memoize = spec.memoize ?? true;
|
|
23
|
+
return {
|
|
24
|
+
provide: spec.provide,
|
|
25
|
+
inject: [
|
|
26
|
+
...spec.inject ?? []
|
|
27
|
+
],
|
|
28
|
+
useFactory: (...deps)=>{
|
|
29
|
+
const build = ()=>spec.useFactory(...deps);
|
|
30
|
+
if (!memoize) return build;
|
|
31
|
+
let cached;
|
|
32
|
+
return ()=>(cached ??= {
|
|
33
|
+
value: build()
|
|
34
|
+
}).value;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const GLOBAL_COMPONENT_TOKENS = {
|
|
39
|
+
guard: APP_GUARD,
|
|
40
|
+
pipe: APP_PIPE,
|
|
41
|
+
interceptor: APP_INTERCEPTOR,
|
|
42
|
+
filter: APP_FILTER,
|
|
43
|
+
middleware: APP_MIDDLEWARE
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The one idiom for registering an app-wide component from a module's
|
|
47
|
+
* providers. Returns registrations to spread:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* providers: [MyService, ...provideGlobal('guard', AuthGuard)]
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Class components are registered as providers and wired via `useExisting`
|
|
54
|
+
* (so DI constructs them with their dependencies); instances via `useValue`.
|
|
55
|
+
* Inside `defineModule`, prefer the equivalent `global:` contribution slot.
|
|
56
|
+
*/ export function provideGlobal(kind, component) {
|
|
57
|
+
const token = GLOBAL_COMPONENT_TOKENS[kind];
|
|
58
|
+
if (typeof component === 'function') {
|
|
59
|
+
return [
|
|
60
|
+
component,
|
|
61
|
+
{
|
|
62
|
+
provide: token,
|
|
63
|
+
useExisting: component
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [
|
|
68
|
+
{
|
|
69
|
+
provide: token,
|
|
70
|
+
useValue: component
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A first-class side-effect-only module: contributes providers/exports without
|
|
76
|
+
* being a configurable module — the supported form of the "empty marker
|
|
77
|
+
* module" trick (i18n's `registerMessages`). Content-derived `key` makes
|
|
78
|
+
* identical contributions dedup (HMR-idempotent) while distinct ones coexist.
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* export function registerMessages(messages: Messages): DynamicModule {
|
|
82
|
+
* return sideEffectModule('I18nMessages', {
|
|
83
|
+
* providers: [{ provide: I18N_MESSAGES, useValue: messages }],
|
|
84
|
+
* exports: [I18N_MESSAGES],
|
|
85
|
+
* });
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/ export function sideEffectModule(name, contributions = {}) {
|
|
89
|
+
const { key, ...rest } = contributions;
|
|
90
|
+
// Computed-property-name idiom: mints a class whose .name is `name` without
|
|
91
|
+
// dynamic code evaluation (edge-safe; no `new Function`).
|
|
92
|
+
const moduleClass = {
|
|
93
|
+
[name]: class {
|
|
94
|
+
}
|
|
95
|
+
}[name];
|
|
96
|
+
Module({})(moduleClass);
|
|
97
|
+
return {
|
|
98
|
+
module: moduleClass,
|
|
99
|
+
key: key ?? stableHash(rest),
|
|
100
|
+
providers: rest.providers ?? [],
|
|
101
|
+
controllers: rest.controllers ?? [],
|
|
102
|
+
imports: rest.imports ?? [],
|
|
103
|
+
exports: rest.exports ?? []
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Blessed token-minting convention: always an `InjectionToken` (never a raw
|
|
108
|
+
* string), named for diagnostics. Namespace it `'<pkg>:<area>:<thing>'`.
|
|
109
|
+
*/ export function moduleToken(name) {
|
|
110
|
+
return new InjectionToken(name);
|
|
111
|
+
}
|
package/dist/openapi/document.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ParamType } from "../constants.js";
|
|
2
|
+
import { getRouteContributors } from "../http/route-contributor.js";
|
|
3
3
|
import { getMetadata } from "../metadata.js";
|
|
4
4
|
import { collectControllers } from "../module/graph.js";
|
|
5
5
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
@@ -215,25 +215,26 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
215
215
|
paths[pathString] = pathItem;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
-
// Second pass: include `@Crud()
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (bridge) {
|
|
218
|
+
// Second pass: include contributor-generated routes (e.g. `@Crud()` via
|
|
219
|
+
// `@velajs/crud`'s registered RouteContributor). Silent when no contributor
|
|
220
|
+
// is registered or no controller carries claiming metadata, so consumers
|
|
221
|
+
// without contributor packages see no behavioral change.
|
|
222
|
+
for (const contributor of getRouteContributors()){
|
|
223
|
+
if (!contributor.buildOpenApiPaths) continue;
|
|
225
224
|
for (const controller of controllers){
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
225
|
+
const meta = getMetadata(contributor.claimsMetaKey, controller);
|
|
226
|
+
if (meta === undefined) continue;
|
|
228
227
|
const controllerPrefix = MetadataRegistry.getControllerPath(controller);
|
|
229
|
-
const
|
|
228
|
+
const contributedPaths = contributor.buildOpenApiPaths({
|
|
229
|
+
controller: controller,
|
|
230
|
+
meta,
|
|
230
231
|
globalPrefix,
|
|
231
232
|
controllerPrefix
|
|
232
233
|
});
|
|
233
|
-
for (const [pathKey, pathItem] of Object.entries(
|
|
234
|
+
for (const [pathKey, pathItem] of Object.entries(contributedPaths)){
|
|
234
235
|
// Verb-level merge: a hand-written `@Get('/')` on the same controller
|
|
235
|
-
// is preserved when the
|
|
236
|
-
//
|
|
236
|
+
// is preserved when the contributor adds `post`/`patch`/etc. on the
|
|
237
|
+
// same path key.
|
|
237
238
|
paths[pathKey] = {
|
|
238
239
|
...paths[pathKey] ?? {},
|
|
239
240
|
...pathItem
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import type { Container } from '../container/container';
|
|
2
2
|
import type { ComponentType, ComponentTypeMap, Constructor, FilterType, GuardType, InterceptorType, MiddlewareType, PipeType } from '../registry/types';
|
|
3
3
|
import type { ArgumentMetadata, CanActivate, ExceptionFilter, ExecutionContext, NestInterceptor, NestMiddleware, PipeTransform } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Component registration + resolution for the controller/handler tiers.
|
|
6
|
+
* App-wide (global) components have exactly ONE source: the per-app
|
|
7
|
+
* `RouteManager` (`APP_*` provider tokens + `useGlobalX()`) — callers merge
|
|
8
|
+
* `routeManager.getGlobalComponents()` with {@link getScopedComponents}.
|
|
9
|
+
*
|
|
10
|
+
* Stateless by design: no process-global container (two apps in one process
|
|
11
|
+
* never cross-talk) — every `resolve*` takes the resolving container.
|
|
12
|
+
*/
|
|
4
13
|
export declare class ComponentManager {
|
|
5
|
-
private static container;
|
|
6
|
-
static init(container: Container): void;
|
|
7
|
-
static registerGlobal<T extends ComponentType>(type: T, ...components: ComponentTypeMap[T][]): void;
|
|
8
14
|
static registerController<T extends ComponentType>(type: T, controller: Constructor, ...components: ComponentTypeMap[T][]): void;
|
|
9
15
|
static registerHandler<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol, ...components: ComponentTypeMap[T][]): void;
|
|
10
|
-
static
|
|
16
|
+
static getScopedComponents<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol): ComponentTypeMap[T][];
|
|
11
17
|
private static resolveAll;
|
|
12
|
-
static resolveMiddleware(items: MiddlewareType[]): NestMiddleware[];
|
|
13
|
-
static resolveGuards(items: GuardType[]): CanActivate[];
|
|
14
|
-
static resolvePipes(items: PipeType[]): PipeTransform[];
|
|
15
|
-
static resolveInterceptors(items: InterceptorType[]): NestInterceptor[];
|
|
16
|
-
static resolveFilters(items: FilterType[]): ExceptionFilter[];
|
|
18
|
+
static resolveMiddleware(items: MiddlewareType[], container: Container): NestMiddleware[];
|
|
19
|
+
static resolveGuards(items: GuardType[], container: Container): CanActivate[];
|
|
20
|
+
static resolvePipes(items: PipeType[], container: Container): PipeTransform[];
|
|
21
|
+
static resolveInterceptors(items: InterceptorType[], container: Container): NestInterceptor[];
|
|
22
|
+
static resolveFilters(items: FilterType[], container: Container): ExceptionFilter[];
|
|
17
23
|
static executePipes(value: unknown, metadata: ArgumentMetadata, pipes: PipeTransform[]): Promise<unknown>;
|
|
18
24
|
static runInterceptorChain(interceptors: NestInterceptor[], context: ExecutionContext, coreHandler: () => Promise<unknown>): Promise<unknown>;
|
|
19
25
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
2
|
+
import { PipelineRunner } from "./pipeline-runner.js";
|
|
2
3
|
function isObject(value) {
|
|
3
4
|
return typeof value === 'object' && value !== null;
|
|
4
5
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* Component registration + resolution for the controller/handler tiers.
|
|
8
|
+
* App-wide (global) components have exactly ONE source: the per-app
|
|
9
|
+
* `RouteManager` (`APP_*` provider tokens + `useGlobalX()`) — callers merge
|
|
10
|
+
* `routeManager.getGlobalComponents()` with {@link getScopedComponents}.
|
|
11
|
+
*
|
|
12
|
+
* Stateless by design: no process-global container (two apps in one process
|
|
13
|
+
* never cross-talk) — every `resolve*` takes the resolving container.
|
|
14
|
+
*/ export class ComponentManager {
|
|
15
|
+
// Scoped registration: controller → handler
|
|
16
16
|
static registerController(type, controller, ...components) {
|
|
17
17
|
for (const component of components){
|
|
18
18
|
MetadataRegistry.registerController(type, controller, component);
|
|
@@ -23,34 +23,32 @@ export class ComponentManager {
|
|
|
23
23
|
MetadataRegistry.registerHandler(type, controller, handlerName, component);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const handlerComponents = MetadataRegistry.getHandler(type, controller, handlerName);
|
|
26
|
+
// Scoped resolution: controller → handler. App-wide components come from
|
|
27
|
+
// RouteManager (APP_* tokens + useGlobalX()) — merged by the caller.
|
|
28
|
+
static getScopedComponents(type, controller, handlerName) {
|
|
30
29
|
return [
|
|
31
|
-
...MetadataRegistry.
|
|
32
|
-
...
|
|
33
|
-
...handlerComponents
|
|
30
|
+
...MetadataRegistry.getController(type, controller),
|
|
31
|
+
...MetadataRegistry.getHandler(type, controller, handlerName)
|
|
34
32
|
];
|
|
35
33
|
}
|
|
36
|
-
// Type-specific resolvers
|
|
37
|
-
static resolveAll(items, methodKey) {
|
|
38
|
-
return items.map((item)=>isObject(item) && methodKey in item ? item :
|
|
34
|
+
// Type-specific resolvers — explicit container, always.
|
|
35
|
+
static resolveAll(items, methodKey, container) {
|
|
36
|
+
return items.map((item)=>isObject(item) && methodKey in item ? item : container.resolve(item));
|
|
39
37
|
}
|
|
40
|
-
static resolveMiddleware(items) {
|
|
41
|
-
return this.resolveAll(items, 'use');
|
|
38
|
+
static resolveMiddleware(items, container) {
|
|
39
|
+
return this.resolveAll(items, 'use', container);
|
|
42
40
|
}
|
|
43
|
-
static resolveGuards(items) {
|
|
44
|
-
return this.resolveAll(items, 'canActivate');
|
|
41
|
+
static resolveGuards(items, container) {
|
|
42
|
+
return this.resolveAll(items, 'canActivate', container);
|
|
45
43
|
}
|
|
46
|
-
static resolvePipes(items) {
|
|
47
|
-
return this.resolveAll(items, 'transform');
|
|
44
|
+
static resolvePipes(items, container) {
|
|
45
|
+
return this.resolveAll(items, 'transform', container);
|
|
48
46
|
}
|
|
49
|
-
static resolveInterceptors(items) {
|
|
50
|
-
return this.resolveAll(items, 'intercept');
|
|
47
|
+
static resolveInterceptors(items, container) {
|
|
48
|
+
return this.resolveAll(items, 'intercept', container);
|
|
51
49
|
}
|
|
52
|
-
static resolveFilters(items) {
|
|
53
|
-
return this.resolveAll(items, 'catch');
|
|
50
|
+
static resolveFilters(items, container) {
|
|
51
|
+
return this.resolveAll(items, 'catch', container);
|
|
54
52
|
}
|
|
55
53
|
// Pipe execution
|
|
56
54
|
static async executePipes(value, metadata, pipes) {
|
|
@@ -60,21 +58,9 @@ export class ComponentManager {
|
|
|
60
58
|
}
|
|
61
59
|
return transformed;
|
|
62
60
|
}
|
|
63
|
-
// Interceptor chain (onion pattern)
|
|
61
|
+
// Interceptor chain (onion pattern) — canonical implementation lives in
|
|
62
|
+
// PipelineRunner; kept here as a stable alias.
|
|
64
63
|
static async runInterceptorChain(interceptors, context, coreHandler) {
|
|
65
|
-
|
|
66
|
-
return coreHandler();
|
|
67
|
-
}
|
|
68
|
-
let next = {
|
|
69
|
-
handle: coreHandler
|
|
70
|
-
};
|
|
71
|
-
for(let i = interceptors.length - 1; i >= 0; i--){
|
|
72
|
-
const interceptor = interceptors[i];
|
|
73
|
-
const currentNext = next;
|
|
74
|
-
next = {
|
|
75
|
-
handle: ()=>interceptor.intercept(context, currentNext)
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return next.handle();
|
|
64
|
+
return PipelineRunner.chainInterceptors(interceptors, context, coreHandler);
|
|
79
65
|
}
|
|
80
66
|
}
|
package/dist/pipeline/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { ComponentManager } from './component.manager';
|
|
2
|
+
export { PipelineRunner } from './pipeline-runner';
|
|
3
|
+
export type { PipelineRunOptions } from './pipeline-runner';
|
|
2
4
|
export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, getCatchTypes, shouldFilterCatch, } from './decorators';
|
|
3
5
|
export { SetMetadata, Reflector } from './reflector';
|
|
4
6
|
export type { ReflectableDecorator, CreateDecoratorOptions } from './reflector';
|
package/dist/pipeline/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { ComponentManager } from "./component.manager.js";
|
|
2
|
+
export { PipelineRunner } from "./pipeline-runner.js";
|
|
2
3
|
export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, getCatchTypes, shouldFilterCatch } from "./decorators.js";
|
|
3
4
|
export { SetMetadata, Reflector } from "./reflector.js";
|
|
4
5
|
export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./tokens.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CanActivate, ExecutionContext, NestInterceptor } from './types';
|
|
2
|
+
export interface PipelineRunOptions {
|
|
3
|
+
context: ExecutionContext;
|
|
4
|
+
/** Pre-merged (global ++ scoped), pre-instantiated. */
|
|
5
|
+
guards: CanActivate[];
|
|
6
|
+
interceptors: NestInterceptor[];
|
|
7
|
+
/** Extract handler arguments (pipes already bound inside the closure). */
|
|
8
|
+
resolveArgs: () => Promise<unknown[]>;
|
|
9
|
+
/** Call the actual handler. */
|
|
10
|
+
invoke: (args: unknown[]) => Promise<unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* HTTP runs argument extraction + pipes BEFORE guards (a deliberate vela
|
|
13
|
+
* deviation — see handler-executor); WebSocket runs guards first. Default
|
|
14
|
+
* false (guards first).
|
|
15
|
+
*/
|
|
16
|
+
argsBeforeGuards?: boolean;
|
|
17
|
+
/** Error thrown when a guard rejects (default `ForbiddenException`). */
|
|
18
|
+
onGuardReject?: () => Error;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The transport-agnostic guard → pipe → interceptor execution core shared by
|
|
22
|
+
* the HTTP `HandlerExecutor` and the WebSocket `WsDispatcher` (and any future
|
|
23
|
+
* entrypoint dispatcher: queue consumers, scheduled jobs, …). Exception
|
|
24
|
+
* FILTERS stay with the caller — their terminal behavior is transport-specific
|
|
25
|
+
* (HTTP maps to a Response, WS sends an error frame).
|
|
26
|
+
*/
|
|
27
|
+
export declare class PipelineRunner {
|
|
28
|
+
static run(options: PipelineRunOptions): Promise<unknown>;
|
|
29
|
+
/** Interceptor onion — innermost is the core handler. */
|
|
30
|
+
static chainInterceptors(interceptors: NestInterceptor[], context: ExecutionContext, coreHandler: () => Promise<unknown>): Promise<unknown>;
|
|
31
|
+
}
|