@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.
- package/CHANGELOG.md +157 -0
- package/dist/application.d.ts +25 -0
- package/dist/application.js +103 -2
- package/dist/container/container.d.ts +46 -1
- package/dist/container/container.js +156 -13
- package/dist/container/disposable.js +1 -1
- package/dist/container/types.d.ts +27 -0
- package/dist/cors/cors.module.d.ts +5 -5
- package/dist/cors/cors.module.js +26 -25
- package/dist/discovery/discoverable.decorator.d.ts +38 -0
- package/dist/discovery/discoverable.decorator.js +42 -0
- package/dist/discovery/discovery.service.d.ts +102 -0
- package/dist/discovery/discovery.service.js +177 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.js +2 -0
- package/dist/entrypoint/entrypoint.registry.d.ts +42 -0
- package/dist/entrypoint/entrypoint.registry.js +112 -0
- package/dist/entrypoint/entrypoint.types.d.ts +41 -0
- package/dist/entrypoint/entrypoint.types.js +3 -0
- package/dist/entrypoint/execution-context.d.ts +15 -0
- package/dist/entrypoint/execution-context.js +21 -0
- package/dist/entrypoint/execution-scope.d.ts +21 -0
- package/dist/entrypoint/execution-scope.js +28 -0
- package/dist/entrypoint/index.d.ts +4 -0
- package/dist/entrypoint/index.js +4 -0
- package/dist/event-emitter/event-emitter.module.js +1 -0
- package/dist/event-emitter/event-emitter.subscriber.d.ts +3 -3
- package/dist/event-emitter/event-emitter.subscriber.js +12 -29
- package/dist/factory/adapter.d.ts +36 -0
- package/dist/factory/adapter.js +18 -0
- package/dist/factory/bootstrap.js +14 -2
- package/dist/factory.d.ts +11 -1
- package/dist/factory.js +23 -2
- package/dist/http/handler-executor.js +22 -19
- package/dist/http/route-contributor.d.ts +68 -0
- package/dist/http/route-contributor.js +22 -0
- package/dist/http/route.manager.js +33 -19
- package/dist/i18n/i18n.module.js +9 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +9 -2
- package/dist/internal.d.ts +0 -2
- package/dist/internal.js +0 -7
- package/dist/module/configurable-module.builder.d.ts +12 -0
- package/dist/module/configurable-module.builder.js +21 -114
- package/dist/module/decorators.js +4 -2
- package/dist/module/define-module.d.ts +112 -0
- package/dist/module/define-module.js +232 -0
- package/dist/module/index.d.ts +3 -1
- package/dist/module/index.js +3 -1
- package/dist/module/lazy-modules.d.ts +82 -0
- package/dist/module/lazy-modules.js +231 -0
- package/dist/module/lazy-provider.d.ts +65 -0
- package/dist/module/lazy-provider.js +111 -0
- package/dist/module/module-loader.d.ts +21 -0
- package/dist/module/module-loader.js +99 -24
- package/dist/openapi/document.js +16 -15
- package/dist/pipeline/component.manager.d.ts +15 -9
- package/dist/pipeline/component.manager.js +32 -46
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +31 -0
- package/dist/pipeline/pipeline-runner.js +40 -0
- package/dist/registry/metadata.registry.d.ts +9 -3
- package/dist/registry/metadata.registry.js +55 -25
- package/dist/registry/types.d.ts +10 -0
- package/dist/schedule/schedule.module.d.ts +5 -0
- package/dist/schedule/schedule.module.js +21 -8
- package/dist/schedule/schedule.registry.d.ts +3 -3
- package/dist/schedule/schedule.registry.js +23 -44
- package/dist/schedule-node/schedule-node.module.d.ts +5 -0
- package/dist/schedule-node/schedule-node.module.js +20 -10
- package/dist/seeder/seeder.module.d.ts +8 -10
- package/dist/seeder/seeder.module.js +19 -15
- package/dist/websocket/index.d.ts +1 -1
- package/dist/websocket/index.js +1 -1
- package/dist/websocket/websocket.module.d.ts +20 -3
- package/dist/websocket/websocket.module.js +50 -39
- package/dist/websocket/websocket.tokens.d.ts +2 -1
- package/dist/websocket/websocket.tokens.js +3 -2
- package/dist/websocket/ws-dispatcher.d.ts +21 -9
- package/dist/websocket/ws-dispatcher.js +44 -38
- package/dist/websocket-node/register-gateways.js +3 -3
- package/package.json +1 -1
- package/dist/http/crud-bridge.d.ts +0 -90
- package/dist/http/crud-bridge.js +0 -25
|
@@ -13,54 +13,33 @@ function _ts_param(paramIndex, decorator) {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
import { Injectable, Inject } from "../container/index.js";
|
|
16
|
-
import {
|
|
17
|
-
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
16
|
+
import { DiscoveryService } from "../discovery/discovery.service.js";
|
|
18
17
|
import { CRON_METADATA, INTERVAL_METADATA } from "./schedule.tokens.js";
|
|
19
18
|
export class ScheduleRegistry {
|
|
20
|
-
|
|
19
|
+
discovery;
|
|
21
20
|
cronJobs = [];
|
|
22
21
|
intervalJobs = [];
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
22
|
+
constructor(discovery){
|
|
23
|
+
this.discovery = discovery;
|
|
25
24
|
}
|
|
26
25
|
onApplicationBootstrap() {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
if (cronMeta) {
|
|
45
|
-
for (const { expression, methodName } of cronMeta){
|
|
46
|
-
this.cronJobs.push({
|
|
47
|
-
expression,
|
|
48
|
-
methodName,
|
|
49
|
-
instance,
|
|
50
|
-
target: token
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (intervalMeta) {
|
|
55
|
-
for (const { ms, methodName } of intervalMeta){
|
|
56
|
-
this.intervalJobs.push({
|
|
57
|
-
ms,
|
|
58
|
-
methodName,
|
|
59
|
-
instance,
|
|
60
|
-
target: token
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
26
|
+
for (const found of this.discovery.methodsWithMeta(CRON_METADATA)){
|
|
27
|
+
if (!found.class.instance) continue;
|
|
28
|
+
this.cronJobs.push({
|
|
29
|
+
expression: found.meta.expression,
|
|
30
|
+
methodName: String(found.methodName),
|
|
31
|
+
instance: found.class.instance,
|
|
32
|
+
target: found.class.metatype
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
for (const found of this.discovery.methodsWithMeta(INTERVAL_METADATA)){
|
|
36
|
+
if (!found.class.instance) continue;
|
|
37
|
+
this.intervalJobs.push({
|
|
38
|
+
ms: found.meta.ms,
|
|
39
|
+
methodName: String(found.methodName),
|
|
40
|
+
instance: found.class.instance,
|
|
41
|
+
target: found.class.metatype
|
|
42
|
+
});
|
|
64
43
|
}
|
|
65
44
|
}
|
|
66
45
|
getCronJobs() {
|
|
@@ -76,9 +55,9 @@ export class ScheduleRegistry {
|
|
|
76
55
|
}
|
|
77
56
|
ScheduleRegistry = _ts_decorate([
|
|
78
57
|
Injectable(),
|
|
79
|
-
_ts_param(0, Inject(
|
|
58
|
+
_ts_param(0, Inject(DiscoveryService)),
|
|
80
59
|
_ts_metadata("design:type", Function),
|
|
81
60
|
_ts_metadata("design:paramtypes", [
|
|
82
|
-
typeof
|
|
61
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService
|
|
83
62
|
])
|
|
84
63
|
], ScheduleRegistry);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { DynamicModule } from '../module/types';
|
|
2
|
+
/**
|
|
3
|
+
* Zero-config module (Tier C): providers live on the `@Module` bag; the
|
|
4
|
+
* `forRoot()` static is NestJS-parity sugar returning the bare dynamic module
|
|
5
|
+
* (default key — repeated calls dedup).
|
|
6
|
+
*/
|
|
2
7
|
export declare class ScheduleNodeModule {
|
|
3
8
|
static forRoot(): DynamicModule;
|
|
4
9
|
}
|
|
@@ -1,18 +1,28 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { Module } from "../module/decorators.js";
|
|
1
8
|
import { ScheduleRegistry } from "../schedule/schedule.registry.js";
|
|
2
9
|
import { ScheduleExecutor } from "./schedule.executor.js";
|
|
3
10
|
export class ScheduleNodeModule {
|
|
4
11
|
static forRoot() {
|
|
5
|
-
const providers = [
|
|
6
|
-
ScheduleRegistry,
|
|
7
|
-
ScheduleExecutor
|
|
8
|
-
];
|
|
9
12
|
return {
|
|
10
|
-
module: ScheduleNodeModule
|
|
11
|
-
providers,
|
|
12
|
-
exports: [
|
|
13
|
-
ScheduleRegistry,
|
|
14
|
-
ScheduleExecutor
|
|
15
|
-
]
|
|
13
|
+
module: ScheduleNodeModule
|
|
16
14
|
};
|
|
17
15
|
}
|
|
18
16
|
}
|
|
17
|
+
ScheduleNodeModule = _ts_decorate([
|
|
18
|
+
Module({
|
|
19
|
+
providers: [
|
|
20
|
+
ScheduleRegistry,
|
|
21
|
+
ScheduleExecutor
|
|
22
|
+
],
|
|
23
|
+
exports: [
|
|
24
|
+
ScheduleRegistry,
|
|
25
|
+
ScheduleExecutor
|
|
26
|
+
]
|
|
27
|
+
})
|
|
28
|
+
], ScheduleNodeModule);
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { VelaApplication } from '../application';
|
|
2
|
-
import type { DynamicModule } from '../module/types';
|
|
3
2
|
import type { Type } from '../container/types';
|
|
4
3
|
import type { SeederResult } from './seeder.types';
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
seeders?: Type[];
|
|
13
|
-
}): DynamicModule;
|
|
4
|
+
export interface SeederModuleOptions {
|
|
5
|
+
seeders?: Type[];
|
|
6
|
+
}
|
|
7
|
+
declare const ConfigurableModuleClass: import("..").ConfigurableModuleClassType<SeederModuleOptions, "forRoot", "create", {
|
|
8
|
+
isGlobal?: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
export declare class SeederModule extends ConfigurableModuleClass {
|
|
14
11
|
}
|
|
15
12
|
/** Convenience runner: resolve the registry from a built app and run all seeders. */
|
|
16
13
|
export declare function runSeeders(app: VelaApplication, options?: {
|
|
17
14
|
stopOnError?: boolean;
|
|
18
15
|
}): Promise<SeederResult[]>;
|
|
16
|
+
export {};
|
|
@@ -5,28 +5,32 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
}
|
|
7
7
|
import { Module } from "../module/decorators.js";
|
|
8
|
+
import { defineModule } from "../module/define-module.js";
|
|
8
9
|
import { stableHash } from "../module/stable-hash.js";
|
|
9
10
|
import { SeederRegistry } from "./seeder.registry.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}),
|
|
11
|
+
const { ConfigurableModuleClass } = defineModule({
|
|
12
|
+
name: 'Seeder',
|
|
13
|
+
// Class references aren't value-hashable — key on the seeder names, the same
|
|
14
|
+
// identity the hand-rolled forRoot used.
|
|
15
|
+
key: (options)=>stableHash({
|
|
16
|
+
seeders: (options.seeders ?? []).map((s)=>s.name)
|
|
17
|
+
}),
|
|
18
|
+
setup: ({ options })=>({
|
|
19
|
+
// Register seeder classes as providers so they're discovered at bootstrap.
|
|
20
|
+
// (Seeders declared in other modules' providers are also discovered — this
|
|
21
|
+
// is just a convenience to co-locate them.)
|
|
22
22
|
providers: [
|
|
23
|
-
...seeders
|
|
23
|
+
...options.seeders ?? []
|
|
24
24
|
]
|
|
25
|
-
}
|
|
26
|
-
|
|
25
|
+
})
|
|
26
|
+
});
|
|
27
|
+
export class SeederModule extends ConfigurableModuleClass {
|
|
27
28
|
}
|
|
28
29
|
SeederModule = _ts_decorate([
|
|
29
30
|
Module({
|
|
31
|
+
// Lazy: the @Seeder metadata scan runs when SeederRegistry is first
|
|
32
|
+
// resolved (runSeeders) — hook replay repopulates it correctly.
|
|
33
|
+
lazy: true,
|
|
30
34
|
providers: [
|
|
31
35
|
SeederRegistry
|
|
32
36
|
],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer, } from './websocket.decorators';
|
|
2
2
|
export { WebSocketModule } from './websocket.module';
|
|
3
3
|
export type { WebSocketModuleOptions } from './websocket.module';
|
|
4
|
-
export { WsDispatcher } from './ws-dispatcher';
|
|
4
|
+
export { WsDispatcher, type WsEntrypointMeta } from './ws-dispatcher';
|
|
5
5
|
export { WsServerImpl, BroadcastOperatorImpl } from './ws-server';
|
|
6
6
|
export { InMemoryRoomRegistry, local } from './ws-sync';
|
|
7
7
|
export type { SyncDriver, RoomRegistry } from './ws-sync';
|
package/dist/websocket/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export { WebSocketGateway, SubscribeMessage, MessageBody, ConnectedSocket, WebSocketServer } from "./websocket.decorators.js";
|
|
3
3
|
// Module
|
|
4
4
|
export { WebSocketModule } from "./websocket.module.js";
|
|
5
|
-
// Dispatcher (injected/called by transports)
|
|
5
|
+
// Dispatcher (injected/called by transports) + the 'websocket' entrypoint meta
|
|
6
6
|
export { WsDispatcher } from "./ws-dispatcher.js";
|
|
7
7
|
// Server handle + rooms + sync
|
|
8
8
|
export { WsServerImpl, BroadcastOperatorImpl } from "./ws-server.js";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { DynamicModule } from '../module/types';
|
|
2
1
|
import { type RoomRegistry, type SyncDriver } from './ws-sync';
|
|
3
2
|
export interface WebSocketModuleOptions {
|
|
4
3
|
/**
|
|
@@ -13,6 +12,24 @@ export interface WebSocketModuleOptions {
|
|
|
13
12
|
*/
|
|
14
13
|
registry?: RoomRegistry;
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Registers the WebSocket gateway machinery: the message dispatcher (discovers
|
|
17
|
+
* `@WebSocketGateway` classes via DiscoveryService at bootstrap), the room
|
|
18
|
+
* registry, the sync driver, and the `@WebSocketServer()`-injected server
|
|
19
|
+
* handle.
|
|
20
|
+
*
|
|
21
|
+
* Construction lives in chained provider factories — registry → driver
|
|
22
|
+
* (bound to THAT registry) → server — so the single shared registry instance
|
|
23
|
+
* is preserved and everything materializes at bootstrap's eager
|
|
24
|
+
* instantiation, before any lifecycle hook or message dispatch.
|
|
25
|
+
*
|
|
26
|
+
* The instance key derives from the sync driver kind: two `forRoot()` calls
|
|
27
|
+
* with the same driver kind dedup (HMR-idempotent); pass an explicit `key`
|
|
28
|
+
* to run multiple same-kind instances side by side.
|
|
29
|
+
*/
|
|
30
|
+
declare const ConfigurableModuleClass: import("..").ConfigurableModuleClassType<WebSocketModuleOptions, "forRoot", "create", {
|
|
31
|
+
isGlobal?: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
export declare class WebSocketModule extends ConfigurableModuleClass {
|
|
18
34
|
}
|
|
35
|
+
export {};
|
|
@@ -1,52 +1,63 @@
|
|
|
1
|
+
import { defineModule } from "../module/define-module.js";
|
|
1
2
|
import { WsDispatcher } from "./ws-dispatcher.js";
|
|
2
3
|
import { WsServerImpl } from "./ws-server.js";
|
|
3
4
|
import { InMemoryRoomRegistry, local } from "./ws-sync.js";
|
|
4
5
|
import { WS_MODULE_OPTIONS, WS_ROOM_REGISTRY, WS_SERVER, WS_SYNC_DRIVER } from "./websocket.tokens.js";
|
|
5
6
|
/**
|
|
6
7
|
* Registers the WebSocket gateway machinery: the message dispatcher (discovers
|
|
7
|
-
* `@WebSocketGateway` classes at bootstrap), the room
|
|
8
|
-
* and the `@WebSocketServer()`-injected server
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
8
|
+
* `@WebSocketGateway` classes via DiscoveryService at bootstrap), the room
|
|
9
|
+
* registry, the sync driver, and the `@WebSocketServer()`-injected server
|
|
10
|
+
* handle.
|
|
11
|
+
*
|
|
12
|
+
* Construction lives in chained provider factories — registry → driver
|
|
13
|
+
* (bound to THAT registry) → server — so the single shared registry instance
|
|
14
|
+
* is preserved and everything materializes at bootstrap's eager
|
|
15
|
+
* instantiation, before any lifecycle hook or message dispatch.
|
|
16
|
+
*
|
|
17
|
+
* The instance key derives from the sync driver kind: two `forRoot()` calls
|
|
18
|
+
* with the same driver kind dedup (HMR-idempotent); pass an explicit `key`
|
|
19
|
+
* to run multiple same-kind instances side by side.
|
|
20
|
+
*/ const { ConfigurableModuleClass } = defineModule({
|
|
21
|
+
name: 'WebSocket',
|
|
22
|
+
optionsToken: WS_MODULE_OPTIONS,
|
|
23
|
+
key: (options)=>`ws#${options.sync?.kind ?? 'local'}`,
|
|
24
|
+
setup: ({ OPTIONS })=>({
|
|
25
|
+
providers: [
|
|
26
|
+
{
|
|
27
|
+
provide: WS_ROOM_REGISTRY,
|
|
28
|
+
useFactory: (o)=>o.registry ?? new InMemoryRoomRegistry(),
|
|
29
|
+
inject: [
|
|
30
|
+
OPTIONS
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
provide: WS_SYNC_DRIVER,
|
|
35
|
+
useFactory: (o, registry)=>{
|
|
36
|
+
const driver = o.sync ?? local();
|
|
37
|
+
driver.bind(registry);
|
|
38
|
+
return driver;
|
|
39
|
+
},
|
|
40
|
+
inject: [
|
|
41
|
+
OPTIONS,
|
|
42
|
+
WS_ROOM_REGISTRY
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
provide: WS_SERVER,
|
|
47
|
+
useFactory: (driver)=>new WsServerImpl(driver),
|
|
48
|
+
inject: [
|
|
49
|
+
WS_SYNC_DRIVER
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
WsDispatcher
|
|
53
|
+
],
|
|
44
54
|
exports: [
|
|
45
55
|
WS_SERVER,
|
|
46
56
|
WS_SYNC_DRIVER,
|
|
47
57
|
WS_ROOM_REGISTRY,
|
|
48
58
|
WsDispatcher
|
|
49
59
|
]
|
|
50
|
-
}
|
|
51
|
-
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
export class WebSocketModule extends ConfigurableModuleClass {
|
|
52
63
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { InjectionToken } from '../container/types';
|
|
2
|
+
import type { WebSocketModuleOptions } from './websocket.module';
|
|
2
3
|
import type { RoomRegistry, SyncDriver } from './ws-sync';
|
|
3
4
|
import type { WsServer } from './websocket.types';
|
|
4
5
|
export declare const WS_GATEWAY_METADATA = "vela:ws-gateway";
|
|
@@ -19,4 +20,4 @@ export declare const WS_SERVER: InjectionToken<WsServer>;
|
|
|
19
20
|
export declare const WS_SYNC_DRIVER: InjectionToken<SyncDriver>;
|
|
20
21
|
/** The local room-membership registry a transport reads/writes. */
|
|
21
22
|
export declare const WS_ROOM_REGISTRY: InjectionToken<RoomRegistry>;
|
|
22
|
-
export declare const WS_MODULE_OPTIONS
|
|
23
|
+
export declare const WS_MODULE_OPTIONS: InjectionToken<WebSocketModuleOptions>;
|
|
@@ -16,5 +16,6 @@ export const WsParamType = {
|
|
|
16
16
|
*/ export const WS_SERVER = new InjectionToken('WS_SERVER');
|
|
17
17
|
/** The active cross-instance sync driver (`local()` by default). */ export const WS_SYNC_DRIVER = new InjectionToken('WS_SYNC_DRIVER');
|
|
18
18
|
/** The local room-membership registry a transport reads/writes. */ export const WS_ROOM_REGISTRY = new InjectionToken('WS_ROOM_REGISTRY');
|
|
19
|
-
// forRoot() options carrier
|
|
20
|
-
|
|
19
|
+
// forRoot() options carrier — a typed InjectionToken like every other module
|
|
20
|
+
// options token (the raw-string form was the odd one out).
|
|
21
|
+
export const WS_MODULE_OPTIONS = new InjectionToken('WS_MODULE_OPTIONS');
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { Container } from '../container/container';
|
|
2
|
+
import { DiscoveryService } from '../discovery/discovery.service';
|
|
3
|
+
import type { ContributesEntrypoints, Entrypoint } from '../entrypoint/entrypoint.types';
|
|
2
4
|
import { RouteManager } from '../http/route.manager';
|
|
3
5
|
import type { OnApplicationBootstrap } from '../lifecycle/index';
|
|
4
6
|
import type { WsClient, WsServer } from './websocket.types';
|
|
7
|
+
/** Metadata carried by each `'websocket'` entrypoint a transport consumes. */
|
|
8
|
+
export interface WsEntrypointMeta {
|
|
9
|
+
/** The gateway's route path (from `@WebSocketGateway({ path })`). */
|
|
10
|
+
path: string;
|
|
11
|
+
/** The dispatcher that routes frames for this gateway. */
|
|
12
|
+
dispatcher: WsDispatcher;
|
|
13
|
+
}
|
|
5
14
|
/**
|
|
6
|
-
* Discovers `@WebSocketGateway` classes at bootstrap (
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `dispatchMessage`, `handleClose`,
|
|
10
|
-
*
|
|
11
|
-
*
|
|
15
|
+
* Discovers `@WebSocketGateway` classes at bootstrap (via `DiscoveryService`)
|
|
16
|
+
* and routes inbound messages to `@SubscribeMessage` handlers. Transports read
|
|
17
|
+
* the gateways from `app.entrypoints.ofKind('websocket')` (this dispatcher
|
|
18
|
+
* contributes them) and call `handleOpen`, `dispatchMessage`, `handleClose`,
|
|
19
|
+
* and `handleError`; the guard → pipe → interceptor → filter pipeline
|
|
20
|
+
* (including `APP_*` global components) runs through the shared
|
|
21
|
+
* `PipelineRunner`.
|
|
12
22
|
*/
|
|
13
|
-
export declare class WsDispatcher implements OnApplicationBootstrap {
|
|
23
|
+
export declare class WsDispatcher implements OnApplicationBootstrap, ContributesEntrypoints {
|
|
14
24
|
private readonly container;
|
|
25
|
+
private readonly discovery;
|
|
15
26
|
private readonly server?;
|
|
16
27
|
private readonly routeManager?;
|
|
17
28
|
private readonly gateways;
|
|
18
|
-
constructor(container: Container, server?: WsServer | undefined, routeManager?: RouteManager | undefined);
|
|
29
|
+
constructor(container: Container, discovery: DiscoveryService, server?: WsServer | undefined, routeManager?: RouteManager | undefined);
|
|
19
30
|
/** Paths of every discovered `@WebSocketGateway` — used by transports to register routes. */
|
|
20
31
|
get gatewayPaths(): string[];
|
|
21
32
|
onApplicationBootstrap(): Promise<void>;
|
|
33
|
+
/** One `'websocket'` entrypoint per discovered gateway (authoritative). */
|
|
34
|
+
collectEntrypoints(): Entrypoint<WsEntrypointMeta>[];
|
|
22
35
|
handleOpen(path: string, client: WsClient): Promise<void>;
|
|
23
36
|
handleClose(path: string, client: WsClient, _code: number, _reason: string): Promise<void>;
|
|
24
37
|
handleError(path: string, _client: WsClient, err: unknown): Promise<void>;
|
|
@@ -28,6 +41,5 @@ export declare class WsDispatcher implements OnApplicationBootstrap {
|
|
|
28
41
|
private trySend;
|
|
29
42
|
private reportBootstrapError;
|
|
30
43
|
private parse;
|
|
31
|
-
private resolveGateway;
|
|
32
44
|
private buildGatewayEntry;
|
|
33
45
|
}
|
|
@@ -14,10 +14,12 @@ function _ts_param(paramIndex, decorator) {
|
|
|
14
14
|
}
|
|
15
15
|
import { Container } from "../container/container.js";
|
|
16
16
|
import { Inject, Injectable, Optional } from "../container/decorators.js";
|
|
17
|
+
import { DiscoveryService } from "../discovery/discovery.service.js";
|
|
17
18
|
import { instantiateMany } from "../http/instantiate.js";
|
|
18
19
|
import { RouteManager } from "../http/route.manager.js";
|
|
19
20
|
import { ComponentManager } from "../pipeline/component.manager.js";
|
|
20
21
|
import { shouldFilterCatch } from "../pipeline/decorators.js";
|
|
22
|
+
import { PipelineRunner } from "../pipeline/pipeline-runner.js";
|
|
21
23
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
22
24
|
import { resolveWsArgs } from "./ws-argument-resolver.js";
|
|
23
25
|
import { buildWsExecutionContext } from "./ws-execution-context.js";
|
|
@@ -37,11 +39,13 @@ function isWsResponse(x) {
|
|
|
37
39
|
}
|
|
38
40
|
export class WsDispatcher {
|
|
39
41
|
container;
|
|
42
|
+
discovery;
|
|
40
43
|
server;
|
|
41
44
|
routeManager;
|
|
42
45
|
gateways = new Map();
|
|
43
|
-
constructor(container, server, routeManager){
|
|
46
|
+
constructor(container, discovery, server, routeManager){
|
|
44
47
|
this.container = container;
|
|
48
|
+
this.discovery = discovery;
|
|
45
49
|
this.server = server;
|
|
46
50
|
this.routeManager = routeManager;
|
|
47
51
|
}
|
|
@@ -51,17 +55,15 @@ export class WsDispatcher {
|
|
|
51
55
|
];
|
|
52
56
|
}
|
|
53
57
|
async onApplicationBootstrap() {
|
|
54
|
-
for (const
|
|
55
|
-
if (
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
if (!instance) continue;
|
|
60
|
-
const entry = this.buildGatewayEntry(token, options, instance);
|
|
58
|
+
for (const found of this.discovery.providersWithMeta(WS_GATEWAY_METADATA)){
|
|
59
|
+
if (!found.instance) continue;
|
|
60
|
+
const gatewayClass = found.metatype;
|
|
61
|
+
const instance = found.instance;
|
|
62
|
+
const entry = this.buildGatewayEntry(gatewayClass, found.meta, instance);
|
|
61
63
|
// Two gateways on the same path (or both defaulting to '') would silently
|
|
62
64
|
// overwrite each other in the routing Map — surface it instead.
|
|
63
65
|
if (this.gateways.has(entry.path)) {
|
|
64
|
-
const msg = `[vela] duplicate @WebSocketGateway path '${entry.path}' ` + `(${
|
|
66
|
+
const msg = `[vela] duplicate @WebSocketGateway path '${entry.path}' ` + `(${gatewayClass.name}); keeping the first. Give each gateway a distinct path.`;
|
|
65
67
|
if (this.container.getDiagnostics() === 'throw') throw new Error(msg);
|
|
66
68
|
console.warn(msg);
|
|
67
69
|
continue;
|
|
@@ -71,11 +73,24 @@ export class WsDispatcher {
|
|
|
71
73
|
try {
|
|
72
74
|
await instance.afterInit(this.server);
|
|
73
75
|
} catch (err) {
|
|
74
|
-
this.reportBootstrapError(
|
|
76
|
+
this.reportBootstrapError(gatewayClass.name, err);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
81
|
+
/** One `'websocket'` entrypoint per discovered gateway (authoritative). */ collectEntrypoints() {
|
|
82
|
+
return [
|
|
83
|
+
...this.gateways.values()
|
|
84
|
+
].map((entry)=>({
|
|
85
|
+
kind: 'websocket',
|
|
86
|
+
token: entry.gatewayClass,
|
|
87
|
+
instance: entry.instance,
|
|
88
|
+
meta: {
|
|
89
|
+
path: entry.path,
|
|
90
|
+
dispatcher: this
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
79
94
|
async handleOpen(path, client) {
|
|
80
95
|
const entry = this.gateways.get(path);
|
|
81
96
|
if (entry && hasHandleConnection(entry.instance)) {
|
|
@@ -131,15 +146,17 @@ export class WsDispatcher {
|
|
|
131
146
|
...instantiateMany(globals?.filters ?? [], this.container)
|
|
132
147
|
];
|
|
133
148
|
try {
|
|
134
|
-
for (const guard of guards){
|
|
135
|
-
if (!await guard.canActivate(ctx)) {
|
|
136
|
-
throw new WsException('Forbidden');
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const args = await resolveWsArgs(handler.paramMeta, client, message.data, pipes, handler.paramTypes);
|
|
140
149
|
const method = entry.instance[handler.methodName];
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
// Guards → args + pipes → interceptor chain → handler, via the shared
|
|
151
|
+
// runner (WS keeps guards-first, unlike HTTP's args-before-guards).
|
|
152
|
+
const result = await PipelineRunner.run({
|
|
153
|
+
context: ctx,
|
|
154
|
+
guards,
|
|
155
|
+
interceptors,
|
|
156
|
+
resolveArgs: ()=>resolveWsArgs(handler.paramMeta, client, message.data, pipes, handler.paramTypes),
|
|
157
|
+
invoke: async (args)=>method.apply(entry.instance, args),
|
|
158
|
+
onGuardReject: ()=>new WsException('Forbidden')
|
|
159
|
+
});
|
|
143
160
|
this.reply(client, message, result);
|
|
144
161
|
} catch (error) {
|
|
145
162
|
await this.runFilters(client, message, error, filters, ctx);
|
|
@@ -193,19 +210,6 @@ export class WsDispatcher {
|
|
|
193
210
|
}
|
|
194
211
|
return parsed;
|
|
195
212
|
}
|
|
196
|
-
resolveGateway(token) {
|
|
197
|
-
try {
|
|
198
|
-
return this.container.resolve(token);
|
|
199
|
-
} catch (err) {
|
|
200
|
-
const mode = this.container.getDiagnostics();
|
|
201
|
-
if (mode === 'throw') throw err;
|
|
202
|
-
if (mode === 'log') {
|
|
203
|
-
const name = typeof token === 'function' ? token.name : String(token);
|
|
204
|
-
console.warn(`[vela] websocket gateway discovery: cannot resolve ${name}:`, err);
|
|
205
|
-
}
|
|
206
|
-
return undefined;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
213
|
buildGatewayEntry(gatewayClass, options, instance) {
|
|
210
214
|
const ctor = gatewayClass;
|
|
211
215
|
const subs = MetadataRegistry.getCustomClassMeta(ctor, WS_SUBSCRIBE_METADATA) ?? [];
|
|
@@ -220,12 +224,12 @@ export class WsDispatcher {
|
|
|
220
224
|
methodName,
|
|
221
225
|
paramMeta,
|
|
222
226
|
paramTypes,
|
|
223
|
-
guards: ComponentManager.
|
|
224
|
-
pipes: ComponentManager.
|
|
225
|
-
interceptors: ComponentManager.
|
|
227
|
+
guards: ComponentManager.getScopedComponents('guard', ctor, methodName),
|
|
228
|
+
pipes: ComponentManager.getScopedComponents('pipe', ctor, methodName),
|
|
229
|
+
interceptors: ComponentManager.getScopedComponents('interceptor', ctor, methodName),
|
|
226
230
|
// Handler → controller → global, so the closest filter runs first (mirrors HandlerExecutor).
|
|
227
231
|
filters: [
|
|
228
|
-
...ComponentManager.
|
|
232
|
+
...ComponentManager.getScopedComponents('filter', ctor, methodName)
|
|
229
233
|
].reverse()
|
|
230
234
|
});
|
|
231
235
|
}
|
|
@@ -240,13 +244,15 @@ export class WsDispatcher {
|
|
|
240
244
|
WsDispatcher = _ts_decorate([
|
|
241
245
|
Injectable(),
|
|
242
246
|
_ts_param(0, Inject(Container)),
|
|
243
|
-
_ts_param(1,
|
|
244
|
-
_ts_param(1, Inject(WS_SERVER)),
|
|
247
|
+
_ts_param(1, Inject(DiscoveryService)),
|
|
245
248
|
_ts_param(2, Optional()),
|
|
246
|
-
_ts_param(2, Inject(
|
|
249
|
+
_ts_param(2, Inject(WS_SERVER)),
|
|
250
|
+
_ts_param(3, Optional()),
|
|
251
|
+
_ts_param(3, Inject(RouteManager)),
|
|
247
252
|
_ts_metadata("design:type", Function),
|
|
248
253
|
_ts_metadata("design:paramtypes", [
|
|
249
254
|
typeof Container === "undefined" ? Object : Container,
|
|
255
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
|
|
250
256
|
typeof WsServer === "undefined" ? Object : WsServer,
|
|
251
257
|
typeof RouteManager === "undefined" ? Object : RouteManager
|
|
252
258
|
])
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WS_ROOM_REGISTRY } from "../websocket/index.js";
|
|
2
2
|
import { NodeWsClient } from "./node-ws-client.js";
|
|
3
3
|
function toText(data) {
|
|
4
4
|
if (typeof data === 'string') return data;
|
|
@@ -24,9 +24,9 @@ function toText(data) {
|
|
|
24
24
|
* ```
|
|
25
25
|
*/ export function registerWebSocketGateways(app, upgradeWebSocket) {
|
|
26
26
|
const hono = app.getHonoApp();
|
|
27
|
-
const dispatcher = app.get(WsDispatcher);
|
|
28
27
|
const registry = app.get(WS_ROOM_REGISTRY);
|
|
29
|
-
for (const
|
|
28
|
+
for (const { meta } of app.entrypoints.ofKind('websocket')){
|
|
29
|
+
const { path, dispatcher } = meta;
|
|
30
30
|
hono.get(path, upgradeWebSocket((c)=>{
|
|
31
31
|
const roomId = c.req.param('id') ?? path;
|
|
32
32
|
let client;
|