@velajs/vela 1.10.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/dist/application.d.ts +25 -0
  3. package/dist/application.js +103 -2
  4. package/dist/container/container.d.ts +46 -1
  5. package/dist/container/container.js +156 -13
  6. package/dist/container/disposable.js +1 -1
  7. package/dist/container/types.d.ts +27 -0
  8. package/dist/cors/cors.module.d.ts +5 -5
  9. package/dist/cors/cors.module.js +26 -25
  10. package/dist/discovery/discoverable.decorator.d.ts +38 -0
  11. package/dist/discovery/discoverable.decorator.js +42 -0
  12. package/dist/discovery/discovery.service.d.ts +102 -0
  13. package/dist/discovery/discovery.service.js +177 -0
  14. package/dist/discovery/index.d.ts +2 -0
  15. package/dist/discovery/index.js +2 -0
  16. package/dist/entrypoint/entrypoint.registry.d.ts +42 -0
  17. package/dist/entrypoint/entrypoint.registry.js +112 -0
  18. package/dist/entrypoint/entrypoint.types.d.ts +41 -0
  19. package/dist/entrypoint/entrypoint.types.js +3 -0
  20. package/dist/entrypoint/execution-context.d.ts +15 -0
  21. package/dist/entrypoint/execution-context.js +21 -0
  22. package/dist/entrypoint/execution-scope.d.ts +21 -0
  23. package/dist/entrypoint/execution-scope.js +28 -0
  24. package/dist/entrypoint/index.d.ts +4 -0
  25. package/dist/entrypoint/index.js +4 -0
  26. package/dist/event-emitter/event-emitter.module.js +1 -0
  27. package/dist/event-emitter/event-emitter.subscriber.d.ts +3 -3
  28. package/dist/event-emitter/event-emitter.subscriber.js +12 -29
  29. package/dist/factory/adapter.d.ts +36 -0
  30. package/dist/factory/adapter.js +18 -0
  31. package/dist/factory/bootstrap.js +14 -2
  32. package/dist/factory.d.ts +11 -1
  33. package/dist/factory.js +23 -2
  34. package/dist/http/handler-executor.js +22 -19
  35. package/dist/http/route-contributor.d.ts +68 -0
  36. package/dist/http/route-contributor.js +22 -0
  37. package/dist/http/route.manager.js +33 -19
  38. package/dist/i18n/i18n.module.js +9 -1
  39. package/dist/index.d.ts +12 -3
  40. package/dist/index.js +9 -2
  41. package/dist/internal.d.ts +0 -2
  42. package/dist/internal.js +0 -7
  43. package/dist/module/configurable-module.builder.d.ts +12 -0
  44. package/dist/module/configurable-module.builder.js +21 -114
  45. package/dist/module/decorators.js +4 -2
  46. package/dist/module/define-module.d.ts +112 -0
  47. package/dist/module/define-module.js +232 -0
  48. package/dist/module/index.d.ts +3 -1
  49. package/dist/module/index.js +3 -1
  50. package/dist/module/lazy-modules.d.ts +82 -0
  51. package/dist/module/lazy-modules.js +231 -0
  52. package/dist/module/lazy-provider.d.ts +65 -0
  53. package/dist/module/lazy-provider.js +111 -0
  54. package/dist/module/module-loader.d.ts +21 -0
  55. package/dist/module/module-loader.js +99 -24
  56. package/dist/openapi/document.js +16 -15
  57. package/dist/pipeline/component.manager.d.ts +15 -9
  58. package/dist/pipeline/component.manager.js +32 -46
  59. package/dist/pipeline/index.d.ts +2 -0
  60. package/dist/pipeline/index.js +1 -0
  61. package/dist/pipeline/pipeline-runner.d.ts +31 -0
  62. package/dist/pipeline/pipeline-runner.js +40 -0
  63. package/dist/registry/metadata.registry.d.ts +9 -3
  64. package/dist/registry/metadata.registry.js +55 -25
  65. package/dist/registry/types.d.ts +10 -0
  66. package/dist/schedule/schedule.module.d.ts +5 -0
  67. package/dist/schedule/schedule.module.js +21 -8
  68. package/dist/schedule/schedule.registry.d.ts +3 -3
  69. package/dist/schedule/schedule.registry.js +23 -44
  70. package/dist/schedule-node/schedule-node.module.d.ts +5 -0
  71. package/dist/schedule-node/schedule-node.module.js +20 -10
  72. package/dist/seeder/seeder.module.d.ts +8 -10
  73. package/dist/seeder/seeder.module.js +19 -15
  74. package/dist/websocket/index.d.ts +1 -1
  75. package/dist/websocket/index.js +1 -1
  76. package/dist/websocket/websocket.module.d.ts +20 -3
  77. package/dist/websocket/websocket.module.js +50 -39
  78. package/dist/websocket/websocket.tokens.d.ts +2 -1
  79. package/dist/websocket/websocket.tokens.js +3 -2
  80. package/dist/websocket/ws-dispatcher.d.ts +21 -9
  81. package/dist/websocket/ws-dispatcher.js +44 -38
  82. package/dist/websocket-node/register-gateways.js +3 -3
  83. package/package.json +1 -1
  84. package/dist/http/crud-bridge.d.ts +0 -90
  85. package/dist/http/crud-bridge.js +0 -25
@@ -0,0 +1,177 @@
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
+ function _ts_metadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ function _ts_param(paramIndex, decorator) {
11
+ return function(target, key) {
12
+ decorator(target, key, paramIndex);
13
+ };
14
+ }
15
+ import { Scope } from "../constants.js";
16
+ import { Container } from "../container/container.js";
17
+ import { Inject, Injectable } from "../container/decorators.js";
18
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
19
+ function isMethodMetaItem(x) {
20
+ return typeof x === 'object' && x !== null && (typeof x.methodName === 'string' || typeof x.methodName === 'symbol');
21
+ }
22
+ function resolveKey(key) {
23
+ return typeof key === 'string' ? key : key.KEY;
24
+ }
25
+ export class DiscoveryService {
26
+ container;
27
+ constructor(container){
28
+ this.container = container;
29
+ }
30
+ /** Every class-token provider registered in the container. */ getProviders(filter) {
31
+ const out = [];
32
+ for (const token of this.container.getTokens()){
33
+ if (typeof token !== 'function') continue;
34
+ const entry = this.buildEntry(token, filter, 'provider discovery');
35
+ if (entry) out.push(entry);
36
+ }
37
+ return out;
38
+ }
39
+ /**
40
+ * Providers whose class carries class-level metadata under `key` — both
41
+ * class decorators (single value) and method decorators that append
42
+ * class-level lists (`@Cron`, `@OnEvent`, `@SubscribeMessage`).
43
+ */ providersWithMeta(key, filter) {
44
+ const metaKey = resolveKey(key);
45
+ const out = [];
46
+ for (const target of this.candidatesWithClassMeta(metaKey)){
47
+ const entry = this.buildEntry(target, filter, `discovery of '${metaKey}'`);
48
+ if (!entry) continue;
49
+ const meta = MetadataRegistry.getCustomClassMeta(target, metaKey);
50
+ out.push({
51
+ ...entry,
52
+ meta
53
+ });
54
+ }
55
+ return out;
56
+ }
57
+ /**
58
+ * Handler methods carrying metadata under `key`. Merges the two storage
59
+ * conventions:
60
+ * - class-level appended lists whose items carry `methodName`
61
+ * (`@Cron`/`@OnEvent`-style method decorators), and
62
+ * - true handler-level metadata (`@SetMetadata`/`createDiscoverableDecorator`
63
+ * applied to a method).
64
+ */ methodsWithMeta(key, filter) {
65
+ const metaKey = resolveKey(key);
66
+ const out = [];
67
+ for (const target of this.candidatesWithClassMeta(metaKey)){
68
+ const classMeta = MetadataRegistry.getCustomClassMeta(target, metaKey);
69
+ if (!Array.isArray(classMeta) || !classMeta.some(isMethodMetaItem)) continue;
70
+ const entry = this.buildEntry(target, filter, `discovery of '${metaKey}'`);
71
+ if (!entry) continue;
72
+ for (const item of classMeta){
73
+ if (!isMethodMetaItem(item)) continue;
74
+ out.push({
75
+ class: entry,
76
+ methodName: item.methodName,
77
+ meta: item
78
+ });
79
+ }
80
+ }
81
+ for (const target of MetadataRegistry.getClassesWithHandlerMeta(metaKey)){
82
+ if (!this.container.has(target)) continue;
83
+ const entry = this.buildEntry(target, filter, `discovery of '${metaKey}'`);
84
+ if (!entry) continue;
85
+ for (const { handler, value } of MetadataRegistry.getHandlersWithMeta(target, metaKey)){
86
+ out.push({
87
+ class: entry,
88
+ methodName: handler,
89
+ meta: value
90
+ });
91
+ }
92
+ }
93
+ return out;
94
+ }
95
+ /**
96
+ * Index-first candidate walk with a legacy full-scan fallback: when the
97
+ * reverse index knows nothing about a key, an older copy of the registry
98
+ * module may have taken the writes (mixed dist/src or package versions in
99
+ * one process) — fall back to scanning container tokens the way the
100
+ * pre-DiscoveryService loops did.
101
+ */ candidatesWithClassMeta(metaKey) {
102
+ const indexed = MetadataRegistry.getClassesWithClassMeta(metaKey);
103
+ if (indexed.size > 0) {
104
+ return [
105
+ ...indexed
106
+ ].filter((t)=>this.container.has(t));
107
+ }
108
+ const out = [];
109
+ for (const token of this.container.getTokens()){
110
+ if (typeof token !== 'function') continue;
111
+ if (MetadataRegistry.getCustomClassMeta(token, metaKey) !== undefined) {
112
+ out.push(token);
113
+ }
114
+ }
115
+ return out;
116
+ }
117
+ buildEntry(metatype, filter, label) {
118
+ const moduleIds = this.container.getOwnerModuleIds(metatype);
119
+ if (moduleIds.length === 0) return undefined;
120
+ if (filter?.moduleId !== undefined) {
121
+ const wanted = Array.isArray(filter.moduleId) ? filter.moduleId : [
122
+ filter.moduleId
123
+ ];
124
+ if (!moduleIds.some((id)=>wanted.includes(id))) return undefined;
125
+ }
126
+ const scope = this.container.getProviderScope(metatype) ?? Scope.SINGLETON;
127
+ if (filter?.deferLazy && this.container.isLazyPending(metatype)) {
128
+ // Deliberate deferral — no diagnostics warning: the entry is complete
129
+ // metadata-wise and consumers re-resolve by token at dispatch time.
130
+ return {
131
+ token: metatype,
132
+ metatype,
133
+ moduleIds,
134
+ scope,
135
+ instance: undefined
136
+ };
137
+ }
138
+ if (scope === Scope.REQUEST && !filter?.includeRequestScoped) {
139
+ if (this.container.getDiagnostics() === 'log') {
140
+ console.warn(`[vela] ${label}: ${metatype.name} is request-scoped and cannot be ` + `materialized at bootstrap — skipped. Pass { includeRequestScoped: true } to override.`);
141
+ }
142
+ return {
143
+ token: metatype,
144
+ metatype,
145
+ moduleIds,
146
+ scope,
147
+ instance: undefined
148
+ };
149
+ }
150
+ let instance;
151
+ try {
152
+ instance = this.container.resolve(metatype);
153
+ } catch (err) {
154
+ const mode = this.container.getDiagnostics();
155
+ if (mode === 'throw') throw err;
156
+ if (mode === 'log') {
157
+ console.warn(`[vela] ${label}: cannot resolve ${metatype.name}:`, err);
158
+ }
159
+ return undefined;
160
+ }
161
+ return {
162
+ token: metatype,
163
+ metatype,
164
+ moduleIds,
165
+ scope,
166
+ instance
167
+ };
168
+ }
169
+ }
170
+ DiscoveryService = _ts_decorate([
171
+ Injectable(),
172
+ _ts_param(0, Inject(Container)),
173
+ _ts_metadata("design:type", Function),
174
+ _ts_metadata("design:paramtypes", [
175
+ typeof Container === "undefined" ? Object : Container
176
+ ])
177
+ ], DiscoveryService);
@@ -0,0 +1,2 @@
1
+ export { DiscoveryService, type DiscoveredClass, type DiscoveredMethodMeta, type DiscoveryFilter, } from './discovery.service';
2
+ export { createDiscoverableDecorator, type CreateDiscoverableDecoratorOptions, type DiscoverableDecorator, } from './discoverable.decorator';
@@ -0,0 +1,2 @@
1
+ export { DiscoveryService } from "./discovery.service.js";
2
+ export { createDiscoverableDecorator } from "./discoverable.decorator.js";
@@ -0,0 +1,42 @@
1
+ import type { DiscoveryService } from '../discovery/discovery.service';
2
+ import { type Entrypoint, type EntrypointKind } from './entrypoint.types';
3
+ /**
4
+ * Declare an entrypoint kind. Idempotent per `kind` (last declaration wins so
5
+ * an HMR-re-evaluated module can re-declare); typically called at module
6
+ * import time next to the decorator definition:
7
+ *
8
+ * ```ts
9
+ * export const WS_GATEWAY_METADATA = 'vela:ws:gateway';
10
+ * registerEntrypointKind({ kind: 'websocket', metaKey: WS_GATEWAY_METADATA, level: 'class' });
11
+ * ```
12
+ */
13
+ export declare function registerEntrypointKind(k: EntrypointKind): void;
14
+ /** All declared kinds (registration order). */
15
+ export declare function getEntrypointKinds(): EntrypointKind[];
16
+ /**
17
+ * Reset all kind declarations. Test-only.
18
+ * @internal
19
+ */
20
+ export declare function _resetEntrypointKinds(): void;
21
+ /**
22
+ * The per-application view of every entrypoint: built once after
23
+ * `onApplicationBootstrap` hooks have run (so `ContributesEntrypoints`
24
+ * providers have finished their own discovery), queried by runtime adapters:
25
+ *
26
+ * ```ts
27
+ * for (const ep of app.entrypoints.ofKind('websocket')) { ... }
28
+ * ```
29
+ *
30
+ * Per-app (never global): two applications in one process each build their
31
+ * own registry from their own container.
32
+ */
33
+ export declare class EntrypointRegistry {
34
+ private readonly byKind;
35
+ static build(discovery: DiscoveryService, eagerInstances: readonly unknown[], options?: {
36
+ deferLazy?: boolean;
37
+ }): Promise<EntrypointRegistry>;
38
+ private add;
39
+ ofKind<M = unknown>(kind: string): Entrypoint<M>[];
40
+ kinds(): string[];
41
+ all(): Entrypoint[];
42
+ }
@@ -0,0 +1,112 @@
1
+ import { contributesEntrypoints } from "./entrypoint.types.js";
2
+ // Kind declarations are process-global and import-time (a module declares its
3
+ // kind next to its decorator, before any app exists), so they are anchored on
4
+ // `globalThis` exactly like MetadataRegistry state: a Vite HMR re-eval must
5
+ // reuse the same store, not mint an empty one.
6
+ const KIND_STORE_KEY = Symbol.for('vela:entrypoints:v1');
7
+ function kindStore() {
8
+ const g = globalThis;
9
+ return g[KIND_STORE_KEY] ??= new Map();
10
+ }
11
+ /**
12
+ * Declare an entrypoint kind. Idempotent per `kind` (last declaration wins so
13
+ * an HMR-re-evaluated module can re-declare); typically called at module
14
+ * import time next to the decorator definition:
15
+ *
16
+ * ```ts
17
+ * export const WS_GATEWAY_METADATA = 'vela:ws:gateway';
18
+ * registerEntrypointKind({ kind: 'websocket', metaKey: WS_GATEWAY_METADATA, level: 'class' });
19
+ * ```
20
+ */ export function registerEntrypointKind(k) {
21
+ kindStore().set(k.kind, k);
22
+ }
23
+ /** All declared kinds (registration order). */ export function getEntrypointKinds() {
24
+ return [
25
+ ...kindStore().values()
26
+ ];
27
+ }
28
+ /**
29
+ * Reset all kind declarations. Test-only.
30
+ * @internal
31
+ */ export function _resetEntrypointKinds() {
32
+ kindStore().clear();
33
+ }
34
+ /**
35
+ * The per-application view of every entrypoint: built once after
36
+ * `onApplicationBootstrap` hooks have run (so `ContributesEntrypoints`
37
+ * providers have finished their own discovery), queried by runtime adapters:
38
+ *
39
+ * ```ts
40
+ * for (const ep of app.entrypoints.ofKind('websocket')) { ... }
41
+ * ```
42
+ *
43
+ * Per-app (never global): two applications in one process each build their
44
+ * own registry from their own container.
45
+ */ export class EntrypointRegistry {
46
+ byKind = new Map();
47
+ static async build(discovery, eagerInstances, options = {}) {
48
+ const registry = new EntrypointRegistry();
49
+ // With deferLazy, providers of unmaterialized lazy modules yield
50
+ // metadata-only entries (instance: undefined). Dispatchers re-resolve by
51
+ // token per event, so the owning module materializes at dispatch time.
52
+ const filter = options.deferLazy ? {
53
+ deferLazy: true
54
+ } : undefined;
55
+ for (const kind of getEntrypointKinds()){
56
+ if (kind.level === 'class') {
57
+ for (const found of discovery.providersWithMeta(kind.metaKey, filter)){
58
+ registry.add({
59
+ kind: kind.kind,
60
+ token: found.token,
61
+ instance: found.instance,
62
+ meta: found.meta
63
+ });
64
+ }
65
+ } else {
66
+ for (const found of discovery.methodsWithMeta(kind.metaKey, filter)){
67
+ registry.add({
68
+ kind: kind.kind,
69
+ token: found.class.token,
70
+ instance: found.class.instance,
71
+ methodName: found.methodName,
72
+ meta: found.meta
73
+ });
74
+ }
75
+ }
76
+ }
77
+ // Computed contributions. A contributor is AUTHORITATIVE for every kind it
78
+ // reports: its entries replace any metaKey-derived ones of the same kind,
79
+ // so a dispatcher that both carries decorator metadata and computes richer
80
+ // entries (path routing, aggregated handlers) never double-reports.
81
+ const contributed = [];
82
+ for (const instance of new Set(eagerInstances)){
83
+ if (!contributesEntrypoints(instance)) continue;
84
+ contributed.push(...await instance.collectEntrypoints(discovery));
85
+ }
86
+ for (const kind of new Set(contributed.map((ep)=>ep.kind))){
87
+ registry.byKind.delete(kind);
88
+ }
89
+ for (const ep of contributed)registry.add(ep);
90
+ return registry;
91
+ }
92
+ add(ep) {
93
+ const list = this.byKind.get(ep.kind);
94
+ if (list) list.push(ep);
95
+ else this.byKind.set(ep.kind, [
96
+ ep
97
+ ]);
98
+ }
99
+ ofKind(kind) {
100
+ return this.byKind.get(kind) ?? [];
101
+ }
102
+ kinds() {
103
+ return [
104
+ ...this.byKind.keys()
105
+ ];
106
+ }
107
+ all() {
108
+ return [
109
+ ...this.byKind.values()
110
+ ].flat();
111
+ }
112
+ }
@@ -0,0 +1,41 @@
1
+ import type { Token } from '../container/types';
2
+ import type { DiscoveryService } from '../discovery/discovery.service';
3
+ /**
4
+ * A declared entrypoint kind: a named category of non-HTTP (or non-standard)
5
+ * entry surface that some module knows how to dispatch — WebSocket gateways,
6
+ * queue consumers, scheduled jobs, CLI commands. Core ships NO fixed set;
7
+ * any module registers its own kind and any runtime adapter can query it.
8
+ *
9
+ * `metaKey` binds the kind to a decorator's metadata key: every provider
10
+ * carrying that metadata becomes an entrypoint of this kind at bootstrap.
11
+ */
12
+ export interface EntrypointKind {
13
+ kind: string;
14
+ metaKey: string;
15
+ /**
16
+ * 'class': one entrypoint per decorated provider (`@WebSocketGateway`).
17
+ * 'method': one entrypoint per decorated method (`@Cron`, `@QueueConsumer`
18
+ * handler methods) — flattened from the class-level list convention.
19
+ */
20
+ level: 'class' | 'method';
21
+ }
22
+ /** One concrete entrypoint discovered at bootstrap. */
23
+ export interface Entrypoint<M = unknown> {
24
+ kind: string;
25
+ token: Token;
26
+ /** The resolved provider backing the entrypoint (undefined if request-scoped). */
27
+ instance: unknown;
28
+ /** Present for method-level kinds. */
29
+ methodName?: string | symbol;
30
+ meta: M;
31
+ }
32
+ /**
33
+ * Escape hatch for providers whose entrypoints are computed rather than purely
34
+ * decorator-declared (e.g. `WsDispatcher` aggregates per-gateway routing state
35
+ * before contributing). Implementers are detected among the app's eagerly
36
+ * instantiated providers after `onApplicationBootstrap` hooks have run.
37
+ */
38
+ export interface ContributesEntrypoints {
39
+ collectEntrypoints(discovery: DiscoveryService): Entrypoint[] | Promise<Entrypoint[]>;
40
+ }
41
+ export declare function contributesEntrypoints(x: unknown): x is ContributesEntrypoints;
@@ -0,0 +1,3 @@
1
+ export function contributesEntrypoints(x) {
2
+ return typeof x === 'object' && x !== null && typeof x.collectEntrypoints === 'function';
3
+ }
@@ -0,0 +1,15 @@
1
+ import type { Type } from '../container/types';
2
+ import type { ExecutionContext } from '../pipeline/types';
3
+ /** ExecutionContext flavor handed to components around entrypoint dispatch. */
4
+ export interface EntrypointExecutionContext extends ExecutionContext {
5
+ /** The dispatch payload (queue batch, scheduled event, …). */
6
+ getPayload<T = unknown>(): T;
7
+ }
8
+ /**
9
+ * Entrypoint sibling of `buildExecutionContext` (HTTP) and
10
+ * `buildWsExecutionContext`: lets guards/interceptors/filters written against
11
+ * `getClass()`/`getHandler()`/`getType()` run unchanged around queue batches,
12
+ * scheduled ticks, and any custom entrypoint kind. Transport-specific
13
+ * accessors throw rather than fabricate fakes.
14
+ */
15
+ export declare function buildEntrypointExecutionContext(kind: string, targetClass: Type, handlerName: string | symbol, payload: unknown): EntrypointExecutionContext;
@@ -0,0 +1,21 @@
1
+ const WRONG_TRANSPORT = (accessor, kind)=>()=>{
2
+ throw new Error(`${accessor} called on a '${kind}' entrypoint ExecutionContext — this ` + `handler runs over an entrypoint dispatch, not that transport. Use ` + `getType()/getPayload() to branch.`);
3
+ };
4
+ /**
5
+ * Entrypoint sibling of `buildExecutionContext` (HTTP) and
6
+ * `buildWsExecutionContext`: lets guards/interceptors/filters written against
7
+ * `getClass()`/`getHandler()`/`getType()` run unchanged around queue batches,
8
+ * scheduled ticks, and any custom entrypoint kind. Transport-specific
9
+ * accessors throw rather than fabricate fakes.
10
+ */ export function buildEntrypointExecutionContext(kind, targetClass, handlerName, payload) {
11
+ return {
12
+ getType: ()=>kind,
13
+ getClass: ()=>targetClass,
14
+ getHandler: ()=>handlerName,
15
+ getPayload: ()=>payload,
16
+ getContext: WRONG_TRANSPORT('getContext()', kind),
17
+ getRequest: WRONG_TRANSPORT('getRequest()', kind),
18
+ switchToHttp: WRONG_TRANSPORT('switchToHttp()', kind),
19
+ switchToWs: WRONG_TRANSPORT('switchToWs()', kind)
20
+ };
21
+ }
@@ -0,0 +1,21 @@
1
+ import type { Container } from '../container/container';
2
+ /**
3
+ * Run one unit of non-HTTP work (a queue batch, a scheduled tick, an RPC
4
+ * call) inside a fresh request-scoped child container — the non-HTTP
5
+ * equivalent of the per-request child the HTTP pipeline creates:
6
+ *
7
+ * ```ts
8
+ * await runInEntrypointScope(app.getContainer(), async (scope) => {
9
+ * const consumer = scope.resolve(ep.token);
10
+ * await consumer.process(batch);
11
+ * });
12
+ * ```
13
+ *
14
+ * Request-scoped providers (and singletons bubbled to request scope) resolve
15
+ * per invocation instead of leaking one boot-time instance across every
16
+ * dispatch; the child's request-scoped disposables are disposed (LIFO) when
17
+ * `fn` settles — success or failure. `REQUEST_CONTEXT` is deliberately NOT
18
+ * seeded: it is an HTTP primitive, and resolving it here keeps throwing its
19
+ * explicit misuse error.
20
+ */
21
+ export declare function runInEntrypointScope<T>(container: Container, fn: (scope: Container) => T | Promise<T>): Promise<T>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Run one unit of non-HTTP work (a queue batch, a scheduled tick, an RPC
3
+ * call) inside a fresh request-scoped child container — the non-HTTP
4
+ * equivalent of the per-request child the HTTP pipeline creates:
5
+ *
6
+ * ```ts
7
+ * await runInEntrypointScope(app.getContainer(), async (scope) => {
8
+ * const consumer = scope.resolve(ep.token);
9
+ * await consumer.process(batch);
10
+ * });
11
+ * ```
12
+ *
13
+ * Request-scoped providers (and singletons bubbled to request scope) resolve
14
+ * per invocation instead of leaking one boot-time instance across every
15
+ * dispatch; the child's request-scoped disposables are disposed (LIFO) when
16
+ * `fn` settles — success or failure. `REQUEST_CONTEXT` is deliberately NOT
17
+ * seeded: it is an HTTP primitive, and resolving it here keeps throwing its
18
+ * explicit misuse error.
19
+ */ export async function runInEntrypointScope(container, fn) {
20
+ const scope = container.createChild();
21
+ try {
22
+ return await fn(scope);
23
+ } finally{
24
+ if (scope.hasDisposables()) {
25
+ await scope.dispose();
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,4 @@
1
+ export { EntrypointRegistry, getEntrypointKinds, registerEntrypointKind, } from './entrypoint.registry';
2
+ export { runInEntrypointScope } from './execution-scope';
3
+ export { buildEntrypointExecutionContext, type EntrypointExecutionContext, } from './execution-context';
4
+ export { contributesEntrypoints, type ContributesEntrypoints, type Entrypoint, type EntrypointKind, } from './entrypoint.types';
@@ -0,0 +1,4 @@
1
+ export { EntrypointRegistry, getEntrypointKinds, registerEntrypointKind } from "./entrypoint.registry.js";
2
+ export { runInEntrypointScope } from "./execution-scope.js";
3
+ export { buildEntrypointExecutionContext } from "./execution-context.js";
4
+ export { contributesEntrypoints } from "./entrypoint.types.js";
@@ -11,6 +11,7 @@ export class EventEmitterModule {
11
11
  }
12
12
  EventEmitterModule = _ts_decorate([
13
13
  Module({
14
+ lazy: true,
14
15
  providers: [
15
16
  EventEmitter,
16
17
  EventEmitterSubscriber
@@ -1,9 +1,9 @@
1
- import { Container } from '../container/container';
1
+ import { DiscoveryService } from '../discovery/discovery.service';
2
2
  import type { OnApplicationBootstrap } from '../lifecycle/index';
3
3
  import { EventEmitter } from './event-emitter.service';
4
4
  export declare class EventEmitterSubscriber implements OnApplicationBootstrap {
5
- private container;
5
+ private readonly discovery;
6
6
  private emitter;
7
- constructor(container: Container, emitter: EventEmitter);
7
+ constructor(discovery: DiscoveryService, emitter: EventEmitter);
8
8
  onApplicationBootstrap(): void;
9
9
  }
@@ -13,50 +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 { EventEmitter } from "./event-emitter.service.js";
19
18
  import { ON_EVENT_METADATA } from "./event-emitter.tokens.js";
20
19
  export class EventEmitterSubscriber {
21
- container;
20
+ discovery;
22
21
  emitter;
23
- constructor(container, emitter){
24
- this.container = container;
22
+ constructor(discovery, emitter){
23
+ this.discovery = discovery;
25
24
  this.emitter = emitter;
26
25
  }
27
26
  onApplicationBootstrap() {
28
- const tokens = this.container.getTokens();
29
- for (const token of tokens){
30
- // Skip non-class tokens
31
- if (typeof token !== 'function') continue;
32
- const metadata = MetadataRegistry.getCustomClassMeta(token, ON_EVENT_METADATA);
33
- if (!metadata || metadata.length === 0) continue;
34
- let instance;
35
- try {
36
- instance = this.container.resolve(token);
37
- } catch (err) {
38
- const mode = this.container.getDiagnostics();
39
- if (mode === 'throw') throw err;
40
- if (mode === 'log') {
41
- console.warn(`[vela] event subscriber discovery: cannot resolve ${token.name || String(token)}:`, err);
42
- }
43
- continue;
44
- }
45
- for (const { event, methodName } of metadata){
46
- const method = instance[methodName];
47
- if (typeof method === 'function') {
48
- this.emitter.on(event, (...args)=>method.apply(instance, args));
49
- }
27
+ for (const found of this.discovery.methodsWithMeta(ON_EVENT_METADATA)){
28
+ const instance = found.class.instance;
29
+ if (!instance) continue;
30
+ const method = instance[found.methodName];
31
+ if (typeof method === 'function') {
32
+ this.emitter.on(found.meta.event, (...args)=>method.apply(instance, args));
50
33
  }
51
34
  }
52
35
  }
53
36
  }
54
37
  EventEmitterSubscriber = _ts_decorate([
55
38
  Injectable(),
56
- _ts_param(0, Inject(Container)),
39
+ _ts_param(0, Inject(DiscoveryService)),
57
40
  _ts_metadata("design:type", Function),
58
41
  _ts_metadata("design:paramtypes", [
59
- typeof Container === "undefined" ? Object : Container,
42
+ typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
60
43
  typeof EventEmitter === "undefined" ? Object : EventEmitter
61
44
  ])
62
45
  ], EventEmitterSubscriber);
@@ -0,0 +1,36 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ import type { VelaApplication } from '../application';
3
+ import type { Container } from '../container/container';
4
+ import type { DiscoveryService } from '../discovery/discovery.service';
5
+ import type { RouteManager } from '../http/route.manager';
6
+ /** The framework's resolved world, handed to adapter hooks. */
7
+ export interface AdapterContext {
8
+ app: VelaApplication;
9
+ container: Container;
10
+ routeManager: RouteManager;
11
+ discovery: DiscoveryService;
12
+ }
13
+ /**
14
+ * The contract a runtime binding implements to plug a platform into a Vela
15
+ * app — what `@velajs/cloudflare` (bindings + Durable Object WebSocket) and
16
+ * the Node/Bun WebSocket transport do, made first-class:
17
+ *
18
+ * ```ts
19
+ * const app = await VelaFactory.create(AppModule, {
20
+ * adapters: [cloudflareAdapter({ bindings })],
21
+ * });
22
+ * ```
23
+ *
24
+ * - `requestMiddleware` is prepended to the global middleware chain (runs
25
+ * before consumer middleware — e.g. capture `c.env` for binding services).
26
+ * - `onBootstrap` runs after DI + lifecycle hooks, with `app.entrypoints`
27
+ * available, BEFORE routes are built — register platform services here.
28
+ * - `onRoutesBuilt` runs after the Hono app exists — mount platform routes
29
+ * (WebSocket upgrades, health endpoints) here via `ctx.app.getHonoApp()`.
30
+ */
31
+ export interface RuntimeAdapter {
32
+ name: string;
33
+ requestMiddleware?: MiddlewareHandler[];
34
+ onBootstrap?(ctx: AdapterContext): void | Promise<void>;
35
+ onRoutesBuilt?(ctx: AdapterContext): void | Promise<void>;
36
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The contract a runtime binding implements to plug a platform into a Vela
3
+ * app — what `@velajs/cloudflare` (bindings + Durable Object WebSocket) and
4
+ * the Node/Bun WebSocket transport do, made first-class:
5
+ *
6
+ * ```ts
7
+ * const app = await VelaFactory.create(AppModule, {
8
+ * adapters: [cloudflareAdapter({ bindings })],
9
+ * });
10
+ * ```
11
+ *
12
+ * - `requestMiddleware` is prepended to the global middleware chain (runs
13
+ * before consumer middleware — e.g. capture `c.env` for binding services).
14
+ * - `onBootstrap` runs after DI + lifecycle hooks, with `app.entrypoints`
15
+ * available, BEFORE routes are built — register platform services here.
16
+ * - `onRoutesBuilt` runs after the Hono app exists — mount platform routes
17
+ * (WebSocket upgrades, health endpoints) here via `ctx.app.getHonoApp()`.
18
+ */ export { };