@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,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 };