@velajs/vela 1.8.8 → 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 (115) 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/openapi/document.js +20 -2
  59. package/dist/openapi/types.d.ts +54 -0
  60. package/dist/pipeline/types.d.ts +21 -4
  61. package/dist/registry/metadata.registry.d.ts +16 -16
  62. package/dist/registry/metadata.registry.js +90 -32
  63. package/dist/seeder/index.d.ts +5 -0
  64. package/dist/seeder/index.js +4 -0
  65. package/dist/seeder/seeder.decorator.d.ts +7 -0
  66. package/dist/seeder/seeder.decorator.js +15 -0
  67. package/dist/seeder/seeder.module.d.ts +18 -0
  68. package/dist/seeder/seeder.module.js +40 -0
  69. package/dist/seeder/seeder.registry.d.ts +23 -0
  70. package/dist/seeder/seeder.registry.js +83 -0
  71. package/dist/seeder/seeder.tokens.d.ts +2 -0
  72. package/dist/seeder/seeder.tokens.js +1 -0
  73. package/dist/seeder/seeder.types.d.ts +20 -0
  74. package/dist/seeder/seeder.types.js +1 -0
  75. package/dist/storage/index.d.ts +4 -0
  76. package/dist/storage/index.js +2 -0
  77. package/dist/storage/path-template.d.ts +9 -0
  78. package/dist/storage/path-template.js +20 -0
  79. package/dist/storage/signed-url.d.ts +12 -0
  80. package/dist/storage/signed-url.js +55 -0
  81. package/dist/storage/storage.types.d.ts +41 -0
  82. package/dist/storage/storage.types.js +5 -0
  83. package/dist/throttler/throttler.module.d.ts +5 -8
  84. package/dist/throttler/throttler.module.js +27 -55
  85. package/dist/websocket/index.d.ts +12 -0
  86. package/dist/websocket/index.js +16 -0
  87. package/dist/websocket/websocket.decorators.d.ts +33 -0
  88. package/dist/websocket/websocket.decorators.js +58 -0
  89. package/dist/websocket/websocket.module.d.ts +18 -0
  90. package/dist/websocket/websocket.module.js +52 -0
  91. package/dist/websocket/websocket.tokens.d.ts +22 -0
  92. package/dist/websocket/websocket.tokens.js +20 -0
  93. package/dist/websocket/websocket.types.d.ts +86 -0
  94. package/dist/websocket/websocket.types.js +4 -0
  95. package/dist/websocket/ws-argument-resolver.d.ts +13 -0
  96. package/dist/websocket/ws-argument-resolver.js +40 -0
  97. package/dist/websocket/ws-dispatcher.d.ts +33 -0
  98. package/dist/websocket/ws-dispatcher.js +253 -0
  99. package/dist/websocket/ws-exception.d.ts +16 -0
  100. package/dist/websocket/ws-exception.js +32 -0
  101. package/dist/websocket/ws-execution-context.d.ts +9 -0
  102. package/dist/websocket/ws-execution-context.js +24 -0
  103. package/dist/websocket/ws-server.d.ts +31 -0
  104. package/dist/websocket/ws-server.js +74 -0
  105. package/dist/websocket/ws-sync.d.ts +47 -0
  106. package/dist/websocket/ws-sync.js +74 -0
  107. package/dist/websocket-node/index.d.ts +4 -0
  108. package/dist/websocket-node/index.js +5 -0
  109. package/dist/websocket-node/node-ws-client.d.ts +24 -0
  110. package/dist/websocket-node/node-ws-client.js +49 -0
  111. package/dist/websocket-node/redis-sync.d.ts +33 -0
  112. package/dist/websocket-node/redis-sync.js +56 -0
  113. package/dist/websocket-node/register-gateways.d.ts +20 -0
  114. package/dist/websocket-node/register-gateways.js +61 -0
  115. 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";
@@ -278,14 +278,32 @@ export function createOpenApiDocument(rootModule, options = {}) {
278
278
  description: options.info.description
279
279
  } : {}
280
280
  },
281
+ // servers sits between info and paths (OpenAPI convention). Omitted when
282
+ // the caller passes none, mirroring the components/tags handling below.
283
+ ...options.servers?.length ? {
284
+ servers: options.servers
285
+ } : {},
281
286
  paths
282
287
  };
288
+ // components.schemas (generated from DTOs) and components.securitySchemes
289
+ // (caller-supplied) share the same `components` object — attach it when
290
+ // either is present so neither clobbers the other.
283
291
  const schemas = registry.build();
284
- if (schemas) {
292
+ if (schemas || options.securitySchemes) {
285
293
  document.components = {
286
- schemas
294
+ ...schemas ? {
295
+ schemas
296
+ } : {},
297
+ ...options.securitySchemes ? {
298
+ securitySchemes: options.securitySchemes
299
+ } : {}
287
300
  };
288
301
  }
302
+ // Document-level default security requirements (each operation may override).
303
+ // Omitted entirely when the caller passes none.
304
+ if (options.security?.length) {
305
+ document.security = options.security;
306
+ }
289
307
  // Only attach `tags` when non-empty. Emitting `tags: []` on a tag-less
290
308
  // spec is the exact symptom the consumer reported, so omit the key
291
309
  // entirely in that case (mirrors the `components` handling above).
@@ -46,6 +46,13 @@ export interface OpenApiResponse {
46
46
  schema: JsonSchema;
47
47
  }>;
48
48
  }
49
+ /**
50
+ * A single security requirement: maps a securityScheme name to the scopes it
51
+ * requires (empty array for apiKey/http schemes). An operation- or document-
52
+ * level array is an OR of these objects. Kept as a plain record so callers can
53
+ * build them inline (`{ cookieAuth: [] }`).
54
+ */
55
+ export type OpenApiSecurityRequirement = Record<string, string[]>;
49
56
  export interface OpenApiOperation {
50
57
  summary?: string;
51
58
  description?: string;
@@ -55,6 +62,35 @@ export interface OpenApiOperation {
55
62
  parameters?: OpenApiParameter[];
56
63
  requestBody?: OpenApiRequestBody;
57
64
  responses: Record<string, OpenApiResponse>;
65
+ /** Per-operation security requirements (overrides the document-level array). */
66
+ security?: OpenApiSecurityRequirement[];
67
+ }
68
+ /** An OpenAPI Server Object. Kept loose so callers can add `variables`. */
69
+ export interface OpenApiServer {
70
+ url: string;
71
+ description?: string;
72
+ variables?: Record<string, {
73
+ enum?: string[];
74
+ default: string;
75
+ description?: string;
76
+ }>;
77
+ [key: string]: unknown;
78
+ }
79
+ /**
80
+ * An OpenAPI Security Scheme Object (apiKey / http / oauth2 / openIdConnect /
81
+ * mutualTLS). Index signature keeps it open so any valid scheme shape passes
82
+ * without fighting the types (mirrors JsonSchema above).
83
+ */
84
+ export interface OpenApiSecurityScheme {
85
+ type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect' | 'mutualTLS';
86
+ description?: string;
87
+ name?: string;
88
+ in?: 'query' | 'header' | 'cookie';
89
+ scheme?: string;
90
+ bearerFormat?: string;
91
+ flows?: Record<string, unknown>;
92
+ openIdConnectUrl?: string;
93
+ [key: string]: unknown;
58
94
  }
59
95
  export type HttpVerb = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head';
60
96
  export type OpenApiPathItem = {
@@ -63,10 +99,13 @@ export type OpenApiPathItem = {
63
99
  export interface OpenApiDocument {
64
100
  openapi: '3.1.0';
65
101
  info: OpenApiInfo;
102
+ servers?: OpenApiServer[];
66
103
  paths: Record<string, OpenApiPathItem>;
67
104
  components?: {
68
105
  schemas?: Record<string, JsonSchema>;
106
+ securitySchemes?: Record<string, OpenApiSecurityScheme>;
69
107
  };
108
+ security?: OpenApiSecurityRequirement[];
70
109
  tags?: Array<{
71
110
  name: string;
72
111
  description?: string;
@@ -101,6 +140,21 @@ export interface CreateOpenApiDocumentOptions {
101
140
  name: string;
102
141
  description?: string;
103
142
  }>;
143
+ /**
144
+ * Top-level `servers` array (base URLs the API is served from). Emitted
145
+ * verbatim between `info` and `paths` when non-empty; omitted otherwise.
146
+ */
147
+ servers?: OpenApiServer[];
148
+ /**
149
+ * Named security schemes, emitted under `components.securitySchemes` (merged
150
+ * with generated `components.schemas`). Reference them from `security`.
151
+ */
152
+ securitySchemes?: Record<string, OpenApiSecurityScheme>;
153
+ /**
154
+ * Document-level default security requirements (applies to every operation
155
+ * unless the operation overrides it). Emitted verbatim when non-empty.
156
+ */
157
+ security?: OpenApiSecurityRequirement[];
104
158
  }
105
159
  export type OpenApiUi = 'swagger' | 'scalar' | 'redoc';
106
160
  export interface MountOpenApiOptions {
@@ -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;