@vampgg/utils 1.0.0-beta.2 → 1.0.0-beta.3
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 +5 -7
- package/dist/extension-router.mjs +35 -202
- 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,24 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Message } from "./bebop.mjs";
|
|
2
|
+
import { CoreChannel } from "./channel-core.mjs";
|
|
3
|
+
import { CallCredential, TempoChannelOptions } from "@tempojs/client";
|
|
4
|
+
import { BebopContentType, TempoError } from "@tempojs/common";
|
|
4
5
|
|
|
5
6
|
//#region src/worker-channel.d.ts
|
|
6
7
|
/**
|
|
7
8
|
* Represents a Tempo channel for communication with a worker thread.
|
|
8
9
|
*/
|
|
9
|
-
declare class TempoWorkerChannel extends
|
|
10
|
+
declare class TempoWorkerChannel extends CoreChannel {
|
|
10
11
|
static readonly defaultMaxRetryAttempts: number;
|
|
11
12
|
static readonly defaultCredential: CallCredential;
|
|
12
13
|
static readonly defaultContentType: BebopContentType;
|
|
13
14
|
private readonly _worker;
|
|
14
|
-
private readonly events;
|
|
15
|
-
private readonly userAgent;
|
|
16
15
|
private _open;
|
|
17
16
|
private _ready;
|
|
18
17
|
private _resolveReady?;
|
|
19
18
|
private _rejectReady?;
|
|
20
|
-
readonly pending: Map<string, (e: unknown) => void>;
|
|
21
|
-
private closed;
|
|
22
19
|
get worker(): Bun.Worker;
|
|
23
20
|
get ready(): Promise<void>;
|
|
24
21
|
/**
|
|
@@ -29,71 +26,18 @@ declare class TempoWorkerChannel extends BaseChannel {
|
|
|
29
26
|
* @protected
|
|
30
27
|
*/
|
|
31
28
|
constructor(url: string, options?: TempoChannelOptions);
|
|
29
|
+
/** Reject a pending `ready` before in-flight calls are settled. */
|
|
30
|
+
protected onClosing(reason: TempoError): void;
|
|
31
|
+
/** Terminate the owned worker on {@link CoreChannel.close}. */
|
|
32
|
+
protected closeTransport(): void;
|
|
33
|
+
protected generateMessageId(): string;
|
|
32
34
|
/**
|
|
33
|
-
*
|
|
34
|
-
* remove all listeners, and terminate the owned worker. Idempotent.
|
|
35
|
-
*/
|
|
36
|
-
close(reason?: TempoError): void;
|
|
37
|
-
removeCredential(): Promise<void>;
|
|
38
|
-
getCredential(): Promise<Credential | undefined>;
|
|
39
|
-
/**
|
|
40
|
-
* Executes a function with retries according to the provided retry policy.
|
|
41
|
-
* The function will be retried if it fails with a TempoError and its status code is included in the retryableStatusCodes of the retry policy.
|
|
42
|
-
* 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.
|
|
43
|
-
*
|
|
44
|
-
* @template T - The type of the result returned by the function.
|
|
45
|
-
* @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.
|
|
46
|
-
* @param {RetryPolicy} retryPolicy - An object defining the retry policy, including maxAttempts, initialBackoff, maxBackoff, backoffMultiplier, and retryableStatusCodes.
|
|
47
|
-
* @param {Deadline} [deadline] - An optional deadline object that manages the timeout for each attempt.
|
|
48
|
-
* @param {AbortController} [abortController] - An optional AbortController instance to cancel the function execution.
|
|
49
|
-
* @returns {Promise<T>} - A Promise that resolves with the result of the function if it completes within the deadline and retry policy constraints.
|
|
50
|
-
* @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.
|
|
51
|
-
*/
|
|
52
|
-
executeWithRetry<T>(func: (retryAttempt: number) => Promise<T>, retryPolicy: RetryPolicy, deadline?: Deadline, abortController?: AbortController): Promise<T>;
|
|
53
|
-
private fetchUnary;
|
|
54
|
-
private fetchClientStream;
|
|
55
|
-
private fetchServerStream;
|
|
56
|
-
private fetchDuplexStream;
|
|
57
|
-
/**
|
|
58
|
-
* Creates a `RequestInit` object for a given payload, context, method and optional call options.
|
|
59
|
-
* This object can be used to make an HTTP request using the Fetch API.
|
|
60
|
-
*
|
|
61
|
-
* @private
|
|
62
|
-
* @param {Uint8Array} payload - The payload to be sent in the request.
|
|
63
|
-
* @param {ClientContext} context - The context of the client making the request.
|
|
64
|
-
* @param {MethodInfo<BebopRecord, BebopRecord>} method - Information about the method being called.
|
|
65
|
-
* @param {CallOptions | undefined} options - Optional configuration for the call.
|
|
66
|
-
* @returns {Promise<RequestInit>} A Promise resolving to the created `RequestInit` object.
|
|
67
|
-
* @throws {TempoError} Throws an error if there's a problem while getting the credential header.
|
|
68
|
-
*/
|
|
69
|
-
private createRequest;
|
|
70
|
-
/**
|
|
71
|
-
* Processes the headers of the response from the server, validating their integrity and correctness.
|
|
72
|
-
* Also sets the incoming metadata from the response headers to the provided context.
|
|
35
|
+
* {@inheritDoc CoreChannel.sendFrame}
|
|
73
36
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @param {ClientContext} context - The context of the client making the request.
|
|
77
|
-
* @param {MethodType} methodType - The type of method being called.
|
|
78
|
-
* @throws {TempoError} Throws an error if any validation checks fail or if there's a problem parsing or storing credentials.
|
|
79
|
-
*/
|
|
80
|
-
private processResponseHeaders;
|
|
81
|
-
/**
|
|
82
|
-
* {@inheritDoc BaseChannel.startUnary}
|
|
83
|
-
*/
|
|
84
|
-
startUnary<TRequest extends BebopRecord, TResponse extends BebopRecord>(request: TRequest, context: ClientContext, method: MethodInfo<TRequest, TResponse>, options?: CallOptions): Promise<TResponse>;
|
|
85
|
-
/**
|
|
86
|
-
* {@inheritDoc BaseChannel.startClientStream}
|
|
87
|
-
*/
|
|
88
|
-
startClientStream<TRequest extends BebopRecord, TResponse extends BebopRecord>(generator: () => AsyncGenerator<TRequest, void, undefined>, context: ClientContext, method: MethodInfo<TRequest, TResponse>, options?: CallOptions): Promise<TResponse>;
|
|
89
|
-
/**
|
|
90
|
-
* {@inheritDoc BaseChannel.startServerStream}
|
|
91
|
-
*/
|
|
92
|
-
startServerStream<TRequest extends BebopRecord, TResponse extends BebopRecord>(request: TRequest, context: ClientContext, method: MethodInfo<TRequest, TResponse>, options?: CallOptions): Promise<AsyncGenerator<TResponse, void, undefined>>;
|
|
93
|
-
/**
|
|
94
|
-
* {@inheritDoc BaseChannel.startDuplexStream}
|
|
37
|
+
* `.slice()` owns a fresh ArrayBuffer; transferring it cannot detach bebop's
|
|
38
|
+
* process-wide singleton write buffer.
|
|
95
39
|
*/
|
|
96
|
-
|
|
40
|
+
protected sendFrame(message: Message): void;
|
|
97
41
|
}
|
|
98
42
|
//#endregion
|
|
99
43
|
export { TempoWorkerChannel };
|
package/dist/worker-channel.mjs
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
import { Message } from "./bebop.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { ConsoleLogger,
|
|
5
|
-
import { BaseChannel, InsecureChannelCredential } from "@tempojs/client";
|
|
6
|
-
import { EventEmitter } from "tseep";
|
|
2
|
+
import { CoreChannel } from "./channel-core.mjs";
|
|
3
|
+
import { InsecureChannelCredential } from "@tempojs/client";
|
|
4
|
+
import { ConsoleLogger, TempoError, TempoStatusCode } from "@tempojs/common";
|
|
7
5
|
//#region src/worker-channel.ts
|
|
8
6
|
/**
|
|
9
7
|
* Represents a Tempo channel for communication with a worker thread.
|
|
10
8
|
*/
|
|
11
|
-
var TempoWorkerChannel = class TempoWorkerChannel extends
|
|
9
|
+
var TempoWorkerChannel = class TempoWorkerChannel extends CoreChannel {
|
|
12
10
|
static defaultMaxRetryAttempts = 5;
|
|
13
11
|
static defaultCredential = InsecureChannelCredential.create();
|
|
14
12
|
static defaultContentType = "bebop";
|
|
15
13
|
_worker;
|
|
16
|
-
events = new EventEmitter();
|
|
17
|
-
userAgent;
|
|
18
14
|
_open = false;
|
|
19
15
|
_ready;
|
|
20
16
|
_resolveReady;
|
|
21
17
|
_rejectReady;
|
|
22
|
-
pending = /* @__PURE__ */ new Map();
|
|
23
|
-
closed = false;
|
|
24
18
|
get worker() {
|
|
25
19
|
return this._worker;
|
|
26
20
|
}
|
|
@@ -37,7 +31,6 @@ var TempoWorkerChannel = class TempoWorkerChannel extends BaseChannel {
|
|
|
37
31
|
constructor(url, options = {}) {
|
|
38
32
|
super(new URL(url, "worker://"), options.logger ?? new ConsoleLogger("TempoWorkerChannel"), options.contentType ?? TempoWorkerChannel.defaultContentType);
|
|
39
33
|
this.logger.trace("creating new TempoWorkerChannel");
|
|
40
|
-
this.userAgent = TempoUtil.buildUserAgent("javascript", TempoVersion, void 0, { runtime: TempoUtil.getEnvironmentName() });
|
|
41
34
|
this._ready = new Promise((resolve, reject) => {
|
|
42
35
|
this._resolveReady = resolve;
|
|
43
36
|
this._rejectReady = reject;
|
|
@@ -81,338 +74,28 @@ var TempoWorkerChannel = class TempoWorkerChannel extends BaseChannel {
|
|
|
81
74
|
});
|
|
82
75
|
this.logger.debug(`created new TempoWorkerChannel`);
|
|
83
76
|
}
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
close
|
|
89
|
-
|
|
90
|
-
this.closed = true;
|
|
91
|
-
const err = reason ?? new TempoError(TempoStatusCode.CANCELLED, "channel disposed");
|
|
92
|
-
this._rejectReady?.(err);
|
|
93
|
-
for (const reject of this.pending.values()) reject(err);
|
|
94
|
-
this.pending.clear();
|
|
95
|
-
this.events.removeAllListeners();
|
|
77
|
+
/** Reject a pending `ready` before in-flight calls are settled. */
|
|
78
|
+
onClosing(reason) {
|
|
79
|
+
this._rejectReady?.(reason);
|
|
80
|
+
}
|
|
81
|
+
/** Terminate the owned worker on {@link CoreChannel.close}. */
|
|
82
|
+
closeTransport() {
|
|
96
83
|
try {
|
|
97
84
|
this._worker.terminate();
|
|
98
85
|
} catch {}
|
|
99
86
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Executes a function with retries according to the provided retry policy.
|
|
104
|
-
* The function will be retried if it fails with a TempoError and its status code is included in the retryableStatusCodes of the retry policy.
|
|
105
|
-
* 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.
|
|
106
|
-
*
|
|
107
|
-
* @template T - The type of the result returned by the function.
|
|
108
|
-
* @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.
|
|
109
|
-
* @param {RetryPolicy} retryPolicy - An object defining the retry policy, including maxAttempts, initialBackoff, maxBackoff, backoffMultiplier, and retryableStatusCodes.
|
|
110
|
-
* @param {Deadline} [deadline] - An optional deadline object that manages the timeout for each attempt.
|
|
111
|
-
* @param {AbortController} [abortController] - An optional AbortController instance to cancel the function execution.
|
|
112
|
-
* @returns {Promise<T>} - A Promise that resolves with the result of the function if it completes within the deadline and retry policy constraints.
|
|
113
|
-
* @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.
|
|
114
|
-
*/
|
|
115
|
-
async executeWithRetry(func, retryPolicy, deadline, abortController) {
|
|
116
|
-
let attempt = 0;
|
|
117
|
-
let lastError;
|
|
118
|
-
const execute = deadline ? (retryAttempt) => deadline.executeWithinDeadline(async () => await func(retryAttempt), abortController) : (retryAttempt) => func(retryAttempt);
|
|
119
|
-
while (attempt < retryPolicy.maxAttempts) try {
|
|
120
|
-
return await execute(attempt);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (!(error instanceof Error)) throw new TempoError(TempoStatusCode.UNKNOWN, `unexpected error`, { data: error });
|
|
123
|
-
lastError = error;
|
|
124
|
-
if (!(error instanceof TempoError) || !retryPolicy.retryableStatusCodes.includes(error.status)) throw error;
|
|
125
|
-
const backoffWithJitter = Math.min(retryPolicy.initialBackoff.multiply(Math.pow(retryPolicy.backoffMultiplier, attempt)).totalMilliseconds, retryPolicy.maxBackoff.totalMilliseconds) * (Math.random() * .5 + .75);
|
|
126
|
-
await new Promise((resolve) => setTimeout(resolve, backoffWithJitter));
|
|
127
|
-
attempt++;
|
|
128
|
-
}
|
|
129
|
-
if (abortController && lastError !== void 0 && !(lastError instanceof Error && lastError.name === "AbortError") && !(lastError instanceof TempoError && lastError.status === TempoStatusCode.ABORTED)) abortController.abort();
|
|
130
|
-
return Promise.reject(lastError || new TempoError(TempoStatusCode.DEADLINE_EXCEEDED, "Failed to execute function with retry policy"));
|
|
131
|
-
}
|
|
132
|
-
async fetchUnary(init, options) {
|
|
133
|
-
const messageId = init.messageId;
|
|
134
|
-
let listener;
|
|
135
|
-
let onAbort;
|
|
136
|
-
const cleanup = () => {
|
|
137
|
-
this.events.off(messageId, listener);
|
|
138
|
-
if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
|
|
139
|
-
this.pending.delete(messageId);
|
|
140
|
-
};
|
|
141
|
-
return await new Promise((resolve, reject) => {
|
|
142
|
-
listener = (message) => resolve(message);
|
|
143
|
-
this.pending.set(messageId, reject);
|
|
144
|
-
if (options?.controller) {
|
|
145
|
-
onAbort = () => reject(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
|
|
146
|
-
options.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
147
|
-
}
|
|
148
|
-
this.events.on(messageId, listener);
|
|
149
|
-
const frame = Message.encode(init).slice();
|
|
150
|
-
this.worker.postMessage(frame, [frame.buffer]);
|
|
151
|
-
}).finally(cleanup);
|
|
152
|
-
}
|
|
153
|
-
async fetchClientStream(init, method, generator, options) {
|
|
154
|
-
const messageId = init.messageId;
|
|
155
|
-
let listener;
|
|
156
|
-
let onAbort;
|
|
157
|
-
const cleanup = () => {
|
|
158
|
-
this.events.off(messageId, listener);
|
|
159
|
-
if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
|
|
160
|
-
this.pending.delete(messageId);
|
|
161
|
-
};
|
|
162
|
-
return await new Promise((resolve, reject) => {
|
|
163
|
-
listener = (message) => resolve(message);
|
|
164
|
-
this.pending.set(messageId, reject);
|
|
165
|
-
if (options?.controller) {
|
|
166
|
-
onAbort = () => reject(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
|
|
167
|
-
options.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
168
|
-
}
|
|
169
|
-
this.events.on(messageId, listener);
|
|
170
|
-
runGenerator.call(this).catch(reject);
|
|
171
|
-
async function runGenerator() {
|
|
172
|
-
for await (const value of generator()) {
|
|
173
|
-
const frame = Message.encode(Message({
|
|
174
|
-
...init,
|
|
175
|
-
data: new Uint8Array(value.encode())
|
|
176
|
-
})).slice();
|
|
177
|
-
this.worker.postMessage(frame, [frame.buffer]);
|
|
178
|
-
}
|
|
179
|
-
const frame = Message.encode(Message({
|
|
180
|
-
...init,
|
|
181
|
-
status: TempoStatusCode.CANCELLED,
|
|
182
|
-
data: new Uint8Array()
|
|
183
|
-
})).slice();
|
|
184
|
-
this.worker.postMessage(frame, [frame.buffer]);
|
|
185
|
-
}
|
|
186
|
-
}).finally(cleanup);
|
|
187
|
-
}
|
|
188
|
-
fetchServerStream(init, context, method, options) {
|
|
189
|
-
const messageId = init.messageId;
|
|
190
|
-
return createEventIterator(({ emit, cancel, error }) => {
|
|
191
|
-
let terminated = false;
|
|
192
|
-
let onAbort;
|
|
193
|
-
const eventHandler = async (message) => {
|
|
194
|
-
try {
|
|
195
|
-
if (message.status === TempoStatusCode.CANCELLED) {
|
|
196
|
-
terminated = true;
|
|
197
|
-
cancel();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
this.processResponseHeaders(message, context, method.type);
|
|
201
|
-
const requestData = message.data;
|
|
202
|
-
const record = method.deserialize(requestData);
|
|
203
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
204
|
-
emit(record);
|
|
205
|
-
} catch (e) {
|
|
206
|
-
error(e);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
if (options?.controller) {
|
|
210
|
-
onAbort = () => error(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
|
|
211
|
-
options.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
212
|
-
}
|
|
213
|
-
this.events.on(messageId, eventHandler);
|
|
214
|
-
const frame = Message.encode(init).slice();
|
|
215
|
-
this.worker.postMessage(frame, [frame.buffer]);
|
|
216
|
-
return () => {
|
|
217
|
-
this.events.off(messageId, eventHandler);
|
|
218
|
-
if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
|
|
219
|
-
if (terminated || this.closed) return;
|
|
220
|
-
const cancelMessage = {
|
|
221
|
-
messageId,
|
|
222
|
-
methodId: init.methodId,
|
|
223
|
-
status: TempoStatusCode.CANCELLED,
|
|
224
|
-
data: new Uint8Array(),
|
|
225
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
226
|
-
};
|
|
227
|
-
const cancelFrame = Message.encode(cancelMessage).slice();
|
|
228
|
-
this.worker.postMessage(cancelFrame, [cancelFrame.buffer]);
|
|
229
|
-
};
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
fetchDuplexStream(init, context, method, generator, options) {
|
|
233
|
-
const messageId = init.messageId;
|
|
234
|
-
return createDuplexIterator(generator(), (value) => {
|
|
235
|
-
const frame = Message.encode(Message({
|
|
236
|
-
...init,
|
|
237
|
-
data: new Uint8Array(method.serialize(value))
|
|
238
|
-
})).slice();
|
|
239
|
-
this.worker.postMessage(frame, [frame.buffer]);
|
|
240
|
-
}, ({ emit, cancel, error }) => {
|
|
241
|
-
let onAbort;
|
|
242
|
-
const eventHandler = async (message) => {
|
|
243
|
-
try {
|
|
244
|
-
if (message.status === TempoStatusCode.CANCELLED) {
|
|
245
|
-
cancel();
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
this.processResponseHeaders(message, context, method.type);
|
|
249
|
-
const requestData = message.data;
|
|
250
|
-
const record = method.deserialize(requestData);
|
|
251
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
252
|
-
emit(record);
|
|
253
|
-
} catch (e) {
|
|
254
|
-
error(e);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
if (options?.controller) {
|
|
258
|
-
onAbort = () => error(new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", {}));
|
|
259
|
-
options.controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
260
|
-
}
|
|
261
|
-
this.events.on(messageId, eventHandler);
|
|
262
|
-
return () => {
|
|
263
|
-
this.events.off(messageId, eventHandler);
|
|
264
|
-
if (onAbort) options?.controller?.signal.removeEventListener("abort", onAbort);
|
|
265
|
-
};
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Creates a `RequestInit` object for a given payload, context, method and optional call options.
|
|
270
|
-
* This object can be used to make an HTTP request using the Fetch API.
|
|
271
|
-
*
|
|
272
|
-
* @private
|
|
273
|
-
* @param {Uint8Array} payload - The payload to be sent in the request.
|
|
274
|
-
* @param {ClientContext} context - The context of the client making the request.
|
|
275
|
-
* @param {MethodInfo<BebopRecord, BebopRecord>} method - Information about the method being called.
|
|
276
|
-
* @param {CallOptions | undefined} options - Optional configuration for the call.
|
|
277
|
-
* @returns {Promise<RequestInit>} A Promise resolving to the created `RequestInit` object.
|
|
278
|
-
* @throws {TempoError} Throws an error if there's a problem while getting the credential header.
|
|
279
|
-
*/
|
|
280
|
-
createRequest(payload, _context, method, options) {
|
|
281
|
-
const customMetadata = new Metadata();
|
|
282
|
-
customMetadata.set("path", `/${method.service}/${method.name}`);
|
|
283
|
-
customMetadata.set("service-name", method.service);
|
|
284
|
-
if (ExecutionEnvironment.isBrowser || ExecutionEnvironment.isWebWorker) customMetadata.set("x-user-agent", this.userAgent);
|
|
285
|
-
else customMetadata.set("user-agent", this.userAgent);
|
|
286
|
-
const requestInit = {
|
|
287
|
-
messageId: Bun.randomUUIDv7(),
|
|
288
|
-
methodId: method.id,
|
|
289
|
-
data: new Uint8Array(payload),
|
|
290
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
291
|
-
};
|
|
292
|
-
if (options?.deadline) requestInit.deadline = new Date(options.deadline.toUnixTimestamp());
|
|
293
|
-
return requestInit;
|
|
87
|
+
generateMessageId() {
|
|
88
|
+
return Bun.randomUUIDv7();
|
|
294
89
|
}
|
|
295
90
|
/**
|
|
296
|
-
*
|
|
297
|
-
* Also sets the incoming metadata from the response headers to the provided context.
|
|
91
|
+
* {@inheritDoc CoreChannel.sendFrame}
|
|
298
92
|
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* @param {ClientContext} context - The context of the client making the request.
|
|
302
|
-
* @param {MethodType} methodType - The type of method being called.
|
|
303
|
-
* @throws {TempoError} Throws an error if any validation checks fail or if there's a problem parsing or storing credentials.
|
|
304
|
-
*/
|
|
305
|
-
processResponseHeaders(response, _context, _methodType) {
|
|
306
|
-
const statusCode = response.status;
|
|
307
|
-
if (statusCode === void 0) throw new TempoError(TempoStatusCode.UNKNOWN, "tempo-status missing from response.");
|
|
308
|
-
if (statusCode !== TempoStatusCode.OK && statusCode !== TempoStatusCode.CANCELLED) {
|
|
309
|
-
let tempoMessage = response.msg;
|
|
310
|
-
if (!tempoMessage) tempoMessage = "unknown error";
|
|
311
|
-
throw new TempoError(statusCode, tempoMessage);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* {@inheritDoc BaseChannel.startUnary}
|
|
316
|
-
*/
|
|
317
|
-
async startUnary(request, context, method, options) {
|
|
318
|
-
try {
|
|
319
|
-
const requestData = request.encode();
|
|
320
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
321
|
-
const requestInit = this.createRequest(requestData, context, method, options);
|
|
322
|
-
let response;
|
|
323
|
-
if (options?.retryPolicy) response = await this.executeWithRetry(async (retryAttempt) => {
|
|
324
|
-
if (retryAttempt > 0) requestInit.previousAttempts = retryAttempt;
|
|
325
|
-
return await this.fetchUnary(requestInit);
|
|
326
|
-
}, options.retryPolicy, options.deadline, options.controller);
|
|
327
|
-
else if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
|
|
328
|
-
return await this.fetchUnary(requestInit, options);
|
|
329
|
-
}, options.controller);
|
|
330
|
-
else response = await this.fetchUnary(requestInit, options);
|
|
331
|
-
this.processResponseHeaders(response, context, method.type);
|
|
332
|
-
if (this.hooks !== void 0) await this.hooks.executeResponseHooks(context);
|
|
333
|
-
const responseData = response.data;
|
|
334
|
-
const record = method.deserialize(responseData);
|
|
335
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
336
|
-
return record;
|
|
337
|
-
} catch (e) {
|
|
338
|
-
if (this.hooks !== void 0 && e instanceof Error) await this.hooks.executeErrorHooks(context, e);
|
|
339
|
-
if (e instanceof TempoError) throw e;
|
|
340
|
-
if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
|
|
341
|
-
else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
|
|
342
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* {@inheritDoc BaseChannel.startClientStream}
|
|
347
|
-
*/
|
|
348
|
-
async startClientStream(generator, context, method, options) {
|
|
349
|
-
try {
|
|
350
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
351
|
-
const requestInit = this.createRequest(new Uint8Array(), context, method, options);
|
|
352
|
-
let response;
|
|
353
|
-
if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
|
|
354
|
-
return await this.fetchClientStream(requestInit, method, generator, options);
|
|
355
|
-
}, options.controller);
|
|
356
|
-
else response = await this.fetchClientStream(requestInit, method, generator, options);
|
|
357
|
-
this.processResponseHeaders(response, context, method.type);
|
|
358
|
-
const responseData = response.data;
|
|
359
|
-
const record = this.deserializeResponse(responseData, method);
|
|
360
|
-
if (this.hooks !== void 0) await this.hooks.executeDecodeHooks(context, record);
|
|
361
|
-
return record;
|
|
362
|
-
} catch (e) {
|
|
363
|
-
if (this.hooks !== void 0 && e instanceof Error) await this.hooks.executeErrorHooks(context, e);
|
|
364
|
-
if (e instanceof TempoError) throw e;
|
|
365
|
-
if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
|
|
366
|
-
else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
|
|
367
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* {@inheritDoc BaseChannel.startServerStream}
|
|
93
|
+
* `.slice()` owns a fresh ArrayBuffer; transferring it cannot detach bebop's
|
|
94
|
+
* process-wide singleton write buffer.
|
|
372
95
|
*/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
377
|
-
const requestInit = this.createRequest(requestData, context, method, options);
|
|
378
|
-
let response;
|
|
379
|
-
if (options?.retryPolicy) response = await this.executeWithRetry(async (retryAttempt) => {
|
|
380
|
-
if (retryAttempt > 0) requestInit.previousAttempts = retryAttempt;
|
|
381
|
-
return this.fetchServerStream(requestInit, context, method, options);
|
|
382
|
-
}, options.retryPolicy, options.deadline, options.controller);
|
|
383
|
-
else if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
|
|
384
|
-
return this.fetchServerStream(requestInit, context, method, options);
|
|
385
|
-
}, options.controller);
|
|
386
|
-
else response = this.fetchServerStream(requestInit, context, method, options);
|
|
387
|
-
return response;
|
|
388
|
-
} catch (e) {
|
|
389
|
-
if (this.hooks !== void 0 && e instanceof Error) await this.hooks.executeErrorHooks(context, e);
|
|
390
|
-
if (e instanceof TempoError) throw e;
|
|
391
|
-
if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
|
|
392
|
-
else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
|
|
393
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* {@inheritDoc BaseChannel.startDuplexStream}
|
|
398
|
-
*/
|
|
399
|
-
async startDuplexStream(generator, context, method, options) {
|
|
400
|
-
try {
|
|
401
|
-
if (this.hooks !== void 0) await this.hooks.executeRequestHooks(context);
|
|
402
|
-
const requestInit = this.createRequest(new Uint8Array(), context, method, options);
|
|
403
|
-
let response;
|
|
404
|
-
if (options?.deadline) response = await options.deadline.executeWithinDeadline(async () => {
|
|
405
|
-
return this.fetchDuplexStream(requestInit, context, method, generator, options);
|
|
406
|
-
}, options.controller);
|
|
407
|
-
else response = this.fetchDuplexStream(requestInit, context, method, generator, options);
|
|
408
|
-
return response;
|
|
409
|
-
} catch (e) {
|
|
410
|
-
if (this.hooks !== void 0 && e instanceof Error) await this.hooks.executeErrorHooks(context, e);
|
|
411
|
-
if (e instanceof TempoError) throw e;
|
|
412
|
-
if (e instanceof Error) if (e.name === "AbortError") throw new TempoError(TempoStatusCode.ABORTED, "RPC fetch aborted", e);
|
|
413
|
-
else throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", e);
|
|
414
|
-
throw new TempoError(TempoStatusCode.UNKNOWN, "an unknown error occurred", { data: e });
|
|
415
|
-
}
|
|
96
|
+
sendFrame(message) {
|
|
97
|
+
const frame = Message.encode(message).slice();
|
|
98
|
+
this._worker.postMessage(frame, [frame.buffer]);
|
|
416
99
|
}
|
|
417
100
|
};
|
|
418
101
|
//#endregion
|
package/dist/worker-router.d.mts
CHANGED
|
@@ -4,15 +4,9 @@ import { AuthInterceptor, BaseRouter, ServiceRegistry, TempoRouterConfiguration
|
|
|
4
4
|
|
|
5
5
|
//#region src/worker-router.d.ts
|
|
6
6
|
declare class TempoWorkerRouter<TEnv> extends BaseRouter<Buffer, TEnv, Message> {
|
|
7
|
-
private readonly
|
|
8
|
-
private readonly clientStreams;
|
|
7
|
+
private readonly core;
|
|
9
8
|
private readonly serverStreams;
|
|
10
9
|
constructor(logger: TempoLogger, registry: ServiceRegistry, configuration?: TempoRouterConfiguration, authInterceptor?: AuthInterceptor);
|
|
11
|
-
private setAuthContext;
|
|
12
|
-
private invokeUnaryMethod;
|
|
13
|
-
private invokeClientStreamMethod;
|
|
14
|
-
private invokeServerStreamMethod;
|
|
15
|
-
private invokeDuplexStreamMethod;
|
|
16
10
|
/**
|
|
17
11
|
* Tear down all open streams for this connection (driven by the host
|
|
18
12
|
* close/error lifecycle). Returns each server-stream generator and emits a
|