@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.
Files changed (161) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/__tests__/workers-als/entry-als.d.ts +9 -0
  3. package/dist/application.d.ts +18 -0
  4. package/dist/application.js +45 -0
  5. package/dist/cache/cache.module.d.ts +5 -7
  6. package/dist/cache/cache.module.js +44 -66
  7. package/dist/cache/cache.types.d.ts +26 -0
  8. package/dist/cache/cache.types.js +1 -1
  9. package/dist/cache/index.d.ts +2 -1
  10. package/dist/cache/index.js +1 -0
  11. package/dist/cache/tiered-cache.store.d.ts +19 -0
  12. package/dist/cache/tiered-cache.store.js +40 -0
  13. package/dist/config/config.module.d.ts +6 -5
  14. package/dist/config/config.module.js +46 -49
  15. package/dist/container/container.d.ts +58 -0
  16. package/dist/container/container.js +223 -18
  17. package/dist/container/disposable.d.ts +5 -0
  18. package/dist/container/disposable.js +33 -0
  19. package/dist/container/types.d.ts +8 -0
  20. package/dist/cors/cors.module.d.ts +5 -5
  21. package/dist/cors/cors.module.js +26 -25
  22. package/dist/discovery/discoverable.decorator.d.ts +38 -0
  23. package/dist/discovery/discoverable.decorator.js +42 -0
  24. package/dist/discovery/discovery.service.d.ts +94 -0
  25. package/dist/discovery/discovery.service.js +166 -0
  26. package/dist/discovery/index.d.ts +2 -0
  27. package/dist/discovery/index.js +2 -0
  28. package/dist/entrypoint/entrypoint.registry.d.ts +40 -0
  29. package/dist/entrypoint/entrypoint.registry.js +106 -0
  30. package/dist/entrypoint/entrypoint.types.d.ts +41 -0
  31. package/dist/entrypoint/entrypoint.types.js +3 -0
  32. package/dist/entrypoint/execution-context.d.ts +15 -0
  33. package/dist/entrypoint/execution-context.js +21 -0
  34. package/dist/entrypoint/execution-scope.d.ts +21 -0
  35. package/dist/entrypoint/execution-scope.js +28 -0
  36. package/dist/entrypoint/index.d.ts +4 -0
  37. package/dist/entrypoint/index.js +4 -0
  38. package/dist/event-emitter/event-emitter.subscriber.d.ts +3 -3
  39. package/dist/event-emitter/event-emitter.subscriber.js +12 -29
  40. package/dist/factory/adapter.d.ts +36 -0
  41. package/dist/factory/adapter.js +18 -0
  42. package/dist/factory/bootstrap.js +20 -2
  43. package/dist/factory.d.ts +11 -1
  44. package/dist/factory.js +23 -2
  45. package/dist/fetch/fetch.module.d.ts +5 -6
  46. package/dist/fetch/fetch.module.js +9 -42
  47. package/dist/http/ambient.d.ts +17 -0
  48. package/dist/http/ambient.js +44 -0
  49. package/dist/http/execution-context.js +4 -1
  50. package/dist/http/handler-executor.js +22 -19
  51. package/dist/http/index.d.ts +1 -0
  52. package/dist/http/index.js +1 -0
  53. package/dist/http/route-contributor.d.ts +68 -0
  54. package/dist/http/route-contributor.js +22 -0
  55. package/dist/http/route.manager.d.ts +19 -0
  56. package/dist/http/route.manager.js +112 -19
  57. package/dist/i18n/deep-merge.d.ts +2 -0
  58. package/dist/i18n/deep-merge.js +15 -0
  59. package/dist/i18n/i18n.middleware.d.ts +15 -0
  60. package/dist/i18n/i18n.middleware.js +94 -0
  61. package/dist/i18n/i18n.module.d.ts +13 -0
  62. package/dist/i18n/i18n.module.js +64 -0
  63. package/dist/i18n/i18n.options.d.ts +30 -0
  64. package/dist/i18n/i18n.options.js +16 -0
  65. package/dist/i18n/i18n.service.d.ts +20 -0
  66. package/dist/i18n/i18n.service.js +49 -0
  67. package/dist/i18n/i18n.tokens.d.ts +6 -0
  68. package/dist/i18n/i18n.tokens.js +3 -0
  69. package/dist/i18n/i18n.types.d.ts +6 -0
  70. package/dist/i18n/i18n.types.js +1 -0
  71. package/dist/i18n/index.d.ts +10 -0
  72. package/dist/i18n/index.js +8 -0
  73. package/dist/i18n/message-loader.service.d.ts +21 -0
  74. package/dist/i18n/message-loader.service.js +90 -0
  75. package/dist/i18n/message-registry.d.ts +16 -0
  76. package/dist/i18n/message-registry.js +49 -0
  77. package/dist/index.d.ts +17 -5
  78. package/dist/index.js +14 -3
  79. package/dist/internal.d.ts +0 -2
  80. package/dist/internal.js +0 -7
  81. package/dist/module/configurable-module.builder.d.ts +56 -0
  82. package/dist/module/configurable-module.builder.js +84 -0
  83. package/dist/module/configurable-module.types.d.ts +93 -0
  84. package/dist/module/configurable-module.types.js +5 -0
  85. package/dist/module/define-module.d.ts +105 -0
  86. package/dist/module/define-module.js +227 -0
  87. package/dist/module/index.d.ts +4 -0
  88. package/dist/module/index.js +3 -0
  89. package/dist/module/lazy-provider.d.ts +65 -0
  90. package/dist/module/lazy-provider.js +111 -0
  91. package/dist/openapi/document.js +16 -15
  92. package/dist/pipeline/component.manager.d.ts +15 -9
  93. package/dist/pipeline/component.manager.js +32 -46
  94. package/dist/pipeline/index.d.ts +2 -0
  95. package/dist/pipeline/index.js +1 -0
  96. package/dist/pipeline/pipeline-runner.d.ts +31 -0
  97. package/dist/pipeline/pipeline-runner.js +40 -0
  98. package/dist/pipeline/types.d.ts +21 -4
  99. package/dist/registry/metadata.registry.d.ts +24 -18
  100. package/dist/registry/metadata.registry.js +137 -49
  101. package/dist/schedule/schedule.module.d.ts +5 -0
  102. package/dist/schedule/schedule.module.js +18 -8
  103. package/dist/schedule/schedule.registry.d.ts +3 -3
  104. package/dist/schedule/schedule.registry.js +23 -44
  105. package/dist/schedule-node/schedule-node.module.d.ts +5 -0
  106. package/dist/schedule-node/schedule-node.module.js +20 -10
  107. package/dist/seeder/index.d.ts +5 -0
  108. package/dist/seeder/index.js +4 -0
  109. package/dist/seeder/seeder.decorator.d.ts +7 -0
  110. package/dist/seeder/seeder.decorator.js +15 -0
  111. package/dist/seeder/seeder.module.d.ts +16 -0
  112. package/dist/seeder/seeder.module.js +41 -0
  113. package/dist/seeder/seeder.registry.d.ts +23 -0
  114. package/dist/seeder/seeder.registry.js +83 -0
  115. package/dist/seeder/seeder.tokens.d.ts +2 -0
  116. package/dist/seeder/seeder.tokens.js +1 -0
  117. package/dist/seeder/seeder.types.d.ts +20 -0
  118. package/dist/seeder/seeder.types.js +1 -0
  119. package/dist/storage/index.d.ts +4 -0
  120. package/dist/storage/index.js +2 -0
  121. package/dist/storage/path-template.d.ts +9 -0
  122. package/dist/storage/path-template.js +20 -0
  123. package/dist/storage/signed-url.d.ts +12 -0
  124. package/dist/storage/signed-url.js +55 -0
  125. package/dist/storage/storage.types.d.ts +41 -0
  126. package/dist/storage/storage.types.js +5 -0
  127. package/dist/throttler/throttler.module.d.ts +5 -8
  128. package/dist/throttler/throttler.module.js +27 -55
  129. package/dist/websocket/index.d.ts +12 -0
  130. package/dist/websocket/index.js +16 -0
  131. package/dist/websocket/websocket.decorators.d.ts +33 -0
  132. package/dist/websocket/websocket.decorators.js +58 -0
  133. package/dist/websocket/websocket.module.d.ts +35 -0
  134. package/dist/websocket/websocket.module.js +63 -0
  135. package/dist/websocket/websocket.tokens.d.ts +23 -0
  136. package/dist/websocket/websocket.tokens.js +21 -0
  137. package/dist/websocket/websocket.types.d.ts +86 -0
  138. package/dist/websocket/websocket.types.js +4 -0
  139. package/dist/websocket/ws-argument-resolver.d.ts +13 -0
  140. package/dist/websocket/ws-argument-resolver.js +40 -0
  141. package/dist/websocket/ws-dispatcher.d.ts +45 -0
  142. package/dist/websocket/ws-dispatcher.js +259 -0
  143. package/dist/websocket/ws-exception.d.ts +16 -0
  144. package/dist/websocket/ws-exception.js +32 -0
  145. package/dist/websocket/ws-execution-context.d.ts +9 -0
  146. package/dist/websocket/ws-execution-context.js +24 -0
  147. package/dist/websocket/ws-server.d.ts +31 -0
  148. package/dist/websocket/ws-server.js +74 -0
  149. package/dist/websocket/ws-sync.d.ts +47 -0
  150. package/dist/websocket/ws-sync.js +74 -0
  151. package/dist/websocket-node/index.d.ts +4 -0
  152. package/dist/websocket-node/index.js +5 -0
  153. package/dist/websocket-node/node-ws-client.d.ts +24 -0
  154. package/dist/websocket-node/node-ws-client.js +49 -0
  155. package/dist/websocket-node/redis-sync.d.ts +33 -0
  156. package/dist/websocket-node/redis-sync.js +56 -0
  157. package/dist/websocket-node/register-gateways.d.ts +20 -0
  158. package/dist/websocket-node/register-gateways.js +61 -0
  159. package/package.json +31 -1
  160. package/dist/http/crud-bridge.d.ts +0 -90
  161. package/dist/http/crud-bridge.js +0 -25
@@ -0,0 +1,40 @@
1
+ import { ForbiddenException } from "../errors/http-exception.js";
2
+ /**
3
+ * The transport-agnostic guard → pipe → interceptor execution core shared by
4
+ * the HTTP `HandlerExecutor` and the WebSocket `WsDispatcher` (and any future
5
+ * entrypoint dispatcher: queue consumers, scheduled jobs, …). Exception
6
+ * FILTERS stay with the caller — their terminal behavior is transport-specific
7
+ * (HTTP maps to a Response, WS sends an error frame).
8
+ */ export class PipelineRunner {
9
+ static async run(options) {
10
+ let args;
11
+ if (options.argsBeforeGuards) {
12
+ args = await options.resolveArgs();
13
+ }
14
+ for (const guard of options.guards){
15
+ const canActivate = await guard.canActivate(options.context);
16
+ if (!canActivate) {
17
+ throw options.onGuardReject?.() ?? new ForbiddenException();
18
+ }
19
+ }
20
+ args ??= await options.resolveArgs();
21
+ const resolvedArgs = args;
22
+ return PipelineRunner.chainInterceptors(options.interceptors, options.context, ()=>options.invoke(resolvedArgs));
23
+ }
24
+ /** Interceptor onion — innermost is the core handler. */ static async chainInterceptors(interceptors, context, coreHandler) {
25
+ if (interceptors.length === 0) {
26
+ return coreHandler();
27
+ }
28
+ let next = {
29
+ handle: coreHandler
30
+ };
31
+ for(let i = interceptors.length - 1; i >= 0; i--){
32
+ const interceptor = interceptors[i];
33
+ const currentNext = next;
34
+ next = {
35
+ handle: ()=>interceptor.intercept(context, currentNext)
36
+ };
37
+ }
38
+ return next.handle();
39
+ }
40
+ }
@@ -11,16 +11,33 @@ export interface HttpArgumentsHost {
11
11
  getRequest<T = Request>(): T;
12
12
  getResponse<T = Context>(): T;
13
13
  }
14
+ /**
15
+ * WebSocket-specific arguments host returned by `ExecutionContext.switchToWs()`.
16
+ * Populated by `@velajs/vela/websocket` when a gateway message is dispatched.
17
+ *
18
+ * - `getClient()` — the connected socket (`WsClient`)
19
+ * - `getData()` — the inbound message payload (the envelope's `data`)
20
+ * - `getPattern()` — the subscribed event name that matched
21
+ */
22
+ export interface WsArgumentsHost {
23
+ getClient<T = unknown>(): T;
24
+ getData<T = unknown>(): T;
25
+ getPattern<T = string>(): T;
26
+ }
27
+ /** The transport a component is executing under. `'http'` for routes, `'ws'` for gateway messages. */
28
+ export type ContextType = 'http' | 'ws';
14
29
  export interface ExecutionContext {
15
- getType<T extends string = 'http'>(): T;
30
+ getType<T extends string = ContextType>(): T;
16
31
  getClass(): Type;
17
32
  getHandler(): string | symbol;
18
- /** Returns the Hono `Context` directly. */
33
+ /** Returns the Hono `Context` directly. Throws on a WebSocket context. */
19
34
  getContext<T = Context>(): T;
20
- /** Shorthand for `switchToHttp().getRequest()` — returns the Web `Request`. */
35
+ /** Shorthand for `switchToHttp().getRequest()` — returns the Web `Request`. Throws on a WebSocket context. */
21
36
  getRequest(): Request;
22
- /** Switch to the HTTP arguments host for NestJS-style `getRequest()` / `getResponse()` access. */
37
+ /** Switch to the HTTP arguments host. Throws on a WebSocket context. */
23
38
  switchToHttp(): HttpArgumentsHost;
39
+ /** Switch to the WebSocket arguments host. Throws on an HTTP context. */
40
+ switchToWs(): WsArgumentsHost;
24
41
  }
25
42
  export interface CanActivate {
26
43
  canActivate(context: ExecutionContext): boolean | Promise<boolean>;
@@ -5,22 +5,23 @@ export interface ControllerOptions {
5
5
  version?: number | number[];
6
6
  }
7
7
  export declare class MetadataRegistry {
8
- private static readonly routes;
9
- private static readonly controllers;
10
- private static readonly controllerOptions;
11
- private static readonly modules;
12
- private static readonly parameters;
13
- private static readonly injectables;
14
- private static readonly scopes;
15
- private static readonly injectTokens;
16
- private static readonly handlerHttpMeta;
17
- private static readonly catchTypes;
18
- private static readonly routeVersions;
19
- private static readonly classMeta;
20
- private static readonly handlerMeta;
21
- private static readonly controllerComponents;
22
- private static readonly handlerComponents;
23
- private static globalComponents;
8
+ private static get routes();
9
+ private static get controllers();
10
+ private static get controllerOptions();
11
+ private static get modules();
12
+ private static get parameters();
13
+ private static get injectables();
14
+ private static get scopes();
15
+ private static get injectTokens();
16
+ private static get handlerHttpMeta();
17
+ private static get catchTypes();
18
+ private static get routeVersions();
19
+ private static get classMeta();
20
+ private static get handlerMeta();
21
+ private static get classMetaIndex();
22
+ private static get handlerMetaIndex();
23
+ private static get controllerComponents();
24
+ private static get handlerComponents();
24
25
  static getRoutes(controller: Constructor): RouteDefinition[];
25
26
  static addRoute(controller: Constructor, route: RouteDefinition): void;
26
27
  static getControllerPath(controller: Constructor): string;
@@ -31,8 +32,6 @@ export declare class MetadataRegistry {
31
32
  static setModuleOptions(module: Constructor, options: ModuleOptions): void;
32
33
  static getParameters(controller: Constructor): Map<string | symbol, ParameterMetadata[]>;
33
34
  static addParameter(controller: Constructor, methodName: string | symbol, param: ParameterMetadata): void;
34
- static registerGlobal<T extends ComponentType>(type: T, component: ComponentTypeMap[T]): void;
35
- static getGlobal<T extends ComponentType>(type: T): Set<ComponentTypeMap[T]>;
36
35
  static registerController<T extends ComponentType>(type: T, controller: Constructor, component: ComponentTypeMap[T]): void;
37
36
  static getController<T extends ComponentType>(type: T, controller: Constructor): ComponentTypeMap[T][];
38
37
  static registerHandler<T extends ComponentType>(type: T, controller: Constructor, methodName: string | symbol, component: ComponentTypeMap[T]): void;
@@ -58,6 +57,13 @@ export declare class MetadataRegistry {
58
57
  static getCustomHandlerMetaAll(target: object, handler: string | symbol): Map<string, unknown> | undefined;
59
58
  static appendCustomClassMeta<T>(target: object, key: string, item: T): void;
60
59
  static appendCustomHandlerMeta<T>(target: object, handler: string | symbol, key: string, item: T): void;
60
+ static getClassesWithClassMeta(key: string): ReadonlySet<object>;
61
+ static getClassesWithHandlerMeta(key: string): ReadonlySet<object>;
62
+ /** Every (handler, value) pair on `target` carrying metadata under `key`. */
63
+ static getHandlersWithMeta(target: object, key: string): Array<{
64
+ handler: string | symbol;
65
+ value: unknown;
66
+ }>;
61
67
  static setReflectMetadata(target: object, key: string, value: unknown, propertyKey?: string | symbol): void;
62
68
  static getReflectMetadata<T = unknown>(target: object, key: string, propertyKey?: string | symbol): T | undefined;
63
69
  static getParamTypes(target: object, propertyKey?: string | symbol): unknown[] | undefined;
@@ -1,13 +1,4 @@
1
1
  import { getOrCreate, getOrCreateArray, getOrCreateMap } from "./util.js";
2
- function emptyComponentStore() {
3
- return {
4
- middleware: new Set(),
5
- guard: new Set(),
6
- pipe: new Set(),
7
- interceptor: new Set(),
8
- filter: new Set()
9
- };
10
- }
11
2
  function emptyComponentByOwner() {
12
3
  return {
13
4
  middleware: new Map(),
@@ -17,37 +8,107 @@ function emptyComponentByOwner() {
17
8
  filter: new Map()
18
9
  };
19
10
  }
20
- export class MetadataRegistry {
21
- // Decoration metadata (set at import time, persists across clear()).
22
- static routes = new Map();
23
- static controllers = new Map();
24
- static controllerOptions = new Map();
25
- static modules = new Map();
26
- static parameters = new Map();
27
- static injectables = new Set();
28
- static scopes = new Map();
29
- static injectTokens = new Map();
30
- static handlerHttpMeta = new Map();
31
- static catchTypes = new Map();
32
- static routeVersions = new Map();
33
- // Free-form key→value class & handler meta. Backs:
34
- // - @SetMetadata (custom user keys)
35
- // - feature decorators (@Cron, @OnEvent, @ApiDoc, …)
36
- // - the SWC shim (Reflect.metadata's design:* keys)
37
- // - external Reflect.defineMetadata / Reflect.getMetadata calls.
38
- static classMeta = new Map();
39
- static handlerMeta = new Map();
40
- // Component decoration (set by @UseGuards/@UsePipes/etc. at decoration time).
41
- static controllerComponents = emptyComponentByOwner();
42
- static handlerComponents = {
43
- middleware: new Map(),
44
- guard: new Map(),
45
- pipe: new Map(),
46
- interceptor: new Map(),
47
- filter: new Map()
11
+ function createRegistryState() {
12
+ return {
13
+ routes: new Map(),
14
+ controllers: new Map(),
15
+ controllerOptions: new Map(),
16
+ modules: new Map(),
17
+ parameters: new Map(),
18
+ injectables: new Set(),
19
+ scopes: new Map(),
20
+ injectTokens: new Map(),
21
+ handlerHttpMeta: new Map(),
22
+ catchTypes: new Map(),
23
+ routeVersions: new Map(),
24
+ classMeta: new Map(),
25
+ handlerMeta: new Map(),
26
+ classMetaIndex: new Map(),
27
+ handlerMetaIndex: new Map(),
28
+ controllerComponents: emptyComponentByOwner(),
29
+ handlerComponents: {
30
+ middleware: new Map(),
31
+ guard: new Map(),
32
+ pipe: new Map(),
33
+ interceptor: new Map(),
34
+ filter: new Map()
35
+ }
48
36
  };
49
- // Global components — app-time state (cleared by clear()).
50
- static globalComponents = emptyComponentStore();
37
+ }
38
+ // HMR-safe: anchor ALL backing state on `globalThis` so a Vite dev re-eval of
39
+ // this module reuses the SAME maps that classes were already decorated against.
40
+ // Without this, re-eval creates fresh empty statics → split-brain (lost routes,
41
+ // spurious "not @Injectable" warnings, duplicated global components). The
42
+ // versioned symbol avoids collisions across framework major versions in one
43
+ // process. `globalThis` + `Symbol.for` exist on every target runtime; no node:*.
44
+ const REGISTRY_STATE_KEY = Symbol.for('vela:registry:v1');
45
+ // Shared empty result for index misses — avoids allocating per lookup.
46
+ const EMPTY_TARGET_SET = new Set();
47
+ function registryState() {
48
+ const g = globalThis;
49
+ const state = g[REGISTRY_STATE_KEY] ??= createRegistryState();
50
+ // Backfill fields added after the v1 state shape was first anchored: a state
51
+ // created by an older copy of this module (dist/src coexistence in tests,
52
+ // mixed package versions in one process) must not crash newer readers.
53
+ state.classMetaIndex ??= new Map();
54
+ state.handlerMetaIndex ??= new Map();
55
+ return state;
56
+ }
57
+ export class MetadataRegistry {
58
+ // Every field is a getter over the globalThis-anchored state (registryState).
59
+ // Method bodies keep using `this.<field>`; the getter returns the live map so
60
+ // `.set`/`.get`/`.clear` mutate the shared state.
61
+ static get routes() {
62
+ return registryState().routes;
63
+ }
64
+ static get controllers() {
65
+ return registryState().controllers;
66
+ }
67
+ static get controllerOptions() {
68
+ return registryState().controllerOptions;
69
+ }
70
+ static get modules() {
71
+ return registryState().modules;
72
+ }
73
+ static get parameters() {
74
+ return registryState().parameters;
75
+ }
76
+ static get injectables() {
77
+ return registryState().injectables;
78
+ }
79
+ static get scopes() {
80
+ return registryState().scopes;
81
+ }
82
+ static get injectTokens() {
83
+ return registryState().injectTokens;
84
+ }
85
+ static get handlerHttpMeta() {
86
+ return registryState().handlerHttpMeta;
87
+ }
88
+ static get catchTypes() {
89
+ return registryState().catchTypes;
90
+ }
91
+ static get routeVersions() {
92
+ return registryState().routeVersions;
93
+ }
94
+ static get classMeta() {
95
+ return registryState().classMeta;
96
+ }
97
+ static get handlerMeta() {
98
+ return registryState().handlerMeta;
99
+ }
100
+ static get classMetaIndex() {
101
+ return registryState().classMetaIndex;
102
+ }
103
+ static get handlerMetaIndex() {
104
+ return registryState().handlerMetaIndex;
105
+ }
106
+ static get controllerComponents() {
107
+ return registryState().controllerComponents;
108
+ }
109
+ static get handlerComponents() {
110
+ return registryState().handlerComponents;
111
+ }
51
112
  // Routes
52
113
  static getRoutes(controller) {
53
114
  return this.routes.get(controller) ?? [];
@@ -83,14 +144,9 @@ export class MetadataRegistry {
83
144
  const methodMap = getOrCreateMap(this.parameters, controller);
84
145
  getOrCreateArray(methodMap, methodName).push(param);
85
146
  }
86
- // Component registration — global
87
- static registerGlobal(type, component) {
88
- this.globalComponents[type].add(component);
89
- }
90
- static getGlobal(type) {
91
- return this.globalComponents[type];
92
- }
93
- // Component registration — controller-level
147
+ // Component registration — controller-level. (There is deliberately NO
148
+ // global tier here: app-wide components live on the per-app RouteManager,
149
+ // one source, never process-global state.)
94
150
  static registerController(type, controller, component) {
95
151
  const map = this.controllerComponents[type];
96
152
  getOrCreateArray(map, controller).push(component);
@@ -170,6 +226,9 @@ export class MetadataRegistry {
170
226
  // external Reflect.defineMetadata calls — one model, one source of truth.
171
227
  static setCustomClassMeta(target, key, value) {
172
228
  getOrCreate(this.classMeta, target, ()=>new Map()).set(key, value);
229
+ if (!key.startsWith('design:')) {
230
+ getOrCreate(this.classMetaIndex, key, ()=>new Set()).add(target);
231
+ }
173
232
  }
174
233
  static getCustomClassMeta(target, key) {
175
234
  return this.classMeta.get(target)?.get(key);
@@ -180,6 +239,9 @@ export class MetadataRegistry {
180
239
  static setCustomHandlerMeta(target, handler, key, value) {
181
240
  const byHandler = getOrCreate(this.handlerMeta, target, ()=>new Map());
182
241
  getOrCreate(byHandler, handler, ()=>new Map()).set(key, value);
242
+ if (!key.startsWith('design:')) {
243
+ getOrCreate(this.handlerMetaIndex, key, ()=>new Set()).add(target);
244
+ }
183
245
  }
184
246
  static getCustomHandlerMeta(target, handler, key) {
185
247
  return this.handlerMeta.get(target)?.get(handler)?.get(key);
@@ -198,6 +260,28 @@ export class MetadataRegistry {
198
260
  list.push(item);
199
261
  this.setCustomHandlerMeta(target, handler, key, list);
200
262
  }
263
+ // Reverse-index readers — the DiscoveryService seam. Return the constructors
264
+ // known to carry class-level (resp. handler-level) metadata under `key`.
265
+ // Callers must still intersect with container-registered tokens: decoration
266
+ // alone does not make a class a provider.
267
+ static getClassesWithClassMeta(key) {
268
+ return this.classMetaIndex.get(key) ?? EMPTY_TARGET_SET;
269
+ }
270
+ static getClassesWithHandlerMeta(key) {
271
+ return this.handlerMetaIndex.get(key) ?? EMPTY_TARGET_SET;
272
+ }
273
+ /** Every (handler, value) pair on `target` carrying metadata under `key`. */ static getHandlersWithMeta(target, key) {
274
+ const byHandler = this.handlerMeta.get(target);
275
+ if (!byHandler) return [];
276
+ const out = [];
277
+ for (const [handler, metaMap] of byHandler){
278
+ if (metaMap.has(key)) out.push({
279
+ handler,
280
+ value: metaMap.get(key)
281
+ });
282
+ }
283
+ return out;
284
+ }
201
285
  // Reflect-style API — same storage as the typed setters above. Lets external
202
286
  // code (and the SWC shim's Reflect.metadata polyfill) write/read uniformly.
203
287
  static setReflectMetadata(target, key, value, propertyKey) {
@@ -236,8 +320,11 @@ export class MetadataRegistry {
236
320
  }
237
321
  // Clear app-time state. Decoration metadata persists — once a class is
238
322
  // decorated, that fact is permanent for the lifetime of the process.
323
+ // Currently a no-op: the last piece of app-time registry state (the global
324
+ // component tier) moved to the per-app RouteManager. Kept because test
325
+ // suites call it between cases and future app-time state belongs here.
239
326
  static clear() {
240
- this.globalComponents = emptyComponentStore();
327
+ // no app-time state to clear
241
328
  }
242
329
  // Full reset, including decoration metadata. Used in framework-internal scenarios.
243
330
  static reset() {
@@ -254,6 +341,8 @@ export class MetadataRegistry {
254
341
  this.routeVersions.clear();
255
342
  this.classMeta.clear();
256
343
  this.handlerMeta.clear();
344
+ this.classMetaIndex.clear();
345
+ this.handlerMetaIndex.clear();
257
346
  for (const type of [
258
347
  'middleware',
259
348
  'guard',
@@ -264,6 +353,5 @@ export class MetadataRegistry {
264
353
  this.controllerComponents[type].clear();
265
354
  this.handlerComponents[type].clear();
266
355
  }
267
- this.globalComponents = emptyComponentStore();
268
356
  }
269
357
  }
@@ -1,4 +1,9 @@
1
1
  import type { DynamicModule } from '../module/types';
2
+ /**
3
+ * Zero-config module (Tier C): providers live on the `@Module` bag; the
4
+ * `forRoot()` static is NestJS-parity sugar returning the bare dynamic module
5
+ * (default key — repeated calls dedup).
6
+ */
2
7
  export declare class ScheduleModule {
3
8
  static forRoot(): DynamicModule;
4
9
  }
@@ -1,15 +1,25 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { Module } from "../module/decorators.js";
1
8
  import { ScheduleRegistry } from "./schedule.registry.js";
2
9
  export class ScheduleModule {
3
10
  static forRoot() {
4
- const providers = [
5
- ScheduleRegistry
6
- ];
7
11
  return {
8
- module: ScheduleModule,
9
- providers,
10
- exports: [
11
- ScheduleRegistry
12
- ]
12
+ module: ScheduleModule
13
13
  };
14
14
  }
15
15
  }
16
+ ScheduleModule = _ts_decorate([
17
+ Module({
18
+ providers: [
19
+ ScheduleRegistry
20
+ ],
21
+ exports: [
22
+ ScheduleRegistry
23
+ ]
24
+ })
25
+ ], ScheduleModule);
@@ -1,4 +1,4 @@
1
- import { Container } from '../container/container';
1
+ import { DiscoveryService } from '../discovery/discovery.service';
2
2
  import type { OnApplicationBootstrap } from '../lifecycle/index';
3
3
  export interface RegisteredCronJob {
4
4
  expression: string;
@@ -13,10 +13,10 @@ export interface RegisteredIntervalJob {
13
13
  target: Function;
14
14
  }
15
15
  export declare class ScheduleRegistry implements OnApplicationBootstrap {
16
- private container;
16
+ private readonly discovery;
17
17
  private cronJobs;
18
18
  private intervalJobs;
19
- constructor(container: Container);
19
+ constructor(discovery: DiscoveryService);
20
20
  onApplicationBootstrap(): void;
21
21
  getCronJobs(): RegisteredCronJob[];
22
22
  getIntervalJobs(): RegisteredIntervalJob[];
@@ -13,54 +13,33 @@ function _ts_param(paramIndex, decorator) {
13
13
  };
14
14
  }
15
15
  import { Injectable, Inject } from "../container/index.js";
16
- import { Container } from "../container/container.js";
17
- import { MetadataRegistry } from "../registry/metadata.registry.js";
16
+ import { DiscoveryService } from "../discovery/discovery.service.js";
18
17
  import { CRON_METADATA, INTERVAL_METADATA } from "./schedule.tokens.js";
19
18
  export class ScheduleRegistry {
20
- container;
19
+ discovery;
21
20
  cronJobs = [];
22
21
  intervalJobs = [];
23
- constructor(container){
24
- this.container = container;
22
+ constructor(discovery){
23
+ this.discovery = discovery;
25
24
  }
26
25
  onApplicationBootstrap() {
27
- const tokens = this.container.getTokens();
28
- for (const token of tokens){
29
- if (typeof token !== 'function') continue;
30
- const cronMeta = MetadataRegistry.getCustomClassMeta(token, CRON_METADATA);
31
- const intervalMeta = MetadataRegistry.getCustomClassMeta(token, INTERVAL_METADATA);
32
- if (!cronMeta && !intervalMeta) continue;
33
- let instance;
34
- try {
35
- instance = this.container.resolve(token);
36
- } catch (err) {
37
- const mode = this.container.getDiagnostics();
38
- if (mode === 'throw') throw err;
39
- if (mode === 'log') {
40
- console.warn(`[vela] schedule discovery: cannot resolve ${token.name || String(token)}:`, err);
41
- }
42
- continue;
43
- }
44
- if (cronMeta) {
45
- for (const { expression, methodName } of cronMeta){
46
- this.cronJobs.push({
47
- expression,
48
- methodName,
49
- instance,
50
- target: token
51
- });
52
- }
53
- }
54
- if (intervalMeta) {
55
- for (const { ms, methodName } of intervalMeta){
56
- this.intervalJobs.push({
57
- ms,
58
- methodName,
59
- instance,
60
- target: token
61
- });
62
- }
63
- }
26
+ for (const found of this.discovery.methodsWithMeta(CRON_METADATA)){
27
+ if (!found.class.instance) continue;
28
+ this.cronJobs.push({
29
+ expression: found.meta.expression,
30
+ methodName: String(found.methodName),
31
+ instance: found.class.instance,
32
+ target: found.class.metatype
33
+ });
34
+ }
35
+ for (const found of this.discovery.methodsWithMeta(INTERVAL_METADATA)){
36
+ if (!found.class.instance) continue;
37
+ this.intervalJobs.push({
38
+ ms: found.meta.ms,
39
+ methodName: String(found.methodName),
40
+ instance: found.class.instance,
41
+ target: found.class.metatype
42
+ });
64
43
  }
65
44
  }
66
45
  getCronJobs() {
@@ -76,9 +55,9 @@ export class ScheduleRegistry {
76
55
  }
77
56
  ScheduleRegistry = _ts_decorate([
78
57
  Injectable(),
79
- _ts_param(0, Inject(Container)),
58
+ _ts_param(0, Inject(DiscoveryService)),
80
59
  _ts_metadata("design:type", Function),
81
60
  _ts_metadata("design:paramtypes", [
82
- typeof Container === "undefined" ? Object : Container
61
+ typeof DiscoveryService === "undefined" ? Object : DiscoveryService
83
62
  ])
84
63
  ], ScheduleRegistry);
@@ -1,4 +1,9 @@
1
1
  import type { DynamicModule } from '../module/types';
2
+ /**
3
+ * Zero-config module (Tier C): providers live on the `@Module` bag; the
4
+ * `forRoot()` static is NestJS-parity sugar returning the bare dynamic module
5
+ * (default key — repeated calls dedup).
6
+ */
2
7
  export declare class ScheduleNodeModule {
3
8
  static forRoot(): DynamicModule;
4
9
  }
@@ -1,18 +1,28 @@
1
+ function _ts_decorate(decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ }
7
+ import { Module } from "../module/decorators.js";
1
8
  import { ScheduleRegistry } from "../schedule/schedule.registry.js";
2
9
  import { ScheduleExecutor } from "./schedule.executor.js";
3
10
  export class ScheduleNodeModule {
4
11
  static forRoot() {
5
- const providers = [
6
- ScheduleRegistry,
7
- ScheduleExecutor
8
- ];
9
12
  return {
10
- module: ScheduleNodeModule,
11
- providers,
12
- exports: [
13
- ScheduleRegistry,
14
- ScheduleExecutor
15
- ]
13
+ module: ScheduleNodeModule
16
14
  };
17
15
  }
18
16
  }
17
+ ScheduleNodeModule = _ts_decorate([
18
+ Module({
19
+ providers: [
20
+ ScheduleRegistry,
21
+ ScheduleExecutor
22
+ ],
23
+ exports: [
24
+ ScheduleRegistry,
25
+ ScheduleExecutor
26
+ ]
27
+ })
28
+ ], ScheduleNodeModule);
@@ -0,0 +1,5 @@
1
+ export { Seeder } from './seeder.decorator';
2
+ export { SeederRegistry } from './seeder.registry';
3
+ export { SeederModule, runSeeders } from './seeder.module';
4
+ export { SEEDER_METADATA } from './seeder.tokens';
5
+ export type { Seeder as ISeeder, SeederMetadata, RegisteredSeeder, SeederResult, } from './seeder.types';
@@ -0,0 +1,4 @@
1
+ export { Seeder } from "./seeder.decorator.js";
2
+ export { SeederRegistry } from "./seeder.registry.js";
3
+ export { SeederModule, runSeeders } from "./seeder.module.js";
4
+ export { SEEDER_METADATA } from "./seeder.tokens.js";
@@ -0,0 +1,7 @@
1
+ import type { SeederMetadata } from './seeder.types';
2
+ /**
3
+ * Mark a class as a seeder. Also marks it `@Injectable()` (singleton) so it only
4
+ * needs to be listed in a module's providers (or `SeederModule.forRoot({ seeders })`)
5
+ * to be discovered by {@link SeederRegistry} at bootstrap.
6
+ */
7
+ export declare function Seeder(options?: SeederMetadata): ClassDecorator;
@@ -0,0 +1,15 @@
1
+ import { Scope } from "../constants.js";
2
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
3
+ import { SEEDER_METADATA } from "./seeder.tokens.js";
4
+ /**
5
+ * Mark a class as a seeder. Also marks it `@Injectable()` (singleton) so it only
6
+ * needs to be listed in a module's providers (or `SeederModule.forRoot({ seeders })`)
7
+ * to be discovered by {@link SeederRegistry} at bootstrap.
8
+ */ export function Seeder(options = {}) {
9
+ return (target)=>{
10
+ const ctor = target;
11
+ MetadataRegistry.markInjectable(ctor);
12
+ MetadataRegistry.setScope(ctor, Scope.SINGLETON);
13
+ MetadataRegistry.setCustomClassMeta(target, SEEDER_METADATA, options);
14
+ };
15
+ }
@@ -0,0 +1,16 @@
1
+ import type { VelaApplication } from '../application';
2
+ import type { Type } from '../container/types';
3
+ import type { SeederResult } from './seeder.types';
4
+ export interface SeederModuleOptions {
5
+ seeders?: Type[];
6
+ }
7
+ declare const ConfigurableModuleClass: import("..").ConfigurableModuleClassType<SeederModuleOptions, "forRoot", "create", {
8
+ isGlobal?: boolean;
9
+ }>;
10
+ export declare class SeederModule extends ConfigurableModuleClass {
11
+ }
12
+ /** Convenience runner: resolve the registry from a built app and run all seeders. */
13
+ export declare function runSeeders(app: VelaApplication, options?: {
14
+ stopOnError?: boolean;
15
+ }): Promise<SeederResult[]>;
16
+ export {};