@velajs/vela 1.9.0 → 1.10.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 (113) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/__tests__/workers-als/entry-als.d.ts +9 -0
  3. package/dist/application.d.ts +10 -0
  4. package/dist/application.js +25 -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 +33 -0
  16. package/dist/container/container.js +156 -7
  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/factory/bootstrap.js +10 -0
  21. package/dist/fetch/fetch.module.d.ts +5 -6
  22. package/dist/fetch/fetch.module.js +9 -42
  23. package/dist/http/ambient.d.ts +17 -0
  24. package/dist/http/ambient.js +44 -0
  25. package/dist/http/execution-context.js +4 -1
  26. package/dist/http/index.d.ts +1 -0
  27. package/dist/http/index.js +1 -0
  28. package/dist/http/route.manager.d.ts +19 -0
  29. package/dist/http/route.manager.js +84 -0
  30. package/dist/i18n/deep-merge.d.ts +2 -0
  31. package/dist/i18n/deep-merge.js +15 -0
  32. package/dist/i18n/i18n.middleware.d.ts +15 -0
  33. package/dist/i18n/i18n.middleware.js +94 -0
  34. package/dist/i18n/i18n.module.d.ts +13 -0
  35. package/dist/i18n/i18n.module.js +61 -0
  36. package/dist/i18n/i18n.options.d.ts +30 -0
  37. package/dist/i18n/i18n.options.js +16 -0
  38. package/dist/i18n/i18n.service.d.ts +20 -0
  39. package/dist/i18n/i18n.service.js +49 -0
  40. package/dist/i18n/i18n.tokens.d.ts +6 -0
  41. package/dist/i18n/i18n.tokens.js +3 -0
  42. package/dist/i18n/i18n.types.d.ts +6 -0
  43. package/dist/i18n/i18n.types.js +1 -0
  44. package/dist/i18n/index.d.ts +10 -0
  45. package/dist/i18n/index.js +8 -0
  46. package/dist/i18n/message-loader.service.d.ts +21 -0
  47. package/dist/i18n/message-loader.service.js +90 -0
  48. package/dist/i18n/message-registry.d.ts +16 -0
  49. package/dist/i18n/message-registry.js +49 -0
  50. package/dist/index.d.ts +7 -4
  51. package/dist/index.js +6 -2
  52. package/dist/module/configurable-module.builder.d.ts +44 -0
  53. package/dist/module/configurable-module.builder.js +177 -0
  54. package/dist/module/configurable-module.types.d.ts +93 -0
  55. package/dist/module/configurable-module.types.js +5 -0
  56. package/dist/module/index.d.ts +2 -0
  57. package/dist/module/index.js +1 -0
  58. package/dist/pipeline/types.d.ts +21 -4
  59. package/dist/registry/metadata.registry.d.ts +16 -16
  60. package/dist/registry/metadata.registry.js +90 -32
  61. package/dist/seeder/index.d.ts +5 -0
  62. package/dist/seeder/index.js +4 -0
  63. package/dist/seeder/seeder.decorator.d.ts +7 -0
  64. package/dist/seeder/seeder.decorator.js +15 -0
  65. package/dist/seeder/seeder.module.d.ts +18 -0
  66. package/dist/seeder/seeder.module.js +40 -0
  67. package/dist/seeder/seeder.registry.d.ts +23 -0
  68. package/dist/seeder/seeder.registry.js +83 -0
  69. package/dist/seeder/seeder.tokens.d.ts +2 -0
  70. package/dist/seeder/seeder.tokens.js +1 -0
  71. package/dist/seeder/seeder.types.d.ts +20 -0
  72. package/dist/seeder/seeder.types.js +1 -0
  73. package/dist/storage/index.d.ts +4 -0
  74. package/dist/storage/index.js +2 -0
  75. package/dist/storage/path-template.d.ts +9 -0
  76. package/dist/storage/path-template.js +20 -0
  77. package/dist/storage/signed-url.d.ts +12 -0
  78. package/dist/storage/signed-url.js +55 -0
  79. package/dist/storage/storage.types.d.ts +41 -0
  80. package/dist/storage/storage.types.js +5 -0
  81. package/dist/throttler/throttler.module.d.ts +5 -8
  82. package/dist/throttler/throttler.module.js +27 -55
  83. package/dist/websocket/index.d.ts +12 -0
  84. package/dist/websocket/index.js +16 -0
  85. package/dist/websocket/websocket.decorators.d.ts +33 -0
  86. package/dist/websocket/websocket.decorators.js +58 -0
  87. package/dist/websocket/websocket.module.d.ts +18 -0
  88. package/dist/websocket/websocket.module.js +52 -0
  89. package/dist/websocket/websocket.tokens.d.ts +22 -0
  90. package/dist/websocket/websocket.tokens.js +20 -0
  91. package/dist/websocket/websocket.types.d.ts +86 -0
  92. package/dist/websocket/websocket.types.js +4 -0
  93. package/dist/websocket/ws-argument-resolver.d.ts +13 -0
  94. package/dist/websocket/ws-argument-resolver.js +40 -0
  95. package/dist/websocket/ws-dispatcher.d.ts +33 -0
  96. package/dist/websocket/ws-dispatcher.js +253 -0
  97. package/dist/websocket/ws-exception.d.ts +16 -0
  98. package/dist/websocket/ws-exception.js +32 -0
  99. package/dist/websocket/ws-execution-context.d.ts +9 -0
  100. package/dist/websocket/ws-execution-context.js +24 -0
  101. package/dist/websocket/ws-server.d.ts +31 -0
  102. package/dist/websocket/ws-server.js +74 -0
  103. package/dist/websocket/ws-sync.d.ts +47 -0
  104. package/dist/websocket/ws-sync.js +74 -0
  105. package/dist/websocket-node/index.d.ts +4 -0
  106. package/dist/websocket-node/index.js +5 -0
  107. package/dist/websocket-node/node-ws-client.d.ts +24 -0
  108. package/dist/websocket-node/node-ws-client.js +49 -0
  109. package/dist/websocket-node/redis-sync.d.ts +33 -0
  110. package/dist/websocket-node/redis-sync.js +56 -0
  111. package/dist/websocket-node/register-gateways.d.ts +20 -0
  112. package/dist/websocket-node/register-gateways.js +61 -0
  113. package/package.json +31 -1
@@ -0,0 +1,177 @@
1
+ import { InjectionToken } from "../container/types.js";
2
+ import { stableHash } from "./stable-hash.js";
3
+ /** Default extra: `isGlobal` toggles `DynamicModule.global`, hiding the naming split. */ const DEFAULT_EXTRAS = {
4
+ isGlobal: false
5
+ };
6
+ const DEFAULT_TRANSFORM = (def, extras)=>extras.isGlobal ? {
7
+ ...def,
8
+ global: true
9
+ } : def;
10
+ /**
11
+ * NestJS-parity builder that generates `forRoot`/`forRootAsync` (and `key`,
12
+ * `global`, factory-param inference) from a tiny spec — so a module is just its
13
+ * tokens + options type + service + a `@Module({...})` bag, while vela keeps its
14
+ * encapsulation (`exports`/visibility) and multi-instance `key` dedup.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
19
+ * new ConfigurableModuleBuilder<FooOptions>({ moduleName: 'Foo' }).build();
20
+ *
21
+ * @Module({
22
+ * providers: [FooService], // derived providers inject MODULE_OPTIONS_TOKEN
23
+ * exports: [FooService],
24
+ * })
25
+ * export class FooModule extends ConfigurableModuleClass {}
26
+ * ```
27
+ */ export class ConfigurableModuleBuilder {
28
+ options;
29
+ classMethodName = 'forRoot';
30
+ factoryMethodName = 'create';
31
+ extrasDefaults = DEFAULT_EXTRAS;
32
+ extrasTransform = DEFAULT_TRANSFORM;
33
+ constructor(options = {}){
34
+ this.options = options;
35
+ }
36
+ /** Declare extra call-site keys (e.g. `isGlobal`) + how they reshape the definition. */ setExtras(defaults, transform) {
37
+ this.extrasDefaults = defaults;
38
+ this.extrasTransform = transform;
39
+ return this;
40
+ }
41
+ /** Rename the sync static (default `forRoot`); the async static becomes `<name>Async`. */ setClassMethodName(name) {
42
+ this.classMethodName = name;
43
+ return this;
44
+ }
45
+ /** Rename the method a `useClass`/`useExisting` options factory must implement (default `create`). */ setFactoryMethodName(name) {
46
+ this.factoryMethodName = name;
47
+ return this;
48
+ }
49
+ build() {
50
+ const moduleName = this.options.moduleName ?? 'ConfigurableModule';
51
+ const optionsToken = this.options.optionsInjectionToken ?? new InjectionToken(`${moduleName}_MODULE_OPTIONS`);
52
+ const syncName = this.classMethodName;
53
+ const asyncName = `${this.classMethodName}Async`;
54
+ const factoryMethodName = this.factoryMethodName;
55
+ const extrasDefaults = this.extrasDefaults;
56
+ const transform = this.extrasTransform;
57
+ let ConfigurableModuleClass = class ConfigurableModuleClass {
58
+ };
59
+ Object.defineProperty(ConfigurableModuleClass, syncName, {
60
+ configurable: true,
61
+ writable: true,
62
+ enumerable: false,
63
+ value (options = {}) {
64
+ const { key, ...rest } = options;
65
+ const definition = {
66
+ module: this,
67
+ key: key ?? stableHash(rest),
68
+ providers: [
69
+ {
70
+ provide: optionsToken,
71
+ useValue: rest
72
+ }
73
+ ]
74
+ };
75
+ return transform(definition, {
76
+ ...extrasDefaults,
77
+ ...rest
78
+ });
79
+ }
80
+ });
81
+ Object.defineProperty(ConfigurableModuleClass, asyncName, {
82
+ configurable: true,
83
+ writable: true,
84
+ enumerable: false,
85
+ value (options = {}) {
86
+ const { key, imports, inject, useFactory, useClass, useExisting, ...restExtras } = options;
87
+ const definition = {
88
+ module: this,
89
+ // Include extras (e.g. isGlobal) in the key — like forRoot — so two
90
+ // async instances differing only in an extra don't wrongly dedup.
91
+ key: key ?? stableHash({
92
+ inject,
93
+ useFactory,
94
+ useClass,
95
+ useExisting,
96
+ ...restExtras
97
+ }),
98
+ imports: imports ?? [],
99
+ providers: buildAsyncOptionsProviders(optionsToken, factoryMethodName, {
100
+ inject,
101
+ useFactory,
102
+ useClass,
103
+ useExisting
104
+ })
105
+ };
106
+ return transform(definition, {
107
+ ...extrasDefaults,
108
+ ...restExtras
109
+ });
110
+ }
111
+ });
112
+ return {
113
+ ConfigurableModuleClass: ConfigurableModuleClass,
114
+ MODULE_OPTIONS_TOKEN: optionsToken,
115
+ // Type-only sentinels — never read at runtime.
116
+ OPTIONS_TYPE: undefined,
117
+ ASYNC_OPTIONS_TYPE: undefined
118
+ };
119
+ }
120
+ }
121
+ /** Lower `useFactory`/`useClass`/`useExisting` async options into provider registrations. */ function buildAsyncOptionsProviders(optionsToken, factoryMethodName, async) {
122
+ if (async.useFactory) {
123
+ return [
124
+ {
125
+ provide: optionsToken,
126
+ useFactory: async.useFactory,
127
+ inject: async.inject ?? []
128
+ }
129
+ ];
130
+ }
131
+ if (async.useClass) {
132
+ const factoryClass = async.useClass;
133
+ return [
134
+ factoryClass,
135
+ {
136
+ provide: optionsToken,
137
+ useFactory: (instance)=>instance[factoryMethodName](),
138
+ inject: [
139
+ factoryClass
140
+ ]
141
+ }
142
+ ];
143
+ }
144
+ if (async.useExisting) {
145
+ return [
146
+ {
147
+ provide: optionsToken,
148
+ useFactory: (instance)=>instance[factoryMethodName](),
149
+ inject: [
150
+ async.useExisting
151
+ ]
152
+ }
153
+ ];
154
+ }
155
+ throw new Error('Async module options require one of `useFactory`, `useClass`, or `useExisting`.');
156
+ }
157
+ /**
158
+ * Lower-level engine for cases a class-mixin can't express — chiefly a
159
+ * runtime-generated module class whose providers depend on a call-time arg
160
+ * (Cloudflare binding modules). Returns an object with a single static named by
161
+ * `spec.methodName` (default `forRoot`).
162
+ */ export function defineConfigurableModule(spec) {
163
+ const methodName = spec.methodName ?? 'forRoot';
164
+ return {
165
+ [methodName] (args) {
166
+ const definition = {
167
+ module: spec.module,
168
+ key: spec.keyFrom(args),
169
+ providers: spec.providers(args)
170
+ };
171
+ if (spec.imports) definition.imports = spec.imports(args);
172
+ if (spec.exports) definition.exports = spec.exports;
173
+ if (spec.global) definition.global = true;
174
+ return definition;
175
+ }
176
+ };
177
+ }
@@ -0,0 +1,93 @@
1
+ import type { InferTokens, InjectionToken, Token, Type } from '../container/types';
2
+ import type { DynamicModule, ModuleImport } from '../registry/types';
3
+ /**
4
+ * Arbitrary extra keys a module accepts alongside its options bag (e.g.
5
+ * `isGlobal`). Kept separate from the options type so the extras-transform can
6
+ * reshape the generated {@link DynamicModule} without polluting `Opts`.
7
+ */
8
+ export type ConfigurableModuleExtras = Record<string, unknown>;
9
+ /**
10
+ * Reshape the generated definition based on the resolved extras. Runs after the
11
+ * options provider + `key` are computed; the return value is the final
12
+ * `DynamicModule`. Mirrors NestJS's `extras` transform.
13
+ */
14
+ export type ConfigurableModuleExtrasTransform<E extends ConfigurableModuleExtras> = (definition: DynamicModule, extras: E) => DynamicModule;
15
+ /**
16
+ * The contract a `useClass`/`useExisting` async-options factory must satisfy:
17
+ * a single method (default name `create`) returning the module options.
18
+ */
19
+ export type ConfigurableModuleOptionsFactory<Opts, MethodName extends string> = {
20
+ [K in MethodName]: () => Opts | Promise<Opts>;
21
+ };
22
+ /**
23
+ * Async configuration accepted by the generated `<method>Async` static.
24
+ *
25
+ * Superset of `AsyncModuleOptions` (`registry/types.ts`): adds NestJS's
26
+ * `useClass`/`useExisting` while preserving the headline `const Inject` tuple
27
+ * inference for `useFactory` params (no `as const` needed at the call site).
28
+ */
29
+ export interface ConfigurableModuleAsyncOptions<Opts, MethodName extends string = 'create', Inject extends readonly Token<unknown>[] = readonly Token<unknown>[]> {
30
+ imports?: ModuleImport[];
31
+ inject?: Inject;
32
+ useFactory?: (...args: InferTokens<Inject>) => Opts | Promise<Opts>;
33
+ useClass?: Type<ConfigurableModuleOptionsFactory<Opts, MethodName>>;
34
+ useExisting?: Token<ConfigurableModuleOptionsFactory<Opts, MethodName>>;
35
+ /** Explicit instance discriminator (see {@link DynamicModule.key}). */
36
+ key?: string;
37
+ }
38
+ export interface ConfigurableModuleBuilderOptions {
39
+ /** Names the generated base class + the auto-minted options token, and feeds diagnostics. */
40
+ moduleName?: string;
41
+ /**
42
+ * Reuse an existing options token instead of minting one. **Critical for
43
+ * migrations** so the module's public token keeps its identity.
44
+ */
45
+ optionsInjectionToken?: InjectionToken<unknown>;
46
+ }
47
+ /**
48
+ * The generated base class type, carrying dynamically-named `<method>` and
49
+ * `<method>Async` statics. A module does `class Foo extends ConfigurableModuleClass {}`
50
+ * and gets `Foo.forRoot(...)` / `Foo.forRootAsync(...)` for free.
51
+ */
52
+ export type ConfigurableModuleClassType<Opts, MethodKey extends string, FactoryMethodKey extends string, Extras extends ConfigurableModuleExtras> = (new () => object) & Record<MethodKey, (options: Opts & Partial<Extras> & {
53
+ key?: string;
54
+ }) => DynamicModule> & Record<`${MethodKey}Async`, <const Inject extends readonly Token<unknown>[]>(options: ConfigurableModuleAsyncOptions<Opts, FactoryMethodKey, Inject> & Partial<Extras> & {
55
+ key?: string;
56
+ }) => DynamicModule>;
57
+ export interface ConfigurableModuleHost<Opts, MethodKey extends string = 'forRoot', FactoryMethodKey extends string = 'create', Extras extends ConfigurableModuleExtras = {
58
+ isGlobal?: boolean;
59
+ }> {
60
+ /** Base class to `extends`. */
61
+ ConfigurableModuleClass: ConfigurableModuleClassType<Opts, MethodKey, FactoryMethodKey, Extras>;
62
+ /** The options token — inject it into derived providers (`inject: [MODULE_OPTIONS_TOKEN]`). */
63
+ MODULE_OPTIONS_TOKEN: InjectionToken<Opts>;
64
+ /** Type-only helper: the shape accepted by the sync `<method>` static. */
65
+ OPTIONS_TYPE: Opts & Partial<Extras> & {
66
+ key?: string;
67
+ };
68
+ /** Type-only helper: the shape accepted by the `<method>Async` static. */
69
+ ASYNC_OPTIONS_TYPE: ConfigurableModuleAsyncOptions<Opts, FactoryMethodKey> & Partial<Extras> & {
70
+ key?: string;
71
+ };
72
+ }
73
+ /**
74
+ * Low-level engine spec (see `defineConfigurableModule`). Used for the cases a
75
+ * class-mixin can't express — notably runtime-generated module classes whose
76
+ * providers depend on a call-time argument (e.g. Cloudflare binding modules).
77
+ */
78
+ export interface DefineConfigurableModuleSpec<Args> {
79
+ /** The module class to reference in `{ module }`. */
80
+ module: Type;
81
+ /** Static method name to generate (e.g. `forRoot`). */
82
+ methodName?: string;
83
+ /** Derive the instance `key` from the call args. */
84
+ keyFrom: (args: Args) => string;
85
+ /** Build the provider list from the call args. */
86
+ providers: (args: Args) => DynamicModule['providers'];
87
+ /** Optional exports (usually prefer declaring these on `@Module`). */
88
+ exports?: DynamicModule['exports'];
89
+ /** Optional imports. */
90
+ imports?: (args: Args) => DynamicModule['imports'];
91
+ /** Mark the produced module global. */
92
+ global?: boolean;
93
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Low-level engine spec (see `defineConfigurableModule`). Used for the cases a
3
+ * class-mixin can't express — notably runtime-generated module classes whose
4
+ * providers depend on a call-time argument (e.g. Cloudflare binding modules).
5
+ */ export { };
@@ -1,5 +1,7 @@
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';
4
+ export type { ConfigurableModuleAsyncOptions, ConfigurableModuleBuilderOptions, ConfigurableModuleClassType, ConfigurableModuleExtras, ConfigurableModuleExtrasTransform, ConfigurableModuleHost, ConfigurableModuleOptionsFactory, DefineConfigurableModuleSpec, } from './configurable-module.types';
3
5
  export { ModuleLoader } from './module-loader';
4
6
  export { MiddlewareBuilder } from './middleware';
5
7
  export type { MiddlewareConsumer, MiddlewareConfigProxy, MiddlewareRouteDefinition, NestModule, RouteInfo, } from './middleware';
@@ -1,4 +1,5 @@
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
4
  export { ModuleLoader } from "./module-loader.js";
4
5
  export { MiddlewareBuilder } from "./middleware.js";
@@ -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,22 @@ 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 controllerComponents();
22
+ private static get handlerComponents();
23
+ private static get globalComponents();
24
24
  static getRoutes(controller: Constructor): RouteDefinition[];
25
25
  static addRoute(controller: Constructor, route: RouteDefinition): void;
26
26
  static getControllerPath(controller: Constructor): string;
@@ -17,37 +17,95 @@ function emptyComponentByOwner() {
17
17
  filter: new Map()
18
18
  };
19
19
  }
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()
20
+ function createRegistryState() {
21
+ return {
22
+ routes: new Map(),
23
+ controllers: new Map(),
24
+ controllerOptions: new Map(),
25
+ modules: new Map(),
26
+ parameters: new Map(),
27
+ injectables: new Set(),
28
+ scopes: new Map(),
29
+ injectTokens: new Map(),
30
+ handlerHttpMeta: new Map(),
31
+ catchTypes: new Map(),
32
+ routeVersions: new Map(),
33
+ classMeta: new Map(),
34
+ handlerMeta: new Map(),
35
+ controllerComponents: emptyComponentByOwner(),
36
+ handlerComponents: {
37
+ middleware: new Map(),
38
+ guard: new Map(),
39
+ pipe: new Map(),
40
+ interceptor: new Map(),
41
+ filter: new Map()
42
+ },
43
+ globalComponents: emptyComponentStore()
48
44
  };
49
- // Global components — app-time state (cleared by clear()).
50
- static globalComponents = emptyComponentStore();
45
+ }
46
+ // HMR-safe: anchor ALL backing state on `globalThis` so a Vite dev re-eval of
47
+ // this module reuses the SAME maps that classes were already decorated against.
48
+ // Without this, re-eval creates fresh empty statics → split-brain (lost routes,
49
+ // spurious "not @Injectable" warnings, duplicated global components). The
50
+ // versioned symbol avoids collisions across framework major versions in one
51
+ // process. `globalThis` + `Symbol.for` exist on every target runtime; no node:*.
52
+ const REGISTRY_STATE_KEY = Symbol.for('vela:registry:v1');
53
+ function registryState() {
54
+ const g = globalThis;
55
+ return g[REGISTRY_STATE_KEY] ??= createRegistryState();
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 controllerComponents() {
101
+ return registryState().controllerComponents;
102
+ }
103
+ static get handlerComponents() {
104
+ return registryState().handlerComponents;
105
+ }
106
+ static get globalComponents() {
107
+ return registryState().globalComponents;
108
+ }
51
109
  // Routes
52
110
  static getRoutes(controller) {
53
111
  return this.routes.get(controller) ?? [];
@@ -237,7 +295,7 @@ export class MetadataRegistry {
237
295
  // Clear app-time state. Decoration metadata persists — once a class is
238
296
  // decorated, that fact is permanent for the lifetime of the process.
239
297
  static clear() {
240
- this.globalComponents = emptyComponentStore();
298
+ registryState().globalComponents = emptyComponentStore();
241
299
  }
242
300
  // Full reset, including decoration metadata. Used in framework-internal scenarios.
243
301
  static reset() {
@@ -264,6 +322,6 @@ export class MetadataRegistry {
264
322
  this.controllerComponents[type].clear();
265
323
  this.handlerComponents[type].clear();
266
324
  }
267
- this.globalComponents = emptyComponentStore();
325
+ registryState().globalComponents = emptyComponentStore();
268
326
  }
269
327
  }
@@ -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,18 @@
1
+ import type { VelaApplication } from '../application';
2
+ import type { DynamicModule } from '../module/types';
3
+ import type { Type } from '../container/types';
4
+ import type { SeederResult } from './seeder.types';
5
+ export declare class SeederModule {
6
+ /**
7
+ * Register seeder classes as providers so they're discovered at bootstrap.
8
+ * (Seeders declared in other modules' providers are also discovered — this is
9
+ * just a convenience to co-locate them.)
10
+ */
11
+ static forRoot(options?: {
12
+ seeders?: Type[];
13
+ }): DynamicModule;
14
+ }
15
+ /** Convenience runner: resolve the registry from a built app and run all seeders. */
16
+ export declare function runSeeders(app: VelaApplication, options?: {
17
+ stopOnError?: boolean;
18
+ }): Promise<SeederResult[]>;
@@ -0,0 +1,40 @@
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";
8
+ import { stableHash } from "../module/stable-hash.js";
9
+ import { SeederRegistry } from "./seeder.registry.js";
10
+ export class SeederModule {
11
+ /**
12
+ * Register seeder classes as providers so they're discovered at bootstrap.
13
+ * (Seeders declared in other modules' providers are also discovered — this is
14
+ * just a convenience to co-locate them.)
15
+ */ static forRoot(options = {}) {
16
+ const seeders = options.seeders ?? [];
17
+ return {
18
+ module: SeederModule,
19
+ key: stableHash({
20
+ seeders: seeders.map((s)=>s.name)
21
+ }),
22
+ providers: [
23
+ ...seeders
24
+ ]
25
+ };
26
+ }
27
+ }
28
+ SeederModule = _ts_decorate([
29
+ Module({
30
+ providers: [
31
+ SeederRegistry
32
+ ],
33
+ exports: [
34
+ SeederRegistry
35
+ ]
36
+ })
37
+ ], SeederModule);
38
+ /** Convenience runner: resolve the registry from a built app and run all seeders. */ export function runSeeders(app, options) {
39
+ return app.get(SeederRegistry).runAll(options);
40
+ }
@@ -0,0 +1,23 @@
1
+ import { Container } from '../container/container';
2
+ import type { OnApplicationBootstrap } from '../lifecycle/index';
3
+ import type { RegisteredSeeder, SeederResult } from './seeder.types';
4
+ /**
5
+ * Discovers `@Seeder()` providers at bootstrap and runs them on demand.
6
+ * Mirrors `ScheduleRegistry`'s discovery (scan container tokens → read metadata).
7
+ */
8
+ export declare class SeederRegistry implements OnApplicationBootstrap {
9
+ private readonly container;
10
+ private seeders;
11
+ constructor(container: Container);
12
+ onApplicationBootstrap(): void;
13
+ /** Registered seeders in run order. */
14
+ list(): RegisteredSeeder[];
15
+ /**
16
+ * Run every seeder in order, each in its own request-scoped child container so
17
+ * request-scoped dependencies resolve. Returns a per-seeder result; when
18
+ * `stopOnError` is true (default) the run aborts on the first failure.
19
+ */
20
+ runAll(options?: {
21
+ stopOnError?: boolean;
22
+ }): Promise<SeederResult[]>;
23
+ }