@vampgg/utils 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.
@@ -0,0 +1,377 @@
1
+ import { Message } from "./bebop.mjs";
2
+ import { createEventIterator } from "./create-event-iterator.mjs";
3
+ import { createDuplexIterator } from "./create-duplex-iterator.mjs";
4
+ import { ConsoleLogger, ExecutionEnvironment, Metadata, TempoError, TempoStatusCode, TempoUtil, TempoVersion } from "@tempojs/common";
5
+ import { BaseChannel } from "@tempojs/client";
6
+ import { EventEmitter } from "tseep";
7
+ import * as browser from "webextension-polyfill";
8
+ //#region src/extension-channel.ts
9
+ /**
10
+ * Represents a Tempo channel for communication with a remote server.
11
+ */
12
+ var TempoExtensionChannel = class TempoExtensionChannel extends BaseChannel {
13
+ static defaultMaxRetryAttempts = 5;
14
+ static defaultMaxReceiveMessageSize = 1024 * 1024 * 4;
15
+ static defaultMaxSendMessageSize = 1024 * 1024 * 4;
16
+ static defaultContentType = "bebop";
17
+ events = new EventEmitter();
18
+ userAgent;
19
+ pending = /* @__PURE__ */ new Map();
20
+ closed = false;
21
+ onMessage = (msg) => {
22
+ let message;
23
+ try {
24
+ message = Message(Message.decode(new Uint8Array(msg)));
25
+ } catch (e) {
26
+ this.logger.warn("dropping undecodable runtime message", {}, e);
27
+ return Promise.resolve();
28
+ }
29
+ if (message.messageId) {
30
+ this.events.emit(message.messageId, message);
31
+ this.logger.debug(`received new message ${message.messageId}`);
32
+ }
33
+ return Promise.resolve();
34
+ };
35
+ /**
36
+ * Constructs a new TempoChannel instance.
37
+ *
38
+ * @param {URL} target - The target URL for the channel.
39
+ * @param {TempoWSChannelOptions} options - The configuration options for the channel.
40
+ * @protected
41
+ */
42
+ constructor(options = {}) {
43
+ super(new URL("runtime://"), options.logger ?? new ConsoleLogger("TempoExtensionChannel"), options.contentType ?? TempoExtensionChannel.defaultContentType);
44
+ this.logger.trace("creating new TempoExtensionChannel");
45
+ this.userAgent = TempoUtil.buildUserAgent("javascript", TempoVersion, void 0, { runtime: TempoUtil.getEnvironmentName() });
46
+ browser.runtime.onMessage.addListener(this.onMessage);
47
+ this.logger.debug(`created new TempoExtensionChannel`);
48
+ }
49
+ /**
50
+ * Tear down the channel: remove the runtime message listener, reject every
51
+ * in-flight call, and clear all event listeners. Idempotent.
52
+ */
53
+ close(reason) {
54
+ if (this.closed) return;
55
+ this.closed = true;
56
+ try {
57
+ browser.runtime.onMessage.removeListener(this.onMessage);
58
+ } catch {}
59
+ const err = reason ?? new TempoError(TempoStatusCode.CANCELLED, "channel disposed");
60
+ for (const reject of this.pending.values()) reject(err);
61
+ this.pending.clear();
62
+ this.events.removeAllListeners();
63
+ }
64
+ async removeCredential() {}
65
+ async getCredential() {}
66
+ /**
67
+ * Executes a function with retries according to the provided retry policy.
68
+ * The function will be retried if it fails with a TempoError and its status code is included in the retryableStatusCodes of the retry policy.
69
+ * If a deadline is provided, the deadline for each attempt will be managed by the provided deadline, but the deadline will not be reset upon each retry.
70
+ *
71
+ * @template T - The type of the result returned by the function.
72
+ * @param {((retryAttempt: number) => Promise<T>)} func - A function that returns a Promise with a result. The function will receive a number indicating the current retry attempt.
73
+ * @param {RetryPolicy} retryPolicy - An object defining the retry policy, including maxAttempts, initialBackoff, maxBackoff, backoffMultiplier, and retryableStatusCodes.
74
+ * @param {Deadline} [deadline] - An optional deadline object that manages the timeout for each attempt.
75
+ * @param {AbortController} [abortController] - An optional AbortController instance to cancel the function execution.
76
+ * @returns {Promise<T>} - A Promise that resolves with the result of the function if it completes within the deadline and retry policy constraints.
77
+ * @throws {Error} - If the function execution fails and the error does not match the retry policy, or if the maximum number of attempts is reached without a successful result.
78
+ */
79
+ async executeWithRetry(func, retryPolicy, deadline, abortController) {
80
+ let attempt = 0;
81
+ let lastError;
82
+ const execute = deadline ? (retryAttempt) => deadline.executeWithinDeadline(async () => await func(retryAttempt), abortController) : (retryAttempt) => func(retryAttempt);
83
+ while (attempt < retryPolicy.maxAttempts) try {
84
+ return await execute(attempt);
85
+ } catch (error) {
86
+ if (!(error instanceof Error)) throw new TempoError(TempoStatusCode.UNKNOWN, `unexpected error`, { data: error });
87
+ lastError = error;
88
+ if (!(error instanceof TempoError) || !retryPolicy.retryableStatusCodes.includes(error.status)) throw error;
89
+ const backoffWithJitter = Math.min(retryPolicy.initialBackoff.multiply(Math.pow(retryPolicy.backoffMultiplier, attempt)).totalMilliseconds, retryPolicy.maxBackoff.totalMilliseconds) * (Math.random() * .5 + .75);
90
+ await new Promise((resolve) => setTimeout(resolve, backoffWithJitter));
91
+ attempt++;
92
+ }
93
+ if (abortController && lastError !== void 0 && !(lastError instanceof Error && lastError.name === "AbortError") && !(lastError instanceof TempoError && lastError.status === TempoStatusCode.ABORTED)) abortController.abort();
94
+ return Promise.reject(lastError || new TempoError(TempoStatusCode.DEADLINE_EXCEEDED, "Failed to execute function with retry policy"));
95
+ }
96
+ async fetchUnary(init, options) {
97
+ const messageId = init.messageId;
98
+ let listener;
99
+ let onAbort;
100
+ const cleanup = () => {
101
+ this.events.off(messageId, listener);
102
+ if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
103
+ this.pending.delete(messageId);
104
+ };
105
+ return await new Promise((resolve, reject) => {
106
+ listener = (message) => resolve(message);
107
+ this.pending.set(messageId, reject);
108
+ if (options?.controller) {
109
+ onAbort = () => reject(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
110
+ options.controller.signal.addEventListener("abort", onAbort, { once: true });
111
+ }
112
+ this.events.on(messageId, listener);
113
+ this.sendMessage(init).catch(reject);
114
+ }).finally(cleanup);
115
+ }
116
+ async fetchClientStream(init, method, generator, options) {
117
+ const messageId = init.messageId;
118
+ let listener;
119
+ let onAbort;
120
+ const cleanup = () => {
121
+ this.events.off(messageId, listener);
122
+ if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
123
+ this.pending.delete(messageId);
124
+ };
125
+ return await new Promise((resolve, reject) => {
126
+ listener = (message) => resolve(message);
127
+ this.pending.set(messageId, reject);
128
+ if (options?.controller) {
129
+ onAbort = () => reject(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
130
+ options.controller.signal.addEventListener("abort", onAbort, { once: true });
131
+ }
132
+ this.events.on(messageId, listener);
133
+ runGenerator.call(this).catch(reject);
134
+ async function runGenerator() {
135
+ for await (const value of generator()) await this.sendMessage(Message({
136
+ ...init,
137
+ data: method.serialize(value)
138
+ }));
139
+ await this.sendMessage(Message({
140
+ ...init,
141
+ status: TempoStatusCode.CANCELLED,
142
+ data: new Uint8Array()
143
+ }));
144
+ }
145
+ }).finally(cleanup);
146
+ }
147
+ fetchServerStream(init, context, method, options) {
148
+ const messageId = init.messageId;
149
+ return createEventIterator(({ emit, cancel, error }) => {
150
+ let terminated = false;
151
+ let onAbort;
152
+ const eventHandler = async (message) => {
153
+ try {
154
+ if (message.status === TempoStatusCode.CANCELLED) {
155
+ terminated = true;
156
+ cancel();
157
+ return;
158
+ }
159
+ this.processResponseHeaders(message, context, method.type);
160
+ const responseData = message.data;
161
+ const record = method.deserialize(responseData);
162
+ if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
163
+ emit(record);
164
+ } catch (e) {
165
+ error(e);
166
+ }
167
+ };
168
+ if (options?.controller) {
169
+ onAbort = () => error(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
170
+ options.controller.signal.addEventListener("abort", onAbort, { once: true });
171
+ }
172
+ this.events.on(messageId, eventHandler);
173
+ this.sendMessage(init);
174
+ return () => {
175
+ this.events.off(messageId, eventHandler);
176
+ if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
177
+ if (terminated || this.closed) return;
178
+ this.sendMessage(Message({
179
+ messageId,
180
+ methodId: init.methodId,
181
+ status: TempoStatusCode.CANCELLED,
182
+ data: new Uint8Array(),
183
+ timestamp: /* @__PURE__ */ new Date()
184
+ }));
185
+ };
186
+ });
187
+ }
188
+ fetchDuplexStream(init, context, method, generator, options) {
189
+ const messageId = init.messageId;
190
+ return createDuplexIterator(generator(), (value) => {
191
+ this.sendMessage(Message({
192
+ ...init,
193
+ data: method.serialize(value)
194
+ }));
195
+ }, ({ emit, cancel, error }) => {
196
+ let onAbort;
197
+ const eventHandler = async (message) => {
198
+ try {
199
+ if (message.status === TempoStatusCode.CANCELLED) {
200
+ cancel();
201
+ return;
202
+ }
203
+ this.processResponseHeaders(message, context, method.type);
204
+ const requestData = message.data;
205
+ const record = method.deserialize(requestData);
206
+ if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
207
+ emit(record);
208
+ } catch (e) {
209
+ error(e);
210
+ }
211
+ };
212
+ if (options?.controller) {
213
+ onAbort = () => error(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
214
+ options.controller.signal.addEventListener("abort", onAbort, { once: true });
215
+ }
216
+ this.events.on(messageId, eventHandler);
217
+ return () => {
218
+ this.events.off(messageId, eventHandler);
219
+ if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
220
+ };
221
+ });
222
+ }
223
+ /**
224
+ * Creates a `RequestInit` object for a given payload, context, method and optional call options.
225
+ * This object can be used to make an HTTP request using the Fetch API.
226
+ *
227
+ * @private
228
+ * @param {Uint8Array} payload - The payload to be sent in the request.
229
+ * @param {ClientContext} context - The context of the client making the request.
230
+ * @param {MethodInfo<BebopRecord, BebopRecord>} method - Information about the method being called.
231
+ * @param {CallOptions | undefined} options - Optional configuration for the call.
232
+ * @returns {Promise<RequestInit>} A Promise resolving to the created `RequestInit` object.
233
+ * @throws {TempoError} Throws an error if there's a problem while getting the credential header.
234
+ */
235
+ createRequest(payload, _context, method, options) {
236
+ const customMetadata = new Metadata();
237
+ customMetadata.set("path", `/${method.service}/${method.name}`);
238
+ customMetadata.set("service-name", method.service);
239
+ if (ExecutionEnvironment.isBrowser || ExecutionEnvironment.isWebWorker) customMetadata.set("x-user-agent", this.userAgent);
240
+ else customMetadata.set("user-agent", this.userAgent);
241
+ const requestInit = {
242
+ messageId: crypto.randomUUID(),
243
+ methodId: method.id,
244
+ data: new Uint8Array(payload),
245
+ timestamp: /* @__PURE__ */ new Date()
246
+ };
247
+ if (options?.deadline) requestInit.deadline = new Date(options.deadline.toUnixTimestamp());
248
+ return requestInit;
249
+ }
250
+ /**
251
+ * Processes the headers of the response from the server, validating their integrity and correctness.
252
+ * Also sets the incoming metadata from the response headers to the provided context.
253
+ *
254
+ * @private
255
+ * @param {Response} response - The response received from the server.
256
+ * @param {ClientContext} context - The context of the client making the request.
257
+ * @param {MethodType} methodType - The type of method being called.
258
+ * @throws {TempoError} Throws an error if any validation checks fail or if there's a problem parsing or storing credentials.
259
+ */
260
+ processResponseHeaders(response, _context, _methodType) {
261
+ const statusCode = response.status;
262
+ if (statusCode === void 0) throw new TempoError(TempoStatusCode.UNKNOWN, "tempo-status missing from response.");
263
+ if (statusCode !== TempoStatusCode.OK && statusCode !== TempoStatusCode.CANCELLED) {
264
+ let tempoMessage = response.msg;
265
+ if (!tempoMessage) tempoMessage = "unknown error";
266
+ throw new TempoError(statusCode, tempoMessage);
267
+ }
268
+ }
269
+ /**
270
+ * {@inheritDoc BaseChannel.startUnary}
271
+ */
272
+ async startUnary(request, context, method, options) {
273
+ try {
274
+ const requestData = method.serialize(request);
275
+ if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
276
+ const requestInit = this.createRequest(requestData, context, method, options);
277
+ let response;
278
+ if (options?.retryPolicy) response = await this.executeWithRetry(async (retryAttempt) => {
279
+ if (retryAttempt > 0) requestInit.previousAttempts = retryAttempt;
280
+ return await this.fetchUnary(requestInit);
281
+ }, options.retryPolicy, options.deadline, options.controller);
282
+ else if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
283
+ return await this.fetchUnary(requestInit, options);
284
+ }, options.controller);
285
+ else response = await this.fetchUnary(requestInit, options);
286
+ this.processResponseHeaders(response, context, method.type);
287
+ if (this.hooks !== void 0) await this.hooks.executeResponseHooks(context);
288
+ const responseData = response.data;
289
+ const record = method.deserialize(responseData);
290
+ if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
291
+ return record;
292
+ } catch (e) {
293
+ if (this.hooks !== void 0 && e instanceof Error) this.hooks.executeErrorHooks(context, e);
294
+ if (e instanceof TempoError) throw e;
295
+ if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
296
+ else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
297
+ throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
298
+ }
299
+ }
300
+ /**
301
+ * {@inheritDoc BaseChannel.startClientStream}
302
+ */
303
+ async startClientStream(generator, context, method, options) {
304
+ try {
305
+ if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
306
+ const requestInit = this.createRequest(new Uint8Array(), context, method, options);
307
+ let response;
308
+ if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
309
+ return await this.fetchClientStream(requestInit, method, generator, options);
310
+ }, options.controller);
311
+ else response = await this.fetchClientStream(requestInit, method, generator, options);
312
+ this.processResponseHeaders(response, context, method.type);
313
+ const responseData = response.data;
314
+ const record = this.deserializeResponse(responseData, method);
315
+ if (this.hooks !== void 0) this.hooks.executeDecodeHooks(context, record);
316
+ return record;
317
+ } catch (e) {
318
+ if (this.hooks !== void 0 && e instanceof Error) this.hooks.executeErrorHooks(context, e);
319
+ if (e instanceof TempoError) throw e;
320
+ if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
321
+ else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
322
+ throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
323
+ }
324
+ }
325
+ /**
326
+ * {@inheritDoc BaseChannel.startServerStream}
327
+ */
328
+ async startServerStream(request, context, method, options) {
329
+ try {
330
+ const requestData = method.serialize(request);
331
+ if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
332
+ const requestInit = this.createRequest(requestData, context, method, options);
333
+ let response;
334
+ if (options?.retryPolicy) response = await this.executeWithRetry(async (retryAttempt) => {
335
+ if (retryAttempt > 0) requestInit.previousAttempts = retryAttempt;
336
+ return this.fetchServerStream(requestInit, context, method, options);
337
+ }, options.retryPolicy, options.deadline, options.controller);
338
+ else if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
339
+ return this.fetchServerStream(requestInit, context, method, options);
340
+ }, options.controller);
341
+ else response = this.fetchServerStream(requestInit, context, method, options);
342
+ return response;
343
+ } catch (e) {
344
+ if (this.hooks !== void 0 && e instanceof Error) this.hooks.executeErrorHooks(context, e);
345
+ if (e instanceof TempoError) throw e;
346
+ if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
347
+ else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
348
+ throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
349
+ }
350
+ }
351
+ /**
352
+ * {@inheritDoc BaseChannel.startDuplexStream}
353
+ */
354
+ async startDuplexStream(generator, context, method, options) {
355
+ try {
356
+ if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
357
+ const requestInit = this.createRequest(new Uint8Array(), context, method, options);
358
+ let response;
359
+ if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
360
+ return this.fetchDuplexStream(requestInit, context, method, generator, options);
361
+ }, options.controller);
362
+ else response = this.fetchDuplexStream(requestInit, context, method, generator, options);
363
+ return response;
364
+ } catch (e) {
365
+ if (this.hooks !== void 0 && e instanceof Error) this.hooks.executeErrorHooks(context, e);
366
+ if (e instanceof TempoError) throw e;
367
+ if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
368
+ else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
369
+ throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
370
+ }
371
+ }
372
+ sendMessage(message) {
373
+ return browser.runtime.sendMessage(Array.apply(null, Message.encode(message)));
374
+ }
375
+ };
376
+ //#endregion
377
+ export { TempoExtensionChannel };
@@ -0,0 +1,154 @@
1
+ import { Message } from "./bebop.mjs";
2
+ import { BebopRecord } from "bebop";
3
+ import { BebopContentType, HookRegistry, Metadata, TempoLogger } from "@tempojs/common";
4
+ import { Runtime } from "webextension-polyfill";
5
+ import { AuthInterceptor, BebopMethodAny, ServerContext, ServiceRegistry, TempoRouterConfiguration } from "@tempojs/server";
6
+
7
+ //#region src/extension-router.d.ts
8
+ /**
9
+ * Interface defining the configuration options for a TempoRouter instance.
10
+ */
11
+ declare class TempoExtensionRouterConfiguration {
12
+ static readonly defaultMaxRetryAttempts: number;
13
+ static readonly defaultMaxReceiveMessageSize: number;
14
+ static readonly defaultMaxSendMessageSize: number;
15
+ /**
16
+ * Optional flag to enable CORS (Cross-Origin Resource Sharing) support.
17
+ */
18
+ enableCors?: boolean;
19
+ /**
20
+ * Optional list of allowed origins for CORS. Ignored if enableCors is false.
21
+ */
22
+ allowedOrigins?: string[];
23
+ /**
24
+ * Optional flag to indicate whether internal errors should be transmitted
25
+ * in the API response. Defaults to false, meaning internal errors are
26
+ * masked and not exposed to clients.
27
+ */
28
+ transmitInternalErrors?: boolean;
29
+ /**
30
+ * The maximum size of the message that can be received. Defaults to the value in `TempoRouterConfiguration.defaultMaxReceiveMessageSize`.
31
+ */
32
+ maxReceiveMessageSize?: number;
33
+ /**
34
+ * The maximum size of the message that can be sent.
35
+ */
36
+ maxSendMessageSize?: number;
37
+ /**
38
+ * The maximum number of retry attempts for failed requests. Defaults to the value in `TempoRouterConfiguration.defaultMaxRetryAttempts`.
39
+ */
40
+ maxRetryAttempts?: number;
41
+ /**
42
+ * Constructs a new instance of TempoRouterConfiguration with default values.
43
+ */
44
+ constructor();
45
+ }
46
+ /**
47
+ * Represents an abstract base class for a router that handles incoming requests, performs validation, and routes
48
+ * them to the appropriate services and methods in a Tempo application.
49
+ *
50
+ * @template TRequest - The type of the request object.
51
+ * @template TEnvironment - The type of the environment/context object.
52
+ * @template TResponse - The type of the response object.
53
+ */
54
+ declare abstract class ExtensionBaseRouter<TRequest, TEnvironment, TResponse> {
55
+ protected readonly logger: TempoLogger;
56
+ protected readonly registry: ServiceRegistry;
57
+ protected readonly configuration: TempoRouterConfiguration;
58
+ protected readonly authInterceptor?: AuthInterceptor | undefined;
59
+ protected readonly corsEnabled: boolean;
60
+ protected readonly allowedCorsOrigins: string[] | undefined;
61
+ protected readonly transmitInternalErrors: boolean;
62
+ protected readonly maxReceiveMessageSize: number;
63
+ protected readonly maxSendMessageSize?: number;
64
+ protected readonly maxRetryAttempts: number;
65
+ protected readonly exposeTempo: boolean;
66
+ protected hooks?: HookRegistry<ServerContext, TEnvironment>;
67
+ protected poweredByHeader: string;
68
+ protected poweredByHeaderValue?: string;
69
+ /**
70
+ * Constructs a new BaseRouter instance.
71
+ * @param logger - The logger to use for logging router-related information.
72
+ * @param registry - The service registry instance that manages services and methods for this router.
73
+ * @param authInterceptor - The interceptor (if any) that will be used for authenticating the peer of an incoming requests.
74
+ */
75
+ constructor(logger: TempoLogger, registry: ServiceRegistry, configuration: TempoRouterConfiguration, authInterceptor?: AuthInterceptor | undefined);
76
+ /**
77
+ * Handles an incoming request by routing it to the appropriate service and method, applying any necessary
78
+ * validation and processing and returns a response.
79
+ *
80
+ * @param request - The incoming request object.
81
+ * @param env - The environment/context object associated with the request.
82
+ * @returns A promise that resolves to the response object.
83
+ */
84
+ abstract handle(request: TRequest, env: TEnvironment): Promise<TResponse>;
85
+ /**
86
+ * Processes an incoming request by routing it to the appropriate service and method, applying any necessary
87
+ * validation and processing, in place on the provided response object.
88
+ *
89
+ * @param request - The incoming request object.
90
+ * @param response - The outgoing response object.
91
+ * @param env - The environment/context object associated with the request.
92
+ */
93
+ abstract process(request: TRequest, response: TResponse, env: TEnvironment): Promise<void>;
94
+ /**
95
+ * Private function that retrieves custom metadata from the header.
96
+ *
97
+ * @private
98
+ * @function
99
+ * @param {string | null} value The custom metadata value from the request.
100
+ * @returns {Metadata} The metadata object.
101
+ */
102
+ protected getCustomMetaData(value: string | null | undefined): Metadata;
103
+ /**
104
+ * Defines a hook registry for the router.
105
+ * @param hooks - The hook registry to be used.
106
+ */
107
+ useHooks(hooks: HookRegistry<ServerContext, TEnvironment>): void;
108
+ protected definePoweredByHeader(variant: string): void;
109
+ /**
110
+ * Deserializes an incoming request based on its content type.
111
+ *
112
+ * @param requestData - The incoming request data as a Uint8Array.
113
+ * @param method - The Bebop method to use for deserialization.
114
+ * @param contentType - The content type of the incoming request.
115
+ * @returns The deserialized request record.
116
+ * @throws {TempoError} When the content type is not valid.
117
+ * @throws {BebopRuntimeError} When the request data cannot be deserialized.
118
+ */
119
+ protected deserializeRequest(requestData: Uint8Array, method: BebopMethodAny, contentType: BebopContentType): BebopRecord;
120
+ /**
121
+ * Serializes a response record based on its content type.
122
+ *
123
+ * @param response - The response record to be serialized.
124
+ * @param method - The Bebop method to use for serialization.
125
+ * @param contentType - The content type of the response.
126
+ * @returns The serialized response as a Uint8Array.
127
+ * @throws {TempoError} When the content type is not valid.
128
+ * @throws {BebopRuntimeError} When the response record cannot be serialized.
129
+ */
130
+ protected serializeResponse(response: BebopRecord, method: BebopMethodAny, contentType: BebopContentType): Uint8Array;
131
+ }
132
+ declare class TempoExtensionRouter<Ctx extends {
133
+ sender: Runtime.MessageSender;
134
+ }> extends ExtensionBaseRouter<Message, Ctx, Message> {
135
+ private readonly events;
136
+ private readonly clientStreams;
137
+ constructor(logger: TempoLogger, registry: ServiceRegistry, configuration?: TempoRouterConfiguration, authInterceptor?: AuthInterceptor);
138
+ private setAuthContext;
139
+ private invokeUnaryMethod;
140
+ private invokeClientStreamMethod;
141
+ private invokeServerStreamMethod;
142
+ private invokeDuplexStreamMethod;
143
+ /**
144
+ * Tear down all open streams for this connection. Emits a synthetic CANCELLED
145
+ * to each in-flight client/duplex stream so its generator ends, then clears
146
+ * all listeners.
147
+ */
148
+ closeConnection(): Promise<void>;
149
+ process(req: Message, response: Message, ctx: Ctx): Promise<void>;
150
+ private send;
151
+ handle(_req: Message, _ctx: Ctx): Promise<Message>;
152
+ }
153
+ //#endregion
154
+ export { ExtensionBaseRouter, TempoExtensionRouter, TempoExtensionRouterConfiguration };