@fluojs/websockets 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.ko.md +133 -0
- package/README.md +133 -0
- package/dist/bun/bun-module.d.ts +15 -0
- package/dist/bun/bun-module.d.ts.map +1 -0
- package/dist/bun/bun-module.js +27 -0
- package/dist/bun/bun-service.d.ts +63 -0
- package/dist/bun/bun-service.d.ts.map +1 -0
- package/dist/bun/bun-service.js +558 -0
- package/dist/bun/bun-types.d.ts +58 -0
- package/dist/bun/bun-types.d.ts.map +1 -0
- package/dist/bun/bun-types.js +1 -0
- package/dist/bun/bun.d.ts +4 -0
- package/dist/bun/bun.d.ts.map +1 -0
- package/dist/bun/bun.js +3 -0
- package/dist/bun.d.ts +2 -0
- package/dist/bun.d.ts.map +1 -0
- package/dist/bun.js +1 -0
- package/dist/cloudflare-workers/cloudflare-workers-module.d.ts +15 -0
- package/dist/cloudflare-workers/cloudflare-workers-module.d.ts.map +1 -0
- package/dist/cloudflare-workers/cloudflare-workers-module.js +27 -0
- package/dist/cloudflare-workers/cloudflare-workers-service.d.ts +61 -0
- package/dist/cloudflare-workers/cloudflare-workers-service.d.ts.map +1 -0
- package/dist/cloudflare-workers/cloudflare-workers-service.js +538 -0
- package/dist/cloudflare-workers/cloudflare-workers-types.d.ts +30 -0
- package/dist/cloudflare-workers/cloudflare-workers-types.d.ts.map +1 -0
- package/dist/cloudflare-workers/cloudflare-workers-types.js +1 -0
- package/dist/cloudflare-workers/cloudflare-workers.d.ts +4 -0
- package/dist/cloudflare-workers/cloudflare-workers.d.ts.map +1 -0
- package/dist/cloudflare-workers/cloudflare-workers.js +3 -0
- package/dist/cloudflare-workers.d.ts +2 -0
- package/dist/cloudflare-workers.d.ts.map +1 -0
- package/dist/cloudflare-workers.js +1 -0
- package/dist/decorators.d.ts +56 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +115 -0
- package/dist/deno/deno-module.d.ts +15 -0
- package/dist/deno/deno-module.d.ts.map +1 -0
- package/dist/deno/deno-module.js +27 -0
- package/dist/deno/deno-service.d.ts +61 -0
- package/dist/deno/deno-service.d.ts.map +1 -0
- package/dist/deno/deno-service.js +533 -0
- package/dist/deno/deno-types.d.ts +25 -0
- package/dist/deno/deno-types.d.ts.map +1 -0
- package/dist/deno/deno-types.js +1 -0
- package/dist/deno/deno.d.ts +4 -0
- package/dist/deno/deno.d.ts.map +1 -0
- package/dist/deno/deno.js +3 -0
- package/dist/deno.d.ts +2 -0
- package/dist/deno.d.ts.map +1 -0
- package/dist/deno.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/shared.d.ts +28 -0
- package/dist/internal/shared.d.ts.map +1 -0
- package/dist/internal/shared.js +188 -0
- package/dist/metadata.d.ts +13 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +81 -0
- package/dist/module.d.ts +26 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +26 -0
- package/dist/node/node-module.d.ts +15 -0
- package/dist/node/node-module.d.ts.map +1 -0
- package/dist/node/node-module.js +27 -0
- package/dist/node/node-service.d.ts +129 -0
- package/dist/node/node-service.d.ts.map +1 -0
- package/dist/node/node-service.js +892 -0
- package/dist/node/node-types.d.ts +81 -0
- package/dist/node/node-types.d.ts.map +1 -0
- package/dist/node/node-types.js +1 -0
- package/dist/node/node.d.ts +4 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +3 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +1 -0
- package/dist/options-token.internal.d.ts +7 -0
- package/dist/options-token.internal.d.ts.map +1 -0
- package/dist/options-token.internal.js +4 -0
- package/dist/service.d.ts +9 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +8 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { getClassDiMetadata } from '@fluojs/core/internal';
|
|
2
|
+
import { getWebSocketGatewayMetadata, getWebSocketHandlerMetadataEntries } from '../metadata.js';
|
|
3
|
+
const textDecoder = new TextDecoder();
|
|
4
|
+
export function isFinitePositiveInteger(value) {
|
|
5
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 && Number.isInteger(value);
|
|
6
|
+
}
|
|
7
|
+
export function normalizeGatewayPath(path) {
|
|
8
|
+
if (path === '/') {
|
|
9
|
+
return '/';
|
|
10
|
+
}
|
|
11
|
+
const normalized = `/${path.replace(/^\/+/, '').replace(/\/+$/, '')}`;
|
|
12
|
+
return normalized === '' ? '/' : normalized;
|
|
13
|
+
}
|
|
14
|
+
export function parseIncomingMessage(data) {
|
|
15
|
+
let text;
|
|
16
|
+
if (typeof data === 'string') {
|
|
17
|
+
text = data;
|
|
18
|
+
} else if (data instanceof ArrayBuffer) {
|
|
19
|
+
text = textDecoder.decode(data);
|
|
20
|
+
} else if (Array.isArray(data)) {
|
|
21
|
+
const totalLength = data.reduce((length, chunk) => length + chunk.byteLength, 0);
|
|
22
|
+
const merged = new Uint8Array(totalLength);
|
|
23
|
+
let offset = 0;
|
|
24
|
+
for (const chunk of data) {
|
|
25
|
+
merged.set(chunk, offset);
|
|
26
|
+
offset += chunk.byteLength;
|
|
27
|
+
}
|
|
28
|
+
text = textDecoder.decode(merged);
|
|
29
|
+
} else {
|
|
30
|
+
text = textDecoder.decode(data);
|
|
31
|
+
}
|
|
32
|
+
let parsed = text;
|
|
33
|
+
try {
|
|
34
|
+
parsed = JSON.parse(text);
|
|
35
|
+
} catch {
|
|
36
|
+
return {
|
|
37
|
+
payload: text
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (typeof parsed === 'object' && parsed !== null && 'event' in parsed) {
|
|
41
|
+
const event = parsed.event;
|
|
42
|
+
if (typeof event === 'string') {
|
|
43
|
+
return {
|
|
44
|
+
event,
|
|
45
|
+
payload: parsed.data
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
payload: parsed
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function discoverGatewayDescriptors(compiledModules, logger, loggerContext) {
|
|
54
|
+
const seenTargets = new Set();
|
|
55
|
+
const descriptors = [];
|
|
56
|
+
for (const candidate of discoveryCandidates(compiledModules)) {
|
|
57
|
+
const gatewayMetadata = getWebSocketGatewayMetadata(candidate.targetType);
|
|
58
|
+
if (!gatewayMetadata) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (candidate.scope !== 'singleton') {
|
|
62
|
+
logger.warn(`${candidate.targetType.name} in module ${candidate.moduleName} declares @WebSocketGateway() but is registered with ${candidate.scope} scope. WebSocket gateways are registered only for singleton providers.`, loggerContext);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (seenTargets.has(candidate.targetType)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
seenTargets.add(candidate.targetType);
|
|
69
|
+
descriptors.push(createGatewayDescriptor(candidate, gatewayMetadata.path));
|
|
70
|
+
}
|
|
71
|
+
return descriptors;
|
|
72
|
+
}
|
|
73
|
+
export async function resolveGatewayInstance(runtimeContainer, descriptor, logger, loggerContext) {
|
|
74
|
+
try {
|
|
75
|
+
return await runtimeContainer.resolve(descriptor.token);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.error(`Failed to resolve WebSocket gateway ${descriptor.targetName} from module ${descriptor.moduleName}.`, error, loggerContext);
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function dispatchGatewayMessage(resolved, socket, request, data, logger, loggerContext) {
|
|
82
|
+
const parsed = parseIncomingMessage(data);
|
|
83
|
+
for (const {
|
|
84
|
+
descriptor,
|
|
85
|
+
instance
|
|
86
|
+
} of resolved) {
|
|
87
|
+
const handlers = descriptor.handlers.filter(handler => handler.type === 'message' && (handler.event === undefined || handler.event === parsed.event));
|
|
88
|
+
for (const handler of handlers) {
|
|
89
|
+
await invokeGatewayMethod(instance, descriptor, handler, [parsed.payload, socket, request], logger, loggerContext);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function dispatchGatewayDisconnect(resolved, socket, code, reason, socketId, logger, loggerContext) {
|
|
94
|
+
for (const {
|
|
95
|
+
descriptor,
|
|
96
|
+
instance
|
|
97
|
+
} of resolved) {
|
|
98
|
+
await runGatewayHandlers(instance, descriptor, 'disconnect', [socket, code, reason, socketId], logger, loggerContext);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function runGatewayHandlers(instance, descriptor, type, args, logger, loggerContext) {
|
|
102
|
+
const handlers = descriptor.handlers.filter(handler => handler.type === type);
|
|
103
|
+
for (const handler of handlers) {
|
|
104
|
+
await invokeGatewayMethod(instance, descriptor, handler, args, logger, loggerContext);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function invokeGatewayMethod(instance, descriptor, handler, args, logger, loggerContext) {
|
|
108
|
+
const value = instance[handler.methodKey];
|
|
109
|
+
if (typeof value !== 'function') {
|
|
110
|
+
logger.warn(`WebSocket gateway handler ${descriptor.targetName}.${handler.methodName} is not callable and was skipped.`, loggerContext);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await Promise.resolve(value.call(instance, ...args));
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.error(`WebSocket gateway handler ${descriptor.targetName}.${handler.methodName} failed.`, error, loggerContext);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function discoveryCandidates(compiledModules) {
|
|
120
|
+
const candidates = [];
|
|
121
|
+
for (const compiledModule of compiledModules) {
|
|
122
|
+
for (const provider of compiledModule.definition.providers ?? []) {
|
|
123
|
+
if (typeof provider === 'function') {
|
|
124
|
+
candidates.push({
|
|
125
|
+
moduleName: compiledModule.type.name,
|
|
126
|
+
scope: scopeFromProvider(provider),
|
|
127
|
+
targetType: provider,
|
|
128
|
+
token: provider
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (isClassProvider(provider)) {
|
|
133
|
+
candidates.push({
|
|
134
|
+
moduleName: compiledModule.type.name,
|
|
135
|
+
scope: scopeFromProvider(provider),
|
|
136
|
+
targetType: provider.useClass,
|
|
137
|
+
token: provider.provide
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const controller of compiledModule.definition.controllers ?? []) {
|
|
142
|
+
candidates.push({
|
|
143
|
+
moduleName: compiledModule.type.name,
|
|
144
|
+
scope: scopeFromProvider(controller),
|
|
145
|
+
targetType: controller,
|
|
146
|
+
token: controller
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return candidates;
|
|
151
|
+
}
|
|
152
|
+
function createGatewayDescriptor(candidate, path) {
|
|
153
|
+
const gatewayMetadata = getWebSocketGatewayMetadata(candidate.targetType);
|
|
154
|
+
if (!gatewayMetadata) {
|
|
155
|
+
throw new Error(`Missing websocket gateway metadata for ${candidate.targetType.name}.`);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
handlers: getWebSocketHandlerMetadataEntries(candidate.targetType.prototype).map(entry => ({
|
|
159
|
+
event: entry.metadata.event,
|
|
160
|
+
methodKey: entry.propertyKey,
|
|
161
|
+
methodName: methodKeyToName(entry.propertyKey),
|
|
162
|
+
type: entry.metadata.type
|
|
163
|
+
})),
|
|
164
|
+
moduleName: candidate.moduleName,
|
|
165
|
+
path: normalizeGatewayPath(path),
|
|
166
|
+
serverBacked: gatewayMetadata.serverBacked ? {
|
|
167
|
+
port: gatewayMetadata.serverBacked.port
|
|
168
|
+
} : undefined,
|
|
169
|
+
targetName: candidate.targetType.name,
|
|
170
|
+
token: candidate.token
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function scopeFromProvider(provider) {
|
|
174
|
+
if (typeof provider === 'function') {
|
|
175
|
+
return getClassDiMetadata(provider)?.scope ?? 'singleton';
|
|
176
|
+
}
|
|
177
|
+
if ('useClass' in provider) {
|
|
178
|
+
const classProvider = provider;
|
|
179
|
+
return classProvider.scope ?? getClassDiMetadata(classProvider.useClass)?.scope ?? 'singleton';
|
|
180
|
+
}
|
|
181
|
+
return 'scope' in provider ? provider.scope ?? 'singleton' : 'singleton';
|
|
182
|
+
}
|
|
183
|
+
function methodKeyToName(methodKey) {
|
|
184
|
+
return typeof methodKey === 'symbol' ? methodKey.toString() : methodKey;
|
|
185
|
+
}
|
|
186
|
+
function isClassProvider(provider) {
|
|
187
|
+
return typeof provider === 'object' && provider !== null && 'useClass' in provider;
|
|
188
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MetadataPropertyKey } from '@fluojs/core';
|
|
2
|
+
import type { WebSocketGatewayHandlerMetadata, WebSocketGatewayMetadata } from './types.js';
|
|
3
|
+
export declare function defineWebSocketGatewayMetadata(target: object, metadata: WebSocketGatewayMetadata): void;
|
|
4
|
+
export declare function getWebSocketGatewayMetadata(target: object): WebSocketGatewayMetadata | undefined;
|
|
5
|
+
export declare function defineWebSocketHandlerMetadata(target: object, propertyKey: MetadataPropertyKey, metadata: WebSocketGatewayHandlerMetadata): void;
|
|
6
|
+
export declare function getWebSocketHandlerMetadata(target: object, propertyKey: MetadataPropertyKey): WebSocketGatewayHandlerMetadata | undefined;
|
|
7
|
+
export declare function getWebSocketHandlerMetadataEntries(target: object): Array<{
|
|
8
|
+
metadata: WebSocketGatewayHandlerMetadata;
|
|
9
|
+
propertyKey: MetadataPropertyKey;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const webSocketGatewayMetadataSymbol: symbol;
|
|
12
|
+
export declare const webSocketHandlerMetadataSymbol: symbol;
|
|
13
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EACV,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,YAAY,CAAC;AA2EpB,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAEvG;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAShG;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,EAChC,QAAQ,EAAE,+BAA+B,GACxC,IAAI,CAEN;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,mBAAmB,GAC/B,+BAA+B,GAAG,SAAS,CAS7C;AAED,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,+BAA+B,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAcxF;AAED,eAAO,MAAM,8BAA8B,QAAsC,CAAC;AAClF,eAAO,MAAM,8BAA8B,QAAsC,CAAC"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const symbolWithMetadata = Symbol;
|
|
2
|
+
const metadataSymbol = symbolWithMetadata.metadata ?? Symbol.for('fluo.symbol.metadata');
|
|
3
|
+
if (!symbolWithMetadata.metadata) {
|
|
4
|
+
Object.defineProperty(Symbol, 'metadata', {
|
|
5
|
+
configurable: true,
|
|
6
|
+
value: metadataSymbol
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
const standardWebSocketGatewayMetadataKey = Symbol.for('fluo.websocket.standard.gateway');
|
|
10
|
+
const standardWebSocketHandlerMetadataKey = Symbol.for('fluo.websocket.standard.handler');
|
|
11
|
+
const gatewayMetadataStore = new WeakMap();
|
|
12
|
+
const handlerMetadataStore = new WeakMap();
|
|
13
|
+
function cloneGatewayMetadata(metadata) {
|
|
14
|
+
return {
|
|
15
|
+
path: metadata.path,
|
|
16
|
+
serverBacked: metadata.serverBacked ? {
|
|
17
|
+
port: metadata.serverBacked.port
|
|
18
|
+
} : undefined
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function cloneHandlerMetadata(metadata) {
|
|
22
|
+
return {
|
|
23
|
+
event: metadata.event,
|
|
24
|
+
type: metadata.type
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function getStandardMetadataBag(target) {
|
|
28
|
+
return target[metadataSymbol];
|
|
29
|
+
}
|
|
30
|
+
function getStandardGatewayMetadata(target) {
|
|
31
|
+
const metadata = getStandardMetadataBag(target)?.[standardWebSocketGatewayMetadataKey];
|
|
32
|
+
if (!metadata) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return cloneGatewayMetadata(metadata);
|
|
36
|
+
}
|
|
37
|
+
function getStandardHandlerMap(target) {
|
|
38
|
+
const constructor = target.constructor;
|
|
39
|
+
return constructor ? getStandardMetadataBag(constructor)?.[standardWebSocketHandlerMetadataKey] : undefined;
|
|
40
|
+
}
|
|
41
|
+
function getOrCreateHandlerMetadataMap(target) {
|
|
42
|
+
let map = handlerMetadataStore.get(target);
|
|
43
|
+
if (!map) {
|
|
44
|
+
map = new Map();
|
|
45
|
+
handlerMetadataStore.set(target, map);
|
|
46
|
+
}
|
|
47
|
+
return map;
|
|
48
|
+
}
|
|
49
|
+
export function defineWebSocketGatewayMetadata(target, metadata) {
|
|
50
|
+
gatewayMetadataStore.set(target, cloneGatewayMetadata(metadata));
|
|
51
|
+
}
|
|
52
|
+
export function getWebSocketGatewayMetadata(target) {
|
|
53
|
+
const stored = gatewayMetadataStore.get(target);
|
|
54
|
+
const standard = getStandardGatewayMetadata(target);
|
|
55
|
+
if (!stored && !standard) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return cloneGatewayMetadata(stored ?? standard);
|
|
59
|
+
}
|
|
60
|
+
export function defineWebSocketHandlerMetadata(target, propertyKey, metadata) {
|
|
61
|
+
getOrCreateHandlerMetadataMap(target).set(propertyKey, cloneHandlerMetadata(metadata));
|
|
62
|
+
}
|
|
63
|
+
export function getWebSocketHandlerMetadata(target, propertyKey) {
|
|
64
|
+
const stored = handlerMetadataStore.get(target)?.get(propertyKey);
|
|
65
|
+
const standard = getStandardHandlerMap(target)?.get(propertyKey);
|
|
66
|
+
if (!stored && !standard) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return cloneHandlerMetadata(stored ?? standard);
|
|
70
|
+
}
|
|
71
|
+
export function getWebSocketHandlerMetadataEntries(target) {
|
|
72
|
+
const stored = handlerMetadataStore.get(target) ?? new Map();
|
|
73
|
+
const standard = getStandardHandlerMap(target) ?? new Map();
|
|
74
|
+
const keys = new Set([...stored.keys(), ...standard.keys()]);
|
|
75
|
+
return Array.from(keys).map(propertyKey => ({
|
|
76
|
+
metadata: getWebSocketHandlerMetadata(target, propertyKey),
|
|
77
|
+
propertyKey
|
|
78
|
+
})).filter(entry => entry.metadata !== undefined);
|
|
79
|
+
}
|
|
80
|
+
export const webSocketGatewayMetadataSymbol = standardWebSocketGatewayMetadataKey;
|
|
81
|
+
export const webSocketHandlerMetadataSymbol = standardWebSocketHandlerMetadataKey;
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ModuleType } from '@fluojs/runtime';
|
|
2
|
+
import type { WebSocketModuleOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Root module entry point that defaults to the Node.js WebSocket adapter.
|
|
5
|
+
*/
|
|
6
|
+
export declare class WebSocketModule {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a module definition backed by the default Node.js WebSocket runtime.
|
|
9
|
+
*
|
|
10
|
+
* @param options WebSocket adapter options shared with the runtime lifecycle service.
|
|
11
|
+
* @returns A runtime module definition that delegates to {@link NodeWebSocketModule.forRoot}.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { Module } from '@fluojs/core';
|
|
16
|
+
* import { WebSocketModule } from '@fluojs/websockets';
|
|
17
|
+
*
|
|
18
|
+
* @Module({
|
|
19
|
+
* imports: [WebSocketModule.forRoot()],
|
|
20
|
+
* })
|
|
21
|
+
* export class AppModule {}
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
static forRoot(options?: WebSocketModuleOptions): ModuleType;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;GAEG;AACH,qBAAa,eAAe;IAC1B;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,sBAA2B,GAAG,UAAU;CAGjE"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NodeWebSocketModule } from './node.js';
|
|
2
|
+
/**
|
|
3
|
+
* Root module entry point that defaults to the Node.js WebSocket adapter.
|
|
4
|
+
*/
|
|
5
|
+
export class WebSocketModule {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a module definition backed by the default Node.js WebSocket runtime.
|
|
8
|
+
*
|
|
9
|
+
* @param options WebSocket adapter options shared with the runtime lifecycle service.
|
|
10
|
+
* @returns A runtime module definition that delegates to {@link NodeWebSocketModule.forRoot}.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { Module } from '@fluojs/core';
|
|
15
|
+
* import { WebSocketModule } from '@fluojs/websockets';
|
|
16
|
+
*
|
|
17
|
+
* @Module({
|
|
18
|
+
* imports: [WebSocketModule.forRoot()],
|
|
19
|
+
* })
|
|
20
|
+
* export class AppModule {}
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
static forRoot(options = {}) {
|
|
24
|
+
return NodeWebSocketModule.forRoot(options);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ModuleType } from '@fluojs/runtime';
|
|
2
|
+
import type { WebSocketModuleOptions } from './node-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Explicit Node.js websocket module entrypoint.
|
|
5
|
+
*/
|
|
6
|
+
export declare class NodeWebSocketModule {
|
|
7
|
+
/**
|
|
8
|
+
* Registers the Node.js websocket lifecycle service for gateway discovery and upgrades.
|
|
9
|
+
*
|
|
10
|
+
* @param options Websocket gateway runtime options for guards, limits, heartbeat, and shutdown behavior.
|
|
11
|
+
* @returns A runtime module definition scoped to the Node.js websocket adapter.
|
|
12
|
+
*/
|
|
13
|
+
static forRoot(options?: WebSocketModuleOptions): ModuleType;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=node-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-module.d.ts","sourceRoot":"","sources":["../../src/node/node-module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAY9D;;GAEG;AACH,qBAAa,mBAAmB;IAC9B;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,sBAA2B,GAAG,UAAU;CAOjE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineModule } from '@fluojs/runtime';
|
|
2
|
+
import { WEBSOCKET_OPTIONS_INTERNAL } from '../options-token.internal.js';
|
|
3
|
+
import { NodeWebSocketGatewayLifecycleService } from './node-service.js';
|
|
4
|
+
function createNodeWebSocketProviders(options = {}) {
|
|
5
|
+
return [{
|
|
6
|
+
provide: WEBSOCKET_OPTIONS_INTERNAL,
|
|
7
|
+
useValue: options
|
|
8
|
+
}, NodeWebSocketGatewayLifecycleService];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Explicit Node.js websocket module entrypoint.
|
|
13
|
+
*/
|
|
14
|
+
export class NodeWebSocketModule {
|
|
15
|
+
/**
|
|
16
|
+
* Registers the Node.js websocket lifecycle service for gateway discovery and upgrades.
|
|
17
|
+
*
|
|
18
|
+
* @param options Websocket gateway runtime options for guards, limits, heartbeat, and shutdown behavior.
|
|
19
|
+
* @returns A runtime module definition scoped to the Node.js websocket adapter.
|
|
20
|
+
*/
|
|
21
|
+
static forRoot(options = {}) {
|
|
22
|
+
class NodeWebSocketRuntimeModule extends NodeWebSocketModule {}
|
|
23
|
+
return defineModule(NodeWebSocketRuntimeModule, {
|
|
24
|
+
providers: createNodeWebSocketProviders(options)
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Container } from '@fluojs/di';
|
|
2
|
+
import type { ApplicationLogger, CompiledModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy } from '@fluojs/runtime';
|
|
3
|
+
import type { HttpApplicationAdapter } from '@fluojs/http';
|
|
4
|
+
import type { WebSocketRoomService } from '../types.js';
|
|
5
|
+
import type { WebSocketModuleOptions } from './node-types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Lifecycle service that discovers WebSocket gateways, attaches upgrade listeners, and manages room state.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* This service preserves the documented runtime behavior for shared-path discovery order, optional server-backed
|
|
11
|
+
* listeners, buffered pre-ready events, heartbeat handling, and graceful shutdown.
|
|
12
|
+
*/
|
|
13
|
+
export declare class NodeWebSocketGatewayLifecycleService implements OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy, WebSocketRoomService {
|
|
14
|
+
private readonly runtimeContainer;
|
|
15
|
+
private readonly compiledModules;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private readonly adapter;
|
|
18
|
+
private readonly moduleOptions;
|
|
19
|
+
private attachments;
|
|
20
|
+
private heartbeatTimer;
|
|
21
|
+
private ownedUpgradeServers;
|
|
22
|
+
private pendingUpgradeReservations;
|
|
23
|
+
private readonly pingPending;
|
|
24
|
+
private readonly pingSentAt;
|
|
25
|
+
private readonly roomSockets;
|
|
26
|
+
private shutdownPromise;
|
|
27
|
+
private readonly socketRegistry;
|
|
28
|
+
private readonly socketRooms;
|
|
29
|
+
private upgradeListener;
|
|
30
|
+
private upgradeServer;
|
|
31
|
+
constructor(runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, adapter: HttpApplicationAdapter, moduleOptions: WebSocketModuleOptions);
|
|
32
|
+
/**
|
|
33
|
+
* Discovers gateway classes and attaches their WebSocket servers once per application lifecycle.
|
|
34
|
+
*/
|
|
35
|
+
onApplicationBootstrap(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Shuts down websocket listeners and connection tracking during application shutdown.
|
|
38
|
+
*/
|
|
39
|
+
onApplicationShutdown(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Shuts down websocket listeners and connection tracking when the containing module is destroyed.
|
|
42
|
+
*/
|
|
43
|
+
onModuleDestroy(): Promise<void>;
|
|
44
|
+
private prepareGatewayAttachments;
|
|
45
|
+
private attachGatewayServers;
|
|
46
|
+
private attachUpgradeServerListener;
|
|
47
|
+
private attachOwnedGatewayServerListener;
|
|
48
|
+
private startHeartbeatIfEnabled;
|
|
49
|
+
private buildGatewayAttachmentGroups;
|
|
50
|
+
private createAttachmentGroup;
|
|
51
|
+
private resolveBindingTarget;
|
|
52
|
+
private resolveServerBackedPort;
|
|
53
|
+
private attachConnectionHandlersToServers;
|
|
54
|
+
private createUpgradeListener;
|
|
55
|
+
private handleUpgradeRequest;
|
|
56
|
+
private resolveUpgradeServer;
|
|
57
|
+
private bindConnectionHandlers;
|
|
58
|
+
private registerSocketConnection;
|
|
59
|
+
private finalizeConnectionBinding;
|
|
60
|
+
private createConnectionHandlerState;
|
|
61
|
+
private getBufferedMessageCount;
|
|
62
|
+
private getQueuedMessageCount;
|
|
63
|
+
private maybeCompactBufferedMessages;
|
|
64
|
+
private clearBufferedMessages;
|
|
65
|
+
private maybeCompactQueuedMessages;
|
|
66
|
+
private clearQueuedMessages;
|
|
67
|
+
private enqueueMessageDispatch;
|
|
68
|
+
private drainMessageQueue;
|
|
69
|
+
private handleMessage;
|
|
70
|
+
private enqueueDisconnectDispatch;
|
|
71
|
+
private attachConnectionListeners;
|
|
72
|
+
private bufferIncomingMessage;
|
|
73
|
+
private resolveConnectionGateways;
|
|
74
|
+
private runConnectHandlers;
|
|
75
|
+
private replayBufferedConnectionEvents;
|
|
76
|
+
private handleDisconnect;
|
|
77
|
+
private resolveUpgradeRejection;
|
|
78
|
+
private closeOversizedPayload;
|
|
79
|
+
private resolveMaxConnectionCount;
|
|
80
|
+
private resolveReservedConnectionCount;
|
|
81
|
+
private tryReserveUpgradeSlot;
|
|
82
|
+
private releaseUpgradeReservation;
|
|
83
|
+
private resolveMaxPayloadBytes;
|
|
84
|
+
private resolveShutdownTimeoutMs;
|
|
85
|
+
private closeServerWithTimeout;
|
|
86
|
+
private shutdown;
|
|
87
|
+
private runShutdownLifecycle;
|
|
88
|
+
private stopHeartbeat;
|
|
89
|
+
private detachUpgradeServerListener;
|
|
90
|
+
private closeOwnedUpgradeServers;
|
|
91
|
+
private closeOwnedUpgradeServerWithTimeout;
|
|
92
|
+
private listenOwnedGatewayServer;
|
|
93
|
+
private closeGatewayAttachments;
|
|
94
|
+
private terminateAttachmentClients;
|
|
95
|
+
private closeGatewayAttachment;
|
|
96
|
+
private clearConnectionTrackingState;
|
|
97
|
+
/**
|
|
98
|
+
* Adds one socket to an in-memory room membership set.
|
|
99
|
+
*
|
|
100
|
+
* @param socketId Socket identifier to add.
|
|
101
|
+
* @param room Room identifier to join.
|
|
102
|
+
*/
|
|
103
|
+
joinRoom(socketId: string, room: string): void;
|
|
104
|
+
/**
|
|
105
|
+
* Removes one socket from an in-memory room membership set.
|
|
106
|
+
*
|
|
107
|
+
* @param socketId Socket identifier to remove.
|
|
108
|
+
* @param room Room identifier to leave.
|
|
109
|
+
*/
|
|
110
|
+
leaveRoom(socketId: string, room: string): void;
|
|
111
|
+
/**
|
|
112
|
+
* Broadcasts one JSON-encoded event frame to every open socket currently joined to a room.
|
|
113
|
+
*
|
|
114
|
+
* @param room Room identifier that should receive the event.
|
|
115
|
+
* @param event Event name delivered to room members.
|
|
116
|
+
* @param data Payload delivered with the event.
|
|
117
|
+
*/
|
|
118
|
+
broadcastToRoom(room: string, event: string, data: unknown): void;
|
|
119
|
+
/**
|
|
120
|
+
* Returns the current in-memory room snapshot for one socket.
|
|
121
|
+
*
|
|
122
|
+
* @param socketId Socket identifier to inspect.
|
|
123
|
+
* @returns The room set currently tracked for that socket.
|
|
124
|
+
*/
|
|
125
|
+
getRooms(socketId: string): ReadonlySet<string>;
|
|
126
|
+
private startHeartbeat;
|
|
127
|
+
private unregisterSocket;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=node-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-service.d.ts","sourceRoot":"","sources":["../../src/node/node-service.ts"],"names":[],"mappings":"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"}
|