@richardmcquiston01/error-log-adapter 0.4.0 → 0.6.0
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 +168 -13
- package/dist/index.cjs +837 -239
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +317 -22
- package/dist/index.d.ts +317 -22
- package/dist/index.mjs +835 -239
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,17 @@ interface ErrorLogEntry<TCode extends string = string> {
|
|
|
10
10
|
source?: string;
|
|
11
11
|
/** ISO 8601 timestamp. Adapters fill this in when it is omitted. */
|
|
12
12
|
timestamp?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional UUID that identifies this entry. Adapters generate a new one for
|
|
15
|
+
* every `log` call when it is omitted, so logging the same entry twice stores
|
|
16
|
+
* it twice. Supply the same `id` (and, for S3, the same `timestamp`) to make
|
|
17
|
+
* a retry safe: sinks that can detect a repeat overwrite or skip the existing
|
|
18
|
+
* record instead of adding another. It must be a UUID, for example from
|
|
19
|
+
* `crypto.randomUUID()`; anything else makes `log` return `ok: false`.
|
|
20
|
+
* Redis, the flat file and NDJSON file adapters, and the console cannot
|
|
21
|
+
* detect a repeat.
|
|
22
|
+
*/
|
|
23
|
+
id?: string;
|
|
13
24
|
}
|
|
14
25
|
/** Outcome of a `log` call. Callers should check `ok`. */
|
|
15
26
|
interface ErrorLogResult {
|
|
@@ -34,8 +45,16 @@ interface SerializedErrorLogEntry {
|
|
|
34
45
|
timestamp: string;
|
|
35
46
|
}
|
|
36
47
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
48
|
+
* Returns a copy of `cause` that `JSON.stringify` can always handle, so a
|
|
49
|
+
* strange cause can never stop an entry from being stored.
|
|
50
|
+
*
|
|
51
|
+
* - `Error` objects (including ones from another realm) become
|
|
52
|
+
* `{ name, message, stack }`, plus `cause` and, for an `AggregateError`,
|
|
53
|
+
* `errors`, which are converted the same way.
|
|
54
|
+
* - Circular references become `"[Circular]"`, and anything nested deeper than
|
|
55
|
+
* `MAX_CAUSE_DEPTH` levels becomes `"[Truncated]"`.
|
|
56
|
+
* - `BigInt`, `Symbol` and function values, and non-finite numbers, become strings.
|
|
57
|
+
* - A property or `toJSON` that throws is replaced by a short description.
|
|
39
58
|
*/
|
|
40
59
|
declare function serializeCause(cause: unknown): unknown;
|
|
41
60
|
/** Returns a JSON-safe copy of the entry, adding a timestamp if missing. */
|
|
@@ -56,13 +75,67 @@ interface WebhookErrorOptions {
|
|
|
56
75
|
headers?: Record<string, string>;
|
|
57
76
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
58
77
|
prefix?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Allow a plain `http://` URL. Entries contain error messages and stack
|
|
80
|
+
* traces, so they are only sent over `https://` unless this is `true`.
|
|
81
|
+
* `http://localhost`, `127.0.0.1` and `[::1]` are always allowed.
|
|
82
|
+
* Defaults to `false`.
|
|
83
|
+
*/
|
|
84
|
+
allowInsecure?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* How long to wait for the webhook to answer, in milliseconds. Defaults to
|
|
87
|
+
* 10000. Pass `Infinity` to wait without limit. When it passes, the request is
|
|
88
|
+
* cancelled, so the connection is closed rather than left open; the server may
|
|
89
|
+
* still have received the entry.
|
|
90
|
+
*/
|
|
91
|
+
timeoutMs?: number;
|
|
92
|
+
/**
|
|
93
|
+
* How many times to try again after a failure that may go away on its own: a
|
|
94
|
+
* network error, a timeout, HTTP 408, 425, 429 or any 5xx. Other 4xx answers and
|
|
95
|
+
* redirects are never retried. Defaults to 0 (send once). The wait doubles
|
|
96
|
+
* after each attempt, starting at `retryDelayMs`, with jitter, and a
|
|
97
|
+
* `Retry-After` header is honored. `timeoutMs` applies to each attempt, and
|
|
98
|
+
* `log()` resolves only after the last one. Every attempt sends the same body,
|
|
99
|
+
* and with `retries` above 0 the body always carries an `id`, so the receiver
|
|
100
|
+
* can drop a repeat if an earlier attempt did arrive.
|
|
101
|
+
*/
|
|
102
|
+
retries?: number;
|
|
103
|
+
/** Wait before the first retry, in milliseconds. Defaults to 500. */
|
|
104
|
+
retryDelayMs?: number;
|
|
105
|
+
/** Longest wait between attempts, in milliseconds. Defaults to 30000. */
|
|
106
|
+
maxRetryDelayMs?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Shared secret for signing each request, so the receiver can verify the
|
|
109
|
+
* entry came from you and was not altered. Adds `X-Signature-Timestamp`
|
|
110
|
+
* (Unix seconds) and `X-Signature-256` (`sha256=` and the hex HMAC-SHA256 of
|
|
111
|
+
* `<timestamp>.<body>`). The receiver should recompute it and reject old
|
|
112
|
+
* timestamps. Needs Web Crypto (Node 20+, browsers, Deno, Bun); without it
|
|
113
|
+
* `log()` fails rather than sending an unsigned entry.
|
|
114
|
+
*/
|
|
115
|
+
signingSecret?: string;
|
|
59
116
|
}
|
|
60
117
|
declare class WebhookErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
61
118
|
private readonly url;
|
|
62
119
|
private readonly options;
|
|
63
120
|
private readonly prefix;
|
|
121
|
+
private readonly timeoutMs;
|
|
122
|
+
private readonly retries;
|
|
123
|
+
private readonly retryDelayMs;
|
|
124
|
+
private readonly maxRetryDelayMs;
|
|
125
|
+
/** Scheme and host only. Webhook URLs often carry a secret in the path or query. */
|
|
126
|
+
private readonly target;
|
|
127
|
+
/** @throws {RangeError} if `url` is not a valid `https://` URL (see `allowInsecure`) or a timeout, retry or signing option is invalid. */
|
|
64
128
|
constructor(url: string, options?: WebhookErrorOptions);
|
|
65
129
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
130
|
+
/** Milliseconds to wait before retry number `retryNumber` (1 for the first). */
|
|
131
|
+
private retryWaitMs;
|
|
132
|
+
private send;
|
|
133
|
+
/** Timestamp and signature headers, or none when no `signingSecret` is set. */
|
|
134
|
+
private signatureHeaders;
|
|
135
|
+
private rejected;
|
|
136
|
+
private redirectMessage;
|
|
137
|
+
/** Replaces the full URL in a message with its origin, in case the runtime put it there. */
|
|
138
|
+
private redact;
|
|
66
139
|
private fail;
|
|
67
140
|
}
|
|
68
141
|
|
|
@@ -73,26 +146,63 @@ interface CollectionLike {
|
|
|
73
146
|
interface MongoDbErrorOptions {
|
|
74
147
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
75
148
|
prefix?: string;
|
|
149
|
+
/**
|
|
150
|
+
* How long `log` waits for the insert before giving up, in milliseconds. Defaults to
|
|
151
|
+
* 10000. Pass `Infinity` to wait without limit. The call cannot be cancelled,
|
|
152
|
+
* so it may still finish after `log` has returned a failure.
|
|
153
|
+
*/
|
|
154
|
+
timeoutMs?: number;
|
|
76
155
|
}
|
|
77
156
|
declare class MongoDbErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
78
157
|
private readonly collection;
|
|
79
158
|
private readonly prefix;
|
|
159
|
+
private readonly timeoutMs;
|
|
160
|
+
/** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */
|
|
80
161
|
constructor(collection: CollectionLike, options?: MongoDbErrorOptions);
|
|
81
162
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
82
163
|
}
|
|
83
164
|
|
|
165
|
+
interface CompositeErrorOptions {
|
|
166
|
+
/**
|
|
167
|
+
* How long to wait for each adapter before reporting it as failed, in
|
|
168
|
+
* milliseconds. Defaults to 30000, longer than the 10000 default of the built-in
|
|
169
|
+
* adapters so their own, more specific timeout messages come first. This only
|
|
170
|
+
* matters for a custom adapter that can hang: one stuck adapter then cannot
|
|
171
|
+
* hold back the result of the others. Pass `Infinity` to wait without limit.
|
|
172
|
+
*/
|
|
173
|
+
timeoutMs?: number;
|
|
174
|
+
}
|
|
84
175
|
/** Sends every entry to several adapters. Succeeds only if all of them do. */
|
|
85
176
|
declare class CompositeErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
86
177
|
private readonly adapters;
|
|
87
|
-
|
|
178
|
+
private readonly timeoutMs;
|
|
179
|
+
/** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */
|
|
180
|
+
constructor(adapters: ReadonlyArray<ErrorLogAdapter<TCode>>, options?: CompositeErrorOptions);
|
|
88
181
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
89
182
|
}
|
|
90
183
|
|
|
91
184
|
/** Event handler slot. Typed loosely so real DOM `IDBTransaction` objects fit. */
|
|
92
185
|
type IdbHandler = ((event: never) => unknown) | null;
|
|
186
|
+
/** The error object of a failed request or transaction (a `DOMException`). */
|
|
187
|
+
interface IdbErrorLike {
|
|
188
|
+
name?: string;
|
|
189
|
+
message?: string;
|
|
190
|
+
}
|
|
191
|
+
/** The subset of an `IDBRequest` this adapter uses. */
|
|
192
|
+
interface IdbRequestLike {
|
|
193
|
+
onerror: IdbHandler;
|
|
194
|
+
readonly error?: IdbErrorLike | null;
|
|
195
|
+
}
|
|
93
196
|
/** The subset of an `IDBObjectStore` this adapter uses. */
|
|
94
197
|
interface IdbObjectStoreLike {
|
|
198
|
+
/** Returns an `IDBRequest`. Fakes that return nothing also work. */
|
|
95
199
|
add(value: unknown): unknown;
|
|
200
|
+
/**
|
|
201
|
+
* Replaces the record with the same key. Used for entries that carry their own
|
|
202
|
+
* `id`, so logging one again does not fail on the existing key. A real
|
|
203
|
+
* `IDBObjectStore` has it; a fake without it falls back to `add`.
|
|
204
|
+
*/
|
|
205
|
+
put?(value: unknown): unknown;
|
|
96
206
|
}
|
|
97
207
|
/** The subset of an `IDBTransaction` this adapter uses. */
|
|
98
208
|
interface IdbTransactionLike {
|
|
@@ -100,9 +210,7 @@ interface IdbTransactionLike {
|
|
|
100
210
|
oncomplete: IdbHandler;
|
|
101
211
|
onerror: IdbHandler;
|
|
102
212
|
onabort: IdbHandler;
|
|
103
|
-
readonly error?:
|
|
104
|
-
message?: string;
|
|
105
|
-
} | null;
|
|
213
|
+
readonly error?: IdbErrorLike | null;
|
|
106
214
|
}
|
|
107
215
|
/** The subset of an `IDBDatabase` this adapter uses. */
|
|
108
216
|
interface IdbDatabaseLike {
|
|
@@ -128,6 +236,13 @@ interface IndexedDbErrorOptions {
|
|
|
128
236
|
storeName?: string;
|
|
129
237
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
130
238
|
prefix?: string;
|
|
239
|
+
/**
|
|
240
|
+
* How long `log` waits for the transaction to finish, in milliseconds. Defaults
|
|
241
|
+
* to 10000. Pass `Infinity` to wait without limit. A transaction can stay
|
|
242
|
+
* pending when a version change in another tab blocks it, or when the browser
|
|
243
|
+
* has evicted the origin's storage. The write may still finish afterwards.
|
|
244
|
+
*/
|
|
245
|
+
timeoutMs?: number;
|
|
131
246
|
}
|
|
132
247
|
/**
|
|
133
248
|
* Creates the error log object store (UUID `id` key, `timestamp` index).
|
|
@@ -138,9 +253,18 @@ declare class IndexedDbErrorAdapter<TCode extends string = string> implements Er
|
|
|
138
253
|
private readonly database;
|
|
139
254
|
private readonly storeName;
|
|
140
255
|
private readonly prefix;
|
|
256
|
+
private readonly timeoutMs;
|
|
257
|
+
/** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */
|
|
141
258
|
constructor(database: IdbDatabaseLike, options?: IndexedDbErrorOptions);
|
|
142
259
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
143
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Resolves once the transaction commits; rejects if the request fails, the
|
|
262
|
+
* transaction aborts, or it does not finish within `timeoutMs`.
|
|
263
|
+
*
|
|
264
|
+
* Error events are handled here (`preventDefault` and `stopPropagation`), so a
|
|
265
|
+
* failed log write does not reach the `onerror` handler an app may have set on
|
|
266
|
+
* its own database object.
|
|
267
|
+
*/
|
|
144
268
|
private write;
|
|
145
269
|
}
|
|
146
270
|
|
|
@@ -166,6 +290,12 @@ interface SqliteErrorOptions {
|
|
|
166
290
|
createTable?: boolean;
|
|
167
291
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
168
292
|
prefix?: string;
|
|
293
|
+
/**
|
|
294
|
+
* How long `log` waits for the driver before giving up, in milliseconds. Defaults
|
|
295
|
+
* to 10000. Pass `Infinity` to wait without limit. The call cannot be
|
|
296
|
+
* cancelled, so it may still finish after `log` has returned a failure.
|
|
297
|
+
*/
|
|
298
|
+
timeoutMs?: number;
|
|
169
299
|
}
|
|
170
300
|
/**
|
|
171
301
|
* Returns the statements that create the error log table and its index.
|
|
@@ -177,13 +307,16 @@ declare class SqliteErrorAdapter<TCode extends string = string> implements Error
|
|
|
177
307
|
private readonly tableName;
|
|
178
308
|
private readonly createTable;
|
|
179
309
|
private readonly prefix;
|
|
310
|
+
private readonly timeoutMs;
|
|
180
311
|
private tableReady;
|
|
181
|
-
/** @throws {RangeError} if `options.tableName` is not a plain identifier. */
|
|
312
|
+
/** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */
|
|
182
313
|
constructor(client: SqliteClientLike, options?: SqliteErrorOptions);
|
|
183
314
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
184
315
|
/** Creates the table once. A failed attempt is retried on the next log call. */
|
|
185
316
|
private ensureTable;
|
|
186
317
|
private runCreateStatements;
|
|
318
|
+
/** Runs one statement, giving up waiting after `timeoutMs`. */
|
|
319
|
+
private run;
|
|
187
320
|
}
|
|
188
321
|
|
|
189
322
|
/**
|
|
@@ -194,6 +327,14 @@ interface StorageLike {
|
|
|
194
327
|
getItem(key: string): string | null;
|
|
195
328
|
setItem(key: string, value: string): void;
|
|
196
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* The part of `navigator.locks` this adapter uses. `localStorage` is shared by
|
|
332
|
+
* every tab of an origin and has no atomic update, so a lock keeps two tabs from
|
|
333
|
+
* overwriting each other's entry.
|
|
334
|
+
*/
|
|
335
|
+
interface LockManagerLike {
|
|
336
|
+
request<T>(name: string, callback: () => T | Promise<T>): Promise<T>;
|
|
337
|
+
}
|
|
197
338
|
/** A stored record: the serialized entry plus its generated UUID. */
|
|
198
339
|
interface WebStorageErrorRecord extends SerializedErrorLogEntry {
|
|
199
340
|
id: string;
|
|
@@ -205,24 +346,51 @@ interface WebStorageErrorOptions {
|
|
|
205
346
|
maxEntries?: number;
|
|
206
347
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
207
348
|
prefix?: string;
|
|
349
|
+
/**
|
|
350
|
+
* Lock manager that makes tabs sharing a `localStorage` take turns. Defaults to
|
|
351
|
+
* `navigator.locks` when the browser has it. This reduces lost entries but does
|
|
352
|
+
* not remove them: a tab in another process can still read a stale copy
|
|
353
|
+
* (measured in Chromium). Pass `null` to turn locking off.
|
|
354
|
+
*/
|
|
355
|
+
locks?: LockManagerLike | null;
|
|
208
356
|
}
|
|
209
357
|
/**
|
|
210
358
|
* Keeps the newest entries in a Web Storage object (`sessionStorage` or
|
|
211
359
|
* `localStorage`) as a JSON array under one key, capped at `maxEntries`.
|
|
212
360
|
* When the store is full, the oldest entries are dropped until the new one fits.
|
|
361
|
+
*
|
|
362
|
+
* `sessionStorage` belongs to one tab, so it is safe. `localStorage` is shared by
|
|
363
|
+
* every tab of the origin and has no atomic update: two tabs logging at the same
|
|
364
|
+
* moment can overwrite each other's entry while both get `ok: true`. In Chromium,
|
|
365
|
+
* tabs in separate processes (the usual case) even read a stale copy, so a lock
|
|
366
|
+
* reduces the loss without removing it. Where tabs may log at the same time and
|
|
367
|
+
* every entry matters, use `IndexedDbErrorAdapter`, whose writes are transactional.
|
|
213
368
|
*/
|
|
214
369
|
declare class WebStorageErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
215
370
|
private readonly storage;
|
|
216
371
|
private readonly key;
|
|
217
372
|
private readonly maxEntries;
|
|
218
373
|
private readonly prefix;
|
|
374
|
+
private readonly locks;
|
|
219
375
|
/** @throws {RangeError} if `options.maxEntries` is not a positive integer. */
|
|
220
376
|
constructor(storage: StorageLike, options?: WebStorageErrorOptions);
|
|
221
377
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
222
|
-
/**
|
|
378
|
+
/**
|
|
379
|
+
* Appends under a cross-tab lock when one is available. If the lock itself
|
|
380
|
+
* cannot be taken (for example a sandboxed frame), the entry is still written
|
|
381
|
+
* without it rather than lost.
|
|
382
|
+
*/
|
|
383
|
+
private appendLocked;
|
|
384
|
+
/** Reads, appends, trims, and writes back. Runs without awaiting, so calls in one tab never interleave. */
|
|
223
385
|
private append;
|
|
224
386
|
/** Returns the stored records, or none if the value is missing or unreadable. */
|
|
225
387
|
private readRecords;
|
|
388
|
+
/**
|
|
389
|
+
* Copies an unusable value to `<key>.corrupt` before it is replaced, so data
|
|
390
|
+
* the host app stored under the same key is not silently destroyed. Only the
|
|
391
|
+
* first one is kept; a later unusable value would not overwrite it.
|
|
392
|
+
*/
|
|
393
|
+
private setAside;
|
|
226
394
|
}
|
|
227
395
|
|
|
228
396
|
/**
|
|
@@ -281,7 +449,7 @@ declare class FlatFileErrorAdapter<TCode extends string = string> implements Err
|
|
|
281
449
|
private readonly prefix;
|
|
282
450
|
private fs;
|
|
283
451
|
private directoryReady;
|
|
284
|
-
private queue;
|
|
452
|
+
private readonly queue;
|
|
285
453
|
/** @throws {RangeError} if `maxBytes` or `maxFiles` is not a positive integer. */
|
|
286
454
|
constructor(filePath: string, options?: FlatFileErrorOptions);
|
|
287
455
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
@@ -334,7 +502,7 @@ declare class JsonFileErrorAdapter<TCode extends string = string> implements Err
|
|
|
334
502
|
private readonly prefix;
|
|
335
503
|
private fs;
|
|
336
504
|
private directoryReady;
|
|
337
|
-
private queue;
|
|
505
|
+
private readonly queue;
|
|
338
506
|
/** @throws {RangeError} if `maxEntries` or `indent` is invalid, or `maxEntries` is used outside array mode. */
|
|
339
507
|
constructor(filePath: string, options?: JsonFileErrorOptions);
|
|
340
508
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
@@ -371,6 +539,12 @@ interface SqlErrorOptions {
|
|
|
371
539
|
createTable?: boolean;
|
|
372
540
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
373
541
|
prefix?: string;
|
|
542
|
+
/**
|
|
543
|
+
* How long `log` waits for each database statement before giving up, in milliseconds. Defaults
|
|
544
|
+
* to 10000. Pass `Infinity` to wait without limit. The call cannot be
|
|
545
|
+
* cancelled, so it may still finish after `log` has returned a failure.
|
|
546
|
+
*/
|
|
547
|
+
timeoutMs?: number;
|
|
374
548
|
}
|
|
375
549
|
/**
|
|
376
550
|
* Returns the statements that create the error log table and its index for a
|
|
@@ -381,6 +555,12 @@ interface SqlErrorOptions {
|
|
|
381
555
|
* cause, and `DATETIME(3)` columns holding UTC times.
|
|
382
556
|
*/
|
|
383
557
|
declare function buildErrorLogTableDdl(dialect: SqlDialect, tableName?: string): string[];
|
|
558
|
+
/**
|
|
559
|
+
* Returns the statement that widens the `code` and `source` columns of a MySQL
|
|
560
|
+
* table created by `buildErrorLogTableDdl` in 0.4.0 or earlier, where they were
|
|
561
|
+
* `VARCHAR(255)` and a longer value made the insert fail. Run it once.
|
|
562
|
+
*/
|
|
563
|
+
declare function buildMySqlWidenColumnsSql(tableName?: string): string[];
|
|
384
564
|
/** Shared by the Postgres and MySQL adapters: only the SQL text and time format differ. */
|
|
385
565
|
declare abstract class SqlErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
386
566
|
private readonly client;
|
|
@@ -389,6 +569,7 @@ declare abstract class SqlErrorAdapter<TCode extends string = string> implements
|
|
|
389
569
|
private readonly tableName;
|
|
390
570
|
private readonly createTable;
|
|
391
571
|
private readonly prefix;
|
|
572
|
+
private readonly timeoutMs;
|
|
392
573
|
private tableReady;
|
|
393
574
|
protected constructor(client: SqlClientLike, dialect: SqlDialect, adapterName: string, options: SqlErrorOptions);
|
|
394
575
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
@@ -396,6 +577,8 @@ declare abstract class SqlErrorAdapter<TCode extends string = string> implements
|
|
|
396
577
|
/** Creates the table once. A failed attempt is retried on the next log call. */
|
|
397
578
|
private ensureTable;
|
|
398
579
|
private runCreateStatements;
|
|
580
|
+
/** Runs one statement, giving up waiting after `timeoutMs`. */
|
|
581
|
+
private query;
|
|
399
582
|
}
|
|
400
583
|
/**
|
|
401
584
|
* Writes entries to Postgres. Pass a `pg` `Pool` or `Client`.
|
|
@@ -454,6 +637,12 @@ interface RedisErrorOptions {
|
|
|
454
637
|
side?: 'left' | 'right';
|
|
455
638
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
456
639
|
prefix?: string;
|
|
640
|
+
/**
|
|
641
|
+
* How long `log` waits for each Redis command before giving up, in milliseconds. Defaults
|
|
642
|
+
* to 10000. Pass `Infinity` to wait without limit. The call cannot be
|
|
643
|
+
* cancelled, so it may still finish after `log` has returned a failure.
|
|
644
|
+
*/
|
|
645
|
+
timeoutMs?: number;
|
|
457
646
|
}
|
|
458
647
|
/**
|
|
459
648
|
* Writes entries to Redis, either as a capped list of JSON strings or as a
|
|
@@ -467,6 +656,7 @@ declare class RedisErrorAdapter<TCode extends string = string> implements ErrorL
|
|
|
467
656
|
private readonly maxEntries;
|
|
468
657
|
private readonly side;
|
|
469
658
|
private readonly prefix;
|
|
659
|
+
private readonly timeoutMs;
|
|
470
660
|
/** @throws {RangeError} if `key` is empty, `maxEntries` is not a positive integer, or `mode` / `side` is unknown. */
|
|
471
661
|
constructor(client: RedisClientLike, options?: RedisErrorOptions);
|
|
472
662
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
@@ -476,6 +666,8 @@ declare class RedisErrorAdapter<TCode extends string = string> implements ErrorL
|
|
|
476
666
|
* stored by now, so a trim failure is reported but does not fail the call;
|
|
477
667
|
* the next successful write trims the list again.
|
|
478
668
|
*/
|
|
669
|
+
/** Sends one command, giving up waiting after `timeoutMs`. */
|
|
670
|
+
private send;
|
|
479
671
|
private trimList;
|
|
480
672
|
}
|
|
481
673
|
|
|
@@ -528,6 +720,13 @@ interface S3ErrorOptions {
|
|
|
528
720
|
batch?: S3BatchOptions;
|
|
529
721
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
530
722
|
prefix?: string;
|
|
723
|
+
/**
|
|
724
|
+
* How long an upload may take before `log` (or `flush`) gives up waiting, in
|
|
725
|
+
* milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The
|
|
726
|
+
* upload cannot be cancelled, so it may still finish afterwards; in batch mode
|
|
727
|
+
* the entries stay buffered and are uploaded again by the next flush.
|
|
728
|
+
*/
|
|
729
|
+
timeoutMs?: number;
|
|
531
730
|
}
|
|
532
731
|
/**
|
|
533
732
|
* Stores entries as objects in an S3-compatible bucket, keyed by date and UUID:
|
|
@@ -546,9 +745,10 @@ declare class S3ErrorAdapter<TCode extends string = string> implements ErrorLogA
|
|
|
546
745
|
private readonly contentType;
|
|
547
746
|
private readonly batch;
|
|
548
747
|
private readonly logPrefix;
|
|
748
|
+
private readonly timeoutMs;
|
|
549
749
|
private buffer;
|
|
550
750
|
private timer;
|
|
551
|
-
private flushQueue;
|
|
751
|
+
private readonly flushQueue;
|
|
552
752
|
/** @throws {RangeError} if `bucket` is empty or the batch options are invalid. */
|
|
553
753
|
constructor(client: S3ClientLike, bucket: string, options?: S3ErrorOptions);
|
|
554
754
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
@@ -604,11 +804,19 @@ interface FirestoreCollectionLike {
|
|
|
604
804
|
* ```ts
|
|
605
805
|
* const refLike: RealtimeDatabaseRefLike = {
|
|
606
806
|
* push: (value) => push(ref(db, 'error_log'), value),
|
|
807
|
+
* child: (key) => ({ set: (value) => set(ref(db, `error_log/${key}`), value) }),
|
|
607
808
|
* };
|
|
608
809
|
* ```
|
|
810
|
+
*
|
|
811
|
+
* `child(key).set(value)` is only needed for entries that carry their own `id`
|
|
812
|
+
* (see `ErrorLogEntry.id`): they are written under that key, so logging the same
|
|
813
|
+
* entry again overwrites the record instead of adding a second one.
|
|
609
814
|
*/
|
|
610
815
|
interface RealtimeDatabaseRefLike {
|
|
611
816
|
push(value: FirebaseErrorRecord): PromiseLike<unknown>;
|
|
817
|
+
child?(key: string): {
|
|
818
|
+
set(value: FirebaseErrorRecord): PromiseLike<unknown>;
|
|
819
|
+
};
|
|
612
820
|
}
|
|
613
821
|
interface FirebaseErrorOptions {
|
|
614
822
|
/**
|
|
@@ -618,7 +826,9 @@ interface FirebaseErrorOptions {
|
|
|
618
826
|
* The Firebase SDKs keep writes queued while the connection is down, and
|
|
619
827
|
* their promises stay pending until it returns, so without a limit `log`
|
|
620
828
|
* could hang for as long as an outage lasts. A write that timed out is still
|
|
621
|
-
* queued by the SDK and may be saved later.
|
|
829
|
+
* queued by the SDK and may be saved later. Retry a timeout only with the same
|
|
830
|
+
* `ErrorLogEntry.id`, so the retry overwrites the first write instead of
|
|
831
|
+
* adding a second one.
|
|
622
832
|
*/
|
|
623
833
|
timeoutMs?: number;
|
|
624
834
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
@@ -626,7 +836,11 @@ interface FirebaseErrorOptions {
|
|
|
626
836
|
}
|
|
627
837
|
/**
|
|
628
838
|
* Writes each entry as a document in a Firestore collection. The document id is
|
|
629
|
-
* the entry's UUID, so
|
|
839
|
+
* the entry's UUID, so the SDK finishing one queued write twice cannot create two
|
|
840
|
+
* documents. Calling `log` again without an `id` on the entry is a new write
|
|
841
|
+
* with a new UUID: if a call reported a timeout, the first write may still be
|
|
842
|
+
* saved later, and logging the same entry again can store it twice. Give the
|
|
843
|
+
* entry an `id` and the retry overwrites the same document.
|
|
630
844
|
*/
|
|
631
845
|
declare class FirestoreErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
632
846
|
private readonly collection;
|
|
@@ -684,26 +898,46 @@ interface SupabaseClientLike {
|
|
|
684
898
|
};
|
|
685
899
|
}
|
|
686
900
|
interface SupabaseErrorOptions {
|
|
687
|
-
/** Table to write to. Defaults to `error_log`. */
|
|
901
|
+
/** Table to write to. Defaults to `error_log`. Letters, digits and `_` only. */
|
|
688
902
|
tableName?: string;
|
|
689
903
|
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
690
904
|
prefix?: string;
|
|
905
|
+
/**
|
|
906
|
+
* How long `log` waits for Supabase to answer before giving up, in milliseconds. Defaults
|
|
907
|
+
* to 10000. Pass `Infinity` to wait without limit. The call cannot be
|
|
908
|
+
* cancelled, so it may still finish after `log` has returned a failure.
|
|
909
|
+
*/
|
|
910
|
+
timeoutMs?: number;
|
|
691
911
|
}
|
|
692
912
|
interface SupabaseSetupOptions {
|
|
693
913
|
/**
|
|
694
|
-
* Postgres roles allowed to insert, for example
|
|
695
|
-
*
|
|
696
|
-
*
|
|
914
|
+
* Postgres roles allowed to insert through the API, for example
|
|
915
|
+
* `['authenticated']`. Leave empty when only a server using the service role
|
|
916
|
+
* key logs: that role bypasses row level security, so it needs no policy, and
|
|
917
|
+
* its key must never be shipped to a browser.
|
|
918
|
+
*
|
|
919
|
+
* The generated policy only checks the size of each field. It does not tie a
|
|
920
|
+
* row to a user, so any holder of an allowed role's key can still add entries
|
|
921
|
+
* of their own making: forged entries can hide real incidents, and a flood of
|
|
922
|
+
* them uses up your database quota. Prefer `authenticated` over `anon`.
|
|
697
923
|
*/
|
|
698
924
|
insertRoles?: string[];
|
|
925
|
+
/**
|
|
926
|
+
* Required to allow `anon` (or `public`) to insert. The `anon` key is part of
|
|
927
|
+
* your frontend, so this lets anyone who loads the page write to the log.
|
|
928
|
+
* Defaults to `false`.
|
|
929
|
+
*/
|
|
930
|
+
allowPublicInsert?: boolean;
|
|
699
931
|
}
|
|
700
932
|
/**
|
|
701
933
|
* Returns the SQL to run once in the Supabase SQL editor: the error log table,
|
|
702
934
|
* with row level security turned on (as Supabase recommends for every table in
|
|
703
935
|
* an exposed schema) and, optionally, an insert-only policy for the given roles.
|
|
704
|
-
* Nobody but the service role can read
|
|
936
|
+
* The policy limits the size of each field. Nobody but the service role can read
|
|
937
|
+
* the log through the API.
|
|
705
938
|
*
|
|
706
|
-
* @throws {RangeError} if the table name or a role is not a plain identifier
|
|
939
|
+
* @throws {RangeError} if the table name or a role is not a plain identifier, or
|
|
940
|
+
* `anon` / `public` is listed without `allowPublicInsert: true`.
|
|
707
941
|
*/
|
|
708
942
|
declare function buildSupabaseSetupSql(tableName?: string, options?: SupabaseSetupOptions): string[];
|
|
709
943
|
/**
|
|
@@ -721,10 +955,71 @@ declare class SupabaseErrorAdapter<TCode extends string = string> implements Err
|
|
|
721
955
|
private readonly client;
|
|
722
956
|
private readonly tableName;
|
|
723
957
|
private readonly prefix;
|
|
724
|
-
|
|
958
|
+
private readonly timeoutMs;
|
|
959
|
+
/** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */
|
|
725
960
|
constructor(client: SupabaseClientLike, options?: SupabaseErrorOptions);
|
|
726
961
|
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
727
962
|
private fail;
|
|
728
963
|
}
|
|
729
964
|
|
|
730
|
-
|
|
965
|
+
/** What listeners receive: the serialized entry plus its UUID. */
|
|
966
|
+
interface EventEmitterErrorRecord extends SerializedErrorLogEntry {
|
|
967
|
+
id: string;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* The only thing this adapter needs from an emitter: `emit(eventName, payload)`.
|
|
971
|
+
* Node's `EventEmitter` and mitt-style emitters fit as they are. A browser
|
|
972
|
+
* `EventTarget` needs a one-line wrapper:
|
|
973
|
+
*
|
|
974
|
+
* ```ts
|
|
975
|
+
* const target = new EventTarget();
|
|
976
|
+
* const emitter: EmitterLike = {
|
|
977
|
+
* emit: (name, payload) =>
|
|
978
|
+
* target.dispatchEvent(new CustomEvent(name, { detail: payload })),
|
|
979
|
+
* };
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
interface EmitterLike {
|
|
983
|
+
emit(eventName: string, payload: EventEmitterErrorRecord): unknown;
|
|
984
|
+
}
|
|
985
|
+
interface EventEmitterErrorOptions {
|
|
986
|
+
/**
|
|
987
|
+
* Name of the event to emit. Defaults to `error-log`. Avoid `error` on a Node
|
|
988
|
+
* `EventEmitter`: it throws when no listener is attached, and `log` would then
|
|
989
|
+
* return `ok: false`.
|
|
990
|
+
*/
|
|
991
|
+
eventName?: string;
|
|
992
|
+
/**
|
|
993
|
+
* Treat an emit that reached nobody as a failure. Node's `EventEmitter.emit`
|
|
994
|
+
* returns `false` when there are no listeners; with this set to `true`, `log`
|
|
995
|
+
* then returns `ok: false` instead of letting the entry vanish. Emitters whose
|
|
996
|
+
* `emit` returns anything else (mitt, an `EventTarget` wrapper) cannot tell,
|
|
997
|
+
* so they always count as delivered. Defaults to `false`.
|
|
998
|
+
*/
|
|
999
|
+
requireListener?: boolean;
|
|
1000
|
+
/** Text printed before failure messages. Defaults to `[error-log-adapter]`. */
|
|
1001
|
+
prefix?: string;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Emits each entry as an event on an emitter you own, for in-process hooks such
|
|
1005
|
+
* as a toast, a metrics counter, or a test assertion. Listeners run
|
|
1006
|
+
* synchronously inside `emit`, so a listener that throws makes `log` return
|
|
1007
|
+
* `ok: false` with the listener's message; the application is not affected.
|
|
1008
|
+
*
|
|
1009
|
+
* ```ts
|
|
1010
|
+
* const bus = new EventEmitter();
|
|
1011
|
+
* bus.on('error-log', (record) => showToast(record.message));
|
|
1012
|
+
* const adapter = new EventEmitterErrorAdapter(bus);
|
|
1013
|
+
* ```
|
|
1014
|
+
*/
|
|
1015
|
+
declare class EventEmitterErrorAdapter<TCode extends string = string> implements ErrorLogAdapter<TCode> {
|
|
1016
|
+
private readonly emitter;
|
|
1017
|
+
private readonly eventName;
|
|
1018
|
+
private readonly requireListener;
|
|
1019
|
+
private readonly prefix;
|
|
1020
|
+
/** @throws {RangeError} if `options.eventName` is empty. */
|
|
1021
|
+
constructor(emitter: EmitterLike, options?: EventEmitterErrorOptions);
|
|
1022
|
+
log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult>;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
export { type CollectionLike, CompositeErrorAdapter, type CompositeErrorOptions, ConsoleErrorAdapter, type ConsoleErrorOptions, type EmitterLike, type ErrorLogAdapter, type ErrorLogEntry, type ErrorLogResult, EventEmitterErrorAdapter, type EventEmitterErrorOptions, type EventEmitterErrorRecord, type FileSystemLike, type FirebaseErrorOptions, type FirebaseErrorRecord, type FirestoreCollectionLike, FirestoreErrorAdapter, FlatFileErrorAdapter, type FlatFileErrorOptions, type IdbDatabaseLike, type IdbObjectStoreLike, type IdbRequestLike, type IdbTransactionLike, type IdbUpgradeDatabaseLike, IndexedDbErrorAdapter, type IndexedDbErrorOptions, type IndexedDbErrorRecord, JsonFileErrorAdapter, type JsonFileErrorOptions, type JsonFileErrorRecord, type LockManagerLike, MongoDbErrorAdapter, type MongoDbErrorOptions, MySqlErrorAdapter, PostgresErrorAdapter, type ReadWriteFileSystemLike, RealtimeDatabaseErrorAdapter, type RealtimeDatabaseRefLike, type RedisClientLike, RedisErrorAdapter, type RedisErrorOptions, type S3BatchOptions, type S3ClientLike, S3ErrorAdapter, type S3ErrorOptions, type S3PutObjectInput, type SerializedErrorLogEntry, type SqlClientLike, type SqlDialect, type SqlErrorOptions, type SqliteClientLike, SqliteErrorAdapter, type SqliteErrorOptions, type StorageLike, type SupabaseClientLike, SupabaseErrorAdapter, type SupabaseErrorLike, type SupabaseErrorOptions, type SupabaseErrorRow, type SupabaseInsertResult, type SupabaseSetupOptions, WebStorageErrorAdapter, type WebStorageErrorOptions, type WebStorageErrorRecord, WebhookErrorAdapter, type WebhookErrorOptions, buildCreateErrorLogTableSql, buildErrorLogTableDdl, buildMySqlWidenColumnsSql, buildSupabaseSetupSql, createErrorLogStore, formatErrorLogLine, serializeCause, serializeEntry };
|