@vampgg/utils 1.0.0-beta.2 → 1.0.0-beta.4
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/README.md +20 -1
- package/dist/channel-core.d.mts +139 -0
- package/dist/channel-core.mjs +411 -0
- package/dist/extension-channel.d.mts +14 -70
- package/dist/extension-channel.mjs +17 -327
- package/dist/extension-router.d.mts +18 -8
- package/dist/extension-router.mjs +62 -209
- package/dist/router-core.d.mts +121 -0
- package/dist/router-core.mjs +282 -0
- package/dist/worker-channel.d.mts +14 -70
- package/dist/worker-channel.mjs +18 -335
- package/dist/worker-router.d.mts +1 -7
- package/dist/worker-router.mjs +40 -216
- package/dist/ws-channel.d.mts +17 -48
- package/dist/ws-channel.mjs +24 -335
- package/dist/ws-router.d.mts +1 -6
- package/dist/ws-router.mjs +46 -204
- package/package.json +3 -9
- package/dist/debug-logger.d.mts +0 -43
- package/dist/debug-logger.mjs +0 -122
- package/dist/error.d.mts +0 -27
- package/dist/error.mjs +0 -49
- package/dist/memory-storage.d.mts +0 -12
- package/dist/memory-storage.mjs +0 -18
- package/dist/sync-storage.d.mts +0 -11
- package/dist/sync-storage.mjs +0 -18
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Message } from "./bebop.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { ServerContext, TempoRouterConfiguration } from "@tempojs/server";
|
|
2
|
+
import { RouterCore } from "./router-core.mjs";
|
|
3
|
+
import { Metadata, TempoError, TempoStatusCode, TempoUtil, TempoVersion } from "@tempojs/common";
|
|
4
|
+
import { runtime, tabs } from "webextension-polyfill";
|
|
5
|
+
import { TempoRouterConfiguration } from "@tempojs/server";
|
|
7
6
|
//#region src/extension-router.ts
|
|
8
7
|
/**
|
|
9
8
|
* Interface defining the configuration options for a TempoRouter instance.
|
|
@@ -39,6 +38,10 @@ var TempoExtensionRouterConfiguration = class {
|
|
|
39
38
|
*/
|
|
40
39
|
maxRetryAttempts;
|
|
41
40
|
/**
|
|
41
|
+
* Optional flag to indicate whether the tempo version, runtime, and variant should be exposed in responses via X-Powered-By (and if the GET endpoint should be enabled). Defaults to false.
|
|
42
|
+
*/
|
|
43
|
+
exposeTempo;
|
|
44
|
+
/**
|
|
42
45
|
* Constructs a new instance of TempoRouterConfiguration with default values.
|
|
43
46
|
*/
|
|
44
47
|
constructor() {
|
|
@@ -146,230 +149,80 @@ var ExtensionBaseRouter = class {
|
|
|
146
149
|
}
|
|
147
150
|
};
|
|
148
151
|
var TempoExtensionRouter = class extends ExtensionBaseRouter {
|
|
149
|
-
|
|
150
|
-
clientStreams = /* @__PURE__ */ new Map();
|
|
152
|
+
core;
|
|
151
153
|
constructor(logger, registry, configuration = new TempoRouterConfiguration(), authInterceptor) {
|
|
152
154
|
super(logger, registry, configuration, authInterceptor);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
await this.setAuthContext(request, context);
|
|
163
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
164
|
-
const record = this.deserializeRequest(request.data, method, "bebop");
|
|
165
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
166
|
-
return await method.invoke(record, context);
|
|
167
|
-
}
|
|
168
|
-
async invokeClientStreamMethod(request, context, method) {
|
|
169
|
-
await this.setAuthContext(request, context);
|
|
170
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
171
|
-
const messageId = request.messageId;
|
|
172
|
-
if (this.clientStreams.has(messageId)) {
|
|
173
|
-
const invocation = this.clientStreams.get(messageId);
|
|
174
|
-
this.events.emit(messageId, request);
|
|
175
|
-
return await invocation;
|
|
176
|
-
}
|
|
177
|
-
const generator = () => {
|
|
178
|
-
return createEventIterator(({ emit, cancel }) => {
|
|
179
|
-
const eventHandler = async (message) => {
|
|
180
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
181
|
-
if (message.status === TempoStatusCode.CANCELLED) {
|
|
182
|
-
cancel();
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const requestData = message.data;
|
|
186
|
-
const record = this.deserializeRequest(requestData, method, "bebop");
|
|
187
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
188
|
-
emit(record);
|
|
189
|
-
};
|
|
190
|
-
this.events.on(messageId, eventHandler);
|
|
191
|
-
return () => {
|
|
192
|
-
this.events.off(messageId, eventHandler);
|
|
193
|
-
this.clientStreams.delete(messageId);
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
};
|
|
197
|
-
const invocation = method.invoke(generator, context);
|
|
198
|
-
this.clientStreams.set(messageId, invocation);
|
|
199
|
-
this.events.emit(messageId, request);
|
|
200
|
-
return await invocation.finally(() => {
|
|
201
|
-
this.clientStreams.delete(messageId);
|
|
155
|
+
this.core = new RouterCore({
|
|
156
|
+
logger,
|
|
157
|
+
maxRetryAttempts: this.maxRetryAttempts,
|
|
158
|
+
transmitInternalErrors: this.transmitInternalErrors,
|
|
159
|
+
authInterceptor,
|
|
160
|
+
getMethod: (methodId) => this.registry.getMethod(methodId),
|
|
161
|
+
getHooks: () => this.hooks,
|
|
162
|
+
deserializeRequest: (data, method, contentType) => this.deserializeRequest(data, method, contentType),
|
|
163
|
+
serializeResponse: (record, method, contentType) => this.serializeResponse(record, method, contentType)
|
|
202
164
|
});
|
|
203
165
|
}
|
|
204
|
-
async invokeServerStreamMethod(request, context, method) {
|
|
205
|
-
await this.setAuthContext(request, context);
|
|
206
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
207
|
-
const requestData = request.data;
|
|
208
|
-
const record = this.deserializeRequest(requestData, method, "bebop");
|
|
209
|
-
if (!TempoUtil.isAsyncGeneratorFunction(method.invoke.bind(method))) throw new TempoError(TempoStatusCode.INTERNAL, "service method incorrect: method must be async generator");
|
|
210
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
211
|
-
return method.invoke(record, context);
|
|
212
|
-
}
|
|
213
|
-
async invokeDuplexStreamMethod(request, context, method) {
|
|
214
|
-
await this.setAuthContext(request, context);
|
|
215
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
216
|
-
const messageId = request.messageId;
|
|
217
|
-
if (this.clientStreams.has(messageId)) {
|
|
218
|
-
const invocation = this.clientStreams.get(messageId);
|
|
219
|
-
this.events.emit(messageId, request);
|
|
220
|
-
return invocation;
|
|
221
|
-
}
|
|
222
|
-
if (!TempoUtil.isAsyncGeneratorFunction(method.invoke.bind(method))) throw new TempoError(TempoStatusCode.INTERNAL, "service method incorrect: method must be async generator");
|
|
223
|
-
const generator = () => {
|
|
224
|
-
return createEventIterator(({ emit, cancel }) => {
|
|
225
|
-
const eventHandler = async (message) => {
|
|
226
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
227
|
-
if (message.status === TempoStatusCode.CANCELLED) {
|
|
228
|
-
cancel();
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
const requestData = message.data;
|
|
232
|
-
const record = this.deserializeRequest(requestData, method, "bebop");
|
|
233
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
234
|
-
emit(record);
|
|
235
|
-
};
|
|
236
|
-
this.events.on(messageId, eventHandler);
|
|
237
|
-
this.events.emit(messageId, request);
|
|
238
|
-
return () => {
|
|
239
|
-
this.events.off(messageId, eventHandler);
|
|
240
|
-
this.clientStreams.delete(messageId);
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
};
|
|
244
|
-
const invocation = method.invoke(generator, context);
|
|
245
|
-
this.clientStreams.set(messageId, invocation);
|
|
246
|
-
return invocation;
|
|
247
|
-
}
|
|
248
166
|
/**
|
|
249
167
|
* Tear down all open streams for this connection. Emits a synthetic CANCELLED
|
|
250
168
|
* to each in-flight client/duplex stream so its generator ends, then clears
|
|
251
169
|
* all listeners.
|
|
252
170
|
*/
|
|
253
171
|
async closeConnection() {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
messageId: id,
|
|
257
|
-
status: TempoStatusCode.CANCELLED,
|
|
258
|
-
data: new Uint8Array()
|
|
259
|
-
}));
|
|
260
|
-
this.clientStreams.delete(id);
|
|
261
|
-
}
|
|
262
|
-
this.events.removeAllListeners();
|
|
172
|
+
this.core.cancelClientStreams();
|
|
173
|
+
this.core.removeAllListeners();
|
|
263
174
|
}
|
|
264
175
|
async process(req, response, ctx) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const incomingContext = {
|
|
281
|
-
headers: new Headers(),
|
|
282
|
-
metadata
|
|
283
|
-
};
|
|
284
|
-
if (deadline !== void 0) incomingContext.deadline = deadline;
|
|
285
|
-
context = new ServerContext(incomingContext, { metadata: outgoingMetadata }, ctx);
|
|
286
|
-
const handleRequest = async () => {
|
|
287
|
-
let recordGenerator = void 0;
|
|
288
|
-
let record;
|
|
289
|
-
switch (method.type) {
|
|
290
|
-
case MethodType.Unary:
|
|
291
|
-
record = await this.invokeUnaryMethod(request, context, method);
|
|
292
|
-
break;
|
|
293
|
-
case MethodType.ClientStream:
|
|
294
|
-
record = await this.invokeClientStreamMethod(request, context, method);
|
|
295
|
-
break;
|
|
296
|
-
case MethodType.ServerStream:
|
|
297
|
-
recordGenerator = await this.invokeServerStreamMethod(request, context, method);
|
|
298
|
-
break;
|
|
299
|
-
case MethodType.DuplexStream:
|
|
300
|
-
recordGenerator = await this.invokeDuplexStreamMethod(request, context, method);
|
|
301
|
-
break;
|
|
302
|
-
default: throw new TempoError(TempoStatusCode.INTERNAL, "service method incorrect: unknown method type");
|
|
303
|
-
}
|
|
304
|
-
const outgoingCredential = context.outgoingCredential;
|
|
305
|
-
if (outgoingCredential) response.credential = stringifyCredential(outgoingCredential);
|
|
306
|
-
response.methodId = request.methodId;
|
|
307
|
-
response.messageId = request.messageId;
|
|
308
|
-
response.status = TempoStatusCode.OK;
|
|
309
|
-
response.timestamp = /* @__PURE__ */ new Date();
|
|
310
|
-
response.msg = "";
|
|
311
|
-
if (this.hooks !== void 0) await this.hooks.executeResponseHooks(context);
|
|
312
|
-
outgoingMetadata.freeze();
|
|
313
|
-
if (outgoingMetadata.size() > 0) response.customMetadata = outgoingMetadata.data;
|
|
314
|
-
if (recordGenerator !== void 0) {
|
|
315
|
-
const writeFrames = async () => {
|
|
316
|
-
for await (const value of recordGenerator) {
|
|
317
|
-
const responseData = this.serializeResponse(value, method, "bebop");
|
|
318
|
-
response.data = new Uint8Array(responseData);
|
|
319
|
-
await this.send(ctx.sender, response);
|
|
320
|
-
}
|
|
321
|
-
response.data = new Uint8Array();
|
|
322
|
-
response.status = TempoStatusCode.CANCELLED;
|
|
323
|
-
await this.send(ctx.sender, response);
|
|
324
|
-
};
|
|
325
|
-
if (deadline) await deadline.executeWithinDeadline(writeFrames);
|
|
326
|
-
else await writeFrames();
|
|
327
|
-
} else {
|
|
328
|
-
if (record === void 0) throw new TempoError(TempoStatusCode.INTERNAL, "service method did not return a record");
|
|
329
|
-
const responseData = this.serializeResponse(record, method, "bebop");
|
|
330
|
-
response.data = new Uint8Array(responseData);
|
|
331
|
-
await this.send(ctx.sender, response);
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
if (deadline !== void 0) await deadline.executeWithinDeadline(handleRequest);
|
|
335
|
-
else await handleRequest();
|
|
336
|
-
} catch (e) {
|
|
337
|
-
let status = TempoStatusCode.UNKNOWN;
|
|
338
|
-
let message = "unknown error";
|
|
339
|
-
if (e instanceof TempoError) {
|
|
340
|
-
status = e.status;
|
|
341
|
-
message = e.message;
|
|
342
|
-
if (e.status === TempoStatusCode.INTERNAL && this.transmitInternalErrors !== true) message = "internal error";
|
|
343
|
-
if (e.status === TempoStatusCode.INTERNAL) this.logger.critical(e.message, void 0, e);
|
|
344
|
-
else this.logger.error(message, void 0, e);
|
|
345
|
-
} else if (e instanceof Error) {
|
|
346
|
-
message = e.message;
|
|
347
|
-
this.logger.error(message, void 0, e);
|
|
348
|
-
}
|
|
349
|
-
if (e instanceof Error && this.hooks !== void 0) await this.hooks.executeErrorHooks(context, e);
|
|
350
|
-
this.clientStreams.delete(request.messageId);
|
|
351
|
-
response.status = status;
|
|
352
|
-
response.msg = message;
|
|
353
|
-
response.messageId = request.messageId;
|
|
354
|
-
response.timestamp = request.timestamp;
|
|
355
|
-
response.methodId = request.methodId;
|
|
356
|
-
await this.send(ctx.sender, response);
|
|
357
|
-
}
|
|
176
|
+
const request = Message(req);
|
|
177
|
+
const send = (message) => this.send(ctx.sender, message);
|
|
178
|
+
await this.core.processRequest(request, response, ctx, {
|
|
179
|
+
send,
|
|
180
|
+
stampErrorResponse: true,
|
|
181
|
+
decorateResponse: (message) => {
|
|
182
|
+
message.msg = "";
|
|
183
|
+
},
|
|
184
|
+
writeStreamFrames: (generator, resp, method) => this.core.writeStreamFrames({
|
|
185
|
+
generator,
|
|
186
|
+
response: resp,
|
|
187
|
+
method,
|
|
188
|
+
send
|
|
189
|
+
})
|
|
190
|
+
});
|
|
358
191
|
}
|
|
359
192
|
async send(sender, message) {
|
|
360
|
-
|
|
361
|
-
this.logger.warn(`ignoring message to discarded tab`, { tab: sender.tab });
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
193
|
+
const payload = Array.from(Message.encode(message));
|
|
364
194
|
try {
|
|
365
|
-
await tabs.sendMessage(sender.tab.id,
|
|
195
|
+
if (sender.tab?.id !== void 0 && sender.tab.discarded !== true) await tabs.sendMessage(sender.tab.id, payload);
|
|
196
|
+
else await runtime.sendMessage(payload);
|
|
366
197
|
} catch (e) {
|
|
367
|
-
this.logger.
|
|
198
|
+
this.logger.debug("extension reply not delivered; receiver likely gone", { tab: sender.tab }, e);
|
|
368
199
|
}
|
|
369
200
|
}
|
|
370
201
|
async handle(_req, _ctx) {
|
|
371
202
|
return Message({});
|
|
372
203
|
}
|
|
373
204
|
};
|
|
205
|
+
/**
|
|
206
|
+
* Inbound guard + dispatch for the background/service-worker `runtime.onMessage`
|
|
207
|
+
* hook, which the router does not install itself. Only frames from this
|
|
208
|
+
* extension that decode to a Message with a registered methodId are processed;
|
|
209
|
+
* everything else (other extensions, reply frames, malformed traffic) is
|
|
210
|
+
* ignored. Replies travel back through the router's send path, so this never
|
|
211
|
+
* uses `sendResponse`.
|
|
212
|
+
*/
|
|
213
|
+
function createExtensionListener(router, registry) {
|
|
214
|
+
return (raw, sender) => {
|
|
215
|
+
if (sender.id !== runtime.id) return void 0;
|
|
216
|
+
if (!Array.isArray(raw)) return void 0;
|
|
217
|
+
let message;
|
|
218
|
+
try {
|
|
219
|
+
message = Message(Message.decode(new Uint8Array(raw)));
|
|
220
|
+
} catch {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (message.methodId === void 0 || !registry.getMethod(message.methodId)) return;
|
|
224
|
+
router.process(message, Message({}), { sender });
|
|
225
|
+
};
|
|
226
|
+
}
|
|
374
227
|
//#endregion
|
|
375
|
-
export { ExtensionBaseRouter, TempoExtensionRouter, TempoExtensionRouterConfiguration };
|
|
228
|
+
export { ExtensionBaseRouter, TempoExtensionRouter, TempoExtensionRouterConfiguration, createExtensionListener };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Message } from "./bebop.mjs";
|
|
2
|
+
import { BebopRecord } from "bebop";
|
|
3
|
+
import { BebopContentType, HookRegistry, Metadata, TempoLogger } from "@tempojs/common";
|
|
4
|
+
import { EventEmitter } from "tseep";
|
|
5
|
+
import { AuthInterceptor, BebopMethodAny, ServerContext } from "@tempojs/server";
|
|
6
|
+
|
|
7
|
+
//#region src/router-core.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* The slice of a concrete router (`BaseRouter` / `ExtensionBaseRouter`
|
|
10
|
+
* subclass) that {@link RouterCore} needs. Supplied as delegate closures from
|
|
11
|
+
* the router's constructor because the underlying members are `protected`.
|
|
12
|
+
*/
|
|
13
|
+
interface RouterCoreHost {
|
|
14
|
+
readonly logger: TempoLogger;
|
|
15
|
+
readonly maxRetryAttempts: number;
|
|
16
|
+
readonly transmitInternalErrors: boolean;
|
|
17
|
+
readonly authInterceptor?: AuthInterceptor;
|
|
18
|
+
/** Resolve a method from the router's service registry. */
|
|
19
|
+
getMethod(methodId: number): BebopMethodAny | undefined;
|
|
20
|
+
/** Read the router's current hook registry (set later via `useHooks`). */
|
|
21
|
+
getHooks(): HookRegistry<ServerContext, any> | undefined;
|
|
22
|
+
deserializeRequest(requestData: Uint8Array, method: BebopMethodAny, contentType: BebopContentType): BebopRecord;
|
|
23
|
+
serializeResponse(response: BebopRecord, method: BebopMethodAny, contentType: BebopContentType): Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The transport-specific pieces of one `process()` invocation, built fresh per
|
|
27
|
+
* request so the callbacks can close over the transport handle (socket, worker
|
|
28
|
+
* port, extension sender) and the request envelope.
|
|
29
|
+
*/
|
|
30
|
+
interface ProcessTransport {
|
|
31
|
+
/** Deliver a fully-populated response envelope to the client. */
|
|
32
|
+
send(response: Message): void | Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Pump a server-stream / duplex generator into response frames (usually via
|
|
35
|
+
* {@link RouterCore.writeStreamFrames}). Runs inside the request deadline.
|
|
36
|
+
*/
|
|
37
|
+
writeStreamFrames(generator: AsyncGenerator<BebopRecord, void, unknown>, response: Message, method: BebopMethodAny): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Build the incoming request {@link Metadata} for the ServerContext.
|
|
40
|
+
* Defaults to parsing the request's custom-metadata header.
|
|
41
|
+
*/
|
|
42
|
+
buildIncomingMetadata?(request: Message): Metadata;
|
|
43
|
+
/**
|
|
44
|
+
* Override the server-stream invocation (the WebSocket router layers
|
|
45
|
+
* CANCELLED handling and generator tracking on top of the shared one).
|
|
46
|
+
*/
|
|
47
|
+
invokeServerStream?(request: Message, context: ServerContext, method: BebopMethodAny): Promise<AsyncGenerator<BebopRecord, void, unknown>>;
|
|
48
|
+
/** Mutate the response envelope after the OK headers are populated. */
|
|
49
|
+
decorateResponse?(response: Message): void;
|
|
50
|
+
/** Stamp messageId/timestamp/methodId from the request onto error frames. */
|
|
51
|
+
stampErrorResponse?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Options for {@link RouterCore.writeStreamFrames}. */
|
|
54
|
+
interface StreamWriteOptions {
|
|
55
|
+
generator: AsyncGenerator<BebopRecord, void, unknown>;
|
|
56
|
+
response: Message;
|
|
57
|
+
method: BebopMethodAny;
|
|
58
|
+
contentType?: BebopContentType;
|
|
59
|
+
/** Deliver one populated frame to the client. */
|
|
60
|
+
send: (response: Message) => void | Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Serialize one record into owned payload bytes. The default copies out of
|
|
63
|
+
* bebop's shared write buffer; the WebSocket router substitutes its
|
|
64
|
+
* encode-once broadcast cache.
|
|
65
|
+
*/
|
|
66
|
+
serializePayload?: (value: BebopRecord) => Uint8Array;
|
|
67
|
+
/** Checked before each frame; `true` stops the pump (worker cancellation). */
|
|
68
|
+
shouldStop?: () => boolean;
|
|
69
|
+
/** Send the terminal frame even when the generator throws (worker semantics). */
|
|
70
|
+
alwaysTerminate?: boolean;
|
|
71
|
+
/** Runs after the pump ends, before the terminal frame (tracking-map reaping). */
|
|
72
|
+
onEnd?: () => void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Shared server-side routing core for the worker, WebSocket, and
|
|
76
|
+
* browser-extension routers.
|
|
77
|
+
*
|
|
78
|
+
* Owns the per-connection stream state (response demultiplexing events and
|
|
79
|
+
* in-flight client/duplex streams) plus every transport-agnostic step of
|
|
80
|
+
* dispatching one framed request: auth interception, hook execution, the four
|
|
81
|
+
* RPC invocation shapes, response population, deadline enforcement, and error
|
|
82
|
+
* mapping. Each concrete router composes an instance and passes the genuinely
|
|
83
|
+
* transport-specific bits per request via {@link ProcessTransport}.
|
|
84
|
+
*
|
|
85
|
+
* Internal to `@vampgg/utils`; the public routers are `TempoWorkerRouter`,
|
|
86
|
+
* `TempoWsRouter`, and `TempoExtensionRouter`.
|
|
87
|
+
*/
|
|
88
|
+
declare class RouterCore {
|
|
89
|
+
private readonly host;
|
|
90
|
+
/** Per-stream frame listeners keyed by messageId. */
|
|
91
|
+
readonly events: EventEmitter<Record<string, (message: Message) => void>>;
|
|
92
|
+
/** In-flight client/duplex stream invocations keyed by messageId. */
|
|
93
|
+
readonly clientStreams: Map<string, Promise<any>>;
|
|
94
|
+
constructor(host: RouterCoreHost);
|
|
95
|
+
private setAuthContext;
|
|
96
|
+
invokeUnaryMethod(request: Message, context: ServerContext, method: BebopMethodAny, contentType: BebopContentType): Promise<BebopRecord>;
|
|
97
|
+
invokeClientStreamMethod(request: Message, context: ServerContext, method: BebopMethodAny, contentType: BebopContentType): Promise<BebopRecord>;
|
|
98
|
+
invokeServerStreamMethod(request: Message, context: ServerContext, method: BebopMethodAny, contentType: BebopContentType): Promise<AsyncGenerator<BebopRecord, void, unknown>>;
|
|
99
|
+
invokeDuplexStreamMethod(request: Message, context: ServerContext, method: BebopMethodAny, contentType: BebopContentType): Promise<AsyncGenerator<BebopRecord, void, unknown>>;
|
|
100
|
+
/**
|
|
101
|
+
* Emit a synthetic CANCELLED to every in-flight client/duplex stream so its
|
|
102
|
+
* generator ends, and drop the tracking entries (connection teardown).
|
|
103
|
+
*/
|
|
104
|
+
cancelClientStreams(): void;
|
|
105
|
+
/** Drop every per-stream frame listener (connection teardown). */
|
|
106
|
+
removeAllListeners(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Pump a server-stream / duplex generator into response frames and emit the
|
|
109
|
+
* terminal CANCELLED frame that tells the client the stream is complete.
|
|
110
|
+
*/
|
|
111
|
+
writeStreamFrames(opts: StreamWriteOptions): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Dispatch one decoded request envelope: resolve the method, enforce retry
|
|
114
|
+
* and deadline limits, build the {@link ServerContext}, invoke the method by
|
|
115
|
+
* type, populate and deliver the response, and map any failure onto an error
|
|
116
|
+
* frame. Transport-specific behavior is injected via `transport`.
|
|
117
|
+
*/
|
|
118
|
+
processRequest(request: Message, response: Message, env: unknown, transport: ProcessTransport, contentType?: BebopContentType): Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { ProcessTransport, RouterCore, RouterCoreHost, StreamWriteOptions };
|