@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.
- package/LICENSE +21 -0
- package/README.md +467 -0
- package/dist/async-queue.d.mts +68 -0
- package/dist/async-queue.mjs +156 -0
- package/dist/bebop.d.mts +37 -0
- package/dist/bebop.mjs +434 -0
- package/dist/context-logger.d.mts +11 -0
- package/dist/context-logger.mjs +31 -0
- package/dist/create-duplex-iterator.d.mts +16 -0
- package/dist/create-duplex-iterator.mjs +36 -0
- package/dist/create-event-iterator.d.mts +39 -0
- package/dist/create-event-iterator.mjs +67 -0
- package/dist/debug-logger.d.mts +43 -0
- package/dist/debug-logger.mjs +122 -0
- package/dist/error.d.mts +27 -0
- package/dist/error.mjs +49 -0
- package/dist/extension-channel.d.mts +96 -0
- package/dist/extension-channel.mjs +377 -0
- package/dist/extension-router.d.mts +154 -0
- package/dist/extension-router.mjs +375 -0
- package/dist/memory-storage.d.mts +12 -0
- package/dist/memory-storage.mjs +18 -0
- package/dist/pino-logger.d.mts +44 -0
- package/dist/pino-logger.mjs +127 -0
- package/dist/sync-storage.d.mts +11 -0
- package/dist/sync-storage.mjs +18 -0
- package/dist/worker-channel.d.mts +99 -0
- package/dist/worker-channel.mjs +419 -0
- package/dist/worker-router.d.mts +26 -0
- package/dist/worker-router.mjs +254 -0
- package/dist/ws-channel.d.mts +108 -0
- package/dist/ws-channel.mjs +520 -0
- package/dist/ws-router.d.mts +52 -0
- package/dist/ws-router.mjs +278 -0
- package/package.json +73 -0
- package/schema/error.bop +7 -0
- package/schema/index.bop +2 -0
- package/schema/message.bop +12 -0
- package/schema/pool.bop +16 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { Message } from "./bebop.mjs";
|
|
2
|
+
import { createEventIterator } from "./create-event-iterator.mjs";
|
|
3
|
+
import { Deadline, Metadata, MethodType, TempoError, TempoStatusCode, TempoUtil, TempoVersion, stringifyCredential } from "@tempojs/common";
|
|
4
|
+
import { EventEmitter } from "tseep";
|
|
5
|
+
import { tabs } from "webextension-polyfill";
|
|
6
|
+
import { ServerContext, TempoRouterConfiguration } from "@tempojs/server";
|
|
7
|
+
//#region src/extension-router.ts
|
|
8
|
+
/**
|
|
9
|
+
* Interface defining the configuration options for a TempoRouter instance.
|
|
10
|
+
*/
|
|
11
|
+
var TempoExtensionRouterConfiguration = class {
|
|
12
|
+
static defaultMaxRetryAttempts = 5;
|
|
13
|
+
static defaultMaxReceiveMessageSize = 1024 * 1024 * 4;
|
|
14
|
+
static defaultMaxSendMessageSize = 1024 * 1024 * 4;
|
|
15
|
+
/**
|
|
16
|
+
* Optional flag to enable CORS (Cross-Origin Resource Sharing) support.
|
|
17
|
+
*/
|
|
18
|
+
enableCors;
|
|
19
|
+
/**
|
|
20
|
+
* Optional list of allowed origins for CORS. Ignored if enableCors is false.
|
|
21
|
+
*/
|
|
22
|
+
allowedOrigins;
|
|
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;
|
|
29
|
+
/**
|
|
30
|
+
* The maximum size of the message that can be received. Defaults to the value in `TempoRouterConfiguration.defaultMaxReceiveMessageSize`.
|
|
31
|
+
*/
|
|
32
|
+
maxReceiveMessageSize;
|
|
33
|
+
/**
|
|
34
|
+
* The maximum size of the message that can be sent.
|
|
35
|
+
*/
|
|
36
|
+
maxSendMessageSize;
|
|
37
|
+
/**
|
|
38
|
+
* The maximum number of retry attempts for failed requests. Defaults to the value in `TempoRouterConfiguration.defaultMaxRetryAttempts`.
|
|
39
|
+
*/
|
|
40
|
+
maxRetryAttempts;
|
|
41
|
+
/**
|
|
42
|
+
* Constructs a new instance of TempoRouterConfiguration with default values.
|
|
43
|
+
*/
|
|
44
|
+
constructor() {
|
|
45
|
+
this.maxReceiveMessageSize = TempoRouterConfiguration.defaultMaxReceiveMessageSize;
|
|
46
|
+
this.maxSendMessageSize = TempoRouterConfiguration.defaultMaxSendMessageSize;
|
|
47
|
+
this.maxRetryAttempts = TempoRouterConfiguration.defaultMaxRetryAttempts;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Represents an abstract base class for a router that handles incoming requests, performs validation, and routes
|
|
52
|
+
* them to the appropriate services and methods in a Tempo application.
|
|
53
|
+
*
|
|
54
|
+
* @template TRequest - The type of the request object.
|
|
55
|
+
* @template TEnvironment - The type of the environment/context object.
|
|
56
|
+
* @template TResponse - The type of the response object.
|
|
57
|
+
*/
|
|
58
|
+
var ExtensionBaseRouter = class {
|
|
59
|
+
logger;
|
|
60
|
+
registry;
|
|
61
|
+
configuration;
|
|
62
|
+
authInterceptor;
|
|
63
|
+
corsEnabled;
|
|
64
|
+
allowedCorsOrigins;
|
|
65
|
+
transmitInternalErrors;
|
|
66
|
+
maxReceiveMessageSize;
|
|
67
|
+
maxSendMessageSize;
|
|
68
|
+
maxRetryAttempts;
|
|
69
|
+
exposeTempo;
|
|
70
|
+
hooks;
|
|
71
|
+
poweredByHeader = "x-powered-by";
|
|
72
|
+
poweredByHeaderValue;
|
|
73
|
+
/**
|
|
74
|
+
* Constructs a new BaseRouter instance.
|
|
75
|
+
* @param logger - The logger to use for logging router-related information.
|
|
76
|
+
* @param registry - The service registry instance that manages services and methods for this router.
|
|
77
|
+
* @param authInterceptor - The interceptor (if any) that will be used for authenticating the peer of an incoming requests.
|
|
78
|
+
*/
|
|
79
|
+
constructor(logger, registry, configuration, authInterceptor) {
|
|
80
|
+
this.logger = logger;
|
|
81
|
+
this.registry = registry;
|
|
82
|
+
this.configuration = configuration;
|
|
83
|
+
this.authInterceptor = authInterceptor;
|
|
84
|
+
this.corsEnabled = configuration.enableCors ?? false;
|
|
85
|
+
this.allowedCorsOrigins = configuration.allowedOrigins;
|
|
86
|
+
this.transmitInternalErrors = configuration.transmitInternalErrors ?? false;
|
|
87
|
+
this.maxReceiveMessageSize = configuration.maxReceiveMessageSize ?? TempoRouterConfiguration.defaultMaxReceiveMessageSize;
|
|
88
|
+
if (configuration.maxSendMessageSize !== void 0) this.maxSendMessageSize = configuration.maxSendMessageSize;
|
|
89
|
+
this.maxRetryAttempts = configuration.maxRetryAttempts ?? TempoRouterConfiguration.defaultMaxRetryAttempts;
|
|
90
|
+
this.exposeTempo = configuration.exposeTempo ?? false;
|
|
91
|
+
this.registry.init();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Private function that retrieves custom metadata from the header.
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
97
|
+
* @function
|
|
98
|
+
* @param {string | null} value The custom metadata value from the request.
|
|
99
|
+
* @returns {Metadata} The metadata object.
|
|
100
|
+
*/
|
|
101
|
+
getCustomMetaData(value) {
|
|
102
|
+
if (!value) return new Metadata();
|
|
103
|
+
return Metadata.fromHttpHeader(value);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Defines a hook registry for the router.
|
|
107
|
+
* @param hooks - The hook registry to be used.
|
|
108
|
+
*/
|
|
109
|
+
useHooks(hooks) {
|
|
110
|
+
this.hooks = hooks;
|
|
111
|
+
}
|
|
112
|
+
definePoweredByHeader(variant) {
|
|
113
|
+
this.poweredByHeaderValue = TempoUtil.buildUserAgent("javascript", TempoVersion, variant, { runtime: TempoUtil.getEnvironmentName() });
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Deserializes an incoming request based on its content type.
|
|
117
|
+
*
|
|
118
|
+
* @param requestData - The incoming request data as a Uint8Array.
|
|
119
|
+
* @param method - The Bebop method to use for deserialization.
|
|
120
|
+
* @param contentType - The content type of the incoming request.
|
|
121
|
+
* @returns The deserialized request record.
|
|
122
|
+
* @throws {TempoError} When the content type is not valid.
|
|
123
|
+
* @throws {BebopRuntimeError} When the request data cannot be deserialized.
|
|
124
|
+
*/
|
|
125
|
+
deserializeRequest(requestData, method, contentType) {
|
|
126
|
+
switch (contentType) {
|
|
127
|
+
case "bebop": return method.deserialize(requestData);
|
|
128
|
+
default: throw new TempoError(TempoStatusCode.UNKNOWN_CONTENT_TYPE, `invalid request content type: ${contentType}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Serializes a response record based on its content type.
|
|
133
|
+
*
|
|
134
|
+
* @param response - The response record to be serialized.
|
|
135
|
+
* @param method - The Bebop method to use for serialization.
|
|
136
|
+
* @param contentType - The content type of the response.
|
|
137
|
+
* @returns The serialized response as a Uint8Array.
|
|
138
|
+
* @throws {TempoError} When the content type is not valid.
|
|
139
|
+
* @throws {BebopRuntimeError} When the response record cannot be serialized.
|
|
140
|
+
*/
|
|
141
|
+
serializeResponse(response, method, contentType) {
|
|
142
|
+
switch (contentType) {
|
|
143
|
+
case "bebop": return method.serialize(response);
|
|
144
|
+
default: throw new TempoError(TempoStatusCode.UNKNOWN_CONTENT_TYPE, `invalid response content type: ${contentType}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var TempoExtensionRouter = class extends ExtensionBaseRouter {
|
|
149
|
+
events = new EventEmitter();
|
|
150
|
+
clientStreams = /* @__PURE__ */ new Map();
|
|
151
|
+
constructor(logger, registry, configuration = new TempoRouterConfiguration(), authInterceptor) {
|
|
152
|
+
super(logger, registry, configuration, authInterceptor);
|
|
153
|
+
}
|
|
154
|
+
async setAuthContext(request, context) {
|
|
155
|
+
const authHeader = request.authorization;
|
|
156
|
+
if (authHeader !== void 0 && this.authInterceptor !== void 0) {
|
|
157
|
+
const authContext = await this.authInterceptor.intercept(context, authHeader);
|
|
158
|
+
if (authContext !== void 0) context.authContext = authContext;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async invokeUnaryMethod(request, context, method) {
|
|
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);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
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
|
+
/**
|
|
249
|
+
* Tear down all open streams for this connection. Emits a synthetic CANCELLED
|
|
250
|
+
* to each in-flight client/duplex stream so its generator ends, then clears
|
|
251
|
+
* all listeners.
|
|
252
|
+
*/
|
|
253
|
+
async closeConnection() {
|
|
254
|
+
for (const id of [...this.clientStreams.keys()]) {
|
|
255
|
+
this.events.emit(id, Message({
|
|
256
|
+
messageId: id,
|
|
257
|
+
status: TempoStatusCode.CANCELLED,
|
|
258
|
+
data: new Uint8Array()
|
|
259
|
+
}));
|
|
260
|
+
this.clientStreams.delete(id);
|
|
261
|
+
}
|
|
262
|
+
this.events.removeAllListeners();
|
|
263
|
+
}
|
|
264
|
+
async process(req, response, ctx) {
|
|
265
|
+
let request = Message(req);
|
|
266
|
+
let context;
|
|
267
|
+
try {
|
|
268
|
+
const methodId = request.methodId;
|
|
269
|
+
const method = this.registry.getMethod(methodId);
|
|
270
|
+
if (!method) throw new TempoError(TempoStatusCode.NOT_FOUND, `no service is registered which contains a method of '${methodId}'`);
|
|
271
|
+
const metadataHeader = request.customMetadata;
|
|
272
|
+
const metadata = metadataHeader ? Metadata.fromHttpHeader(metadataHeader) : new Metadata();
|
|
273
|
+
const previousAttempts = request.previousAttempts;
|
|
274
|
+
if (previousAttempts !== void 0 && previousAttempts > this.maxRetryAttempts) throw new TempoError(TempoStatusCode.RESOURCE_EXHAUSTED, "max retry attempts exceeded");
|
|
275
|
+
let deadline;
|
|
276
|
+
const deadlineHeader = request.deadline;
|
|
277
|
+
if (deadlineHeader !== void 0) deadline = Deadline.fromUnixTimestamp(deadlineHeader.valueOf());
|
|
278
|
+
if (deadline !== void 0 && deadline.isExpired()) throw new TempoError(TempoStatusCode.DEADLINE_EXCEEDED, "incoming request has already exceeded its deadline");
|
|
279
|
+
const outgoingMetadata = new Metadata();
|
|
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
|
+
}
|
|
358
|
+
}
|
|
359
|
+
async send(sender, message) {
|
|
360
|
+
if (!sender.tab?.id || sender.tab?.discarded === true) {
|
|
361
|
+
this.logger.warn(`ignoring message to discarded tab`, { tab: sender.tab });
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
await tabs.sendMessage(sender.tab.id, Array.apply(null, Message.encode(message)));
|
|
366
|
+
} catch (e) {
|
|
367
|
+
this.logger.error("failed to send message, tab is likely inactive", {}, e);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async handle(_req, _ctx) {
|
|
371
|
+
return Message({});
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
//#endregion
|
|
375
|
+
export { ExtensionBaseRouter, TempoExtensionRouter, TempoExtensionRouterConfiguration };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Credential } from "@tempojs/common";
|
|
2
|
+
import { CredentialStorage } from "@tempojs/client";
|
|
3
|
+
|
|
4
|
+
//#region src/memory-storage.d.ts
|
|
5
|
+
declare class MemoryStorageStrategy implements CredentialStorage {
|
|
6
|
+
storage: Map<string, string>;
|
|
7
|
+
getCredential(key: string): Promise<Credential | undefined>;
|
|
8
|
+
storeCredential(key: string, _credential: Credential): Promise<void>;
|
|
9
|
+
removeCredential(key: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { MemoryStorageStrategy };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { parseCredential, stringifyCredential } from "@tempojs/common";
|
|
2
|
+
//#region src/memory-storage.ts
|
|
3
|
+
var MemoryStorageStrategy = class {
|
|
4
|
+
storage = /* @__PURE__ */ new Map();
|
|
5
|
+
async getCredential(key) {
|
|
6
|
+
let credentials = this.storage.get(key);
|
|
7
|
+
if (!credentials) return void 0;
|
|
8
|
+
return parseCredential(credentials);
|
|
9
|
+
}
|
|
10
|
+
async storeCredential(key, _credential) {
|
|
11
|
+
this.storage.set(key, stringifyCredential(_credential));
|
|
12
|
+
}
|
|
13
|
+
async removeCredential(key) {
|
|
14
|
+
this.storage.delete(key);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { MemoryStorageStrategy };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ContextLogger } from "./context-logger.mjs";
|
|
2
|
+
import { ConsoleLogger, TempoLogLevel, TempoLogger } from "@tempojs/common";
|
|
3
|
+
import pino from "pino";
|
|
4
|
+
|
|
5
|
+
//#region src/pino-logger.d.ts
|
|
6
|
+
declare class PinoLogger extends ContextLogger implements ConsoleLogger {
|
|
7
|
+
logger: pino.Logger;
|
|
8
|
+
sourceName: string;
|
|
9
|
+
logLevel: TempoLogLevel;
|
|
10
|
+
children: Set<TempoLogger>;
|
|
11
|
+
constructor(sourceName: string, logLevel?: TempoLogLevel, parent?: pino.Logger, bindings?: Record<string, unknown>);
|
|
12
|
+
static getPinoLevel(level: string): string;
|
|
13
|
+
clone<TLogger extends TempoLogger>(sourceName: string, asOrphan?: boolean, bindings?: Record<string, unknown>): TLogger;
|
|
14
|
+
write(level: TempoLogLevel, message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
15
|
+
/**
|
|
16
|
+
* @inheritDoc
|
|
17
|
+
*/
|
|
18
|
+
trace(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
19
|
+
/**
|
|
20
|
+
* @inheritDoc
|
|
21
|
+
*/
|
|
22
|
+
debug(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
23
|
+
/**
|
|
24
|
+
* @inheritDoc
|
|
25
|
+
*/
|
|
26
|
+
info(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
27
|
+
/**
|
|
28
|
+
* @inheritDoc
|
|
29
|
+
*/
|
|
30
|
+
warn(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
31
|
+
/**
|
|
32
|
+
* @inheritDoc
|
|
33
|
+
*/
|
|
34
|
+
error(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
35
|
+
/**
|
|
36
|
+
* @inheritDoc
|
|
37
|
+
*/
|
|
38
|
+
critical(message: string, data?: Record<string, unknown>, error?: Error): void;
|
|
39
|
+
setLogLevel(level: TempoLogLevel): void;
|
|
40
|
+
private tempoPinoLevelMap;
|
|
41
|
+
formatErrorMessage(error: Error, indentLevel?: number): string;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { PinoLogger };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ContextLogger } from "./context-logger.mjs";
|
|
2
|
+
import { TempoLogLevel } from "@tempojs/common";
|
|
3
|
+
import pino from "pino";
|
|
4
|
+
//#region src/pino-logger.ts
|
|
5
|
+
var PinoLogger = class PinoLogger extends ContextLogger {
|
|
6
|
+
logger;
|
|
7
|
+
sourceName;
|
|
8
|
+
logLevel;
|
|
9
|
+
children = /* @__PURE__ */ new Set();
|
|
10
|
+
constructor(sourceName, logLevel = TempoLogLevel.Info, parent = pino(), bindings) {
|
|
11
|
+
super(sourceName, logLevel, void 0, bindings);
|
|
12
|
+
this.sourceName = sourceName;
|
|
13
|
+
this.logLevel = logLevel;
|
|
14
|
+
this.logger = parent.child({
|
|
15
|
+
source: sourceName,
|
|
16
|
+
...bindings
|
|
17
|
+
});
|
|
18
|
+
this.logger.level = PinoLogger.getPinoLevel(String(logLevel));
|
|
19
|
+
}
|
|
20
|
+
static getPinoLevel(level) {
|
|
21
|
+
switch (level) {
|
|
22
|
+
case "0": return "trace";
|
|
23
|
+
case "1": return "debug";
|
|
24
|
+
case "2": return "info";
|
|
25
|
+
case "3": return "warn";
|
|
26
|
+
case "4":
|
|
27
|
+
case "5": return "error";
|
|
28
|
+
default: return "info";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
clone(sourceName, asOrphan, bindings) {
|
|
32
|
+
return new PinoLogger(sourceName, this.logLevel, asOrphan ? void 0 : this.logger, bindings);
|
|
33
|
+
}
|
|
34
|
+
write(level, message, data = {}, error) {
|
|
35
|
+
if (level < this.logLevel) return;
|
|
36
|
+
let logError = "";
|
|
37
|
+
if (error) {
|
|
38
|
+
logError = `\n${this.formatErrorMessage(error)}`;
|
|
39
|
+
data.error = logError;
|
|
40
|
+
}
|
|
41
|
+
switch (level) {
|
|
42
|
+
case TempoLogLevel.Trace:
|
|
43
|
+
this.logger.trace(data, message);
|
|
44
|
+
break;
|
|
45
|
+
case TempoLogLevel.Debug:
|
|
46
|
+
this.logger.debug(data, message);
|
|
47
|
+
break;
|
|
48
|
+
case TempoLogLevel.Info:
|
|
49
|
+
this.logger.info(data, message);
|
|
50
|
+
break;
|
|
51
|
+
case TempoLogLevel.Warn:
|
|
52
|
+
this.logger.warn(data, message);
|
|
53
|
+
break;
|
|
54
|
+
case TempoLogLevel.Error:
|
|
55
|
+
this.logger.error(data, message);
|
|
56
|
+
break;
|
|
57
|
+
case TempoLogLevel.Critical:
|
|
58
|
+
this.logger.error(data, message);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @inheritDoc
|
|
64
|
+
*/
|
|
65
|
+
trace(message, data, error) {
|
|
66
|
+
this.write(0, message, data, error);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @inheritDoc
|
|
70
|
+
*/
|
|
71
|
+
debug(message, data, error) {
|
|
72
|
+
this.write(1, message, data, error);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @inheritDoc
|
|
76
|
+
*/
|
|
77
|
+
info(message, data, error) {
|
|
78
|
+
this.write(2, message, data, error);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* @inheritDoc
|
|
82
|
+
*/
|
|
83
|
+
warn(message, data, error) {
|
|
84
|
+
this.write(3, message, data, error);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @inheritDoc
|
|
88
|
+
*/
|
|
89
|
+
error(message, data, error) {
|
|
90
|
+
this.write(4, message, data, error);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* @inheritDoc
|
|
94
|
+
*/
|
|
95
|
+
critical(message, data, error) {
|
|
96
|
+
this.write(5, message, data, error);
|
|
97
|
+
}
|
|
98
|
+
setLogLevel(level) {
|
|
99
|
+
this.logLevel = level;
|
|
100
|
+
const pinoLevel = this.tempoPinoLevelMap(level);
|
|
101
|
+
this.logger.level = pinoLevel;
|
|
102
|
+
}
|
|
103
|
+
tempoPinoLevelMap(level) {
|
|
104
|
+
switch (level) {
|
|
105
|
+
case TempoLogLevel.Trace: return "trace";
|
|
106
|
+
case TempoLogLevel.Debug: return "debug";
|
|
107
|
+
case TempoLogLevel.Info: return "info";
|
|
108
|
+
case TempoLogLevel.Warn: return "warn";
|
|
109
|
+
case TempoLogLevel.Error: return "error";
|
|
110
|
+
case TempoLogLevel.Critical: return "fatal";
|
|
111
|
+
default: return "info";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
formatErrorMessage(error, indentLevel = 0) {
|
|
115
|
+
const indent = " ".repeat(indentLevel);
|
|
116
|
+
const message = error.message || "Unknown error";
|
|
117
|
+
const indentedStack = (error.stack || "Stack unavailable").split("\n").map((line) => `${indent}${line}`).join("\n");
|
|
118
|
+
let cause = "";
|
|
119
|
+
if ("cause" in error) cause = error?.cause instanceof Error ? this.formatErrorMessage(error.cause, indentLevel + 1) : "";
|
|
120
|
+
return `
|
|
121
|
+
${indent}Error message: ${message}
|
|
122
|
+
${indentedStack}
|
|
123
|
+
${indent}${cause ? `Cause: ${cause}` : ""}`.trimEnd();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
//#endregion
|
|
127
|
+
export { PinoLogger };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Credential } from "@tempojs/common";
|
|
2
|
+
import { CredentialStorage } from "@tempojs/client";
|
|
3
|
+
|
|
4
|
+
//#region src/sync-storage.d.ts
|
|
5
|
+
declare class SyncStorageStrategy implements CredentialStorage {
|
|
6
|
+
getCredential(key: string): Promise<Credential | undefined>;
|
|
7
|
+
storeCredential(key: string, _credential: Credential): Promise<void>;
|
|
8
|
+
removeCredential(key: string): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { SyncStorageStrategy };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { parseCredential, stringifyCredential } from "@tempojs/common";
|
|
2
|
+
import { storage } from "webextension-polyfill";
|
|
3
|
+
//#region src/sync-storage.ts
|
|
4
|
+
var SyncStorageStrategy = class {
|
|
5
|
+
async getCredential(key) {
|
|
6
|
+
let credentials = await storage.sync.get(key);
|
|
7
|
+
if (!credentials[key]) return void 0;
|
|
8
|
+
return parseCredential(credentials[key]);
|
|
9
|
+
}
|
|
10
|
+
async storeCredential(key, _credential) {
|
|
11
|
+
await storage.sync.set({ [key]: stringifyCredential(_credential) });
|
|
12
|
+
}
|
|
13
|
+
async removeCredential(key) {
|
|
14
|
+
await storage.sync.remove(key);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { SyncStorageStrategy };
|