silgi 0.53.0 → 0.53.2
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/dist/builder.mjs +32 -6
- package/dist/caller.mjs +65 -55
- package/dist/compile.d.mts +15 -8
- package/dist/compile.mjs +157 -142
- package/dist/core/handler.d.mts +3 -3
- package/dist/core/handler.mjs +69 -73
- package/dist/core/input.mjs +95 -33
- package/dist/core/schema-converter.d.mts +68 -63
- package/dist/core/schema-converter.mjs +85 -56
- package/dist/core/serve.d.mts +18 -17
- package/dist/core/serve.mjs +154 -64
- package/dist/core/sse.d.mts +5 -6
- package/dist/core/sse.mjs +86 -46
- package/dist/core/task.d.mts +15 -4
- package/dist/core/task.mjs +160 -76
- package/dist/plugins/cache.d.mts +62 -126
- package/dist/plugins/cache.mjs +146 -128
- package/dist/scalar.d.mts +24 -13
- package/dist/scalar.mjs +292 -201
- package/dist/silgi.mjs +160 -117
- package/dist/ws.d.mts +26 -27
- package/dist/ws.mjs +126 -87
- package/package.json +1 -1
package/dist/silgi.mjs
CHANGED
|
@@ -21,36 +21,44 @@ import { createHooks } from "hookable";
|
|
|
21
21
|
* const k = silgi({ context: (req) => ({ db, headers }) })
|
|
22
22
|
* // k.$input(), k.$resolve(), k.$use(), k.guard(), k.router(), k.handler()
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
meta: null
|
|
35
|
-
};
|
|
36
|
-
if (args.length === 2 && typeof args[1] === "function") return {
|
|
24
|
+
/**
|
|
25
|
+
* Build a `ProcedureDef` with all optional slots set to `null`.
|
|
26
|
+
*
|
|
27
|
+
* Procedures carry eight slots — `type`, `input`, `output`, `errors`,
|
|
28
|
+
* `use`, `resolve`, `route`, `meta`. Most call sites set only a couple;
|
|
29
|
+
* funnelling construction through this helper keeps the shape in one
|
|
30
|
+
* place so new slots do not have to be added to every short form.
|
|
31
|
+
*/
|
|
32
|
+
function makeProcedureDef(type, input, resolve) {
|
|
33
|
+
return {
|
|
37
34
|
type,
|
|
38
|
-
input
|
|
35
|
+
input,
|
|
39
36
|
output: null,
|
|
40
37
|
errors: null,
|
|
41
38
|
use: null,
|
|
42
|
-
resolve
|
|
39
|
+
resolve,
|
|
43
40
|
route: null,
|
|
44
41
|
meta: null
|
|
45
42
|
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Dispatch on the call shape of `$resolve` / `subscription`.
|
|
46
|
+
*
|
|
47
|
+
* createProcedure(type) → chainable builder
|
|
48
|
+
* createProcedure(type, resolve) → single-shot procedure
|
|
49
|
+
* createProcedure(type, input, resolve) → single-shot with input schema
|
|
50
|
+
*/
|
|
51
|
+
function createProcedure(type, ...args) {
|
|
52
|
+
if (args.length === 0) return createProcedureBuilder(type);
|
|
53
|
+
if (args.length === 1 && typeof args[0] === "function") return makeProcedureDef(type, null, args[0]);
|
|
54
|
+
if (args.length === 2 && typeof args[1] === "function") return makeProcedureDef(type, args[0], args[1]);
|
|
46
55
|
throw new TypeError(`Invalid arguments for ${type}()`);
|
|
47
56
|
}
|
|
48
57
|
/**
|
|
49
|
-
* Stamp root wraps onto a router def
|
|
50
|
-
* property. Idempotent
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* context shapes.
|
|
58
|
+
* Stamp root wraps onto a router def via a non-enumerable Symbol-keyed
|
|
59
|
+
* property. Idempotent for the same wrap reference; throws when a
|
|
60
|
+
* different silgi instance has already registered the def, because a
|
|
61
|
+
* single compiled router cannot serve two different context shapes.
|
|
54
62
|
*
|
|
55
63
|
* @internal
|
|
56
64
|
*/
|
|
@@ -66,6 +74,55 @@ function stampRootWraps(def, wraps) {
|
|
|
66
74
|
});
|
|
67
75
|
}
|
|
68
76
|
/**
|
|
77
|
+
* Validate + freeze the `wraps` config array.
|
|
78
|
+
*
|
|
79
|
+
* Guards are rejected at the instance level because a guard's return
|
|
80
|
+
* type must flow into every procedure's context, and the instance-level
|
|
81
|
+
* config cannot express that across an unknown router shape. Guards
|
|
82
|
+
* must be attached via route-level `.$use()` where the context
|
|
83
|
+
* enrichment can be typed.
|
|
84
|
+
*/
|
|
85
|
+
function prepareRootWraps(wraps) {
|
|
86
|
+
if (!wraps || wraps.length === 0) return null;
|
|
87
|
+
for (const w of wraps) if (!w || w.kind !== "wrap") throw new TypeError("silgi({ wraps }) only accepts wrap middleware — use route-level .$use() for guards.");
|
|
88
|
+
return Object.freeze([...wraps]);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Register the user-provided hook listeners on a fresh `Hookable`.
|
|
92
|
+
* Accepts either a single function or an array of functions per hook,
|
|
93
|
+
* matching the shape documented on `SilgiConfig['hooks']`.
|
|
94
|
+
*/
|
|
95
|
+
function registerHooks(hooks, config) {
|
|
96
|
+
if (!config) return;
|
|
97
|
+
for (const [name, fn] of Object.entries(config)) {
|
|
98
|
+
const key = name;
|
|
99
|
+
if (Array.isArray(fn)) for (const f of fn) hooks.hook(key, f);
|
|
100
|
+
else if (fn) hooks.hook(key, fn);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build the storage-ready promise. Resolves immediately when no storage
|
|
105
|
+
* is configured — and crucially, never triggers the dynamic import in
|
|
106
|
+
* that case, so tree-shakers can drop the driver code entirely.
|
|
107
|
+
*/
|
|
108
|
+
function makeStorageReady(storage) {
|
|
109
|
+
if (!storage) return Promise.resolve();
|
|
110
|
+
return import("./core/storage.mjs").then((m) => {
|
|
111
|
+
m.initStorage(storage);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Recursively search a router def for any `subscription` procedure.
|
|
116
|
+
* Used to decide whether `handler()` should bother to lazy-load the
|
|
117
|
+
* crossws hooks for the `/_ws` mount.
|
|
118
|
+
*/
|
|
119
|
+
function routerHasSubscriptions(def) {
|
|
120
|
+
if (!def || typeof def !== "object") return false;
|
|
121
|
+
if (def.type === "subscription") return true;
|
|
122
|
+
for (const child of Object.values(def)) if (routerHasSubscriptions(child)) return true;
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
69
126
|
* Create a Silgi RPC instance with typed context.
|
|
70
127
|
*
|
|
71
128
|
* @remarks
|
|
@@ -101,20 +158,80 @@ function silgi(config) {
|
|
|
101
158
|
const schemaRegistry = createSchemaRegistry(config.schemaConverters ?? []);
|
|
102
159
|
const bridge = createContextBridge();
|
|
103
160
|
const hooks = createHooks();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
const readyPromise = config.storage ? import("./core/storage.mjs").then((m) => {
|
|
109
|
-
m.initStorage(config.storage);
|
|
110
|
-
}) : Promise.resolve();
|
|
161
|
+
registerHooks(hooks, config.hooks);
|
|
162
|
+
const readyPromise = makeStorageReady(config.storage);
|
|
163
|
+
const rootWraps = prepareRootWraps(config.wraps);
|
|
111
164
|
const ctxFactory = () => contextFactory(new Request("http://localhost"));
|
|
112
|
-
let rootWraps = null;
|
|
113
|
-
if (config.wraps && config.wraps.length > 0) {
|
|
114
|
-
for (const w of config.wraps) if (!w || w.kind !== "wrap") throw new TypeError("silgi({ wraps }) only accepts wrap middleware — use route-level .$use() for guards.");
|
|
115
|
-
rootWraps = Object.freeze([...config.wraps]);
|
|
116
|
-
}
|
|
117
165
|
const rootWrapsGetter = rootWraps ? () => rootWraps : null;
|
|
166
|
+
const startBuilder = () => createProcedureBuilder("query", ctxFactory, rootWrapsGetter);
|
|
167
|
+
const registerRouter = (def) => {
|
|
168
|
+
const assigned = assignPaths(def);
|
|
169
|
+
if (rootWraps) {
|
|
170
|
+
stampRootWraps(def, rootWraps);
|
|
171
|
+
stampRootWraps(assigned, rootWraps);
|
|
172
|
+
}
|
|
173
|
+
const compiled = compileRouter(assigned);
|
|
174
|
+
routerCache.set(def, compiled);
|
|
175
|
+
routerCache.set(assigned, compiled);
|
|
176
|
+
return def;
|
|
177
|
+
};
|
|
178
|
+
const buildHandler = (routerDef, options) => {
|
|
179
|
+
const prefix = options?.basePath ? normalizePrefix(options.basePath) : void 0;
|
|
180
|
+
const fetchHandler = wrapHandler(createFetchHandler(routerDef, contextFactory, hooks, prefix, bridge), routerDef, options ? {
|
|
181
|
+
...options,
|
|
182
|
+
schemaRegistry,
|
|
183
|
+
hooks
|
|
184
|
+
} : {
|
|
185
|
+
schemaRegistry,
|
|
186
|
+
hooks
|
|
187
|
+
}, prefix);
|
|
188
|
+
if (!routerHasSubscriptions(routerDef)) return fetchHandler;
|
|
189
|
+
let wsHooks;
|
|
190
|
+
let wsInitPromise;
|
|
191
|
+
const initWsHooks = async () => {
|
|
192
|
+
const { _createWSHooks } = await import("./ws.mjs");
|
|
193
|
+
wsHooks = _createWSHooks(routerDef, { context: (peer) => {
|
|
194
|
+
return contextFactory(peer?.request instanceof Request ? peer.request : peer);
|
|
195
|
+
} });
|
|
196
|
+
};
|
|
197
|
+
const wsPath = prefix ? `${prefix}/_ws` : "/_ws";
|
|
198
|
+
return async (request) => {
|
|
199
|
+
if (new URL(request.url).pathname === wsPath) {
|
|
200
|
+
if (!wsHooks) {
|
|
201
|
+
wsInitPromise ??= initWsHooks();
|
|
202
|
+
await wsInitPromise;
|
|
203
|
+
}
|
|
204
|
+
const response = new Response(null, { status: 200 });
|
|
205
|
+
response.crossws = wsHooks;
|
|
206
|
+
return response;
|
|
207
|
+
}
|
|
208
|
+
return fetchHandler(request);
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
const buildServe = async (routerDef, options) => {
|
|
212
|
+
const { createServeHandler } = await import("./core/serve.mjs");
|
|
213
|
+
const server = await createServeHandler(routerDef, contextFactory, hooks, options, schemaRegistry, bridge);
|
|
214
|
+
const { collectCronTasks, startCronJobs, stopCronJobs } = await import("./core/task.mjs");
|
|
215
|
+
const cronTasks = collectCronTasks(routerDef);
|
|
216
|
+
if (cronTasks.length > 0) {
|
|
217
|
+
await startCronJobs(cronTasks);
|
|
218
|
+
console.log(` ${cronTasks.length} cron task(s) scheduled`);
|
|
219
|
+
}
|
|
220
|
+
const originalClose = server.close.bind(server);
|
|
221
|
+
const wrappedClose = async (force) => {
|
|
222
|
+
stopCronJobs();
|
|
223
|
+
return originalClose(force);
|
|
224
|
+
};
|
|
225
|
+
const silgiServer = Object.assign(Object.create(Object.getPrototypeOf(server)), server, { close: wrappedClose });
|
|
226
|
+
if (options?.handleSignals) {
|
|
227
|
+
const onSignal = () => {
|
|
228
|
+
wrappedClose().catch(() => {});
|
|
229
|
+
};
|
|
230
|
+
process.once("SIGINT", onSignal);
|
|
231
|
+
process.once("SIGTERM", onSignal);
|
|
232
|
+
}
|
|
233
|
+
return silgiServer;
|
|
234
|
+
};
|
|
118
235
|
return {
|
|
119
236
|
hook: hooks.hook.bind(hooks),
|
|
120
237
|
removeHook: hooks.removeHook.bind(hooks),
|
|
@@ -140,96 +257,22 @@ function silgi(config) {
|
|
|
140
257
|
fn
|
|
141
258
|
}),
|
|
142
259
|
$resolve: ((fn) => createProcedure("query", fn)),
|
|
143
|
-
$input: ((schema) =>
|
|
260
|
+
$input: ((schema) => startBuilder().$input(schema)),
|
|
144
261
|
$use: ((...middleware) => {
|
|
145
|
-
const b =
|
|
262
|
+
const b = startBuilder();
|
|
146
263
|
for (const m of middleware) b.$use(m);
|
|
147
264
|
return b;
|
|
148
265
|
}),
|
|
149
|
-
$output: ((schema) =>
|
|
150
|
-
$errors: ((errors) =>
|
|
151
|
-
$route: ((route) =>
|
|
152
|
-
$meta: ((meta) =>
|
|
266
|
+
$output: ((schema) => startBuilder().$output(schema)),
|
|
267
|
+
$errors: ((errors) => startBuilder().$errors(errors)),
|
|
268
|
+
$route: ((route) => startBuilder().$route(route)),
|
|
269
|
+
$meta: ((meta) => startBuilder().$meta(meta)),
|
|
153
270
|
subscription: ((...args) => createProcedure("subscription", ...args)),
|
|
154
|
-
$task: ((
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (rootWraps) {
|
|
160
|
-
stampRootWraps(def, rootWraps);
|
|
161
|
-
stampRootWraps(assigned, rootWraps);
|
|
162
|
-
}
|
|
163
|
-
const flat = compileRouter(assigned);
|
|
164
|
-
routerCache.set(def, flat);
|
|
165
|
-
routerCache.set(assigned, flat);
|
|
166
|
-
return def;
|
|
167
|
-
},
|
|
168
|
-
createCaller: (routerDef, options) => {
|
|
169
|
-
return createCaller(routerDef, contextFactory, options);
|
|
170
|
-
},
|
|
171
|
-
handler: (routerDef, options) => {
|
|
172
|
-
const prefix = options?.basePath ? normalizePrefix(options.basePath) : void 0;
|
|
173
|
-
const fetchHandler = wrapHandler(createFetchHandler(routerDef, contextFactory, hooks, prefix, bridge), routerDef, options ? {
|
|
174
|
-
...options,
|
|
175
|
-
schemaRegistry,
|
|
176
|
-
hooks
|
|
177
|
-
} : {
|
|
178
|
-
schemaRegistry,
|
|
179
|
-
hooks
|
|
180
|
-
}, prefix);
|
|
181
|
-
if (!(function checkWs(def) {
|
|
182
|
-
if (!def || typeof def !== "object") return false;
|
|
183
|
-
if (def.type === "subscription") return true;
|
|
184
|
-
for (const v of Object.values(def)) if (checkWs(v)) return true;
|
|
185
|
-
return false;
|
|
186
|
-
})(routerDef)) return fetchHandler;
|
|
187
|
-
let wsHooks;
|
|
188
|
-
let wsInitPromise;
|
|
189
|
-
async function initWsHooks() {
|
|
190
|
-
const { _createWSHooks } = await import("./ws.mjs");
|
|
191
|
-
wsHooks = _createWSHooks(routerDef, { context: (peer) => {
|
|
192
|
-
return contextFactory(peer?.request instanceof Request ? peer.request : peer);
|
|
193
|
-
} });
|
|
194
|
-
}
|
|
195
|
-
const wsPath = prefix ? `${prefix}/_ws` : "/_ws";
|
|
196
|
-
return async (request) => {
|
|
197
|
-
if (new URL(request.url).pathname === wsPath) {
|
|
198
|
-
if (!wsHooks) {
|
|
199
|
-
wsInitPromise ??= initWsHooks();
|
|
200
|
-
await wsInitPromise;
|
|
201
|
-
}
|
|
202
|
-
const response = new Response(null, { status: 200 });
|
|
203
|
-
response.crossws = wsHooks;
|
|
204
|
-
return response;
|
|
205
|
-
}
|
|
206
|
-
return fetchHandler(request);
|
|
207
|
-
};
|
|
208
|
-
},
|
|
209
|
-
serve: async (routerDef, options) => {
|
|
210
|
-
const { createServeHandler } = await import("./core/serve.mjs");
|
|
211
|
-
const server = await createServeHandler(routerDef, contextFactory, hooks, options, schemaRegistry, bridge);
|
|
212
|
-
const { collectCronTasks, startCronJobs, stopCronJobs } = await import("./core/task.mjs");
|
|
213
|
-
const cronTasks = collectCronTasks(routerDef);
|
|
214
|
-
if (cronTasks.length > 0) {
|
|
215
|
-
await startCronJobs(cronTasks);
|
|
216
|
-
console.log(` ${cronTasks.length} cron task(s) scheduled`);
|
|
217
|
-
}
|
|
218
|
-
const originalClose = server.close.bind(server);
|
|
219
|
-
const wrappedClose = async (force) => {
|
|
220
|
-
stopCronJobs();
|
|
221
|
-
return originalClose(force);
|
|
222
|
-
};
|
|
223
|
-
const silgiServer = Object.assign(Object.create(Object.getPrototypeOf(server)), server, { close: wrappedClose });
|
|
224
|
-
if (options?.handleSignals) {
|
|
225
|
-
const onSignal = () => {
|
|
226
|
-
wrappedClose().catch(() => {});
|
|
227
|
-
};
|
|
228
|
-
process.once("SIGINT", onSignal);
|
|
229
|
-
process.once("SIGTERM", onSignal);
|
|
230
|
-
}
|
|
231
|
-
return silgiServer;
|
|
232
|
-
}
|
|
271
|
+
$task: ((cfg) => createTaskFromProcedure(cfg, cfg.resolve, null, null, ctxFactory, rootWrapsGetter)),
|
|
272
|
+
router: registerRouter,
|
|
273
|
+
createCaller: (routerDef, options) => createCaller(routerDef, contextFactory, options),
|
|
274
|
+
handler: buildHandler,
|
|
275
|
+
serve: buildServe
|
|
233
276
|
};
|
|
234
277
|
}
|
|
235
278
|
//#endregion
|
package/dist/ws.d.mts
CHANGED
|
@@ -7,23 +7,21 @@ interface WSAdapterOptions<TCtx extends Record<string, unknown> = Record<string,
|
|
|
7
7
|
/**
|
|
8
8
|
* Wire protocol for WebSocket message encoding.
|
|
9
9
|
*
|
|
10
|
-
* - `'json'` — default, text frames with JSON
|
|
11
|
-
* - `'messagepack'` — binary frames with MessagePack
|
|
10
|
+
* - `'json'` — default, text frames with JSON.
|
|
11
|
+
* - `'messagepack'` — binary frames with MessagePack.
|
|
12
12
|
*
|
|
13
13
|
* @default 'json'
|
|
14
14
|
*/
|
|
15
15
|
protocol?: 'json' | 'messagepack';
|
|
16
|
-
/**
|
|
17
|
-
* @deprecated Use `protocol: 'messagepack'` instead.
|
|
18
|
-
*/
|
|
16
|
+
/** @deprecated Use `protocol: 'messagepack'` instead. */
|
|
19
17
|
binary?: boolean;
|
|
20
|
-
/** Context factory —
|
|
18
|
+
/** Context factory — invoked for every incoming peer message. */
|
|
21
19
|
context?: (peer: Peer) => TCtx | Promise<TCtx>;
|
|
22
20
|
/**
|
|
23
21
|
* Enable per-message-deflate compression.
|
|
24
22
|
*
|
|
25
|
-
* - `true
|
|
26
|
-
* -
|
|
23
|
+
* - `true` — enable with library defaults.
|
|
24
|
+
* - object — zlib tuning, forwarded to `ws.perMessageDeflate`.
|
|
27
25
|
*
|
|
28
26
|
* @default false
|
|
29
27
|
*/
|
|
@@ -35,43 +33,44 @@ interface WSAdapterOptions<TCtx extends Record<string, unknown> = Record<string,
|
|
|
35
33
|
clientMaxWindowBits?: number;
|
|
36
34
|
};
|
|
37
35
|
/**
|
|
38
|
-
* Maximum allowed message size in bytes.
|
|
39
|
-
*
|
|
36
|
+
* Maximum allowed message size in bytes. Exceeding the limit closes
|
|
37
|
+
* the connection.
|
|
40
38
|
*
|
|
41
39
|
* @default 1_048_576 (1 MB)
|
|
42
40
|
*/
|
|
43
41
|
maxPayload?: number;
|
|
44
42
|
/**
|
|
45
|
-
* Keepalive ping interval in milliseconds.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* Keepalive ping interval in milliseconds. The server sends a ping
|
|
44
|
+
* every `keepalive` ms; if the client does not pong before the next
|
|
45
|
+
* ping, the socket is terminated.
|
|
48
46
|
*
|
|
49
|
-
* Set to `0` or `false` to disable.
|
|
47
|
+
* Set to `0` or `false` to disable keepalive entirely.
|
|
50
48
|
*
|
|
51
|
-
* @default 30_000
|
|
49
|
+
* @default 30_000
|
|
52
50
|
*/
|
|
53
51
|
keepalive?: number | false;
|
|
54
52
|
}
|
|
55
53
|
/**
|
|
56
|
-
*
|
|
54
|
+
* Build the crossws hook set that implements silgi's WebSocket RPC.
|
|
55
|
+
*
|
|
56
|
+
* @internal
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* This is not part of the public API — `silgi({...}).handler()`,
|
|
59
|
+
* `serve({ ws: true })`, and `attachWebSocket()` are the three supported
|
|
60
|
+
* entry points. They all go through this builder so protocol behavior
|
|
61
|
+
* stays identical everywhere.
|
|
60
62
|
*/
|
|
61
|
-
/** @internal — exported only for use by silgi.ts handler() and attachWebSocket(). Not part of the public API. */
|
|
62
63
|
declare function _createWSHooks<TCtx extends Record<string, unknown>>(routerDef: RouterDef, options?: WSAdapterOptions<TCtx>): Partial<Hooks>;
|
|
63
64
|
/**
|
|
64
|
-
* Attach WebSocket RPC
|
|
65
|
+
* Attach silgi's WebSocket RPC to an existing Node.js `http.Server`.
|
|
65
66
|
*
|
|
66
67
|
* @example
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* import { attachWebSocket } from "silgi/ws";
|
|
68
|
+
* import { createServer } from 'node:http'
|
|
69
|
+
* import { attachWebSocket } from 'silgi/ws'
|
|
70
70
|
*
|
|
71
|
-
*
|
|
72
|
-
* attachWebSocket(server, appRouter)
|
|
73
|
-
*
|
|
74
|
-
* ```
|
|
71
|
+
* const server = createServer(httpHandler)
|
|
72
|
+
* await attachWebSocket(server, appRouter)
|
|
73
|
+
* server.listen(3000)
|
|
75
74
|
*/
|
|
76
75
|
declare function attachWebSocket<TCtx extends Record<string, unknown>>(server: Server, routerDef: RouterDef, options?: WSAdapterOptions<TCtx>): Promise<void>;
|
|
77
76
|
//#endregion
|