@richardmcquiston01/error-log-adapter 0.9.0 → 0.10.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/serialize.ts","../src/fail.ts","../src/adapters/console-error-adapter.ts","../src/backoff.ts","../src/uuid.ts","../src/entry-id.ts","../src/timeout.ts","../src/adapters/webhook-error-adapter.ts","../src/adapters/mongodb-error-adapter.ts","../src/adapters/composite-error-adapter.ts","../src/adapters/indexeddb-error-adapter.ts","../src/sql-ident.ts","../src/adapters/sqlite-error-adapter.ts","../src/adapters/web-storage-error-adapter.ts","../src/queue.ts","../src/file-system.ts","../src/adapters/flat-file-error-adapter.ts","../src/adapters/json-file-error-adapter.ts","../src/adapters/sql-error-adapter.ts","../src/adapters/redis-error-adapter.ts","../src/adapters/s3-error-adapter.ts","../src/adapters/firebase-error-adapter.ts","../src/adapters/supabase-error-adapter.ts","../src/adapters/event-emitter-error-adapter.ts","../src/adapters/websocket-error-adapter.ts","../src/adapters/cloudwatch-logs-error-adapter.ts","../src/adapters/batching-error-adapter.ts"],"sourcesContent":["export type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from './types.js';\nexport type { SerializedErrorLogEntry } from './serialize.js';\nexport { serializeEntry, serializeCause } from './serialize.js';\nexport { ConsoleErrorAdapter } from './adapters/console-error-adapter.js';\nexport type { ConsoleErrorOptions } from './adapters/console-error-adapter.js';\nexport { WebhookErrorAdapter } from './adapters/webhook-error-adapter.js';\nexport type { WebhookErrorOptions } from './adapters/webhook-error-adapter.js';\nexport { MongoDbErrorAdapter } from './adapters/mongodb-error-adapter.js';\nexport type {\n CollectionLike,\n MongoDbErrorOptions,\n} from './adapters/mongodb-error-adapter.js';\nexport { CompositeErrorAdapter } from './adapters/composite-error-adapter.js';\nexport type { CompositeErrorOptions } from './adapters/composite-error-adapter.js';\nexport {\n IndexedDbErrorAdapter,\n createErrorLogStore,\n} from './adapters/indexeddb-error-adapter.js';\nexport type {\n IdbDatabaseLike,\n IdbObjectStoreLike,\n IdbRequestLike,\n IdbTransactionLike,\n IdbUpgradeDatabaseLike,\n IndexedDbErrorOptions,\n IndexedDbErrorRecord,\n} from './adapters/indexeddb-error-adapter.js';\nexport {\n SqliteErrorAdapter,\n buildCreateErrorLogTableSql,\n} from './adapters/sqlite-error-adapter.js';\nexport type {\n SqliteClientLike,\n SqliteErrorOptions,\n} from './adapters/sqlite-error-adapter.js';\nexport { WebStorageErrorAdapter } from './adapters/web-storage-error-adapter.js';\nexport type {\n LockManagerLike,\n StorageLike,\n WebStorageErrorOptions,\n WebStorageErrorRecord,\n} from './adapters/web-storage-error-adapter.js';\nexport {\n FlatFileErrorAdapter,\n formatErrorLogLine,\n} from './adapters/flat-file-error-adapter.js';\nexport type { FlatFileErrorOptions } from './adapters/flat-file-error-adapter.js';\nexport type { FileSystemLike, ReadWriteFileSystemLike } from './file-system.js';\nexport { JsonFileErrorAdapter } from './adapters/json-file-error-adapter.js';\nexport type {\n JsonFileErrorOptions,\n JsonFileErrorRecord,\n} from './adapters/json-file-error-adapter.js';\nexport {\n MySqlErrorAdapter,\n PostgresErrorAdapter,\n buildErrorLogTableDdl,\n buildMySqlWidenColumnsSql,\n} from './adapters/sql-error-adapter.js';\nexport type {\n SqlClientLike,\n SqlDialect,\n SqlErrorOptions,\n} from './adapters/sql-error-adapter.js';\nexport { RedisErrorAdapter } from './adapters/redis-error-adapter.js';\nexport type {\n RedisClientLike,\n RedisErrorOptions,\n} from './adapters/redis-error-adapter.js';\nexport { S3ErrorAdapter } from './adapters/s3-error-adapter.js';\nexport type {\n S3BatchOptions,\n S3ClientLike,\n S3ErrorOptions,\n S3PutObjectInput,\n} from './adapters/s3-error-adapter.js';\nexport {\n FirestoreErrorAdapter,\n RealtimeDatabaseErrorAdapter,\n} from './adapters/firebase-error-adapter.js';\nexport type {\n FirebaseErrorOptions,\n FirebaseErrorRecord,\n FirestoreCollectionLike,\n RealtimeDatabaseRefLike,\n} from './adapters/firebase-error-adapter.js';\nexport {\n SupabaseErrorAdapter,\n buildSupabaseSetupSql,\n} from './adapters/supabase-error-adapter.js';\nexport type {\n SupabaseClientLike,\n SupabaseErrorLike,\n SupabaseErrorOptions,\n SupabaseErrorRow,\n SupabaseInsertResult,\n SupabaseSetupOptions,\n} from './adapters/supabase-error-adapter.js';\nexport { EventEmitterErrorAdapter } from './adapters/event-emitter-error-adapter.js';\nexport type {\n EmitterLike,\n EventEmitterErrorOptions,\n EventEmitterErrorRecord,\n} from './adapters/event-emitter-error-adapter.js';\nexport { WebSocketErrorAdapter } from './adapters/websocket-error-adapter.js';\nexport type {\n WebSocketErrorOptions,\n WebSocketLike,\n} from './adapters/websocket-error-adapter.js';\nexport { CloudWatchLogsErrorAdapter } from './adapters/cloudwatch-logs-error-adapter.js';\nexport type {\n CloudWatchBatchOptions,\n CloudWatchLogEvent,\n CloudWatchLogsClientLike,\n CloudWatchLogsErrorOptions,\n CloudWatchPutLogEventsInput,\n} from './adapters/cloudwatch-logs-error-adapter.js';\nexport { BatchingErrorAdapter } from './adapters/batching-error-adapter.js';\nexport type {\n BatchCapableAdapter,\n BatchingErrorOptions,\n} from './adapters/batching-error-adapter.js';\n","import type { ErrorLogEntry } from './types.js';\n\n/** A JSON-safe copy of an `ErrorLogEntry`. */\nexport interface SerializedErrorLogEntry {\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n}\n\n/** How many levels of nested causes and objects are kept before the rest is replaced by a marker. */\nexport const MAX_CAUSE_DEPTH = 6;\n\nfunction isErrorLike(value: object): value is {\n name: string;\n message: string;\n stack?: unknown;\n cause?: unknown;\n errors?: unknown;\n} {\n if (value instanceof Error) {\n return true;\n }\n // An error from another realm (vm, iframe) fails `instanceof Error`.\n const candidate = value as { name?: unknown; message?: unknown };\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.message === 'string' &&\n typeof (value as { stack?: unknown }).stack === 'string'\n );\n}\n\nfunction readProperty(source: object, key: string): unknown {\n try {\n return (source as Record<string, unknown>)[key];\n } catch (error: unknown) {\n return `[Unreadable: ${error instanceof Error ? error.message : String(error)}]`;\n }\n}\n\nfunction toJsonSafe(\n value: unknown,\n depth: number,\n ancestors: WeakSet<object>\n): unknown {\n switch (typeof value) {\n case 'string':\n case 'boolean':\n case 'undefined':\n return value;\n case 'number':\n return Number.isFinite(value) ? value : String(value);\n case 'bigint':\n return `${value}n`;\n case 'symbol':\n return value.toString();\n case 'function':\n return `[Function ${value.name || 'anonymous'}]`;\n default:\n break;\n }\n if (value === null) {\n return null;\n }\n const objectValue: object = value as object;\n if (ancestors.has(objectValue)) {\n return '[Circular]';\n }\n if (depth >= MAX_CAUSE_DEPTH) {\n return '[Truncated]';\n }\n ancestors.add(objectValue);\n try {\n if (isErrorLike(objectValue)) {\n const result: Record<string, unknown> = {\n name: objectValue.name,\n message: objectValue.message,\n stack: objectValue.stack,\n };\n const nested: unknown = readProperty(objectValue, 'cause');\n if (nested !== undefined) {\n result['cause'] = toJsonSafe(nested, depth + 1, ancestors);\n }\n const errors: unknown = readProperty(objectValue, 'errors');\n if (Array.isArray(errors)) {\n result['errors'] = errors.map((item: unknown): unknown =>\n toJsonSafe(item, depth + 1, ancestors)\n );\n }\n return result;\n }\n if (Array.isArray(value)) {\n return value.map((item: unknown): unknown =>\n toJsonSafe(item, depth + 1, ancestors)\n );\n }\n const toJson: unknown = readProperty(objectValue, 'toJSON');\n if (typeof toJson === 'function') {\n try {\n return toJsonSafe(\n (toJson as () => unknown).call(objectValue),\n depth + 1,\n ancestors\n );\n } catch (error: unknown) {\n return `[toJSON failed: ${error instanceof Error ? error.message : String(error)}]`;\n }\n }\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(objectValue)) {\n result[key] = toJsonSafe(\n readProperty(objectValue, key),\n depth + 1,\n ancestors\n );\n }\n return result;\n } finally {\n ancestors.delete(objectValue);\n }\n}\n\n/**\n * Returns a copy of `cause` that `JSON.stringify` can always handle, so a\n * strange cause can never stop an entry from being stored.\n *\n * - `Error` objects (including ones from another realm) become\n * `{ name, message, stack }`, plus `cause` and, for an `AggregateError`,\n * `errors`, which are converted the same way.\n * - Circular references become `\"[Circular]\"`, and anything nested deeper than\n * `MAX_CAUSE_DEPTH` levels becomes `\"[Truncated]\"`.\n * - `BigInt`, `Symbol` and function values, and non-finite numbers, become strings.\n * - A property or `toJSON` that throws is replaced by a short description.\n */\nexport function serializeCause(cause: unknown): unknown {\n return toJsonSafe(cause, 0, new WeakSet<object>());\n}\n\n/** Returns a JSON-safe copy of the entry, adding a timestamp if missing. */\nexport function serializeEntry(entry: ErrorLogEntry): SerializedErrorLogEntry {\n const serialized: SerializedErrorLogEntry = {\n code: entry.code,\n message: entry.message,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n };\n if (entry.cause !== undefined) {\n serialized.cause = serializeCause(entry.cause);\n }\n if (entry.source !== undefined) {\n serialized.source = entry.source;\n }\n return serialized;\n}\n\n/** Turns an unknown thrown value into a descriptive message. */\nexport function describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { describeError } from './serialize.js';\nimport type { ErrorLogResult } from './types.js';\n\n/** Text printed before failure messages unless an adapter's `prefix` option says otherwise. */\nexport const DEFAULT_PREFIX = '[error-log-adapter]';\n\nexport interface FailureReport {\n /** Class name of the adapter, such as `PostgresErrorAdapter`. */\n adapterName: string;\n /** The adapter's `prefix` option. */\n prefix: string;\n /** What was being done, completing \"<adapter> failed ...\", such as `to write to table \"error_log\"`. */\n action: string;\n /** What went wrong. It is printed with `console.error`, and its message is returned. */\n error: unknown;\n /** Replaces the message taken from `error`, for sinks that describe failures in their own way. */\n reason?: string;\n}\n\n/**\n * Reports a failed `log` call the same way for every adapter: the problem goes\n * to `console.error` under the adapter's prefix, and the caller gets\n * `{ ok: false, error: \"<adapter> failed <action>: <reason>\" }`.\n */\nexport function reportFailure(report: FailureReport): ErrorLogResult {\n console.error(`${report.prefix} ${report.adapterName}:`, report.error);\n return {\n ok: false,\n error: `${report.adapterName} failed ${report.action}: ${report.reason ?? describeError(report.error)}`,\n };\n}\n","import { DEFAULT_PREFIX } from '../fail.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface ConsoleErrorOptions {\n /** Text printed before each entry. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\nexport class ConsoleErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n\n constructor(options: ConsoleErrorOptions = {}) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n console.error(this.prefix, entry);\n return { ok: true };\n } catch (error: unknown) {\n return {\n ok: false,\n error: `ConsoleErrorAdapter failed: ${String(error)}`,\n };\n }\n }\n}\n","/**\n * Milliseconds to wait before retry number `retryNumber` (1 for the first):\n * `baseMs` doubling each time, never more than `maxMs`. Half of it is fixed and\n * half random, so many callers that failed together do not retry in lockstep.\n */\nexport function backoffDelayMs(\n retryNumber: number,\n baseMs: number,\n maxMs: number\n): number {\n const ceiling: number = Math.min(maxMs, baseMs * 2 ** (retryNumber - 1));\n return ceiling / 2 + (Math.random() * ceiling) / 2;\n}\n","/** The parts of the Web Crypto API used here. */\ninterface CryptoLike {\n randomUUID?: () => string;\n getRandomValues?: (array: Uint8Array) => Uint8Array;\n}\n\n/**\n * Returns a random UUID (version 4).\n *\n * `crypto.randomUUID` only exists in secure contexts (`https://` and\n * `localhost`), so on a plain `http://` page it is undefined and calling it\n * would throw. `crypto.getRandomValues` is available everywhere, so it is used\n * as the fallback, with `Math.random` as the last resort for runtimes without\n * any `crypto`.\n */\nexport function generateId(): string {\n const cryptoApi: CryptoLike | undefined = (\n globalThis as { crypto?: CryptoLike }\n ).crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === 'function') {\n return cryptoApi.randomUUID();\n }\n const bytes: Uint8Array = new Uint8Array(16);\n if (\n cryptoApi !== undefined &&\n typeof cryptoApi.getRandomValues === 'function'\n ) {\n cryptoApi.getRandomValues(bytes);\n } else {\n for (let index = 0; index < bytes.length; index += 1) {\n bytes[index] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex: string = Array.from(bytes, (byte: number): string =>\n byte.toString(16).padStart(2, '0')\n ).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","import type { ErrorLogEntry } from './types.js';\nimport { generateId } from './uuid.js';\n\nconst UUID_PATTERN: RegExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** True when the caller gave the entry its own `id`. */\nexport function hasCallerId(entry: ErrorLogEntry): boolean {\n return entry.id !== undefined;\n}\n\n/**\n * Returns the id to store an entry under: the caller's `id` (lower-cased, so the\n * same UUID always maps to the same record), or a new random UUID.\n *\n * @throws {RangeError} if the caller's `id` is not a UUID. Adapters call this\n * inside `log`'s try block, so the problem is reported as `ok: false`.\n */\nexport function resolveEntryId(entry: ErrorLogEntry): string {\n if (entry.id === undefined) {\n return generateId();\n }\n if (typeof entry.id !== 'string' || !UUID_PATTERN.test(entry.id)) {\n throw new RangeError(\n `entry.id must be a UUID such as one from crypto.randomUUID(), received ${JSON.stringify(entry.id)}`\n );\n }\n return entry.id.toLowerCase();\n}\n","/** Default time to wait for a sink to answer, in milliseconds. */\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n\n/** Thrown (as a rejection) by `withTimeout` when the deadline passes first. */\nexport class SinkTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SinkTimeoutError';\n }\n}\n\n/**\n * Validates a `timeoutMs` option and applies the default.\n *\n * @throws {RangeError} unless the value is a positive number or `Infinity`.\n */\nexport function resolveTimeoutMs(\n value: number | undefined,\n adapterName: string\n): number {\n const timeoutMs: number = value ?? DEFAULT_TIMEOUT_MS;\n if (Number.isNaN(timeoutMs) || timeoutMs <= 0) {\n throw new RangeError(\n `${adapterName}: timeoutMs must be a positive number or Infinity, received ${String(value)}.`\n );\n }\n return timeoutMs;\n}\n\n/** What a caller should know when a sink did not answer in time. */\nexport function timeoutMessage(\n sink: string,\n timeoutMs: number,\n action: string = 'respond'\n): string {\n return `${sink} did not ${action} within ${timeoutMs} ms, so the entry may or may not have been stored (the operation can still finish later, so retrying could store it twice)`;\n}\n\n/**\n * Runs `start()` and waits for its result, but rejects with `message` if it takes\n * longer than `timeoutMs`. Drivers cannot generally cancel a call, so the call may\n * still complete after the deadline; `log()` just stops waiting for it. A `start`\n * that throws synchronously rejects too. `Infinity` waits without limit.\n */\nexport function withTimeout<T>(\n start: () => PromiseLike<T> | T,\n timeoutMs: number,\n message: string\n): Promise<T> {\n if (timeoutMs === Infinity) {\n return new Promise<T>((resolve, reject): void => {\n try {\n resolve(start() as T | PromiseLike<T>);\n } catch (error: unknown) {\n reject(error);\n }\n });\n }\n return new Promise<T>(\n (resolve: (value: T) => void, reject: (reason: unknown) => void): void => {\n const timer = setTimeout((): void => {\n reject(new SinkTimeoutError(message));\n }, timeoutMs);\n const settle =\n <A extends unknown[]>(\n done: (...args: A) => void\n ): ((...args: A) => void) =>\n (...args: A): void => {\n clearTimeout(timer);\n done(...args);\n };\n try {\n Promise.resolve(start()).then(settle(resolve), settle(reject));\n } catch (error: unknown) {\n settle(reject)(error);\n }\n }\n );\n}\n","import { backoffDelayMs } from '../backoff.js';\nimport { DEFAULT_PREFIX } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport { resolveTimeoutMs, timeoutMessage } from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface WebhookErrorOptions {\n /** Extra headers merged into the request. */\n headers?: Record<string, string>;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * Allow a plain `http://` URL. Entries contain error messages and stack\n * traces, so they are only sent over `https://` unless this is `true`.\n * `http://localhost`, `127.0.0.1` and `[::1]` are always allowed.\n * Defaults to `false`.\n */\n allowInsecure?: boolean;\n /**\n * How long to wait for the webhook to answer, in milliseconds. Defaults to\n * 10000. Pass `Infinity` to wait without limit. When it passes, the request is\n * cancelled, so the connection is closed rather than left open; the server may\n * still have received the entry.\n */\n timeoutMs?: number;\n /**\n * How many times to try again after a failure that may go away on its own: a\n * network error, a timeout, HTTP 408, 425, 429 or any 5xx. Other 4xx answers and\n * redirects are never retried. Defaults to 0 (send once). The wait doubles\n * after each attempt, starting at `retryDelayMs`, with jitter, and a\n * `Retry-After` header is honored. `timeoutMs` applies to each attempt, and\n * `log()` resolves only after the last one. Every attempt sends the same body,\n * and with `retries` above 0 the body always carries an `id`, so the receiver\n * can drop a repeat if an earlier attempt did arrive.\n */\n retries?: number;\n /** Wait before the first retry, in milliseconds. Defaults to 500. */\n retryDelayMs?: number;\n /** Longest wait between attempts, in milliseconds. Defaults to 30000. */\n maxRetryDelayMs?: number;\n /**\n * Shared secret for signing each request, so the receiver can verify the\n * entry came from you and was not altered. Adds `X-Signature-Timestamp`\n * (Unix seconds) and `X-Signature-256` (`sha256=` and the hex HMAC-SHA256 of\n * `<timestamp>.<body>`). The receiver should recompute it and reject old\n * timestamps. Needs Web Crypto (Node 20+, browsers, Deno, Bun); without it\n * `log()` fails rather than sending an unsigned entry.\n */\n signingSecret?: string;\n}\n\nconst DEFAULT_RETRY_DELAY_MS = 500;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst RETRYABLE_STATUSES: ReadonlySet<number> = new Set([408, 425, 429]);\n\n/** What one attempt came to. */\ntype Attempt =\n | { ok: true }\n | { ok: false; reason: string; retryable: boolean; retryAfterMs?: number };\n\n/** The parts of Web Crypto used for signing. */\ninterface SubtleLike {\n importKey(\n format: 'raw',\n keyData: Uint8Array,\n algorithm: { name: 'HMAC'; hash: 'SHA-256' },\n extractable: boolean,\n usages: string[]\n ): Promise<unknown>;\n sign(algorithm: 'HMAC', key: unknown, data: Uint8Array): Promise<ArrayBuffer>;\n}\n\n/** Signing could not be done at all, as opposed to the webhook failing. */\nclass SigningUnavailableError extends Error {}\n\n/** Hex HMAC-SHA256 of `message`. @throws {SigningUnavailableError} when Web Crypto is missing. */\nasync function hmacSha256Hex(secret: string, message: string): Promise<string> {\n const subtle: SubtleLike | undefined = (\n globalThis as { crypto?: { subtle?: SubtleLike } }\n ).crypto?.subtle;\n if (subtle === undefined) {\n throw new SigningUnavailableError(\n 'signingSecret needs Web Crypto (crypto.subtle), which this runtime does not provide, so the entry was not sent unsigned'\n );\n }\n const encoder = new TextEncoder();\n const key: unknown = await subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const signature: ArrayBuffer = await subtle.sign(\n 'HMAC',\n key,\n encoder.encode(message)\n );\n return Array.from(new Uint8Array(signature), (byte: number): string =>\n byte.toString(16).padStart(2, '0')\n ).join('');\n}\n\n/** Reads `Retry-After` (seconds or an HTTP date) as milliseconds, or undefined. */\nfunction parseRetryAfterMs(\n value: string | null | undefined\n): number | undefined {\n if (value === null || value === undefined || value.trim() === '') {\n return undefined;\n }\n const seconds: number = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return seconds * 1000;\n }\n const date: number = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction validateRetryOptions(options: WebhookErrorOptions): {\n retries: number;\n retryDelayMs: number;\n maxRetryDelayMs: number;\n} {\n const retries: number = options.retries ?? 0;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new RangeError(\n `WebhookErrorAdapter: retries must be a whole number of 0 or more, received ${String(options.retries)}.`\n );\n }\n const retryDelayMs: number = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n if (!Number.isFinite(retryDelayMs) || retryDelayMs <= 0) {\n throw new RangeError(\n `WebhookErrorAdapter: retryDelayMs must be a positive number, received ${String(options.retryDelayMs)}.`\n );\n }\n const maxRetryDelayMs: number =\n options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n if (!Number.isFinite(maxRetryDelayMs) || maxRetryDelayMs < retryDelayMs) {\n throw new RangeError(\n `WebhookErrorAdapter: maxRetryDelayMs must be a number no smaller than retryDelayMs (${retryDelayMs}), received ${String(options.maxRetryDelayMs)}.`\n );\n }\n if (options.signingSecret === '') {\n throw new RangeError(\n 'WebhookErrorAdapter: signingSecret must not be empty; leave it out to send unsigned.'\n );\n }\n return { retries, retryDelayMs, maxRetryDelayMs };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve: () => void): void => {\n setTimeout(resolve, ms);\n });\n}\n\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/** Parses the URL, and rejects one that is invalid or would send entries unencrypted. */\nfunction parseWebhookUrl(url: string, allowInsecure: boolean): URL {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new RangeError(\n 'WebhookErrorAdapter: the webhook URL is not a valid absolute URL (for example https://example.com/errors).'\n );\n }\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n if (\n parsed.protocol === 'http:' &&\n (allowInsecure || LOOPBACK_HOSTS.has(parsed.hostname))\n ) {\n return parsed;\n }\n throw new RangeError(\n `WebhookErrorAdapter: the webhook URL must use https:// because entries contain error messages and stack traces (received ${parsed.protocol}//${parsed.host}). Pass allowInsecure: true to send over http.`\n );\n}\n\nexport class WebhookErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private readonly retries: number;\n private readonly retryDelayMs: number;\n private readonly maxRetryDelayMs: number;\n /** Scheme and host only. Webhook URLs often carry a secret in the path or query. */\n private readonly target: string;\n\n /** @throws {RangeError} if `url` is not a valid `https://` URL (see `allowInsecure`) or a timeout, retry or signing option is invalid. */\n constructor(\n private readonly url: string,\n private readonly options: WebhookErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'WebhookErrorAdapter');\n ({\n retries: this.retries,\n retryDelayMs: this.retryDelayMs,\n maxRetryDelayMs: this.maxRetryDelayMs,\n } = validateRetryOptions(options));\n this.target = parseWebhookUrl(url, options.allowInsecure ?? false).origin;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n let body: string;\n try {\n // A caller-supplied id goes along so the receiver can drop a repeat; a\n // retry can repeat too, so it gets one as well.\n body = JSON.stringify(\n hasCallerId(entry) || this.retries > 0\n ? { id: resolveEntryId(entry), ...serializeEntry(entry) }\n : serializeEntry(entry)\n );\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n return this.fail(\n `could not prepare the entry: ${this.redact(describeError(error))}`\n );\n }\n\n return this.deliver(body);\n }\n\n /**\n * Sends several entries in one request, as a JSON array of the same records\n * `log` sends one at a time (each with an `id`). `BatchingErrorAdapter` uses\n * this when it wraps a webhook, so the receiver must accept an array. The\n * whole request succeeds or fails together.\n */\n async logBatch(entries: ErrorLogEntry<TCode>[]): Promise<ErrorLogResult> {\n if (entries.length === 0) {\n return { ok: true };\n }\n let body: string;\n try {\n body = JSON.stringify(\n entries.map((entry: ErrorLogEntry<TCode>) => ({\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n }))\n );\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n return this.fail(\n `could not prepare the entries: ${this.redact(describeError(error))}`\n );\n }\n return this.deliver(body);\n }\n\n /** Sends one prepared body, trying again as `retries` allows. */\n private async deliver(body: string): Promise<ErrorLogResult> {\n let attempt: Attempt = await this.send(body);\n let attempts = 1;\n while (!attempt.ok && attempt.retryable && attempts <= this.retries) {\n await sleep(this.retryWaitMs(attempts, attempt.retryAfterMs));\n attempt = await this.send(body);\n attempts += 1;\n }\n if (attempt.ok) {\n return { ok: true };\n }\n return this.fail(\n attempts > 1\n ? `${attempt.reason} (after ${attempts} attempts)`\n : attempt.reason\n );\n }\n\n /** Milliseconds to wait before retry number `retryNumber` (1 for the first). */\n private retryWaitMs(retryNumber: number, retryAfterMs?: number): number {\n if (retryAfterMs !== undefined) {\n return Math.min(retryAfterMs, this.maxRetryDelayMs);\n }\n return backoffDelayMs(retryNumber, this.retryDelayMs, this.maxRetryDelayMs);\n }\n\n private async send(body: string): Promise<Attempt> {\n const controller: AbortController = new AbortController();\n let timedOut = false;\n const timer: ReturnType<typeof setTimeout> | undefined =\n this.timeoutMs === Infinity\n ? undefined\n : setTimeout((): void => {\n timedOut = true;\n controller.abort();\n }, this.timeoutMs);\n try {\n const response: Response = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(await this.signatureHeaders(body)),\n },\n body,\n // A redirect would turn the POST into a GET without a body (302, 303)\n // or re-send the entry and headers to another host (307, 308).\n redirect: 'manual',\n signal: controller.signal,\n });\n if (response.type === 'opaqueredirect') {\n return this.rejected(this.redirectMessage('a redirect'));\n }\n if (!response.ok) {\n if (response.status >= 300 && response.status < 400) {\n return this.rejected(this.redirectMessage(`HTTP ${response.status}`));\n }\n return {\n ok: false,\n reason: `webhook ${this.target} responded with HTTP ${response.status}`,\n retryable:\n response.status >= 500 || RETRYABLE_STATUSES.has(response.status),\n retryAfterMs: parseRetryAfterMs(response.headers?.get('retry-after')),\n };\n }\n return { ok: true };\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n if (error instanceof SigningUnavailableError) {\n // A missing Web Crypto will not appear on a second try.\n return this.rejected(error.message);\n }\n if (timedOut) {\n return {\n ok: false,\n reason: timeoutMessage(`webhook ${this.target}`, this.timeoutMs),\n retryable: true,\n };\n }\n return {\n ok: false,\n reason: `could not reach webhook ${this.target}: ${this.redact(describeError(error))}`,\n retryable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Timestamp and signature headers, or none when no `signingSecret` is set. */\n private async signatureHeaders(\n body: string\n ): Promise<Record<string, string>> {\n const secret: string | undefined = this.options.signingSecret;\n if (secret === undefined) {\n return {};\n }\n const timestamp: string = String(Math.floor(Date.now() / 1000));\n const signature: string = await hmacSha256Hex(\n secret,\n `${timestamp}.${body}`\n );\n return {\n 'X-Signature-Timestamp': timestamp,\n 'X-Signature-256': `sha256=${signature}`,\n };\n }\n\n private rejected(reason: string): Attempt {\n return { ok: false, reason, retryable: false };\n }\n\n private redirectMessage(what: string): string {\n return `webhook ${this.target} responded with ${what}, which is not followed because the entry would not arrive intact; use the final URL instead`;\n }\n\n /** Replaces the full URL in a message with its origin, in case the runtime put it there. */\n private redact(message: string): string {\n return message.split(this.url).join(this.target);\n }\n\n private fail(reason: string): ErrorLogResult {\n return { ok: false, error: `WebhookErrorAdapter failed: ${reason}` };\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** The subset of a MongoDB `Collection` this adapter uses. */\nexport interface CollectionLike {\n insertOne(doc: object): Promise<unknown>;\n}\n\nexport interface MongoDbErrorOptions {\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the insert before giving up, in milliseconds. Defaults to\n * 10000. Pass `Infinity` to wait without limit. The call cannot be cancelled,\n * so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** MongoDB reports a repeated `_id` as error code 11000. */\nfunction isDuplicateKeyError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 11000\n );\n}\n\nexport class MongoDbErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly collection: CollectionLike,\n options: MongoDbErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'MongoDbErrorAdapter');\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const document: object = hasCallerId(entry)\n ? { _id: resolveEntryId(entry), ...serializeEntry(entry) }\n : serializeEntry(entry);\n await withTimeout(\n (): Promise<unknown> => this.collection.insertOne(document),\n this.timeoutMs,\n timeoutMessage('MongoDB', this.timeoutMs)\n );\n return { ok: true };\n } catch (error: unknown) {\n if (hasCallerId(entry) && isDuplicateKeyError(error)) {\n // The same id was stored before: this call is a retry that already succeeded.\n return { ok: true };\n }\n return reportFailure({\n adapterName: 'MongoDbErrorAdapter',\n prefix: this.prefix,\n action: 'to insert entry',\n error,\n });\n }\n }\n}\n","import {\n DEFAULT_TIMEOUT_MS,\n SinkTimeoutError,\n resolveTimeoutMs,\n withTimeout,\n} from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface CompositeErrorOptions {\n /**\n * How long to wait for each adapter before reporting it as failed, in\n * milliseconds. Defaults to 30000, longer than the 10000 default of the built-in\n * adapters so their own, more specific timeout messages come first. This only\n * matters for a custom adapter that can hang: one stuck adapter then cannot\n * hold back the result of the others. Pass `Infinity` to wait without limit.\n */\n timeoutMs?: number;\n}\n\n/** Default wait for each adapter of a composite. */\nexport const DEFAULT_COMPOSITE_TIMEOUT_MS: number = DEFAULT_TIMEOUT_MS * 3;\n\n/** Sends every entry to several adapters. Succeeds only if all of them do. */\nexport class CompositeErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly adapters: ReadonlyArray<ErrorLogAdapter<TCode>>,\n options: CompositeErrorOptions = {}\n ) {\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs ?? DEFAULT_COMPOSITE_TIMEOUT_MS,\n 'CompositeErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n const results: ErrorLogResult[] = await Promise.all(\n this.adapters.map(\n async (\n adapter: ErrorLogAdapter<TCode>,\n index: number\n ): Promise<ErrorLogResult> => {\n try {\n return await withTimeout(\n (): Promise<ErrorLogResult> => adapter.log(entry),\n this.timeoutMs,\n `adapter ${index} (${adapter.constructor.name}) did not return within ${this.timeoutMs} ms, so the entry may or may not have been stored there`\n );\n } catch (error: unknown) {\n if (error instanceof SinkTimeoutError) {\n return { ok: false, error: error.message };\n }\n // Adapters should not throw, but a faulty custom one must not break the others.\n return {\n ok: false,\n error: `adapter threw instead of returning a result: ${String(error)}`,\n };\n }\n }\n )\n );\n const failures: string[] = results.flatMap(\n (result: ErrorLogResult): string[] =>\n result.ok ? [] : [result.error ?? 'unknown failure']\n );\n if (failures.length === 0) {\n return { ok: true };\n }\n return {\n ok: false,\n error: `${failures.length} of ${results.length} adapters failed: ${failures.join('; ')}`,\n };\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default object store name. */\nexport const DEFAULT_STORE_NAME = 'error_log';\n\n/** Event handler slot. Typed loosely so real DOM `IDBTransaction` objects fit. */\ntype IdbHandler = ((event: never) => unknown) | null;\n\n/** The parts of a DOM event this adapter calls. */\ninterface IdbEventLike {\n preventDefault?(): void;\n stopPropagation?(): void;\n}\n\n/** The error object of a failed request or transaction (a `DOMException`). */\ninterface IdbErrorLike {\n name?: string;\n message?: string;\n}\n\n/** The subset of an `IDBRequest` this adapter uses. */\nexport interface IdbRequestLike {\n onerror: IdbHandler;\n readonly error?: IdbErrorLike | null;\n}\n\n/** The subset of an `IDBObjectStore` this adapter uses. */\nexport interface IdbObjectStoreLike {\n /** Returns an `IDBRequest`. Fakes that return nothing also work. */\n add(value: unknown): unknown;\n /**\n * Replaces the record with the same key. Used for entries that carry their own\n * `id`, so logging one again does not fail on the existing key. A real\n * `IDBObjectStore` has it; a fake without it falls back to `add`.\n */\n put?(value: unknown): unknown;\n}\n\n/** The subset of an `IDBTransaction` this adapter uses. */\nexport interface IdbTransactionLike {\n objectStore(name: string): IdbObjectStoreLike;\n oncomplete: IdbHandler;\n onerror: IdbHandler;\n onabort: IdbHandler;\n readonly error?: IdbErrorLike | null;\n}\n\n/** The subset of an `IDBDatabase` this adapter uses. */\nexport interface IdbDatabaseLike {\n transaction(storeNames: string, mode: 'readwrite'): IdbTransactionLike;\n}\n\n/** The subset of an `IDBDatabase` available inside `onupgradeneeded`. */\nexport interface IdbUpgradeDatabaseLike {\n readonly objectStoreNames: { contains(name: string): boolean };\n createObjectStore(\n name: string,\n options: { keyPath: string }\n ): { createIndex(name: string, keyPath: string): unknown };\n}\n\n/** A stored record: the serialized entry plus its generated UUID key. */\nexport interface IndexedDbErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface IndexedDbErrorOptions {\n /** Object store to write to. Defaults to `error_log`. */\n storeName?: string;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the transaction to finish, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. A transaction can stay\n * pending when a version change in another tab blocks it, or when the browser\n * has evicted the origin's storage. The write may still finish afterwards.\n */\n timeoutMs?: number;\n}\n\n/**\n * Creates the error log object store (UUID `id` key, `timestamp` index).\n * Call it from your `onupgradeneeded` handler. Does nothing if it already exists.\n */\nexport function createErrorLogStore(\n database: IdbUpgradeDatabaseLike,\n storeName: string = DEFAULT_STORE_NAME\n): void {\n if (database.objectStoreNames.contains(storeName)) {\n return;\n }\n database\n .createObjectStore(storeName, { keyPath: 'id' })\n .createIndex('timestamp', 'timestamp');\n}\n\nexport class IndexedDbErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly storeName: string;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly database: IdbDatabaseLike,\n options: IndexedDbErrorOptions = {}\n ) {\n this.storeName = options.storeName ?? DEFAULT_STORE_NAME;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'IndexedDbErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: IndexedDbErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n await this.write(record, hasCallerId(entry));\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'IndexedDbErrorAdapter',\n prefix: this.prefix,\n action: `to write to object store \"${this.storeName}\"`,\n error,\n });\n }\n }\n\n /**\n * Resolves once the transaction commits; rejects if the request fails, the\n * transaction aborts, or it does not finish within `timeoutMs`.\n *\n * Error events are handled here (`preventDefault` and `stopPropagation`), so a\n * failed log write does not reach the `onerror` handler an app may have set on\n * its own database object.\n */\n private write(\n record: IndexedDbErrorRecord,\n replaceExisting: boolean\n ): Promise<void> {\n return withTimeout(\n (): Promise<void> =>\n new Promise<void>(\n (resolve: () => void, reject: (reason: unknown) => void): void => {\n const transaction: IdbTransactionLike = this.database.transaction(\n this.storeName,\n 'readwrite'\n );\n const fail =\n (what: string, source: { error?: IdbErrorLike | null }) =>\n (event?: IdbEventLike): void => {\n event?.preventDefault?.();\n event?.stopPropagation?.();\n reject(new Error(`${what}: ${describeDomError(source.error)}`));\n };\n transaction.oncomplete = (): void => resolve();\n transaction.onerror = fail('transaction failed', transaction);\n transaction.onabort = fail('transaction aborted', transaction);\n const store: IdbObjectStoreLike = transaction.objectStore(\n this.storeName\n );\n const request: unknown =\n replaceExisting && typeof store.put === 'function'\n ? store.put(record)\n : store.add(record);\n if (typeof request === 'object' && request !== null) {\n // The request error holds the details (for example a\n // ConstraintError); the transaction's own error is null for it.\n (request as IdbRequestLike).onerror = fail(\n 'request failed',\n request as IdbRequestLike\n );\n }\n }\n ),\n this.timeoutMs,\n timeoutMessage('IndexedDB', this.timeoutMs, 'finish the transaction')\n );\n }\n}\n\n/** `ConstraintError: Unable to add key to index...`, or a note that nothing was given. */\nfunction describeDomError(error: IdbErrorLike | null | undefined): string {\n if (error === null || error === undefined) {\n return 'no details provided';\n }\n const text: string = [error.name, error.message]\n .filter((part): part is string => typeof part === 'string' && part !== '')\n .join(': ');\n return text === '' ? 'no details provided' : text;\n}\n","/** Default table name for the SQL, SQLite and Supabase adapters. */\nexport const DEFAULT_TABLE_NAME = 'error_log';\n\nconst IDENTIFIER_PATTERN: RegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Throws a descriptive `RangeError` unless `value` is a plain SQL identifier:\n * letters, digits and underscores, not starting with a digit. Quoting is then\n * never needed, and nothing in the name can alter a statement.\n *\n * @param label What the value is, for the message, such as `table name`.\n * @param owner Who is asking, such as `PostgresErrorAdapter`.\n * @param maxLength Longest allowed length, when the caller needs a limit.\n */\nexport function assertIdentifier(\n value: string,\n label: string,\n owner: string,\n maxLength?: number\n): void {\n if (!IDENTIFIER_PATTERN.test(value)) {\n throw new RangeError(\n `${owner}: invalid ${label} \"${value}\". Use only letters, digits and underscores, and do not start with a digit.`\n );\n }\n if (maxLength !== undefined && value.length > maxLength) {\n throw new RangeError(\n `${owner}: ${label} \"${value}\" is ${value.length} characters long, but the limit is ${maxLength}.`\n );\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * The only thing this adapter needs from a SQLite driver: run one statement\n * with positional (`?`) parameters. `bun:sqlite` already matches this shape.\n * Other drivers need a one-line wrapper, for example:\n *\n * ```ts\n * // better-sqlite3 or node:sqlite\n * const client: SqliteClientLike = {\n * run: (sql, params) => db.prepare(sql).run(...params),\n * };\n * ```\n */\nexport interface SqliteClientLike {\n run(sql: string, params: unknown[]): unknown;\n}\n\nexport interface SqliteErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only. */\n tableName?: string;\n /** Create the table (and index) on first use. Defaults to `true`. */\n createTable?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the driver before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Throws a descriptive error unless `tableName` is a plain SQL identifier. */\nfunction assertValidTableName(tableName: string): void {\n assertIdentifier(tableName, 'table name', 'SqliteErrorAdapter');\n}\n\n/**\n * Returns the statements that create the error log table and its index.\n * Use this to run your own migration when `createTable` is disabled.\n */\nexport function buildCreateErrorLogTableSql(\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName);\n return [\n `CREATE TABLE IF NOT EXISTS ${tableName} (` +\n 'id TEXT PRIMARY KEY NOT NULL, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause TEXT, ' +\n 'source TEXT, ' +\n 'occurred_at TEXT NOT NULL, ' +\n \"created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))\" +\n ')',\n `CREATE INDEX IF NOT EXISTS ${tableName}_occurred_at_idx ON ${tableName} (occurred_at)`,\n ];\n}\n\nexport class SqliteErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly createTable: boolean;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private tableReady: Promise<void> | null = null;\n\n /** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */\n constructor(\n private readonly client: SqliteClientLike,\n options: SqliteErrorOptions = {}\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertValidTableName(this.tableName);\n this.createTable = options.createTable ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'SqliteErrorAdapter');\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n await this.ensureTable();\n const serialized = serializeEntry(entry);\n await this.run(\n `INSERT INTO ${this.tableName} (id, code, message, cause, source, occurred_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO NOTHING`,\n [\n resolveEntryId(entry),\n serialized.code,\n serialized.message,\n serialized.cause === undefined\n ? null\n : JSON.stringify(serialized.cause),\n serialized.source ?? null,\n serialized.timestamp,\n ]\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'SqliteErrorAdapter',\n prefix: this.prefix,\n action: `to write to table \"${this.tableName}\"`,\n error,\n });\n }\n }\n\n /** Creates the table once. A failed attempt is retried on the next log call. */\n private ensureTable(): Promise<void> {\n if (!this.createTable) {\n return Promise.resolve();\n }\n if (this.tableReady === null) {\n this.tableReady = this.runCreateStatements().catch(\n (error: unknown): never => {\n this.tableReady = null;\n throw error;\n }\n );\n }\n return this.tableReady;\n }\n\n private async runCreateStatements(): Promise<void> {\n for (const sql of buildCreateErrorLogTableSql(this.tableName)) {\n await this.run(sql, []);\n }\n }\n\n /** Runs one statement, giving up waiting after `timeoutMs`. */\n private run(sql: string, params: unknown[]): Promise<unknown> {\n return withTimeout(\n (): unknown => this.client.run(sql, params),\n this.timeoutMs,\n timeoutMessage('SQLite', this.timeoutMs)\n );\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default storage key. */\nexport const DEFAULT_STORAGE_KEY = 'error_log';\n\n/** Default number of entries kept before the oldest are dropped. */\nexport const DEFAULT_MAX_ENTRIES = 100;\n\n/**\n * The subset of the Web Storage API this adapter uses. Both `sessionStorage`\n * and `localStorage` fit.\n */\nexport interface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/**\n * The part of `navigator.locks` this adapter uses. `localStorage` is shared by\n * every tab of an origin and has no atomic update, so a lock keeps two tabs from\n * overwriting each other's entry.\n */\nexport interface LockManagerLike {\n request<T>(name: string, callback: () => T | Promise<T>): Promise<T>;\n}\n\n/** A stored record: the serialized entry plus its generated UUID. */\nexport interface WebStorageErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface WebStorageErrorOptions {\n /** Key the entries are stored under, as one JSON array. Defaults to `error_log`. */\n key?: string;\n /** Most entries to keep. The oldest are dropped first. Defaults to `100`. */\n maxEntries?: number;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * Lock manager that makes tabs sharing a `localStorage` take turns. Defaults to\n * `navigator.locks` when the browser has it. This reduces lost entries but does\n * not remove them: a tab in another process can still read a stale copy\n * (measured in Chromium). Pass `null` to turn locking off.\n */\n locks?: LockManagerLike | null;\n}\n\n/** Browsers report a full store with different names and codes. */\nfunction isQuotaError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n const { name, code } = error as { name?: unknown; code?: unknown };\n return (\n (typeof name === 'string' && name.toLowerCase().includes('quota')) ||\n code === 22 ||\n code === 1014\n );\n}\n\n/**\n * Keeps the newest entries in a Web Storage object (`sessionStorage` or\n * `localStorage`) as a JSON array under one key, capped at `maxEntries`.\n * When the store is full, the oldest entries are dropped until the new one fits.\n *\n * `sessionStorage` belongs to one tab, so it is safe. `localStorage` is shared by\n * every tab of the origin and has no atomic update: two tabs logging at the same\n * moment can overwrite each other's entry while both get `ok: true`. In Chromium,\n * tabs in separate processes (the usual case) even read a stale copy, so a lock\n * reduces the loss without removing it. Where tabs may log at the same time and\n * every entry matters, use `IndexedDbErrorAdapter`, whose writes are transactional.\n */\nexport class WebStorageErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly key: string;\n private readonly maxEntries: number;\n private readonly prefix: string;\n private readonly locks: LockManagerLike | null;\n\n /** @throws {RangeError} if `options.maxEntries` is not a positive integer. */\n constructor(\n private readonly storage: StorageLike,\n options: WebStorageErrorOptions = {}\n ) {\n this.key = options.key ?? DEFAULT_STORAGE_KEY;\n this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.locks =\n options.locks === undefined ? defaultLockManager() : options.locks;\n if (!Number.isInteger(this.maxEntries) || this.maxEntries < 1) {\n throw new RangeError(\n `WebStorageErrorAdapter: maxEntries must be a positive integer, received ${String(options.maxEntries)}.`\n );\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: WebStorageErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n await this.appendLocked(record);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'WebStorageErrorAdapter',\n prefix: this.prefix,\n action: `to write to storage key \"${this.key}\"`,\n error,\n });\n }\n }\n\n /**\n * Appends under a cross-tab lock when one is available. If the lock itself\n * cannot be taken (for example a sandboxed frame), the entry is still written\n * without it rather than lost.\n */\n private async appendLocked(record: WebStorageErrorRecord): Promise<void> {\n if (this.locks === null) {\n this.append(record);\n return;\n }\n let appended = false;\n try {\n await this.locks.request(`error-log-adapter:${this.key}`, (): void => {\n appended = true;\n this.append(record);\n });\n } catch (error: unknown) {\n if (appended) {\n throw error;\n }\n this.append(record);\n }\n }\n\n /** Reads, appends, trims, and writes back. Runs without awaiting, so calls in one tab never interleave. */\n private append(record: WebStorageErrorRecord): void {\n // An entry logged again with the same id replaces its earlier record.\n const records: WebStorageErrorRecord[] = [\n ...this.readRecords().filter(\n (existing: WebStorageErrorRecord): boolean => existing?.id !== record.id\n ),\n record,\n ].slice(-this.maxEntries);\n for (;;) {\n try {\n this.storage.setItem(this.key, JSON.stringify(records));\n return;\n } catch (error: unknown) {\n if (!isQuotaError(error) || records.length <= 1) {\n throw error;\n }\n records.shift();\n }\n }\n }\n\n /** Returns the stored records, or none if the value is missing or unreadable. */\n private readRecords(): WebStorageErrorRecord[] {\n const stored: string | null = this.storage.getItem(this.key);\n if (stored === null) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(stored);\n if (Array.isArray(parsed)) {\n return parsed as WebStorageErrorRecord[];\n }\n this.setAside(stored, 'did not hold an array');\n } catch (error: unknown) {\n this.setAside(stored, `held invalid JSON (${describeError(error)})`);\n }\n return [];\n }\n\n /**\n * Copies an unusable value to `<key>.corrupt` before it is replaced, so data\n * the host app stored under the same key is not silently destroyed. Only the\n * first one is kept; a later unusable value would not overwrite it.\n */\n private setAside(stored: string, reason: string): void {\n const backupKey = `${this.key}${CORRUPT_KEY_SUFFIX}`;\n let savedTo = 'but it could not be copied aside';\n try {\n if (this.storage.getItem(backupKey) === null) {\n this.storage.setItem(backupKey, stored);\n savedTo = `and a copy was kept under \"${backupKey}\"`;\n } else {\n savedTo = `and \"${backupKey}\" already holds an earlier copy`;\n }\n } catch {\n // The store may be full; the original value is replaced regardless.\n }\n console.error(\n `${this.prefix} WebStorageErrorAdapter: storage key \"${this.key}\" ${reason}, so it was replaced, ${savedTo}.`\n );\n }\n}\n\n/** Suffix of the key an unusable value is copied to before being replaced. */\nexport const CORRUPT_KEY_SUFFIX = '.corrupt';\n\nfunction defaultLockManager(): LockManagerLike | null {\n const locks: unknown = (globalThis as { navigator?: { locks?: unknown } })\n .navigator?.locks;\n return typeof (locks as LockManagerLike | undefined)?.request === 'function'\n ? (locks as LockManagerLike)\n : null;\n}\n","/**\n * Runs tasks one after another, in the order they were queued. A task that\n * rejects does not stop the ones queued behind it.\n */\nexport class SerialQueue {\n private tail: Promise<unknown> = Promise.resolve();\n\n /** Starts `task` once every earlier task has finished, and resolves with its result. */\n run<T>(task: () => Promise<T>): Promise<T> {\n const result: Promise<T> = this.tail.then(task);\n this.tail = result.catch((): undefined => undefined);\n return result;\n }\n}\n","/**\n * The subset of `node:fs/promises` the file adapters use. Pass `fs.promises`\n * (or `node:fs/promises`) yourself, or leave `fs` unset in Node and Bun.\n */\nexport interface FileSystemLike {\n appendFile(path: string, data: string): Promise<unknown>;\n mkdir(path: string, options: { recursive: true }): Promise<unknown>;\n stat(path: string): Promise<{ size: number }>;\n rename(from: string, to: string): Promise<unknown>;\n rm(path: string, options: { force: true }): Promise<unknown>;\n}\n\n/** `FileSystemLike` plus the reading and replacing that whole-file writes need. */\nexport interface ReadWriteFileSystemLike extends FileSystemLike {\n readFile(path: string, encoding: 'utf8'): Promise<string>;\n writeFile(path: string, data: string): Promise<unknown>;\n}\n\n/** Loads `node:fs/promises`. Only call this from code that runs in Node or Bun. */\nexport async function loadNodeFileSystem(): Promise<ReadWriteFileSystemLike> {\n // A variable specifier keeps browser bundlers from resolving it.\n const moduleName: string = 'node:fs/promises';\n return (await import(moduleName)) as ReadWriteFileSystemLike;\n}\n\n/** A filesystem root: `/`, `\\\\`, `C:\\\\` or `C:/`, which always exists and cannot be created. */\nconst ROOT_PATTERN: RegExp = /^(?:[A-Za-z]:)?[\\\\/]+$/;\n\n/**\n * Returns the folder to create for a file path, or `null` when there is nothing\n * to create: the path has no folder part (`app.log`), or its folder is a\n * filesystem root that always exists (`/app.log`, `C:\\\\app.log`, `\\\\app.log`).\n * Calling `mkdir` on a Windows drive root fails with `EPERM`.\n */\nexport function parentDirectory(filePath: string): string | null {\n const lastSeparator: number = Math.max(\n filePath.lastIndexOf('/'),\n filePath.lastIndexOf('\\\\')\n );\n if (lastSeparator < 0) {\n return null;\n }\n const directory: string = filePath.slice(0, lastSeparator + 1);\n if (ROOT_PATTERN.test(directory)) {\n return null;\n }\n return filePath.slice(0, lastSeparator);\n}\n\n/** True for the error Node throws when a file or folder does not exist. */\nexport function isMissingFileError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","import { SerialQueue } from '../queue.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport {\n isMissingFileError,\n loadNodeFileSystem,\n parentDirectory,\n} from '../file-system.js';\nimport type { FileSystemLike } from '../file-system.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default number of rotated files kept when `maxBytes` is set. */\nexport const DEFAULT_MAX_FILES = 3;\n\nexport interface FlatFileErrorOptions {\n /** File system to write to. Defaults to `node:fs/promises`, loaded on first use. */\n fs?: FileSystemLike;\n /** Turns an entry into one line of text. Defaults to `formatErrorLogLine`. */\n formatLine?: (entry: SerializedErrorLogEntry) => string;\n /** Rotate the file before it would grow past this many bytes. Off by default. */\n maxBytes?: number;\n /** Rotated files to keep (`app.log.1` is newest). Defaults to `3`. Needs `maxBytes`. */\n maxFiles?: number;\n /** Create missing parent directories on first use. Defaults to `true`. */\n createDirectory?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/** Keeps one entry on one line by escaping line breaks. */\nfunction escapeLineBreaks(text: string): string {\n return text.replace(/\\r?\\n/g, '\\\\n');\n}\n\nfunction describeCause(cause: unknown): string {\n if (typeof cause === 'string') {\n return cause;\n }\n if (typeof cause === 'object' && cause !== null) {\n const { name, message } = cause as { name?: unknown; message?: unknown };\n if (typeof name === 'string' && typeof message === 'string') {\n return `${name}: ${message}`;\n }\n }\n try {\n return JSON.stringify(cause) ?? String(cause);\n } catch {\n return String(cause);\n }\n}\n\n/**\n * The default line format:\n * `2026-01-02T03:04:05.000Z [CODE] source: message (cause: Error: details)`.\n * `source` and `cause` are left out when the entry has none.\n */\nexport function formatErrorLogLine(entry: SerializedErrorLogEntry): string {\n const source: string = entry.source === undefined ? '' : `${entry.source}: `;\n const cause: string =\n entry.cause === undefined ? '' : ` (cause: ${describeCause(entry.cause)})`;\n return escapeLineBreaks(\n `${entry.timestamp} [${entry.code}] ${source}${entry.message}${cause}`\n );\n}\n\nfunction assertPositiveInteger(value: number, name: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throw new RangeError(\n `FlatFileErrorAdapter: ${name} must be a positive integer, received ${String(value)}.`\n );\n }\n}\n\n/**\n * Appends one text line per entry to a file, optionally rotating it by size.\n * Writes are queued, so concurrent `log` calls never interleave or race a rotation.\n */\nexport class FlatFileErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly formatLine: (entry: SerializedErrorLogEntry) => string;\n private readonly maxBytes: number | undefined;\n private readonly maxFiles: number;\n private readonly createDirectory: boolean;\n private readonly prefix: string;\n private fs: FileSystemLike | undefined;\n private directoryReady: boolean = false;\n private readonly queue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `maxBytes` or `maxFiles` is not a positive integer. */\n constructor(\n private readonly filePath: string,\n options: FlatFileErrorOptions = {}\n ) {\n this.fs = options.fs;\n this.formatLine = options.formatLine ?? formatErrorLogLine;\n this.maxBytes = options.maxBytes;\n this.maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;\n this.createDirectory = options.createDirectory ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n if (this.maxBytes !== undefined) {\n assertPositiveInteger(this.maxBytes, 'maxBytes');\n }\n assertPositiveInteger(this.maxFiles, 'maxFiles');\n }\n\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n return this.queue.run((): Promise<ErrorLogResult> => this.write(entry));\n }\n\n private async write(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const fs: FileSystemLike = await this.getFileSystem();\n const formatted: string = this.formatLine(serializeEntry(entry));\n const line: string = formatted.endsWith('\\n')\n ? formatted\n : `${formatted}\\n`;\n await this.prepareDirectory(fs);\n await this.rotateIfNeeded(fs, new TextEncoder().encode(line).length);\n await fs.appendFile(this.filePath, line);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'FlatFileErrorAdapter',\n prefix: this.prefix,\n action: `to append to \"${this.filePath}\"`,\n error,\n });\n }\n }\n\n /** Uses the injected file system, or loads `node:fs/promises` on first use. */\n private async getFileSystem(): Promise<FileSystemLike> {\n if (this.fs === undefined) {\n this.fs = await loadNodeFileSystem();\n }\n return this.fs;\n }\n\n private async prepareDirectory(fs: FileSystemLike): Promise<void> {\n if (!this.createDirectory || this.directoryReady) {\n return;\n }\n const directory: string | null = parentDirectory(this.filePath);\n if (directory !== null) {\n await fs.mkdir(directory, { recursive: true });\n }\n this.directoryReady = true;\n }\n\n /**\n * Moves `app.log` to `app.log.1` (shifting older files up, dropping the\n * oldest) when the next line would push it past `maxBytes`. A failed rotation\n * is reported, but the entry is still written so it is not lost.\n */\n private async rotateIfNeeded(\n fs: FileSystemLike,\n lineBytes: number\n ): Promise<void> {\n if (this.maxBytes === undefined) {\n return;\n }\n try {\n let currentSize: number;\n try {\n currentSize = (await fs.stat(this.filePath)).size;\n } catch (error: unknown) {\n if (isMissingFileError(error)) {\n return;\n }\n throw error;\n }\n if (currentSize === 0 || currentSize + lineBytes <= this.maxBytes) {\n return;\n }\n await fs.rm(`${this.filePath}.${this.maxFiles}`, { force: true });\n for (let index = this.maxFiles - 1; index >= 1; index -= 1) {\n await this.renameIfPresent(\n fs,\n `${this.filePath}.${index}`,\n `${this.filePath}.${index + 1}`\n );\n }\n await fs.rename(this.filePath, `${this.filePath}.1`);\n } catch (error: unknown) {\n console.error(\n `${this.prefix} FlatFileErrorAdapter: could not rotate \"${this.filePath}\", so it will keep growing: ${describeError(error)}`\n );\n }\n }\n\n private async renameIfPresent(\n fs: FileSystemLike,\n from: string,\n to: string\n ): Promise<void> {\n try {\n await fs.rename(from, to);\n } catch (error: unknown) {\n if (!isMissingFileError(error)) {\n throw error;\n }\n }\n }\n}\n","import { SerialQueue } from '../queue.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport {\n isMissingFileError,\n loadNodeFileSystem,\n parentDirectory,\n} from '../file-system.js';\nimport type { ReadWriteFileSystemLike } from '../file-system.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default number of spaces used to indent array mode output. */\nexport const DEFAULT_INDENT = 2;\n\n/** A stored record: the serialized entry plus its generated UUID. */\nexport interface JsonFileErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface JsonFileErrorOptions {\n /**\n * `ndjson` (default) appends one compact JSON object per line, which is cheap\n * and safe to tail. `array` keeps the whole file as one JSON array that is\n * rewritten on every entry.\n */\n mode?: 'ndjson' | 'array';\n /** Array mode only: most entries to keep. The oldest are dropped first. */\n maxEntries?: number;\n /** Array mode only: spaces of indentation. Defaults to `2`; `0` is compact. */\n indent?: number;\n /** File system to write to. Defaults to `node:fs/promises`, loaded on first use. */\n fs?: ReadWriteFileSystemLike;\n /** Create missing parent directories on first use. Defaults to `true`. */\n createDirectory?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Stores entries as JSON in a file (Node and Bun), either as newline-delimited\n * JSON or as one array. Writes are queued, so concurrent `log` calls stay in\n * order. Only one process should write to a given file.\n */\nexport class JsonFileErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly mode: 'ndjson' | 'array';\n private readonly maxEntries: number | undefined;\n private readonly indent: number;\n private readonly createDirectory: boolean;\n private readonly prefix: string;\n private fs: ReadWriteFileSystemLike | undefined;\n private directoryReady: boolean = false;\n private readonly queue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `maxEntries` or `indent` is invalid, or `maxEntries` is used outside array mode. */\n constructor(\n private readonly filePath: string,\n options: JsonFileErrorOptions = {}\n ) {\n this.mode = options.mode ?? 'ndjson';\n this.maxEntries = options.maxEntries;\n this.indent = options.indent ?? DEFAULT_INDENT;\n this.fs = options.fs;\n this.createDirectory = options.createDirectory ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n if (this.maxEntries !== undefined) {\n if (this.mode !== 'array') {\n throw new RangeError(\n `JsonFileErrorAdapter: maxEntries only applies to mode \"array\", but mode is \"${this.mode}\". Use mode \"array\" or remove maxEntries.`\n );\n }\n if (!Number.isInteger(this.maxEntries) || this.maxEntries < 1) {\n throw new RangeError(\n `JsonFileErrorAdapter: maxEntries must be a positive integer, received ${String(this.maxEntries)}.`\n );\n }\n }\n if (!Number.isInteger(this.indent) || this.indent < 0) {\n throw new RangeError(\n `JsonFileErrorAdapter: indent must be a non-negative integer, received ${String(this.indent)}.`\n );\n }\n }\n\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n return this.queue.run((): Promise<ErrorLogResult> => this.write(entry));\n }\n\n private async write(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: JsonFileErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const fs: ReadWriteFileSystemLike = await this.getFileSystem();\n await this.prepareDirectory(fs);\n if (this.mode === 'ndjson') {\n await fs.appendFile(this.filePath, `${JSON.stringify(record)}\\n`);\n } else {\n await this.rewriteArray(fs, record);\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'JsonFileErrorAdapter',\n prefix: this.prefix,\n action: `to write \"${this.filePath}\" in ${this.mode} mode`,\n error,\n });\n }\n }\n\n private async getFileSystem(): Promise<ReadWriteFileSystemLike> {\n if (this.fs === undefined) {\n this.fs = await loadNodeFileSystem();\n }\n return this.fs;\n }\n\n private async prepareDirectory(fs: ReadWriteFileSystemLike): Promise<void> {\n if (!this.createDirectory || this.directoryReady) {\n return;\n }\n const directory: string | null = parentDirectory(this.filePath);\n if (directory !== null) {\n await fs.mkdir(directory, { recursive: true });\n }\n this.directoryReady = true;\n }\n\n /**\n * Adds the record to the array and replaces the file. The new content is\n * written to a temporary file first and renamed into place, so a crash\n * mid-write cannot leave a half-written file.\n */\n private async rewriteArray(\n fs: ReadWriteFileSystemLike,\n record: JsonFileErrorRecord\n ): Promise<void> {\n // An entry logged again with the same id replaces its earlier record.\n const records: JsonFileErrorRecord[] = [\n ...(await this.readRecords(fs)).filter(\n (existing: JsonFileErrorRecord): boolean => existing?.id !== record.id\n ),\n record,\n ];\n const kept: JsonFileErrorRecord[] =\n this.maxEntries === undefined ? records : records.slice(-this.maxEntries);\n const temporaryPath: string = `${this.filePath}.tmp`;\n await fs.writeFile(\n temporaryPath,\n `${JSON.stringify(kept, null, this.indent)}\\n`\n );\n await fs.rename(temporaryPath, this.filePath);\n }\n\n /**\n * Returns the records already in the file. A missing or empty file has none.\n * A file that is not a JSON array is moved to `<path>.corrupt` (so nothing is\n * lost) and logging continues with a fresh file.\n */\n private async readRecords(\n fs: ReadWriteFileSystemLike\n ): Promise<JsonFileErrorRecord[]> {\n let content: string;\n try {\n content = await fs.readFile(this.filePath, 'utf8');\n } catch (error: unknown) {\n if (isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n if (content.trim() === '') {\n return [];\n }\n let problem: string;\n try {\n const parsed: unknown = JSON.parse(content);\n if (Array.isArray(parsed)) {\n return parsed as JsonFileErrorRecord[];\n }\n problem = 'did not hold a JSON array';\n } catch (error: unknown) {\n problem = `held invalid JSON (${describeError(error)})`;\n }\n const corruptPath: string = `${this.filePath}.corrupt`;\n await fs.rename(this.filePath, corruptPath);\n console.error(\n `${this.prefix} JsonFileErrorAdapter: \"${this.filePath}\" ${problem}, so it was moved to \"${corruptPath}\" and a new file was started.`\n );\n return [];\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * Longest allowed table name. The index name adds `_occurred_at_idx` (16\n * characters) and Postgres limits identifiers to 63.\n */\nexport const MAX_TABLE_NAME_LENGTH = 47;\n\nexport type SqlDialect = 'postgres' | 'mysql';\n\n/**\n * The only thing these adapters need from a database driver: run one\n * parameterized statement. `pg` (`Pool` / `Client`) and `mysql2/promise`\n * (`Pool` / `Connection`) both fit as they are.\n */\nexport interface SqlClientLike {\n query(sql: string, params: unknown[]): Promise<unknown>;\n}\n\nexport interface SqlErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only, up to 47 characters. */\n tableName?: string;\n /** Create the table (and index) on first use. Defaults to `true`. */\n createTable?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for each database statement before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Throws a descriptive error unless `tableName` is a short, plain SQL identifier. */\nfunction assertValidTableName(tableName: string, adapterName: string): void {\n assertIdentifier(tableName, 'table name', adapterName, MAX_TABLE_NAME_LENGTH);\n}\n\nfunction quoteIdentifier(dialect: SqlDialect, identifier: string): string {\n return dialect === 'postgres' ? `\"${identifier}\"` : `\\`${identifier}\\``;\n}\n\n/**\n * Returns the statements that create the error log table and its index for a\n * dialect. Use this to run your own migration when `createTable` is disabled.\n *\n * Postgres gets a native `UUID` key, `JSONB` for the cause, and `TIMESTAMPTZ`\n * columns. MySQL gets a `CHAR(36)` UUID key (it has no UUID type), a `JSON`\n * cause, and `DATETIME(3)` columns holding UTC times.\n */\nexport function buildErrorLogTableDdl(\n dialect: SqlDialect,\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName, 'buildErrorLogTableDdl');\n const table: string = quoteIdentifier(dialect, tableName);\n const index: string = quoteIdentifier(\n dialect,\n `${tableName}_occurred_at_idx`\n );\n if (dialect === 'postgres') {\n return [\n `CREATE TABLE IF NOT EXISTS ${table} (` +\n 'id UUID PRIMARY KEY, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause JSONB, ' +\n 'source TEXT, ' +\n 'occurred_at TIMESTAMPTZ NOT NULL, ' +\n 'created_at TIMESTAMPTZ NOT NULL DEFAULT now()' +\n ')',\n `CREATE INDEX IF NOT EXISTS ${index} ON ${table} (occurred_at)`,\n ];\n }\n // MySQL has no CREATE INDEX IF NOT EXISTS, so the index is part of the table.\n return [\n `CREATE TABLE IF NOT EXISTS ${table} (` +\n 'id CHAR(36) NOT NULL PRIMARY KEY, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause JSON NULL, ' +\n 'source TEXT NULL, ' +\n 'occurred_at DATETIME(3) NOT NULL, ' +\n 'created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), ' +\n `INDEX ${index} (occurred_at)` +\n ')',\n ];\n}\n\n/**\n * Returns the statement that widens the `code` and `source` columns of a MySQL\n * table created by `buildErrorLogTableDdl` in 0.4.0 or earlier, where they were\n * `VARCHAR(255)` and a longer value made the insert fail. Run it once.\n */\nexport function buildMySqlWidenColumnsSql(\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName, 'buildMySqlWidenColumnsSql');\n return [\n `ALTER TABLE ${quoteIdentifier('mysql', tableName)} MODIFY code TEXT NOT NULL, MODIFY source TEXT NULL`,\n ];\n}\n\n/** `2026-01-02T03:04:05.678Z` becomes `2026-01-02 03:04:05.678`, which MySQL accepts. */\nfunction toMysqlDateTime(date: Date): string {\n return date.toISOString().replace('T', ' ').replace('Z', '');\n}\n\n/** Shared by the Postgres and MySQL adapters: only the SQL text and time format differ. */\nabstract class SqlErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly createTable: boolean;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private tableReady: Promise<void> | null = null;\n\n protected constructor(\n private readonly client: SqlClientLike,\n private readonly dialect: SqlDialect,\n private readonly adapterName: string,\n options: SqlErrorOptions\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertValidTableName(this.tableName, adapterName);\n this.createTable = options.createTable ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, adapterName);\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const serialized = serializeEntry(entry);\n const occurredAt: Date = new Date(serialized.timestamp);\n if (Number.isNaN(occurredAt.getTime())) {\n throw new RangeError(\n `timestamp \"${serialized.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n const params: unknown[] = [\n resolveEntryId(entry),\n serialized.code,\n serialized.message,\n serialized.cause === undefined\n ? null\n : JSON.stringify(serialized.cause),\n serialized.source ?? null,\n this.dialect === 'postgres'\n ? occurredAt.toISOString()\n : toMysqlDateTime(occurredAt),\n ];\n await this.ensureTable();\n await this.query(this.insertSql(), params);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: this.adapterName,\n prefix: this.prefix,\n action: `to write to table \"${this.tableName}\"`,\n error,\n });\n }\n }\n\n private insertSql(): string {\n const placeholders: string =\n this.dialect === 'postgres'\n ? '$1, $2, $3, $4, $5, $6'\n : '?, ?, ?, ?, ?, ?';\n // A repeated entry id is a retry: keep the stored row instead of failing on the key.\n const onRepeat: string =\n this.dialect === 'postgres'\n ? 'ON CONFLICT (id) DO NOTHING'\n : 'ON DUPLICATE KEY UPDATE id = id';\n return `INSERT INTO ${quoteIdentifier(this.dialect, this.tableName)} (id, code, message, cause, source, occurred_at) VALUES (${placeholders}) ${onRepeat}`;\n }\n\n /** Creates the table once. A failed attempt is retried on the next log call. */\n private ensureTable(): Promise<void> {\n if (!this.createTable) {\n return Promise.resolve();\n }\n if (this.tableReady === null) {\n this.tableReady = this.runCreateStatements().catch(\n (error: unknown): never => {\n this.tableReady = null;\n throw error;\n }\n );\n }\n return this.tableReady;\n }\n\n private async runCreateStatements(): Promise<void> {\n for (const sql of buildErrorLogTableDdl(this.dialect, this.tableName)) {\n await this.query(sql, []);\n }\n }\n\n /** Runs one statement, giving up waiting after `timeoutMs`. */\n private query(sql: string, params: unknown[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> => this.client.query(sql, params),\n this.timeoutMs,\n timeoutMessage(\n this.dialect === 'postgres' ? 'Postgres' : 'MySQL',\n this.timeoutMs\n )\n );\n }\n}\n\n/**\n * Writes entries to Postgres. Pass a `pg` `Pool` or `Client`.\n *\n * ```ts\n * const adapter = new PostgresErrorAdapter(new Pool({ connectionString }));\n * ```\n */\nexport class PostgresErrorAdapter<\n TCode extends string = string,\n> extends SqlErrorAdapter<TCode> {\n /** @throws {RangeError} if `options.tableName` is not a plain identifier of 47 characters or fewer. */\n constructor(client: SqlClientLike, options: SqlErrorOptions = {}) {\n super(client, 'postgres', 'PostgresErrorAdapter', options);\n }\n}\n\n/**\n * Writes entries to MySQL or MariaDB. Pass a `mysql2/promise` `Pool` or `Connection`.\n *\n * ```ts\n * const adapter = new MySqlErrorAdapter(mysql.createPool(connectionUri));\n * ```\n */\nexport class MySqlErrorAdapter<\n TCode extends string = string,\n> extends SqlErrorAdapter<TCode> {\n /** @throws {RangeError} if `options.tableName` is not a plain identifier of 47 characters or fewer. */\n constructor(client: SqlClientLike, options: SqlErrorOptions = {}) {\n super(client, 'mysql', 'MySqlErrorAdapter', options);\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default Redis key. */\nexport const DEFAULT_REDIS_KEY = 'error_log';\n\n/**\n * The only thing this adapter needs from a Redis client: send one raw command\n * as an array of strings. node-redis (`client.sendCommand`) already matches.\n * ioredis needs a one-line wrapper:\n *\n * ```ts\n * const client: RedisClientLike = {\n * sendCommand: (args) => redis.call(args[0] as string, ...args.slice(1)),\n * };\n * ```\n */\nexport interface RedisClientLike {\n sendCommand(args: string[]): Promise<unknown>;\n}\n\nexport interface RedisErrorOptions {\n /**\n * `list` (default) pushes each entry onto a Redis list as one JSON string.\n * `stream` appends each entry to a Redis stream (`XADD`) as separate fields.\n */\n mode?: 'list' | 'stream';\n /** Key to write to. Defaults to `error_log`. */\n key?: string;\n /**\n * Most entries to keep. The oldest are dropped first. Unlimited by default.\n * Lists are trimmed with `LTRIM` after the push; streams use an exact `MAXLEN`.\n */\n maxEntries?: number;\n /**\n * List mode only: which end to push onto. `right` (default, `RPUSH`) keeps\n * the newest entry last; `left` (`LPUSH`) keeps the newest entry first.\n */\n side?: 'left' | 'right';\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for each Redis command before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/**\n * Writes entries to Redis, either as a capped list of JSON strings or as a\n * stream. Each record holds a UUID `id` plus the serialized entry. In stream\n * mode `cause` and `source` fields are left out when the entry has none.\n */\nexport class RedisErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly mode: 'list' | 'stream';\n private readonly key: string;\n private readonly maxEntries: number | undefined;\n private readonly side: 'left' | 'right';\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `key` is empty, `maxEntries` is not a positive integer, or `mode` / `side` is unknown. */\n constructor(\n private readonly client: RedisClientLike,\n options: RedisErrorOptions = {}\n ) {\n this.mode = options.mode ?? 'list';\n this.key = options.key ?? DEFAULT_REDIS_KEY;\n this.maxEntries = options.maxEntries;\n this.side = options.side ?? 'right';\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'RedisErrorAdapter');\n if (this.mode !== 'list' && this.mode !== 'stream') {\n throw new RangeError(\n `RedisErrorAdapter: mode must be \"list\" or \"stream\", received \"${String(options.mode)}\".`\n );\n }\n if (this.side !== 'left' && this.side !== 'right') {\n throw new RangeError(\n `RedisErrorAdapter: side must be \"left\" or \"right\", received \"${String(options.side)}\".`\n );\n }\n if (this.key === '') {\n throw new RangeError('RedisErrorAdapter: key must not be empty.');\n }\n if (\n this.maxEntries !== undefined &&\n (!Number.isInteger(this.maxEntries) || this.maxEntries < 1)\n ) {\n throw new RangeError(\n `RedisErrorAdapter: maxEntries must be a positive integer, received ${String(this.maxEntries)}.`\n );\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record = { id: resolveEntryId(entry), ...serializeEntry(entry) };\n if (this.mode === 'stream') {\n await this.send(this.buildStreamCommand(record));\n } else {\n await this.send([\n this.side === 'left' ? 'LPUSH' : 'RPUSH',\n this.key,\n JSON.stringify(record),\n ]);\n await this.trimList();\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'RedisErrorAdapter',\n prefix: this.prefix,\n action: `to write to ${this.mode} \"${this.key}\"`,\n error,\n });\n }\n }\n\n private buildStreamCommand(record: {\n id: string;\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n }): string[] {\n const command: string[] = ['XADD', this.key];\n if (this.maxEntries !== undefined) {\n command.push('MAXLEN', '=', String(this.maxEntries));\n }\n command.push(\n '*',\n 'id',\n record.id,\n 'code',\n record.code,\n 'message',\n record.message,\n 'timestamp',\n record.timestamp\n );\n if (record.cause !== undefined) {\n command.push('cause', JSON.stringify(record.cause));\n }\n if (record.source !== undefined) {\n command.push('source', record.source);\n }\n return command;\n }\n\n /**\n * Drops the oldest list entries beyond `maxEntries`. The entry is already\n * stored by now, so a trim failure is reported but does not fail the call;\n * the next successful write trims the list again.\n */\n /** Sends one command, giving up waiting after `timeoutMs`. */\n private send(args: string[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> => this.client.sendCommand(args),\n this.timeoutMs,\n timeoutMessage('Redis', this.timeoutMs)\n );\n }\n\n private async trimList(): Promise<void> {\n if (this.maxEntries === undefined) {\n return;\n }\n const keep: number = this.maxEntries;\n const [start, stop]: [string, string] =\n this.side === 'right' ? [String(-keep), '-1'] : ['0', String(keep - 1)];\n try {\n await this.send(['LTRIM', this.key, start, stop]);\n } catch (error: unknown) {\n console.error(\n `${this.prefix} RedisErrorAdapter: stored the entry but could not trim list \"${this.key}\" to ${keep} entries: ${describeError(error)}`\n );\n }\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { SerialQueue } from '../queue.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { generateId } from '../uuid.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default key prefix for stored objects. */\nexport const DEFAULT_S3_PREFIX = 'error_log/';\n\n/** Buffered entries kept after failed uploads: this many times `batchSize`, oldest dropped first. */\nexport const BUFFER_LIMIT_MULTIPLIER = 10;\n\n/** The parameters this adapter passes to `putObject`. */\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: string;\n ContentType: string;\n}\n\n/**\n * The only thing this adapter needs from an S3 client: `putObject`. The AWS\n * SDK v3 aggregated client (`new S3({ ... })` from `@aws-sdk/client-s3`) fits as\n * it is, and so does anything else that follows the S3 API, including\n * DigitalOcean Spaces, Cloudflare R2 and MinIO when you point the client at\n * their endpoint.\n *\n * Some S3-compatible providers reject the checksum headers that recent AWS SDK\n * v3 versions add by default. If uploads fail with a checksum or header error,\n * create the client with `requestChecksumCalculation: 'WHEN_REQUIRED'` and\n * `responseChecksumValidation: 'WHEN_REQUIRED'`.\n */\nexport interface S3ClientLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n}\n\nexport interface S3BatchOptions {\n /** Upload once this many entries are buffered. */\n batchSize: number;\n /**\n * Also upload when the oldest buffered entry has waited this long (ms). The\n * timer does not keep the process alive, so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n}\n\nexport interface S3ErrorOptions {\n /**\n * Key prefix, for example `logs/app/`. A trailing slash is added if missing.\n * Defaults to `error_log/`. Use `''` for no prefix.\n */\n keyPrefix?: string;\n /**\n * Content type of the uploaded objects. Defaults to `application/json`, or\n * `application/x-ndjson` when batching.\n */\n contentType?: string;\n /**\n * Buffer entries and upload them together as one newline-delimited JSON\n * object. Off by default: one object per entry.\n */\n batch?: S3BatchOptions;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long an upload may take before `log` (or `flush`) gives up waiting, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The\n * upload cannot be cancelled, so it may still finish afterwards; in batch mode\n * the entries stay buffered and are uploaded again by the next flush.\n */\n timeoutMs?: number;\n}\n\ninterface S3Record {\n id: string;\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n}\n\n/** Turns `2026-01-02T03:04:05.678Z` into `2026/01/02/20260102T030405678Z`. */\nfunction datePath(date: Date): string {\n const compact: string = date.toISOString().replace(/[-:.]/g, '');\n return `${compact.slice(0, 4)}/${compact.slice(4, 6)}/${compact.slice(6, 8)}/${compact}`;\n}\n\nfunction normalizePrefix(prefix: string): string {\n const trimmed: string = prefix.replace(/^\\/+/, '');\n return trimmed === '' || trimmed.endsWith('/') ? trimmed : `${trimmed}/`;\n}\n\n/**\n * Stores entries as objects in an S3-compatible bucket, keyed by date and UUID:\n * `error_log/2026/01/02/20260102T030405678Z-<uuid>.json`.\n *\n * By default each entry becomes its own JSON object and `log` resolves once the\n * upload succeeds. With `batch`, entries are buffered and uploaded together as\n * one `.ndjson` object; `log` then resolves ok as soon as the entry is\n * buffered (it is not stored yet), except for the call that fills the batch,\n * which resolves with the upload result. Call `flush()` before shutting down.\n */\nexport class S3ErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly keyPrefix: string;\n private readonly contentType: string;\n private readonly batch: S3BatchOptions | undefined;\n private readonly logPrefix: string;\n private readonly timeoutMs: number;\n private buffer: S3Record[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushQueue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `bucket` is empty or the batch options are invalid. */\n constructor(\n private readonly client: S3ClientLike,\n private readonly bucket: string,\n options: S3ErrorOptions = {}\n ) {\n this.keyPrefix = normalizePrefix(options.keyPrefix ?? DEFAULT_S3_PREFIX);\n this.batch = options.batch;\n this.contentType =\n options.contentType ??\n (this.batch === undefined ? 'application/json' : 'application/x-ndjson');\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'S3ErrorAdapter');\n if (bucket === '') {\n throw new RangeError('S3ErrorAdapter: bucket must not be empty.');\n }\n if (this.batch !== undefined) {\n if (!Number.isInteger(this.batch.batchSize) || this.batch.batchSize < 1) {\n throw new RangeError(\n `S3ErrorAdapter: batch.batchSize must be a positive integer, received ${String(this.batch.batchSize)}.`\n );\n }\n if (\n this.batch.maxWaitMs !== undefined &&\n (!Number.isFinite(this.batch.maxWaitMs) || this.batch.maxWaitMs <= 0)\n ) {\n throw new RangeError(\n `S3ErrorAdapter: batch.maxWaitMs must be a positive number, received ${String(this.batch.maxWaitMs)}.`\n );\n }\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: S3Record = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const occurredAt: Date = new Date(record.timestamp);\n if (Number.isNaN(occurredAt.getTime())) {\n throw new RangeError(\n `timestamp \"${record.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n if (this.batch === undefined) {\n const key: string = `${this.keyPrefix}${datePath(occurredAt)}-${record.id}.json`;\n await this.upload(key, `${JSON.stringify(record)}\\n`);\n return { ok: true };\n }\n return await this.addToBatch(record, this.batch);\n } catch (error: unknown) {\n return this.fail(error);\n }\n }\n\n /**\n * Uploads any buffered entries now. Resolves ok when nothing is buffered.\n * Entries from a failed upload stay buffered and are retried by the next flush.\n */\n flush(): Promise<ErrorLogResult> {\n return this.flushQueue.run((): Promise<ErrorLogResult> =>\n this.uploadBuffered()\n );\n }\n\n private async addToBatch(\n record: S3Record,\n batch: S3BatchOptions\n ): Promise<ErrorLogResult> {\n this.buffer.push(record);\n const limit: number = batch.batchSize * BUFFER_LIMIT_MULTIPLIER;\n if (this.buffer.length > limit) {\n const dropped: number = this.buffer.length - limit;\n this.buffer = this.buffer.slice(dropped);\n console.error(\n `${this.logPrefix} S3ErrorAdapter: dropped the ${dropped} oldest buffered entries because uploads to bucket \"${this.bucket}\" keep failing and the buffer is full (${limit} entries).`\n );\n }\n if (this.buffer.length >= batch.batchSize) {\n return this.flush();\n }\n this.scheduleFlush(batch);\n return { ok: true };\n }\n\n private scheduleFlush(batch: S3BatchOptions): void {\n if (batch.maxWaitMs === undefined || this.timer !== null) {\n return;\n }\n const timer = setTimeout((): void => {\n this.timer = null;\n void this.flush();\n }, batch.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.timer = timer;\n }\n\n private async uploadBuffered(): Promise<ErrorLogResult> {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const pending: S3Record[] = this.buffer.slice();\n if (pending.length === 0) {\n return { ok: true };\n }\n try {\n const key: string = `${this.keyPrefix}${datePath(new Date())}-${generateId()}.ndjson`;\n const body: string = pending\n .map((record: S3Record): string => JSON.stringify(record))\n .join('\\n');\n await this.upload(key, `${body}\\n`);\n const uploaded: Set<S3Record> = new Set(pending);\n this.buffer = this.buffer.filter(\n (record: S3Record): boolean => !uploaded.has(record)\n );\n return { ok: true };\n } catch (error: unknown) {\n if (this.batch !== undefined) {\n this.scheduleFlush(this.batch);\n }\n return this.fail(error, pending.length);\n }\n }\n\n private upload(key: string, body: string): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> =>\n this.client.putObject({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: this.contentType,\n }),\n this.timeoutMs,\n timeoutMessage('S3', this.timeoutMs)\n );\n }\n\n private fail(error: unknown, bufferedCount?: number): ErrorLogResult {\n const retry: string =\n bufferedCount === undefined\n ? ''\n : ` (${bufferedCount} buffered ${bufferedCount === 1 ? 'entry was' : 'entries were'} kept for the next flush)`;\n return reportFailure({\n adapterName: 'S3ErrorAdapter',\n prefix: this.logPrefix,\n action: `to upload to bucket \"${this.bucket}\"`,\n error,\n reason: `${describeError(error)}${retry}`,\n });\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport {\n DEFAULT_TIMEOUT_MS,\n resolveTimeoutMs,\n withTimeout,\n} from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** The fields stored for each entry, in Firestore and in the Realtime Database. */\nexport interface FirebaseErrorRecord {\n /** UUID. In Firestore it is also the document id. */\n id: string;\n code: string;\n message: string;\n /** ISO 8601 timestamp of the entry. */\n timestamp: string;\n source?: string;\n /**\n * The entry's cause as a JSON string. Plain text avoids Firestore's limits on\n * custom objects and nested arrays, and the Realtime Database's limits on key\n * characters, so an odd cause can never make the write fail.\n */\n cause?: string;\n}\n\n/**\n * The only thing the Firestore adapter needs from a collection: `doc(id).set(data)`.\n * A `firebase-admin` collection (`getFirestore().collection('error_log')`) fits\n * as it is. With the web SDK, wrap it:\n *\n * ```ts\n * const collectionLike: FirestoreCollectionLike = {\n * doc: (id) => ({ set: (data) => setDoc(doc(db, 'error_log', id), data) }),\n * };\n * ```\n */\nexport interface FirestoreCollectionLike {\n doc(documentId: string): { set(data: FirebaseErrorRecord): Promise<unknown> };\n}\n\n/**\n * The only thing the Realtime Database adapter needs from a reference:\n * `push(value)`, which resolves once the write is saved. A `firebase-admin`\n * reference (`getDatabase().ref('error_log')`) fits as it is. With the web SDK, wrap it:\n *\n * ```ts\n * const refLike: RealtimeDatabaseRefLike = {\n * push: (value) => push(ref(db, 'error_log'), value),\n * child: (key) => ({ set: (value) => set(ref(db, `error_log/${key}`), value) }),\n * };\n * ```\n *\n * `child(key).set(value)` is only needed for entries that carry their own `id`\n * (see `ErrorLogEntry.id`): they are written under that key, so logging the same\n * entry again overwrites the record instead of adding a second one.\n */\nexport interface RealtimeDatabaseRefLike {\n push(value: FirebaseErrorRecord): PromiseLike<unknown>;\n child?(key: string): {\n set(value: FirebaseErrorRecord): PromiseLike<unknown>;\n };\n}\n\n/** Default time to wait for a write to be saved, in milliseconds. */\nexport const DEFAULT_WRITE_TIMEOUT_MS: number = DEFAULT_TIMEOUT_MS;\n\nexport interface FirebaseErrorOptions {\n /**\n * How long `log` waits for the write to be saved before giving up, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit.\n *\n * The Firebase SDKs keep writes queued while the connection is down, and\n * their promises stay pending until it returns, so without a limit `log`\n * could hang for as long as an outage lasts. A write that timed out is still\n * queued by the SDK and may be saved later. Retry a timeout only with the same\n * `ErrorLogEntry.id`, so the retry overwrites the first write instead of\n * adding a second one.\n */\n timeoutMs?: number;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/** Waits for the write, but rejects with a descriptive error if it takes longer than `timeoutMs`. */\nfunction waitForWrite(\n start: () => PromiseLike<unknown>,\n timeoutMs: number\n): Promise<unknown> {\n return withTimeout(\n start,\n timeoutMs,\n `the write was not saved within ${timeoutMs} ms (the connection may be down; the SDK keeps the write queued and may still save it later, so logging the entry again could store it twice unless it carries an id)`\n );\n}\n\nfunction buildRecord<TCode extends string>(\n entry: ErrorLogEntry<TCode>\n): FirebaseErrorRecord {\n const serialized = serializeEntry(entry);\n const record: FirebaseErrorRecord = {\n id: resolveEntryId(entry),\n code: serialized.code,\n message: serialized.message,\n timestamp: serialized.timestamp,\n };\n if (serialized.source !== undefined) {\n record.source = serialized.source;\n }\n if (serialized.cause !== undefined) {\n record.cause = JSON.stringify(serialized.cause);\n }\n return record;\n}\n\n/**\n * Writes each entry as a document in a Firestore collection. The document id is\n * the entry's UUID, so the SDK finishing one queued write twice cannot create two\n * documents. Calling `log` again without an `id` on the entry is a new write\n * with a new UUID: if a call reported a timeout, the first write may still be\n * saved later, and logging the same entry again can store it twice. Give the\n * entry an `id` and the retry overwrites the same document.\n */\nexport class FirestoreErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`. */\n constructor(\n private readonly collection: FirestoreCollectionLike,\n options: FirebaseErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'FirestoreErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: FirebaseErrorRecord = buildRecord(entry);\n await waitForWrite(\n () => this.collection.doc(record.id).set(record),\n this.timeoutMs\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'FirestoreErrorAdapter',\n prefix: this.prefix,\n action: `to write the document`,\n error,\n });\n }\n }\n}\n\n/**\n * Writes each entry under a Realtime Database reference with `push`, which\n * generates a chronologically ordered key.\n */\nexport class RealtimeDatabaseErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`. */\n constructor(\n private readonly reference: RealtimeDatabaseRefLike,\n options: FirebaseErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'RealtimeDatabaseErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: FirebaseErrorRecord = buildRecord(entry);\n let write: PromiseLike<unknown>;\n if (hasCallerId(entry)) {\n if (this.reference.child === undefined) {\n throw new TypeError(\n 'the entry has an id, which needs a reference with child(key).set(value) so a repeat overwrites the first write; add child to the reference or log without an id'\n );\n }\n write = this.reference.child(record.id).set(record);\n } else {\n write = this.reference.push(record);\n }\n await waitForWrite(() => write, this.timeoutMs);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'RealtimeDatabaseErrorAdapter',\n prefix: this.prefix,\n action: `to write the entry`,\n error,\n });\n }\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\nimport { buildErrorLogTableDdl } from './sql-error-adapter.js';\n\n/** The row inserted for each entry. The columns match the Postgres adapter's table. */\nexport interface SupabaseErrorRow {\n /** UUID primary key. */\n id: string;\n code: string;\n message: string;\n /** The cause as JSON, or `null` when the entry has none. */\n cause: unknown;\n source: string | null;\n /** ISO 8601 timestamp of the entry. */\n occurred_at: string;\n}\n\n/** The error object `supabase-js` returns (a `PostgrestError`) instead of throwing. */\nexport interface SupabaseErrorLike {\n message: string;\n code?: string;\n details?: string | null;\n hint?: string | null;\n}\n\n/** What `insert` resolves to. The status fields are used when the error has no message. */\nexport interface SupabaseInsertResult {\n error: SupabaseErrorLike | null;\n status?: number;\n statusText?: string;\n}\n\n/**\n * The only thing the Supabase adapter needs from a client: `from(table).insert(row)`.\n * A `supabase-js` client (`createClient(url, key)`) fits as it is.\n */\nexport interface SupabaseClientLike {\n from(table: string): {\n insert(row: SupabaseErrorRow): PromiseLike<SupabaseInsertResult>;\n };\n}\n\nexport interface SupabaseErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only. */\n tableName?: string;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for Supabase to answer before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Longest `code` or `source` an insert policy accepts, in characters. */\nexport const POLICY_MAX_CODE_LENGTH = 255;\n/** Longest `message` an insert policy accepts, in characters. */\nexport const POLICY_MAX_MESSAGE_LENGTH = 4096;\n/** Largest `cause` an insert policy accepts, in bytes of stored JSON. */\nexport const POLICY_MAX_CAUSE_BYTES = 8192;\n\nexport interface SupabaseSetupOptions {\n /**\n * Postgres roles allowed to insert through the API, for example\n * `['authenticated']`. Leave empty when only a server using the service role\n * key logs: that role bypasses row level security, so it needs no policy, and\n * its key must never be shipped to a browser.\n *\n * The generated policy only checks the size of each field. It does not tie a\n * row to a user, so any holder of an allowed role's key can still add entries\n * of their own making: forged entries can hide real incidents, and a flood of\n * them uses up your database quota. Prefer `authenticated` over `anon`.\n */\n insertRoles?: string[];\n /**\n * Required to allow `anon` (or `public`) to insert. The `anon` key is part of\n * your frontend, so this lets anyone who loads the page write to the log.\n * Defaults to `false`.\n */\n allowPublicInsert?: boolean;\n}\n\n/** The `WITH CHECK` condition of the insert policy: every field must be a reasonable size. */\nconst INSERT_POLICY_CHECK: string =\n `char_length(code) <= ${POLICY_MAX_CODE_LENGTH} ` +\n `AND char_length(message) <= ${POLICY_MAX_MESSAGE_LENGTH} ` +\n `AND (source IS NULL OR char_length(source) <= ${POLICY_MAX_CODE_LENGTH}) ` +\n `AND (cause IS NULL OR pg_column_size(cause) <= ${POLICY_MAX_CAUSE_BYTES})`;\n\n/**\n * Returns the SQL to run once in the Supabase SQL editor: the error log table,\n * with row level security turned on (as Supabase recommends for every table in\n * an exposed schema) and, optionally, an insert-only policy for the given roles.\n * The policy limits the size of each field. Nobody but the service role can read\n * the log through the API.\n *\n * @throws {RangeError} if the table name or a role is not a plain identifier, or\n * `anon` / `public` is listed without `allowPublicInsert: true`.\n */\nexport function buildSupabaseSetupSql(\n tableName: string = DEFAULT_TABLE_NAME,\n options: SupabaseSetupOptions = {}\n): string[] {\n const insertRoles: string[] = options.insertRoles ?? [];\n for (const role of insertRoles) {\n assertIdentifier(role, 'role name', 'buildSupabaseSetupSql');\n if (\n (role.toLowerCase() === 'anon' || role.toLowerCase() === 'public') &&\n options.allowPublicInsert !== true\n ) {\n throw new RangeError(\n `buildSupabaseSetupSql: role \"${role}\" would let anyone who loads your page write to the log, because the anon key is public. Use \"authenticated\" instead, or pass allowPublicInsert: true to accept the risk of forged entries and quota use.`\n );\n }\n }\n const statements: string[] = [\n ...buildErrorLogTableDdl('postgres', tableName),\n `ALTER TABLE \"${tableName}\" ENABLE ROW LEVEL SECURITY`,\n ];\n if (insertRoles.length > 0) {\n const policy: string = `${tableName}_insert_only`;\n statements.push(\n `DROP POLICY IF EXISTS \"${policy}\" ON \"${tableName}\"`,\n `CREATE POLICY \"${policy}\" ON \"${tableName}\" FOR INSERT TO ${insertRoles.map((role: string): string => `\"${role}\"`).join(', ')} WITH CHECK (${INSERT_POLICY_CHECK})`\n );\n }\n return statements;\n}\n\n/**\n * Joins the parts of a Supabase error that are present into one message.\n * `supabase-js` can return an error with no message at all (an empty-bodied\n * 404 for a table the API cannot see, for example), so the HTTP status stands\n * in for it. Only the first line of `details` is kept, since network failures\n * put a full stack trace there.\n */\nfunction describeSupabaseError(result: SupabaseInsertResult): string {\n const error: SupabaseErrorLike = result.error ?? { message: '' };\n const parts: string[] = [];\n if (error.message) {\n parts.push(error.message);\n } else if (result.status) {\n parts.push(\n `HTTP ${result.status}${result.statusText ? ` ${result.statusText}` : ''} with no error message (a 404 usually means the table does not exist or is not exposed by the API)`\n );\n } else {\n parts.push('unknown error with no message');\n }\n if (error.code) {\n parts.push(`code ${error.code}`);\n }\n const detailsLine: string = (error.details ?? '').split('\\n')[0].trim();\n if (detailsLine && detailsLine !== error.message) {\n parts.push(`details: ${detailsLine}`);\n }\n if (error.hint) {\n parts.push(`hint: ${error.hint}`);\n }\n return parts.join('; ');\n}\n\n/**\n * Writes entries to a Supabase table through `supabase-js`. Create the table\n * first with `buildSupabaseSetupSql` (or `buildErrorLogTableDdl('postgres')`).\n *\n * ```ts\n * const adapter = new SupabaseErrorAdapter(createClient(url, serviceRoleKey));\n * ```\n *\n * `supabase-js` reports failures in a returned `error` field rather than\n * throwing; the adapter turns both that and a thrown error into `{ ok: false }`.\n */\nexport class SupabaseErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */\n constructor(\n private readonly client: SupabaseClientLike,\n options: SupabaseErrorOptions = {}\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertIdentifier(this.tableName, 'table name', 'SupabaseErrorAdapter');\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'SupabaseErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const serialized = serializeEntry(entry);\n const row: SupabaseErrorRow = {\n id: resolveEntryId(entry),\n code: serialized.code,\n message: serialized.message,\n cause: serialized.cause === undefined ? null : serialized.cause,\n source: serialized.source ?? null,\n occurred_at: serialized.timestamp,\n };\n const result: SupabaseInsertResult = await withTimeout(\n (): PromiseLike<SupabaseInsertResult> =>\n this.client.from(this.tableName).insert(row),\n this.timeoutMs,\n timeoutMessage('Supabase', this.timeoutMs)\n );\n if (result.error) {\n if (hasCallerId(entry) && result.error.code === '23505') {\n // Unique violation on the id: this entry was stored by an earlier call.\n return { ok: true };\n }\n return this.fail(result, describeSupabaseError(result));\n }\n return { ok: true };\n } catch (error: unknown) {\n return this.fail(error, describeError(error));\n }\n }\n\n private fail(logged: unknown, reason: string): ErrorLogResult {\n return reportFailure({\n adapterName: 'SupabaseErrorAdapter',\n prefix: this.prefix,\n action: `to insert into table \"${this.tableName}\"`,\n error: logged,\n reason,\n });\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default event name. It is not `error`, which Node's `EventEmitter` throws on when nobody listens. */\nexport const DEFAULT_EVENT_NAME = 'error-log';\n\n/** What listeners receive: the serialized entry plus its UUID. */\nexport interface EventEmitterErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\n/**\n * The only thing this adapter needs from an emitter: `emit(eventName, payload)`.\n * Node's `EventEmitter` and mitt-style emitters fit as they are. A browser\n * `EventTarget` needs a one-line wrapper:\n *\n * ```ts\n * const target = new EventTarget();\n * const emitter: EmitterLike = {\n * emit: (name, payload) =>\n * target.dispatchEvent(new CustomEvent(name, { detail: payload })),\n * };\n * ```\n */\nexport interface EmitterLike {\n emit(eventName: string, payload: EventEmitterErrorRecord): unknown;\n}\n\nexport interface EventEmitterErrorOptions {\n /**\n * Name of the event to emit. Defaults to `error-log`. Avoid `error` on a Node\n * `EventEmitter`: it throws when no listener is attached, and `log` would then\n * return `ok: false`.\n */\n eventName?: string;\n /**\n * Treat an emit that reached nobody as a failure. Node's `EventEmitter.emit`\n * returns `false` when there are no listeners; with this set to `true`, `log`\n * then returns `ok: false` instead of letting the entry vanish. Emitters whose\n * `emit` returns anything else (mitt, an `EventTarget` wrapper) cannot tell,\n * so they always count as delivered. Defaults to `false`.\n */\n requireListener?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Emits each entry as an event on an emitter you own, for in-process hooks such\n * as a toast, a metrics counter, or a test assertion. Listeners run\n * synchronously inside `emit`, so a listener that throws makes `log` return\n * `ok: false` with the listener's message; the application is not affected.\n *\n * ```ts\n * const bus = new EventEmitter();\n * bus.on('error-log', (record) => showToast(record.message));\n * const adapter = new EventEmitterErrorAdapter(bus);\n * ```\n */\nexport class EventEmitterErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly eventName: string;\n private readonly requireListener: boolean;\n private readonly prefix: string;\n\n /** @throws {RangeError} if `options.eventName` is empty. */\n constructor(\n private readonly emitter: EmitterLike,\n options: EventEmitterErrorOptions = {}\n ) {\n this.eventName = options.eventName ?? DEFAULT_EVENT_NAME;\n if (this.eventName === '') {\n throw new RangeError(\n 'EventEmitterErrorAdapter: eventName must not be empty.'\n );\n }\n this.requireListener = options.requireListener ?? false;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: EventEmitterErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const delivered: unknown = this.emitter.emit(this.eventName, record);\n if (this.requireListener && delivered === false) {\n throw new Error(\n `no listener is attached to \"${this.eventName}\", so nothing received the entry`\n );\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'EventEmitterErrorAdapter',\n prefix: this.prefix,\n action: `to emit \"${this.eventName}\"`,\n error,\n });\n }\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** `readyState` of an open socket, the same in browsers, Node, Deno, Bun, and the `ws` package. */\nconst OPEN = 1;\n\nconst STATE_NAMES: Readonly<Record<number, string>> = {\n 0: 'still connecting',\n 1: 'open',\n 2: 'closing',\n 3: 'closed',\n};\n\n/**\n * The only things this adapter needs from a socket: `send` and `readyState`. The\n * browser `WebSocket`, Node's global `WebSocket`, Deno, Bun, and the `ws` package\n * all fit as they are.\n */\nexport interface WebSocketLike {\n send(data: string): unknown;\n readonly readyState: number;\n}\n\nexport interface WebSocketErrorOptions {\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Sends each entry as one JSON text message (the serialized entry plus its UUID\n * `id`) over a socket you own. You connect, and reconnect, the socket; this\n * adapter never opens one and keeps no buffer. If the socket is not open when\n * `log` is called, the entry is not sent and `log` returns `ok: false`, so\n * combine it with a durable adapter in a `CompositeErrorAdapter` when entries\n * must not be lost while the socket is down.\n *\n * `ok: true` means the socket accepted the message, not that the server\n * received it: a WebSocket gives no acknowledgement, and a connection that\n * drops after `send` loses the message.\n *\n * ```ts\n * const socket = new WebSocket('wss://logs.example.com/errors');\n * const adapter = new WebSocketErrorAdapter(socket);\n * ```\n */\nexport class WebSocketErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n\n constructor(\n private readonly socket: WebSocketLike,\n options: WebSocketErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const state: number = this.socket.readyState;\n if (state !== OPEN) {\n throw new Error(\n `the socket is ${STATE_NAMES[state] ?? `in unknown state ${String(state)}`} (readyState ${String(state)}), so the entry was not sent; connect or reconnect it first`\n );\n }\n this.socket.send(\n JSON.stringify({ id: resolveEntryId(entry), ...serializeEntry(entry) })\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'WebSocketErrorAdapter',\n prefix: this.prefix,\n action: 'to send over the socket',\n error,\n });\n }\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { SerialQueue } from '../queue.js';\nimport { serializeEntry } from '../serialize.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Largest event CloudWatch accepts: 256 KiB, counting 26 bytes of overhead per event. */\nexport const MAX_EVENT_BYTES = 262_144;\n/** Overhead CloudWatch adds to every event when it measures size. */\nexport const EVENT_OVERHEAD_BYTES = 26;\n/** Largest `PutLogEvents` request: 1 MiB of events. */\nexport const MAX_BATCH_BYTES = 1_048_576;\n/** Most events in one `PutLogEvents` request. */\nexport const MAX_BATCH_EVENTS = 10_000;\n/** The events of one request may not span more than this. */\nconst MAX_BATCH_SPAN_MS = 24 * 60 * 60 * 1000;\n\n/** Buffered events kept after failed sends: this many times `batchSize`, oldest dropped first. */\nexport const BUFFER_LIMIT_MULTIPLIER = 10;\n\n/** One event as `PutLogEvents` takes it. */\nexport interface CloudWatchLogEvent {\n /** Milliseconds since the epoch. */\n timestamp: number;\n message: string;\n}\n\n/** The parameters this adapter passes to `putLogEvents`. */\nexport interface CloudWatchPutLogEventsInput {\n logGroupName: string;\n logStreamName: string;\n logEvents: CloudWatchLogEvent[];\n}\n\n/**\n * What an AWS SDK client needs to offer. The AWS SDK v3 aggregated client\n * (`new CloudWatchLogs({ ... })` from `@aws-sdk/client-cloudwatch-logs`) fits as\n * it is. `createLogGroup` and `createLogStream` are only used when\n * `createMissing` is on (the default); leave them out of a hand-written client\n * that cannot create them.\n */\nexport interface CloudWatchLogsClientLike {\n putLogEvents(input: CloudWatchPutLogEventsInput): Promise<unknown>;\n createLogGroup?(input: { logGroupName: string }): Promise<unknown>;\n createLogStream?(input: {\n logGroupName: string;\n logStreamName: string;\n }): Promise<unknown>;\n}\n\nexport interface CloudWatchBatchOptions {\n /** Send once this many events are buffered. */\n batchSize: number;\n /**\n * Also send when the oldest buffered event has waited this long (ms). The\n * timer does not keep the process alive, so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n}\n\nexport interface CloudWatchLogsErrorOptions {\n /**\n * Create the log stream, and the log group if that is missing too, when\n * `PutLogEvents` says they do not exist, then try again once. This needs the\n * `logs:CreateLogStream` (and `logs:CreateLogGroup`) permissions and only runs\n * after a \"not found\" answer, so a stream that exists costs nothing. A group\n * created this way never expires its logs; set a retention policy on it.\n * Defaults to `true`.\n */\n createMissing?: boolean;\n /**\n * Buffer events and send them together, up to the 10,000 events and 1 MiB\n * that one request allows. Off by default: one request per entry.\n */\n batch?: CloudWatchBatchOptions;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long a request may take before `log` (or `flush`) gives up waiting, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The\n * request cannot be cancelled, so it may still finish afterwards; in batch\n * mode the events stay buffered and are sent again by the next flush.\n */\n timeoutMs?: number;\n}\n\ninterface BufferedEvent extends CloudWatchLogEvent {\n /** Entry id, for failure messages. */\n id: string;\n}\n\ninterface RejectedLogEventsInfo {\n tooNewLogEventStartIndex?: number;\n tooOldLogEventEndIndex?: number;\n expiredLogEventEndIndex?: number;\n}\n\nconst encoder = new TextEncoder();\n\nfunction eventBytes(message: string): number {\n return encoder.encode(message).length + EVENT_OVERHEAD_BYTES;\n}\n\n/** The AWS SDK names its errors; this works for both SDK v3 errors and hand-made ones. */\nfunction errorName(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null\n ? (error as { name?: string }).name\n : undefined;\n}\n\n/**\n * Splits events (oldest first) into groups that fit one `PutLogEvents` request:\n * at most 10,000 events, 1 MiB, and 24 hours between the first and the last.\n */\nexport function splitIntoRequests<T extends CloudWatchLogEvent>(\n events: readonly T[]\n): T[][] {\n const requests: T[][] = [];\n let current: T[] = [];\n let bytes = 0;\n for (const event of events) {\n const size: number = eventBytes(event.message);\n const first: T | undefined = current[0];\n if (\n first !== undefined &&\n (current.length >= MAX_BATCH_EVENTS ||\n bytes + size > MAX_BATCH_BYTES ||\n event.timestamp - first.timestamp > MAX_BATCH_SPAN_MS)\n ) {\n requests.push(current);\n current = [];\n bytes = 0;\n }\n current.push(event);\n bytes += size;\n }\n if (current.length > 0) {\n requests.push(current);\n }\n return requests;\n}\n\n/**\n * Sends entries to one CloudWatch Logs stream through `PutLogEvents`, each as a\n * JSON event (the serialized entry plus its UUID `id`) stamped with the entry's\n * time, so Logs Insights can query its fields. AWS no longer asks for sequence\n * tokens, so none are tracked.\n *\n * By default each entry is its own request and `log` resolves once CloudWatch\n * accepts it. With `batch`, events are buffered and sent together; `log` then\n * resolves ok as soon as the event is buffered (it is not stored yet), except\n * for the call that fills the batch, which resolves with the send result. Call\n * `flush()` before shutting down.\n *\n * CloudWatch can answer ok and still reject events it considers too old (over\n * 14 days) or too new (over 2 hours ahead); that is reported as `ok: false`. An\n * event over 256 KiB is refused before sending, since CloudWatch would reject\n * the whole request.\n *\n * In AWS Lambda, ECS and similar runtimes, anything written to stdout already\n * reaches CloudWatch; this adapter is for apps running elsewhere.\n */\nexport class CloudWatchLogsErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly createMissing: boolean;\n private readonly batch: CloudWatchBatchOptions | undefined;\n private readonly logPrefix: string;\n private readonly timeoutMs: number;\n private buffer: BufferedEvent[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushQueue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if a name is empty, the batch or timeout options are invalid, or `createMissing` is on but the client cannot create streams. */\n constructor(\n private readonly client: CloudWatchLogsClientLike,\n private readonly logGroupName: string,\n private readonly logStreamName: string,\n options: CloudWatchLogsErrorOptions = {}\n ) {\n this.createMissing = options.createMissing ?? true;\n this.batch = options.batch;\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'CloudWatchLogsErrorAdapter'\n );\n if (logGroupName === '') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: logGroupName must not be empty.'\n );\n }\n if (logStreamName === '') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: logStreamName must not be empty.'\n );\n }\n if (this.createMissing && typeof client.createLogStream !== 'function') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: createMissing is on, but the client has no createLogStream method. Pass createMissing: false, or use an AWS SDK CloudWatchLogs client.'\n );\n }\n if (this.batch !== undefined) {\n if (!Number.isInteger(this.batch.batchSize) || this.batch.batchSize < 1) {\n throw new RangeError(\n `CloudWatchLogsErrorAdapter: batch.batchSize must be a positive integer, received ${String(this.batch.batchSize)}.`\n );\n }\n if (\n this.batch.maxWaitMs !== undefined &&\n (!Number.isFinite(this.batch.maxWaitMs) || this.batch.maxWaitMs <= 0)\n ) {\n throw new RangeError(\n `CloudWatchLogsErrorAdapter: batch.maxWaitMs must be a positive number, received ${String(this.batch.maxWaitMs)}.`\n );\n }\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const id: string = resolveEntryId(entry);\n const record = { id, ...serializeEntry(entry) };\n const timestamp: number = Date.parse(record.timestamp);\n if (Number.isNaN(timestamp)) {\n throw new RangeError(\n `timestamp \"${record.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n const message: string = JSON.stringify(record);\n const size: number = eventBytes(message);\n if (size > MAX_EVENT_BYTES) {\n throw new RangeError(\n `the event is ${size} bytes, over the ${MAX_EVENT_BYTES} byte limit CloudWatch Logs puts on one event, so it was not sent; shorten the message or cause`\n );\n }\n const event: BufferedEvent = { id, timestamp, message };\n if (this.batch === undefined) {\n const rejection: string | undefined = await this.send([event]);\n if (rejection !== undefined) {\n throw new Error(rejection);\n }\n return { ok: true };\n }\n return await this.addToBatch(event, this.batch);\n } catch (error: unknown) {\n return this.fail(error);\n }\n }\n\n /**\n * Sends any buffered events now. Resolves ok when nothing is buffered. Events\n * from a failed send stay buffered and are retried by the next flush.\n */\n flush(): Promise<ErrorLogResult> {\n return this.flushQueue.run((): Promise<ErrorLogResult> =>\n this.sendBuffered()\n );\n }\n\n private async addToBatch(\n event: BufferedEvent,\n batch: CloudWatchBatchOptions\n ): Promise<ErrorLogResult> {\n this.buffer.push(event);\n const limit: number = batch.batchSize * BUFFER_LIMIT_MULTIPLIER;\n if (this.buffer.length > limit) {\n const dropped: number = this.buffer.length - limit;\n this.buffer = this.buffer.slice(dropped);\n console.error(\n `${this.logPrefix} CloudWatchLogsErrorAdapter: dropped the ${dropped} oldest buffered events because sends to log stream \"${this.logStreamName}\" keep failing and the buffer is full (${limit} events).`\n );\n }\n if (this.buffer.length >= batch.batchSize) {\n return this.flush();\n }\n this.scheduleFlush(batch);\n return { ok: true };\n }\n\n private scheduleFlush(batch: CloudWatchBatchOptions): void {\n if (batch.maxWaitMs === undefined || this.timer !== null) {\n return;\n }\n const timer = setTimeout((): void => {\n this.timer = null;\n void this.flush();\n }, batch.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.timer = timer;\n }\n\n private async sendBuffered(): Promise<ErrorLogResult> {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const pending: BufferedEvent[] = this.buffer.slice();\n if (pending.length === 0) {\n return { ok: true };\n }\n // PutLogEvents wants events oldest first. The sort is stable, so events\n // with the same time keep the order they were logged in.\n pending.sort(\n (a: BufferedEvent, b: BufferedEvent): number => a.timestamp - b.timestamp\n );\n let rejectedTotal = 0;\n let lastRejection = '';\n try {\n for (const request of splitIntoRequests(pending)) {\n const rejection: string | undefined = await this.send(request);\n // Accepted or rejected for good (too old will never be accepted), the\n // events leave the buffer; only a thrown error keeps them for a retry.\n const sent: Set<BufferedEvent> = new Set(request);\n this.buffer = this.buffer.filter(\n (event: BufferedEvent): boolean => !sent.has(event)\n );\n if (rejection !== undefined) {\n rejectedTotal += request.length;\n lastRejection = rejection;\n }\n }\n } catch (error: unknown) {\n if (this.batch !== undefined) {\n this.scheduleFlush(this.batch);\n }\n return this.fail(error, this.buffer.length);\n }\n if (rejectedTotal > 0) {\n return this.fail(new Error(lastRejection), rejectedTotal);\n }\n return { ok: true };\n }\n\n /**\n * Sends one request, creating the stream first if CloudWatch says it is\n * missing. Resolves to a description of any events CloudWatch refused, or\n * undefined when it took them all.\n */\n private async send(events: BufferedEvent[]): Promise<string | undefined> {\n let output: unknown;\n try {\n output = await this.putLogEvents(events);\n } catch (error: unknown) {\n if (\n !this.createMissing ||\n errorName(error) !== 'ResourceNotFoundException'\n ) {\n throw error;\n }\n await this.createStream();\n output = await this.putLogEvents(events);\n }\n return describeRejection(output, events.length);\n }\n\n private putLogEvents(events: BufferedEvent[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> =>\n this.client.putLogEvents({\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents: events.map(\n ({ timestamp, message }: BufferedEvent): CloudWatchLogEvent => ({\n timestamp,\n message,\n })\n ),\n }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs)\n );\n }\n\n private async createStream(): Promise<void> {\n const createStream = (): Promise<unknown> =>\n withTimeout(\n (): Promise<unknown> =>\n (\n this.client.createLogStream as NonNullable<\n CloudWatchLogsClientLike['createLogStream']\n >\n )({\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs, 'create the stream')\n );\n try {\n await createStream();\n } catch (error: unknown) {\n const name: string | undefined = errorName(error);\n if (name === 'ResourceAlreadyExistsException') {\n return;\n }\n if (\n name !== 'ResourceNotFoundException' ||\n typeof this.client.createLogGroup !== 'function'\n ) {\n throw error;\n }\n // The group is missing too.\n const createGroup = this.client.createLogGroup.bind(this.client);\n try {\n await withTimeout(\n (): Promise<unknown> =>\n createGroup({ logGroupName: this.logGroupName }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs, 'create the group')\n );\n } catch (groupError: unknown) {\n if (errorName(groupError) !== 'ResourceAlreadyExistsException') {\n throw groupError;\n }\n }\n try {\n await createStream();\n } catch (retryError: unknown) {\n if (errorName(retryError) !== 'ResourceAlreadyExistsException') {\n throw retryError;\n }\n }\n }\n }\n\n private fail(error: unknown, count?: number): ErrorLogResult {\n return reportFailure({\n adapterName: 'CloudWatchLogsErrorAdapter',\n prefix: this.logPrefix,\n action: `to send to log stream \"${this.logStreamName}\" in log group \"${this.logGroupName}\"${count === undefined ? '' : ` (${count} event${count === 1 ? '' : 's'} affected)`}`,\n error,\n });\n }\n}\n\n/** Explains `rejectedLogEventsInfo`, which CloudWatch returns with an otherwise successful answer. */\nfunction describeRejection(\n output: unknown,\n eventCount: number\n): string | undefined {\n const info: RejectedLogEventsInfo | undefined =\n typeof output === 'object' && output !== null\n ? (output as { rejectedLogEventsInfo?: RejectedLogEventsInfo })\n .rejectedLogEventsInfo\n : undefined;\n if (info === undefined || info === null) {\n return undefined;\n }\n const reasons: string[] = [];\n if (info.tooOldLogEventEndIndex !== undefined) {\n reasons.push(\n `${info.tooOldLogEventEndIndex} event(s) were older than the 14 days CloudWatch accepts`\n );\n }\n if (info.expiredLogEventEndIndex !== undefined) {\n reasons.push(\n `${info.expiredLogEventEndIndex} event(s) were older than the log group's retention period`\n );\n }\n if (info.tooNewLogEventStartIndex !== undefined) {\n reasons.push(\n `${Math.max(0, eventCount - info.tooNewLogEventStartIndex)} event(s) were more than 2 hours ahead of the current time`\n );\n }\n return reasons.length === 0\n ? undefined\n : `CloudWatch Logs accepted the request but rejected events: ${reasons.join('; ')}`;\n}\n","import { backoffDelayMs } from '../backoff.js';\nimport { DEFAULT_PREFIX } from '../fail.js';\nimport { describeError } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * An adapter that can send several entries in one go, such as\n * `WebhookErrorAdapter` (one request carrying a JSON array).\n * `BatchingErrorAdapter` uses it when it is there. The whole batch succeeds or\n * fails together.\n */\nexport interface BatchCapableAdapter<\n TCode extends string = string,\n> extends ErrorLogAdapter<TCode> {\n logBatch(entries: ErrorLogEntry<TCode>[]): Promise<ErrorLogResult>;\n}\n\nexport interface BatchingErrorOptions<TCode extends string = string> {\n /** Deliver once this many entries are waiting. Defaults to 20. */\n batchSize?: number;\n /**\n * Also deliver once the oldest waiting entry has waited this long (ms).\n * Defaults to 5000. Pass `Infinity` to deliver only when `batchSize` is\n * reached or `flush()` is called. The timer does not keep the process alive,\n * so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n /**\n * How many times to try an entry again after a failed delivery before giving\n * up on it. Defaults to 3 (four attempts in all).\n */\n retries?: number;\n /** Wait before the first retry, in milliseconds; it doubles after that. Defaults to 500. */\n retryDelayMs?: number;\n /** Longest wait between attempts, in milliseconds. Defaults to 30000. */\n maxRetryDelayMs?: number;\n /**\n * Most entries kept while the sink is slow or down. Past this, the oldest\n * are dropped. Defaults to 1000. Must be at least `batchSize`.\n */\n maxBuffered?: number;\n /**\n * Use the wrapped adapter's `logBatch` when it has one. Defaults to `true`.\n * Pass `false` to deliver one entry at a time even then (a wrapped\n * `WebhookErrorAdapter` then sends one request per entry, not an array).\n */\n useBatchMethod?: boolean;\n /**\n * Called when entries are given up on, either because they failed every\n * attempt or because the buffer was full. Errors it throws are ignored.\n */\n onDrop?: (entries: ErrorLogEntry<TCode>[], reason: string) => void;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\ninterface Pending<TCode extends string> {\n entry: ErrorLogEntry<TCode>;\n /** Failed deliveries so far. */\n failures: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_MAX_WAIT_MS = 5000;\nconst DEFAULT_RETRIES = 3;\nconst DEFAULT_RETRY_DELAY_MS = 500;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst DEFAULT_MAX_BUFFERED = 1000;\n\nfunction hasLogBatch<TCode extends string>(\n adapter: ErrorLogAdapter<TCode>\n): adapter is BatchCapableAdapter<TCode> {\n return (\n typeof (adapter as Partial<BatchCapableAdapter<TCode>>).logBatch ===\n 'function'\n );\n}\n\n/**\n * Puts a queue in front of any other adapter. `log()` returns `ok: true` as\n * soon as the entry is queued, so a slow or failing sink never holds up the\n * application; entries are then delivered in order, in batches, and a failed\n * delivery is tried again with exponential backoff before the entry is given up\n * on (see `onDrop`).\n *\n * Delivery uses the wrapped adapter's `logBatch` when it has one (the webhook\n * sends one request for the whole batch); otherwise the entries go one at a\n * time through `log`, stopping at the first failure so a sink that is down is\n * not hammered. Because of that, `ok: true` from `log()` means \"queued\", not\n * \"stored\": call `flush()` before the process exits, and watch `onDrop` or the\n * console for entries that could not be delivered. The queue is in memory, so\n * entries still waiting when the process dies are lost; put a durable adapter\n * beside it in a `CompositeErrorAdapter` when that matters.\n *\n * ```ts\n * const errorLog = new BatchingErrorAdapter(\n * new WebhookErrorAdapter('https://example.com/errors'),\n * { batchSize: 50, maxWaitMs: 2000 }\n * );\n * process.on('beforeExit', () => void errorLog.flush());\n * ```\n *\n * Do not also turn on the wrapped adapter's own retries (for example the\n * webhook's `retries`): the two multiply.\n */\nexport class BatchingErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly batchSize: number;\n private readonly maxWaitMs: number;\n private readonly retries: number;\n private readonly retryDelayMs: number;\n private readonly maxRetryDelayMs: number;\n private readonly maxBuffered: number;\n private readonly useBatchMethod: boolean;\n private readonly onDrop: BatchingErrorOptions<TCode>['onDrop'];\n private readonly logPrefix: string;\n\n private queue: Pending<TCode>[] = [];\n private waitTimer: ReturnType<typeof setTimeout> | null = null;\n private backoffTimer: ReturnType<typeof setTimeout> | null = null;\n private draining: Promise<void> | null = null;\n private flushers = 0;\n private droppedSinceFlush = 0;\n private lastDropReason = '';\n\n /** @throws {RangeError} if an option is out of range. */\n constructor(\n private readonly inner: ErrorLogAdapter<TCode>,\n options: BatchingErrorOptions<TCode> = {}\n ) {\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;\n this.retries = options.retries ?? DEFAULT_RETRIES;\n this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n this.maxRetryDelayMs =\n options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n this.maxBuffered = options.maxBuffered ?? DEFAULT_MAX_BUFFERED;\n this.useBatchMethod = options.useBatchMethod ?? true;\n this.onDrop = options.onDrop;\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new RangeError(\n `BatchingErrorAdapter: batchSize must be a positive integer, received ${String(options.batchSize)}.`\n );\n }\n if (Number.isNaN(this.maxWaitMs) || this.maxWaitMs <= 0) {\n throw new RangeError(\n `BatchingErrorAdapter: maxWaitMs must be a positive number or Infinity, received ${String(options.maxWaitMs)}.`\n );\n }\n if (!Number.isInteger(this.retries) || this.retries < 0) {\n throw new RangeError(\n `BatchingErrorAdapter: retries must be a whole number of 0 or more, received ${String(options.retries)}.`\n );\n }\n if (!Number.isFinite(this.retryDelayMs) || this.retryDelayMs <= 0) {\n throw new RangeError(\n `BatchingErrorAdapter: retryDelayMs must be a positive number, received ${String(options.retryDelayMs)}.`\n );\n }\n if (\n !Number.isFinite(this.maxRetryDelayMs) ||\n this.maxRetryDelayMs < this.retryDelayMs\n ) {\n throw new RangeError(\n `BatchingErrorAdapter: maxRetryDelayMs must be a number no smaller than retryDelayMs (${this.retryDelayMs}), received ${String(options.maxRetryDelayMs)}.`\n );\n }\n if (\n !Number.isInteger(this.maxBuffered) ||\n this.maxBuffered < this.batchSize\n ) {\n throw new RangeError(\n `BatchingErrorAdapter: maxBuffered must be a whole number no smaller than batchSize (${this.batchSize}), received ${String(options.maxBuffered)}.`\n );\n }\n }\n\n /** Queues the entry. Resolves ok straight away; delivery happens in the background. */\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n this.queue.push({ entry, failures: 0 });\n if (this.queue.length > this.maxBuffered) {\n const overflow: Pending<TCode>[] = this.queue.splice(\n 0,\n this.queue.length - this.maxBuffered\n );\n this.drop(\n overflow,\n `the buffer of ${this.maxBuffered} entries was full because the wrapped adapter keeps failing`\n );\n }\n if (this.queue.length >= this.batchSize) {\n this.startDraining();\n } else {\n this.scheduleDrain();\n }\n return Promise.resolve({ ok: true });\n }\n\n /**\n * Delivers everything queued, retrying as configured, and resolves once the\n * queue is empty. Resolves `ok: false` if any entries were given up on since\n * the last flush. With retries on and the sink down this can take a while\n * (the waits add up to well over a minute with the defaults).\n */\n async flush(): Promise<ErrorLogResult> {\n this.flushers += 1;\n // A process waiting in flush() must not exit during a backoff sleep.\n this.backoffTimer?.ref?.();\n try {\n this.startDraining();\n while (this.draining !== null) {\n await this.draining;\n }\n } finally {\n this.flushers -= 1;\n }\n if (this.droppedSinceFlush > 0) {\n const count: number = this.droppedSinceFlush;\n const reason: string = this.lastDropReason;\n this.droppedSinceFlush = 0;\n this.lastDropReason = '';\n return {\n ok: false,\n error: `BatchingErrorAdapter gave up on ${count} entr${count === 1 ? 'y' : 'ies'}: ${reason}`,\n };\n }\n return { ok: true };\n }\n\n private scheduleDrain(): void {\n if (this.waitTimer !== null || this.maxWaitMs === Infinity) {\n return;\n }\n const timer = setTimeout((): void => {\n this.waitTimer = null;\n this.startDraining();\n }, this.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.waitTimer = timer;\n }\n\n private startDraining(): void {\n if (this.waitTimer !== null) {\n clearTimeout(this.waitTimer);\n this.waitTimer = null;\n }\n if (this.draining !== null || this.queue.length === 0) {\n return;\n }\n this.draining = this.drain()\n .catch((error: unknown): void => {\n // drain() handles every failure itself; this only guards a bug.\n console.error(`${this.logPrefix} BatchingErrorAdapter:`, error);\n })\n .finally((): void => {\n this.draining = null;\n // Entries logged after the last batch was taken still need a timer.\n if (this.queue.length > 0) {\n this.scheduleDrain();\n }\n });\n }\n\n /** Delivers batches until the queue is empty, waiting out a backoff after each failure. */\n private async drain(): Promise<void> {\n let failedRounds = 0;\n while (this.queue.length > 0) {\n const batch: Pending<TCode>[] = this.queue.slice(0, this.batchSize);\n const failure: string | undefined = await this.deliver(batch);\n if (failure === undefined) {\n failedRounds = 0;\n continue;\n }\n failedRounds += 1;\n const exhausted: Pending<TCode>[] = this.queue.filter(\n (item: Pending<TCode>): boolean => item.failures > this.retries\n );\n if (exhausted.length > 0) {\n this.queue = this.queue.filter(\n (item: Pending<TCode>): boolean => item.failures <= this.retries\n );\n this.drop(\n exhausted,\n `delivery failed ${this.retries + 1} time${this.retries === 0 ? '' : 's'}; last error: ${failure}`\n );\n }\n if (this.queue.length > 0) {\n await this.backoff(failedRounds);\n }\n }\n }\n\n /**\n * Tries to deliver `batch` (the front of the queue). Delivered entries leave\n * the queue; an entry that failed has its failure count raised and stays.\n * Resolves to the failure message, or undefined when everything went through.\n */\n private async deliver(batch: Pending<TCode>[]): Promise<string | undefined> {\n const remove = (delivered: Pending<TCode>[]): void => {\n const gone: Set<Pending<TCode>> = new Set(delivered);\n this.queue = this.queue.filter(\n (item: Pending<TCode>): boolean => !gone.has(item)\n );\n };\n\n if (this.useBatchMethod && hasLogBatch(this.inner)) {\n const result: ErrorLogResult = await this.safely(\n (): Promise<ErrorLogResult> =>\n (this.inner as BatchCapableAdapter<TCode>).logBatch(\n batch.map(\n (item: Pending<TCode>): ErrorLogEntry<TCode> => item.entry\n )\n )\n );\n if (result.ok) {\n remove(batch);\n return undefined;\n }\n for (const item of batch) {\n item.failures += 1;\n }\n return result.error ?? 'the wrapped adapter reported a failure';\n }\n\n for (const item of batch) {\n const result: ErrorLogResult = await this.safely(\n (): Promise<ErrorLogResult> => this.inner.log(item.entry)\n );\n if (result.ok) {\n remove([item]);\n continue;\n }\n // Stop here: the entries after this one stay queued without a failure\n // counted against them, and are tried after the backoff.\n item.failures += 1;\n return result.error ?? 'the wrapped adapter reported a failure';\n }\n return undefined;\n }\n\n /** The wrapped adapter should not throw, but a bug in one must not stop the queue. */\n private async safely(\n call: () => Promise<ErrorLogResult>\n ): Promise<ErrorLogResult> {\n try {\n return await call();\n } catch (error: unknown) {\n return { ok: false, error: describeError(error) };\n }\n }\n\n private backoff(failedRounds: number): Promise<void> {\n return new Promise<void>((resolve: () => void): void => {\n const timer = setTimeout(\n (): void => {\n this.backoffTimer = null;\n resolve();\n },\n backoffDelayMs(failedRounds, this.retryDelayMs, this.maxRetryDelayMs)\n );\n // Only a caller waiting in flush() should be kept alive by this wait.\n if (this.flushers === 0) {\n (timer as { unref?: () => void }).unref?.();\n }\n this.backoffTimer = timer;\n });\n }\n\n private drop(items: Pending<TCode>[], reason: string): void {\n if (items.length === 0) {\n return;\n }\n this.droppedSinceFlush += items.length;\n this.lastDropReason = reason;\n console.error(\n `${this.logPrefix} BatchingErrorAdapter: gave up on ${items.length} entr${items.length === 1 ? 'y' : 'ies'}: ${reason}`\n );\n try {\n this.onDrop?.(\n items.map((item: Pending<TCode>): ErrorLogEntry<TCode> => item.entry),\n reason\n );\n } catch {\n // A failing callback must not stop delivery.\n }\n }\n}\n"],"mappings":"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","names":["result","encoder","assertValidTableName","BUFFER_LIMIT_MULTIPLIER","DEFAULT_RETRY_DELAY_MS","DEFAULT_MAX_RETRY_DELAY_MS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/serialize.ts","../src/fail.ts","../src/adapters/console-error-adapter.ts","../src/backoff.ts","../src/uuid.ts","../src/entry-id.ts","../src/timeout.ts","../src/adapters/webhook-error-adapter.ts","../src/adapters/mongodb-error-adapter.ts","../src/adapters/composite-error-adapter.ts","../src/adapters/indexeddb-error-adapter.ts","../src/sql-ident.ts","../src/adapters/sqlite-error-adapter.ts","../src/adapters/web-storage-error-adapter.ts","../src/queue.ts","../src/file-system.ts","../src/adapters/flat-file-error-adapter.ts","../src/adapters/json-file-error-adapter.ts","../src/adapters/sql-error-adapter.ts","../src/adapters/redis-error-adapter.ts","../src/adapters/s3-error-adapter.ts","../src/adapters/firebase-error-adapter.ts","../src/adapters/supabase-error-adapter.ts","../src/adapters/event-emitter-error-adapter.ts","../src/adapters/websocket-error-adapter.ts","../src/adapters/cloudwatch-logs-error-adapter.ts","../src/adapters/batching-error-adapter.ts"],"sourcesContent":["export type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from './types.js';\nexport type { SerializedErrorLogEntry } from './serialize.js';\nexport { serializeEntry, serializeCause } from './serialize.js';\nexport { ConsoleErrorAdapter } from './adapters/console-error-adapter.js';\nexport type { ConsoleErrorOptions } from './adapters/console-error-adapter.js';\nexport { WebhookErrorAdapter } from './adapters/webhook-error-adapter.js';\nexport type { WebhookErrorOptions } from './adapters/webhook-error-adapter.js';\nexport { MongoDbErrorAdapter } from './adapters/mongodb-error-adapter.js';\nexport type {\n CollectionLike,\n MongoDbErrorOptions,\n} from './adapters/mongodb-error-adapter.js';\nexport { CompositeErrorAdapter } from './adapters/composite-error-adapter.js';\nexport type { CompositeErrorOptions } from './adapters/composite-error-adapter.js';\nexport {\n IndexedDbErrorAdapter,\n createErrorLogStore,\n} from './adapters/indexeddb-error-adapter.js';\nexport type {\n IdbDatabaseLike,\n IdbObjectStoreLike,\n IdbRequestLike,\n IdbTransactionLike,\n IdbUpgradeDatabaseLike,\n IndexedDbErrorOptions,\n IndexedDbErrorRecord,\n} from './adapters/indexeddb-error-adapter.js';\nexport {\n SqliteErrorAdapter,\n buildCreateErrorLogTableSql,\n} from './adapters/sqlite-error-adapter.js';\nexport type {\n SqliteClientLike,\n SqliteErrorOptions,\n} from './adapters/sqlite-error-adapter.js';\nexport { WebStorageErrorAdapter } from './adapters/web-storage-error-adapter.js';\nexport type {\n LockManagerLike,\n StorageLike,\n WebStorageErrorOptions,\n WebStorageErrorRecord,\n} from './adapters/web-storage-error-adapter.js';\nexport {\n FlatFileErrorAdapter,\n formatErrorLogLine,\n} from './adapters/flat-file-error-adapter.js';\nexport type { FlatFileErrorOptions } from './adapters/flat-file-error-adapter.js';\nexport type { FileSystemLike, ReadWriteFileSystemLike } from './file-system.js';\nexport { JsonFileErrorAdapter } from './adapters/json-file-error-adapter.js';\nexport type {\n JsonFileErrorOptions,\n JsonFileErrorRecord,\n} from './adapters/json-file-error-adapter.js';\nexport {\n MySqlErrorAdapter,\n PostgresErrorAdapter,\n buildErrorLogTableDdl,\n buildMySqlWidenColumnsSql,\n} from './adapters/sql-error-adapter.js';\nexport type {\n SqlClientLike,\n SqlDialect,\n SqlErrorOptions,\n} from './adapters/sql-error-adapter.js';\nexport { RedisErrorAdapter } from './adapters/redis-error-adapter.js';\nexport type {\n RedisClientLike,\n RedisErrorOptions,\n} from './adapters/redis-error-adapter.js';\nexport { S3ErrorAdapter } from './adapters/s3-error-adapter.js';\nexport type {\n S3BatchOptions,\n S3ClientLike,\n S3ErrorOptions,\n S3PutObjectInput,\n} from './adapters/s3-error-adapter.js';\nexport {\n FirestoreErrorAdapter,\n RealtimeDatabaseErrorAdapter,\n} from './adapters/firebase-error-adapter.js';\nexport type {\n FirebaseErrorOptions,\n FirebaseErrorRecord,\n FirestoreCollectionLike,\n FirestoreErrorOptions,\n FirestoreErrorRecord,\n RealtimeDatabaseRefLike,\n} from './adapters/firebase-error-adapter.js';\nexport {\n SupabaseErrorAdapter,\n buildSupabaseSetupSql,\n} from './adapters/supabase-error-adapter.js';\nexport type {\n SupabaseClientLike,\n SupabaseErrorLike,\n SupabaseErrorOptions,\n SupabaseErrorRow,\n SupabaseInsertResult,\n SupabaseSetupOptions,\n} from './adapters/supabase-error-adapter.js';\nexport { EventEmitterErrorAdapter } from './adapters/event-emitter-error-adapter.js';\nexport type {\n EmitterLike,\n EventEmitterErrorOptions,\n EventEmitterErrorRecord,\n} from './adapters/event-emitter-error-adapter.js';\nexport { WebSocketErrorAdapter } from './adapters/websocket-error-adapter.js';\nexport type {\n WebSocketErrorOptions,\n WebSocketLike,\n} from './adapters/websocket-error-adapter.js';\nexport { CloudWatchLogsErrorAdapter } from './adapters/cloudwatch-logs-error-adapter.js';\nexport type {\n CloudWatchBatchOptions,\n CloudWatchLogEvent,\n CloudWatchLogsClientLike,\n CloudWatchLogsErrorOptions,\n CloudWatchPutLogEventsInput,\n} from './adapters/cloudwatch-logs-error-adapter.js';\nexport { BatchingErrorAdapter } from './adapters/batching-error-adapter.js';\nexport type {\n BatchCapableAdapter,\n BatchingErrorOptions,\n} from './adapters/batching-error-adapter.js';\n","import type { ErrorLogEntry } from './types.js';\n\n/** A JSON-safe copy of an `ErrorLogEntry`. */\nexport interface SerializedErrorLogEntry {\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n}\n\n/** How many levels of nested causes and objects are kept before the rest is replaced by a marker. */\nexport const MAX_CAUSE_DEPTH = 6;\n\nfunction isErrorLike(value: object): value is {\n name: string;\n message: string;\n stack?: unknown;\n cause?: unknown;\n errors?: unknown;\n} {\n if (value instanceof Error) {\n return true;\n }\n // An error from another realm (vm, iframe) fails `instanceof Error`.\n const candidate = value as { name?: unknown; message?: unknown };\n return (\n typeof candidate.name === 'string' &&\n typeof candidate.message === 'string' &&\n typeof (value as { stack?: unknown }).stack === 'string'\n );\n}\n\nfunction readProperty(source: object, key: string): unknown {\n try {\n return (source as Record<string, unknown>)[key];\n } catch (error: unknown) {\n return `[Unreadable: ${error instanceof Error ? error.message : String(error)}]`;\n }\n}\n\nfunction toJsonSafe(\n value: unknown,\n depth: number,\n ancestors: WeakSet<object>\n): unknown {\n switch (typeof value) {\n case 'string':\n case 'boolean':\n case 'undefined':\n return value;\n case 'number':\n return Number.isFinite(value) ? value : String(value);\n case 'bigint':\n return `${value}n`;\n case 'symbol':\n return value.toString();\n case 'function':\n return `[Function ${value.name || 'anonymous'}]`;\n default:\n break;\n }\n if (value === null) {\n return null;\n }\n const objectValue: object = value as object;\n if (ancestors.has(objectValue)) {\n return '[Circular]';\n }\n if (depth >= MAX_CAUSE_DEPTH) {\n return '[Truncated]';\n }\n ancestors.add(objectValue);\n try {\n if (isErrorLike(objectValue)) {\n const result: Record<string, unknown> = {\n name: objectValue.name,\n message: objectValue.message,\n stack: objectValue.stack,\n };\n const nested: unknown = readProperty(objectValue, 'cause');\n if (nested !== undefined) {\n result['cause'] = toJsonSafe(nested, depth + 1, ancestors);\n }\n const errors: unknown = readProperty(objectValue, 'errors');\n if (Array.isArray(errors)) {\n result['errors'] = errors.map((item: unknown): unknown =>\n toJsonSafe(item, depth + 1, ancestors)\n );\n }\n return result;\n }\n if (Array.isArray(value)) {\n return value.map((item: unknown): unknown =>\n toJsonSafe(item, depth + 1, ancestors)\n );\n }\n const toJson: unknown = readProperty(objectValue, 'toJSON');\n if (typeof toJson === 'function') {\n try {\n return toJsonSafe(\n (toJson as () => unknown).call(objectValue),\n depth + 1,\n ancestors\n );\n } catch (error: unknown) {\n return `[toJSON failed: ${error instanceof Error ? error.message : String(error)}]`;\n }\n }\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(objectValue)) {\n result[key] = toJsonSafe(\n readProperty(objectValue, key),\n depth + 1,\n ancestors\n );\n }\n return result;\n } finally {\n ancestors.delete(objectValue);\n }\n}\n\n/**\n * Returns a copy of `cause` that `JSON.stringify` can always handle, so a\n * strange cause can never stop an entry from being stored.\n *\n * - `Error` objects (including ones from another realm) become\n * `{ name, message, stack }`, plus `cause` and, for an `AggregateError`,\n * `errors`, which are converted the same way.\n * - Circular references become `\"[Circular]\"`, and anything nested deeper than\n * `MAX_CAUSE_DEPTH` levels becomes `\"[Truncated]\"`.\n * - `BigInt`, `Symbol` and function values, and non-finite numbers, become strings.\n * - A property or `toJSON` that throws is replaced by a short description.\n */\nexport function serializeCause(cause: unknown): unknown {\n return toJsonSafe(cause, 0, new WeakSet<object>());\n}\n\n/** Returns a JSON-safe copy of the entry, adding a timestamp if missing. */\nexport function serializeEntry(entry: ErrorLogEntry): SerializedErrorLogEntry {\n const serialized: SerializedErrorLogEntry = {\n code: entry.code,\n message: entry.message,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n };\n if (entry.cause !== undefined) {\n serialized.cause = serializeCause(entry.cause);\n }\n if (entry.source !== undefined) {\n serialized.source = entry.source;\n }\n return serialized;\n}\n\n/** Turns an unknown thrown value into a descriptive message. */\nexport function describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { describeError } from './serialize.js';\nimport type { ErrorLogResult } from './types.js';\n\n/** Text printed before failure messages unless an adapter's `prefix` option says otherwise. */\nexport const DEFAULT_PREFIX = '[error-log-adapter]';\n\nexport interface FailureReport {\n /** Class name of the adapter, such as `PostgresErrorAdapter`. */\n adapterName: string;\n /** The adapter's `prefix` option. */\n prefix: string;\n /** What was being done, completing \"<adapter> failed ...\", such as `to write to table \"error_log\"`. */\n action: string;\n /** What went wrong. It is printed with `console.error`, and its message is returned. */\n error: unknown;\n /** Replaces the message taken from `error`, for sinks that describe failures in their own way. */\n reason?: string;\n}\n\n/**\n * Reports a failed `log` call the same way for every adapter: the problem goes\n * to `console.error` under the adapter's prefix, and the caller gets\n * `{ ok: false, error: \"<adapter> failed <action>: <reason>\" }`.\n */\nexport function reportFailure(report: FailureReport): ErrorLogResult {\n console.error(`${report.prefix} ${report.adapterName}:`, report.error);\n return {\n ok: false,\n error: `${report.adapterName} failed ${report.action}: ${report.reason ?? describeError(report.error)}`,\n };\n}\n","import { DEFAULT_PREFIX } from '../fail.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface ConsoleErrorOptions {\n /** Text printed before each entry. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\nexport class ConsoleErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n\n constructor(options: ConsoleErrorOptions = {}) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n console.error(this.prefix, entry);\n return { ok: true };\n } catch (error: unknown) {\n return {\n ok: false,\n error: `ConsoleErrorAdapter failed: ${String(error)}`,\n };\n }\n }\n}\n","/**\n * Milliseconds to wait before retry number `retryNumber` (1 for the first):\n * `baseMs` doubling each time, never more than `maxMs`. Half of it is fixed and\n * half random, so many callers that failed together do not retry in lockstep.\n */\nexport function backoffDelayMs(\n retryNumber: number,\n baseMs: number,\n maxMs: number\n): number {\n const ceiling: number = Math.min(maxMs, baseMs * 2 ** (retryNumber - 1));\n return ceiling / 2 + (Math.random() * ceiling) / 2;\n}\n","/** The parts of the Web Crypto API used here. */\ninterface CryptoLike {\n randomUUID?: () => string;\n getRandomValues?: (array: Uint8Array) => Uint8Array;\n}\n\n/**\n * Returns a random UUID (version 4).\n *\n * `crypto.randomUUID` only exists in secure contexts (`https://` and\n * `localhost`), so on a plain `http://` page it is undefined and calling it\n * would throw. `crypto.getRandomValues` is available everywhere, so it is used\n * as the fallback, with `Math.random` as the last resort for runtimes without\n * any `crypto`.\n */\nexport function generateId(): string {\n const cryptoApi: CryptoLike | undefined = (\n globalThis as { crypto?: CryptoLike }\n ).crypto;\n if (cryptoApi !== undefined && typeof cryptoApi.randomUUID === 'function') {\n return cryptoApi.randomUUID();\n }\n const bytes: Uint8Array = new Uint8Array(16);\n if (\n cryptoApi !== undefined &&\n typeof cryptoApi.getRandomValues === 'function'\n ) {\n cryptoApi.getRandomValues(bytes);\n } else {\n for (let index = 0; index < bytes.length; index += 1) {\n bytes[index] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex: string = Array.from(bytes, (byte: number): string =>\n byte.toString(16).padStart(2, '0')\n ).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","import type { ErrorLogEntry } from './types.js';\nimport { generateId } from './uuid.js';\n\nconst UUID_PATTERN: RegExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** True when the caller gave the entry its own `id`. */\nexport function hasCallerId(entry: ErrorLogEntry): boolean {\n return entry.id !== undefined;\n}\n\n/**\n * Returns the id to store an entry under: the caller's `id` (lower-cased, so the\n * same UUID always maps to the same record), or a new random UUID.\n *\n * @throws {RangeError} if the caller's `id` is not a UUID. Adapters call this\n * inside `log`'s try block, so the problem is reported as `ok: false`.\n */\nexport function resolveEntryId(entry: ErrorLogEntry): string {\n if (entry.id === undefined) {\n return generateId();\n }\n if (typeof entry.id !== 'string' || !UUID_PATTERN.test(entry.id)) {\n throw new RangeError(\n `entry.id must be a UUID such as one from crypto.randomUUID(), received ${JSON.stringify(entry.id)}`\n );\n }\n return entry.id.toLowerCase();\n}\n","/** Default time to wait for a sink to answer, in milliseconds. */\nexport const DEFAULT_TIMEOUT_MS = 10_000;\n\n/** Thrown (as a rejection) by `withTimeout` when the deadline passes first. */\nexport class SinkTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SinkTimeoutError';\n }\n}\n\n/**\n * Validates a `timeoutMs` option and applies the default.\n *\n * @throws {RangeError} unless the value is a positive number or `Infinity`.\n */\nexport function resolveTimeoutMs(\n value: number | undefined,\n adapterName: string\n): number {\n const timeoutMs: number = value ?? DEFAULT_TIMEOUT_MS;\n if (Number.isNaN(timeoutMs) || timeoutMs <= 0) {\n throw new RangeError(\n `${adapterName}: timeoutMs must be a positive number or Infinity, received ${String(value)}.`\n );\n }\n return timeoutMs;\n}\n\n/** What a caller should know when a sink did not answer in time. */\nexport function timeoutMessage(\n sink: string,\n timeoutMs: number,\n action: string = 'respond'\n): string {\n return `${sink} did not ${action} within ${timeoutMs} ms, so the entry may or may not have been stored (the operation can still finish later, so retrying could store it twice)`;\n}\n\n/**\n * Runs `start()` and waits for its result, but rejects with `message` if it takes\n * longer than `timeoutMs`. Drivers cannot generally cancel a call, so the call may\n * still complete after the deadline; `log()` just stops waiting for it. A `start`\n * that throws synchronously rejects too. `Infinity` waits without limit.\n */\nexport function withTimeout<T>(\n start: () => PromiseLike<T> | T,\n timeoutMs: number,\n message: string\n): Promise<T> {\n if (timeoutMs === Infinity) {\n return new Promise<T>((resolve, reject): void => {\n try {\n resolve(start() as T | PromiseLike<T>);\n } catch (error: unknown) {\n reject(error);\n }\n });\n }\n return new Promise<T>(\n (resolve: (value: T) => void, reject: (reason: unknown) => void): void => {\n const timer = setTimeout((): void => {\n reject(new SinkTimeoutError(message));\n }, timeoutMs);\n const settle =\n <A extends unknown[]>(\n done: (...args: A) => void\n ): ((...args: A) => void) =>\n (...args: A): void => {\n clearTimeout(timer);\n done(...args);\n };\n try {\n Promise.resolve(start()).then(settle(resolve), settle(reject));\n } catch (error: unknown) {\n settle(reject)(error);\n }\n }\n );\n}\n","import { backoffDelayMs } from '../backoff.js';\nimport { DEFAULT_PREFIX } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport { resolveTimeoutMs, timeoutMessage } from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface WebhookErrorOptions {\n /** Extra headers merged into the request. */\n headers?: Record<string, string>;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * Allow a plain `http://` URL. Entries contain error messages and stack\n * traces, so they are only sent over `https://` unless this is `true`.\n * `http://localhost`, `127.0.0.1` and `[::1]` are always allowed.\n * Defaults to `false`.\n */\n allowInsecure?: boolean;\n /**\n * How long to wait for the webhook to answer, in milliseconds. Defaults to\n * 10000. Pass `Infinity` to wait without limit. When it passes, the request is\n * cancelled, so the connection is closed rather than left open; the server may\n * still have received the entry.\n */\n timeoutMs?: number;\n /**\n * How many times to try again after a failure that may go away on its own: a\n * network error, a timeout, HTTP 408, 425, 429 or any 5xx. Other 4xx answers and\n * redirects are never retried. Defaults to 0 (send once). The wait doubles\n * after each attempt, starting at `retryDelayMs`, with jitter, and a\n * `Retry-After` header is honored. `timeoutMs` applies to each attempt, and\n * `log()` resolves only after the last one. Every attempt sends the same body,\n * and with `retries` above 0 the body always carries an `id`, so the receiver\n * can drop a repeat if an earlier attempt did arrive.\n */\n retries?: number;\n /** Wait before the first retry, in milliseconds. Defaults to 500. */\n retryDelayMs?: number;\n /** Longest wait between attempts, in milliseconds. Defaults to 30000. */\n maxRetryDelayMs?: number;\n /**\n * Shared secret for signing each request, so the receiver can verify the\n * entry came from you and was not altered. Adds `X-Signature-Timestamp`\n * (Unix seconds) and `X-Signature-256` (`sha256=` and the hex HMAC-SHA256 of\n * `<timestamp>.<body>`). The receiver should recompute it and reject old\n * timestamps. Needs Web Crypto (Node 20+, browsers, Deno, Bun); without it\n * `log()` fails rather than sending an unsigned entry.\n */\n signingSecret?: string;\n}\n\nconst DEFAULT_RETRY_DELAY_MS = 500;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst RETRYABLE_STATUSES: ReadonlySet<number> = new Set([408, 425, 429]);\n\n/** What one attempt came to. */\ntype Attempt =\n | { ok: true }\n | { ok: false; reason: string; retryable: boolean; retryAfterMs?: number };\n\n/** The parts of Web Crypto used for signing. */\ninterface SubtleLike {\n importKey(\n format: 'raw',\n keyData: Uint8Array,\n algorithm: { name: 'HMAC'; hash: 'SHA-256' },\n extractable: boolean,\n usages: string[]\n ): Promise<unknown>;\n sign(algorithm: 'HMAC', key: unknown, data: Uint8Array): Promise<ArrayBuffer>;\n}\n\n/** Signing could not be done at all, as opposed to the webhook failing. */\nclass SigningUnavailableError extends Error {}\n\n/** Hex HMAC-SHA256 of `message`. @throws {SigningUnavailableError} when Web Crypto is missing. */\nasync function hmacSha256Hex(secret: string, message: string): Promise<string> {\n const subtle: SubtleLike | undefined = (\n globalThis as { crypto?: { subtle?: SubtleLike } }\n ).crypto?.subtle;\n if (subtle === undefined) {\n throw new SigningUnavailableError(\n 'signingSecret needs Web Crypto (crypto.subtle), which this runtime does not provide, so the entry was not sent unsigned'\n );\n }\n const encoder = new TextEncoder();\n const key: unknown = await subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n const signature: ArrayBuffer = await subtle.sign(\n 'HMAC',\n key,\n encoder.encode(message)\n );\n return Array.from(new Uint8Array(signature), (byte: number): string =>\n byte.toString(16).padStart(2, '0')\n ).join('');\n}\n\n/** Reads `Retry-After` (seconds or an HTTP date) as milliseconds, or undefined. */\nfunction parseRetryAfterMs(\n value: string | null | undefined\n): number | undefined {\n if (value === null || value === undefined || value.trim() === '') {\n return undefined;\n }\n const seconds: number = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return seconds * 1000;\n }\n const date: number = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction validateRetryOptions(options: WebhookErrorOptions): {\n retries: number;\n retryDelayMs: number;\n maxRetryDelayMs: number;\n} {\n const retries: number = options.retries ?? 0;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new RangeError(\n `WebhookErrorAdapter: retries must be a whole number of 0 or more, received ${String(options.retries)}.`\n );\n }\n const retryDelayMs: number = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n if (!Number.isFinite(retryDelayMs) || retryDelayMs <= 0) {\n throw new RangeError(\n `WebhookErrorAdapter: retryDelayMs must be a positive number, received ${String(options.retryDelayMs)}.`\n );\n }\n const maxRetryDelayMs: number =\n options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n if (!Number.isFinite(maxRetryDelayMs) || maxRetryDelayMs < retryDelayMs) {\n throw new RangeError(\n `WebhookErrorAdapter: maxRetryDelayMs must be a number no smaller than retryDelayMs (${retryDelayMs}), received ${String(options.maxRetryDelayMs)}.`\n );\n }\n if (options.signingSecret === '') {\n throw new RangeError(\n 'WebhookErrorAdapter: signingSecret must not be empty; leave it out to send unsigned.'\n );\n }\n return { retries, retryDelayMs, maxRetryDelayMs };\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve: () => void): void => {\n setTimeout(resolve, ms);\n });\n}\n\nconst LOOPBACK_HOSTS: ReadonlySet<string> = new Set([\n 'localhost',\n '127.0.0.1',\n '[::1]',\n]);\n\n/** Parses the URL, and rejects one that is invalid or would send entries unencrypted. */\nfunction parseWebhookUrl(url: string, allowInsecure: boolean): URL {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n throw new RangeError(\n 'WebhookErrorAdapter: the webhook URL is not a valid absolute URL (for example https://example.com/errors).'\n );\n }\n if (parsed.protocol === 'https:') {\n return parsed;\n }\n if (\n parsed.protocol === 'http:' &&\n (allowInsecure || LOOPBACK_HOSTS.has(parsed.hostname))\n ) {\n return parsed;\n }\n throw new RangeError(\n `WebhookErrorAdapter: the webhook URL must use https:// because entries contain error messages and stack traces (received ${parsed.protocol}//${parsed.host}). Pass allowInsecure: true to send over http.`\n );\n}\n\nexport class WebhookErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private readonly retries: number;\n private readonly retryDelayMs: number;\n private readonly maxRetryDelayMs: number;\n /** Scheme and host only. Webhook URLs often carry a secret in the path or query. */\n private readonly target: string;\n\n /** @throws {RangeError} if `url` is not a valid `https://` URL (see `allowInsecure`) or a timeout, retry or signing option is invalid. */\n constructor(\n private readonly url: string,\n private readonly options: WebhookErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'WebhookErrorAdapter');\n ({\n retries: this.retries,\n retryDelayMs: this.retryDelayMs,\n maxRetryDelayMs: this.maxRetryDelayMs,\n } = validateRetryOptions(options));\n this.target = parseWebhookUrl(url, options.allowInsecure ?? false).origin;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n let body: string;\n try {\n // A caller-supplied id goes along so the receiver can drop a repeat; a\n // retry can repeat too, so it gets one as well.\n body = JSON.stringify(\n hasCallerId(entry) || this.retries > 0\n ? { id: resolveEntryId(entry), ...serializeEntry(entry) }\n : serializeEntry(entry)\n );\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n return this.fail(\n `could not prepare the entry: ${this.redact(describeError(error))}`\n );\n }\n\n return this.deliver(body);\n }\n\n /**\n * Sends several entries in one request, as a JSON array of the same records\n * `log` sends one at a time (each with an `id`). `BatchingErrorAdapter` uses\n * this when it wraps a webhook, so the receiver must accept an array. The\n * whole request succeeds or fails together.\n */\n async logBatch(entries: ErrorLogEntry<TCode>[]): Promise<ErrorLogResult> {\n if (entries.length === 0) {\n return { ok: true };\n }\n let body: string;\n try {\n body = JSON.stringify(\n entries.map((entry: ErrorLogEntry<TCode>) => ({\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n }))\n );\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n return this.fail(\n `could not prepare the entries: ${this.redact(describeError(error))}`\n );\n }\n return this.deliver(body);\n }\n\n /** Sends one prepared body, trying again as `retries` allows. */\n private async deliver(body: string): Promise<ErrorLogResult> {\n let attempt: Attempt = await this.send(body);\n let attempts = 1;\n while (!attempt.ok && attempt.retryable && attempts <= this.retries) {\n await sleep(this.retryWaitMs(attempts, attempt.retryAfterMs));\n attempt = await this.send(body);\n attempts += 1;\n }\n if (attempt.ok) {\n return { ok: true };\n }\n return this.fail(\n attempts > 1\n ? `${attempt.reason} (after ${attempts} attempts)`\n : attempt.reason\n );\n }\n\n /** Milliseconds to wait before retry number `retryNumber` (1 for the first). */\n private retryWaitMs(retryNumber: number, retryAfterMs?: number): number {\n if (retryAfterMs !== undefined) {\n return Math.min(retryAfterMs, this.maxRetryDelayMs);\n }\n return backoffDelayMs(retryNumber, this.retryDelayMs, this.maxRetryDelayMs);\n }\n\n private async send(body: string): Promise<Attempt> {\n const controller: AbortController = new AbortController();\n let timedOut = false;\n const timer: ReturnType<typeof setTimeout> | undefined =\n this.timeoutMs === Infinity\n ? undefined\n : setTimeout((): void => {\n timedOut = true;\n controller.abort();\n }, this.timeoutMs);\n try {\n const response: Response = await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.headers,\n ...(await this.signatureHeaders(body)),\n },\n body,\n // A redirect would turn the POST into a GET without a body (302, 303)\n // or re-send the entry and headers to another host (307, 308).\n redirect: 'manual',\n signal: controller.signal,\n });\n if (response.type === 'opaqueredirect') {\n return this.rejected(this.redirectMessage('a redirect'));\n }\n if (!response.ok) {\n if (response.status >= 300 && response.status < 400) {\n return this.rejected(this.redirectMessage(`HTTP ${response.status}`));\n }\n return {\n ok: false,\n reason: `webhook ${this.target} responded with HTTP ${response.status}`,\n retryable:\n response.status >= 500 || RETRYABLE_STATUSES.has(response.status),\n retryAfterMs: parseRetryAfterMs(response.headers?.get('retry-after')),\n };\n }\n return { ok: true };\n } catch (error: unknown) {\n console.error(`${this.prefix} WebhookErrorAdapter:`, error);\n if (error instanceof SigningUnavailableError) {\n // A missing Web Crypto will not appear on a second try.\n return this.rejected(error.message);\n }\n if (timedOut) {\n return {\n ok: false,\n reason: timeoutMessage(`webhook ${this.target}`, this.timeoutMs),\n retryable: true,\n };\n }\n return {\n ok: false,\n reason: `could not reach webhook ${this.target}: ${this.redact(describeError(error))}`,\n retryable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** Timestamp and signature headers, or none when no `signingSecret` is set. */\n private async signatureHeaders(\n body: string\n ): Promise<Record<string, string>> {\n const secret: string | undefined = this.options.signingSecret;\n if (secret === undefined) {\n return {};\n }\n const timestamp: string = String(Math.floor(Date.now() / 1000));\n const signature: string = await hmacSha256Hex(\n secret,\n `${timestamp}.${body}`\n );\n return {\n 'X-Signature-Timestamp': timestamp,\n 'X-Signature-256': `sha256=${signature}`,\n };\n }\n\n private rejected(reason: string): Attempt {\n return { ok: false, reason, retryable: false };\n }\n\n private redirectMessage(what: string): string {\n return `webhook ${this.target} responded with ${what}, which is not followed because the entry would not arrive intact; use the final URL instead`;\n }\n\n /** Replaces the full URL in a message with its origin, in case the runtime put it there. */\n private redact(message: string): string {\n return message.split(this.url).join(this.target);\n }\n\n private fail(reason: string): ErrorLogResult {\n return { ok: false, error: `WebhookErrorAdapter failed: ${reason}` };\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** The subset of a MongoDB `Collection` this adapter uses. */\nexport interface CollectionLike {\n insertOne(doc: object): Promise<unknown>;\n}\n\nexport interface MongoDbErrorOptions {\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the insert before giving up, in milliseconds. Defaults to\n * 10000. Pass `Infinity` to wait without limit. The call cannot be cancelled,\n * so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** MongoDB reports a repeated `_id` as error code 11000. */\nfunction isDuplicateKeyError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 11000\n );\n}\n\nexport class MongoDbErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly collection: CollectionLike,\n options: MongoDbErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'MongoDbErrorAdapter');\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const document: object = hasCallerId(entry)\n ? { _id: resolveEntryId(entry), ...serializeEntry(entry) }\n : serializeEntry(entry);\n await withTimeout(\n (): Promise<unknown> => this.collection.insertOne(document),\n this.timeoutMs,\n timeoutMessage('MongoDB', this.timeoutMs)\n );\n return { ok: true };\n } catch (error: unknown) {\n if (hasCallerId(entry) && isDuplicateKeyError(error)) {\n // The same id was stored before: this call is a retry that already succeeded.\n return { ok: true };\n }\n return reportFailure({\n adapterName: 'MongoDbErrorAdapter',\n prefix: this.prefix,\n action: 'to insert entry',\n error,\n });\n }\n }\n}\n","import {\n DEFAULT_TIMEOUT_MS,\n SinkTimeoutError,\n resolveTimeoutMs,\n withTimeout,\n} from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\nexport interface CompositeErrorOptions {\n /**\n * How long to wait for each adapter before reporting it as failed, in\n * milliseconds. Defaults to 30000, longer than the 10000 default of the built-in\n * adapters so their own, more specific timeout messages come first. This only\n * matters for a custom adapter that can hang: one stuck adapter then cannot\n * hold back the result of the others. Pass `Infinity` to wait without limit.\n */\n timeoutMs?: number;\n}\n\n/** Default wait for each adapter of a composite. */\nexport const DEFAULT_COMPOSITE_TIMEOUT_MS: number = DEFAULT_TIMEOUT_MS * 3;\n\n/** Sends every entry to several adapters. Succeeds only if all of them do. */\nexport class CompositeErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly adapters: ReadonlyArray<ErrorLogAdapter<TCode>>,\n options: CompositeErrorOptions = {}\n ) {\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs ?? DEFAULT_COMPOSITE_TIMEOUT_MS,\n 'CompositeErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n const results: ErrorLogResult[] = await Promise.all(\n this.adapters.map(\n async (\n adapter: ErrorLogAdapter<TCode>,\n index: number\n ): Promise<ErrorLogResult> => {\n try {\n return await withTimeout(\n (): Promise<ErrorLogResult> => adapter.log(entry),\n this.timeoutMs,\n `adapter ${index} (${adapter.constructor.name}) did not return within ${this.timeoutMs} ms, so the entry may or may not have been stored there`\n );\n } catch (error: unknown) {\n if (error instanceof SinkTimeoutError) {\n return { ok: false, error: error.message };\n }\n // Adapters should not throw, but a faulty custom one must not break the others.\n return {\n ok: false,\n error: `adapter threw instead of returning a result: ${String(error)}`,\n };\n }\n }\n )\n );\n const failures: string[] = results.flatMap(\n (result: ErrorLogResult): string[] =>\n result.ok ? [] : [result.error ?? 'unknown failure']\n );\n if (failures.length === 0) {\n return { ok: true };\n }\n return {\n ok: false,\n error: `${failures.length} of ${results.length} adapters failed: ${failures.join('; ')}`,\n };\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default object store name. */\nexport const DEFAULT_STORE_NAME = 'error_log';\n\n/** Event handler slot. Typed loosely so real DOM `IDBTransaction` objects fit. */\ntype IdbHandler = ((event: never) => unknown) | null;\n\n/** The parts of a DOM event this adapter calls. */\ninterface IdbEventLike {\n preventDefault?(): void;\n stopPropagation?(): void;\n}\n\n/** The error object of a failed request or transaction (a `DOMException`). */\ninterface IdbErrorLike {\n name?: string;\n message?: string;\n}\n\n/** The subset of an `IDBRequest` this adapter uses. */\nexport interface IdbRequestLike {\n onerror: IdbHandler;\n readonly error?: IdbErrorLike | null;\n}\n\n/** The subset of an `IDBObjectStore` this adapter uses. */\nexport interface IdbObjectStoreLike {\n /** Returns an `IDBRequest`. Fakes that return nothing also work. */\n add(value: unknown): unknown;\n /**\n * Replaces the record with the same key. Used for entries that carry their own\n * `id`, so logging one again does not fail on the existing key. A real\n * `IDBObjectStore` has it; a fake without it falls back to `add`.\n */\n put?(value: unknown): unknown;\n}\n\n/** The subset of an `IDBTransaction` this adapter uses. */\nexport interface IdbTransactionLike {\n objectStore(name: string): IdbObjectStoreLike;\n oncomplete: IdbHandler;\n onerror: IdbHandler;\n onabort: IdbHandler;\n readonly error?: IdbErrorLike | null;\n}\n\n/** The subset of an `IDBDatabase` this adapter uses. */\nexport interface IdbDatabaseLike {\n transaction(storeNames: string, mode: 'readwrite'): IdbTransactionLike;\n}\n\n/** The subset of an `IDBDatabase` available inside `onupgradeneeded`. */\nexport interface IdbUpgradeDatabaseLike {\n readonly objectStoreNames: { contains(name: string): boolean };\n createObjectStore(\n name: string,\n options: { keyPath: string }\n ): { createIndex(name: string, keyPath: string): unknown };\n}\n\n/** A stored record: the serialized entry plus its generated UUID key. */\nexport interface IndexedDbErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface IndexedDbErrorOptions {\n /** Object store to write to. Defaults to `error_log`. */\n storeName?: string;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the transaction to finish, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. A transaction can stay\n * pending when a version change in another tab blocks it, or when the browser\n * has evicted the origin's storage. The write may still finish afterwards.\n */\n timeoutMs?: number;\n}\n\n/**\n * Creates the error log object store (UUID `id` key, `timestamp` index).\n * Call it from your `onupgradeneeded` handler. Does nothing if it already exists.\n */\nexport function createErrorLogStore(\n database: IdbUpgradeDatabaseLike,\n storeName: string = DEFAULT_STORE_NAME\n): void {\n if (database.objectStoreNames.contains(storeName)) {\n return;\n }\n database\n .createObjectStore(storeName, { keyPath: 'id' })\n .createIndex('timestamp', 'timestamp');\n}\n\nexport class IndexedDbErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly storeName: string;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or Infinity. */\n constructor(\n private readonly database: IdbDatabaseLike,\n options: IndexedDbErrorOptions = {}\n ) {\n this.storeName = options.storeName ?? DEFAULT_STORE_NAME;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'IndexedDbErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: IndexedDbErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n await this.write(record, hasCallerId(entry));\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'IndexedDbErrorAdapter',\n prefix: this.prefix,\n action: `to write to object store \"${this.storeName}\"`,\n error,\n });\n }\n }\n\n /**\n * Resolves once the transaction commits; rejects if the request fails, the\n * transaction aborts, or it does not finish within `timeoutMs`.\n *\n * Error events are handled here (`preventDefault` and `stopPropagation`), so a\n * failed log write does not reach the `onerror` handler an app may have set on\n * its own database object.\n */\n private write(\n record: IndexedDbErrorRecord,\n replaceExisting: boolean\n ): Promise<void> {\n return withTimeout(\n (): Promise<void> =>\n new Promise<void>(\n (resolve: () => void, reject: (reason: unknown) => void): void => {\n const transaction: IdbTransactionLike = this.database.transaction(\n this.storeName,\n 'readwrite'\n );\n const fail =\n (what: string, source: { error?: IdbErrorLike | null }) =>\n (event?: IdbEventLike): void => {\n event?.preventDefault?.();\n event?.stopPropagation?.();\n reject(new Error(`${what}: ${describeDomError(source.error)}`));\n };\n transaction.oncomplete = (): void => resolve();\n transaction.onerror = fail('transaction failed', transaction);\n transaction.onabort = fail('transaction aborted', transaction);\n const store: IdbObjectStoreLike = transaction.objectStore(\n this.storeName\n );\n const request: unknown =\n replaceExisting && typeof store.put === 'function'\n ? store.put(record)\n : store.add(record);\n if (typeof request === 'object' && request !== null) {\n // The request error holds the details (for example a\n // ConstraintError); the transaction's own error is null for it.\n (request as IdbRequestLike).onerror = fail(\n 'request failed',\n request as IdbRequestLike\n );\n }\n }\n ),\n this.timeoutMs,\n timeoutMessage('IndexedDB', this.timeoutMs, 'finish the transaction')\n );\n }\n}\n\n/** `ConstraintError: Unable to add key to index...`, or a note that nothing was given. */\nfunction describeDomError(error: IdbErrorLike | null | undefined): string {\n if (error === null || error === undefined) {\n return 'no details provided';\n }\n const text: string = [error.name, error.message]\n .filter((part): part is string => typeof part === 'string' && part !== '')\n .join(': ');\n return text === '' ? 'no details provided' : text;\n}\n","/** Default table name for the SQL, SQLite and Supabase adapters. */\nexport const DEFAULT_TABLE_NAME = 'error_log';\n\nconst IDENTIFIER_PATTERN: RegExp = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Throws a descriptive `RangeError` unless `value` is a plain SQL identifier:\n * letters, digits and underscores, not starting with a digit. Quoting is then\n * never needed, and nothing in the name can alter a statement.\n *\n * @param label What the value is, for the message, such as `table name`.\n * @param owner Who is asking, such as `PostgresErrorAdapter`.\n * @param maxLength Longest allowed length, when the caller needs a limit.\n */\nexport function assertIdentifier(\n value: string,\n label: string,\n owner: string,\n maxLength?: number\n): void {\n if (!IDENTIFIER_PATTERN.test(value)) {\n throw new RangeError(\n `${owner}: invalid ${label} \"${value}\". Use only letters, digits and underscores, and do not start with a digit.`\n );\n }\n if (maxLength !== undefined && value.length > maxLength) {\n throw new RangeError(\n `${owner}: ${label} \"${value}\" is ${value.length} characters long, but the limit is ${maxLength}.`\n );\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * The only thing this adapter needs from a SQLite driver: run one statement\n * with positional (`?`) parameters. `bun:sqlite` already matches this shape.\n * Other drivers need a one-line wrapper, for example:\n *\n * ```ts\n * // better-sqlite3 or node:sqlite\n * const client: SqliteClientLike = {\n * run: (sql, params) => db.prepare(sql).run(...params),\n * };\n * ```\n */\nexport interface SqliteClientLike {\n run(sql: string, params: unknown[]): unknown;\n}\n\nexport interface SqliteErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only. */\n tableName?: string;\n /** Create the table (and index) on first use. Defaults to `true`. */\n createTable?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for the driver before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Throws a descriptive error unless `tableName` is a plain SQL identifier. */\nfunction assertValidTableName(tableName: string): void {\n assertIdentifier(tableName, 'table name', 'SqliteErrorAdapter');\n}\n\n/**\n * Returns the statements that create the error log table and its index.\n * Use this to run your own migration when `createTable` is disabled.\n */\nexport function buildCreateErrorLogTableSql(\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName);\n return [\n `CREATE TABLE IF NOT EXISTS ${tableName} (` +\n 'id TEXT PRIMARY KEY NOT NULL, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause TEXT, ' +\n 'source TEXT, ' +\n 'occurred_at TEXT NOT NULL, ' +\n \"created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))\" +\n ')',\n `CREATE INDEX IF NOT EXISTS ${tableName}_occurred_at_idx ON ${tableName} (occurred_at)`,\n ];\n}\n\nexport class SqliteErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly createTable: boolean;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private tableReady: Promise<void> | null = null;\n\n /** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */\n constructor(\n private readonly client: SqliteClientLike,\n options: SqliteErrorOptions = {}\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertValidTableName(this.tableName);\n this.createTable = options.createTable ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'SqliteErrorAdapter');\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n await this.ensureTable();\n const serialized = serializeEntry(entry);\n await this.run(\n `INSERT INTO ${this.tableName} (id, code, message, cause, source, occurred_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT (id) DO NOTHING`,\n [\n resolveEntryId(entry),\n serialized.code,\n serialized.message,\n serialized.cause === undefined\n ? null\n : JSON.stringify(serialized.cause),\n serialized.source ?? null,\n serialized.timestamp,\n ]\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'SqliteErrorAdapter',\n prefix: this.prefix,\n action: `to write to table \"${this.tableName}\"`,\n error,\n });\n }\n }\n\n /** Creates the table once. A failed attempt is retried on the next log call. */\n private ensureTable(): Promise<void> {\n if (!this.createTable) {\n return Promise.resolve();\n }\n if (this.tableReady === null) {\n this.tableReady = this.runCreateStatements().catch(\n (error: unknown): never => {\n this.tableReady = null;\n throw error;\n }\n );\n }\n return this.tableReady;\n }\n\n private async runCreateStatements(): Promise<void> {\n for (const sql of buildCreateErrorLogTableSql(this.tableName)) {\n await this.run(sql, []);\n }\n }\n\n /** Runs one statement, giving up waiting after `timeoutMs`. */\n private run(sql: string, params: unknown[]): Promise<unknown> {\n return withTimeout(\n (): unknown => this.client.run(sql, params),\n this.timeoutMs,\n timeoutMessage('SQLite', this.timeoutMs)\n );\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default storage key. */\nexport const DEFAULT_STORAGE_KEY = 'error_log';\n\n/** Default number of entries kept before the oldest are dropped. */\nexport const DEFAULT_MAX_ENTRIES = 100;\n\n/**\n * The subset of the Web Storage API this adapter uses. Both `sessionStorage`\n * and `localStorage` fit.\n */\nexport interface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/**\n * The part of `navigator.locks` this adapter uses. `localStorage` is shared by\n * every tab of an origin and has no atomic update, so a lock keeps two tabs from\n * overwriting each other's entry.\n */\nexport interface LockManagerLike {\n request<T>(name: string, callback: () => T | Promise<T>): Promise<T>;\n}\n\n/** A stored record: the serialized entry plus its generated UUID. */\nexport interface WebStorageErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface WebStorageErrorOptions {\n /** Key the entries are stored under, as one JSON array. Defaults to `error_log`. */\n key?: string;\n /** Most entries to keep. The oldest are dropped first. Defaults to `100`. */\n maxEntries?: number;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * Lock manager that makes tabs sharing a `localStorage` take turns. Defaults to\n * `navigator.locks` when the browser has it. This reduces lost entries but does\n * not remove them: a tab in another process can still read a stale copy\n * (measured in Chromium). Pass `null` to turn locking off.\n */\n locks?: LockManagerLike | null;\n}\n\n/** Browsers report a full store with different names and codes. */\nfunction isQuotaError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n const { name, code } = error as { name?: unknown; code?: unknown };\n return (\n (typeof name === 'string' && name.toLowerCase().includes('quota')) ||\n code === 22 ||\n code === 1014\n );\n}\n\n/**\n * Keeps the newest entries in a Web Storage object (`sessionStorage` or\n * `localStorage`) as a JSON array under one key, capped at `maxEntries`.\n * When the store is full, the oldest entries are dropped until the new one fits.\n *\n * `sessionStorage` belongs to one tab, so it is safe. `localStorage` is shared by\n * every tab of the origin and has no atomic update: two tabs logging at the same\n * moment can overwrite each other's entry while both get `ok: true`. In Chromium,\n * tabs in separate processes (the usual case) even read a stale copy, so a lock\n * reduces the loss without removing it. Where tabs may log at the same time and\n * every entry matters, use `IndexedDbErrorAdapter`, whose writes are transactional.\n */\nexport class WebStorageErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly key: string;\n private readonly maxEntries: number;\n private readonly prefix: string;\n private readonly locks: LockManagerLike | null;\n\n /** @throws {RangeError} if `options.maxEntries` is not a positive integer. */\n constructor(\n private readonly storage: StorageLike,\n options: WebStorageErrorOptions = {}\n ) {\n this.key = options.key ?? DEFAULT_STORAGE_KEY;\n this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.locks =\n options.locks === undefined ? defaultLockManager() : options.locks;\n if (!Number.isInteger(this.maxEntries) || this.maxEntries < 1) {\n throw new RangeError(\n `WebStorageErrorAdapter: maxEntries must be a positive integer, received ${String(options.maxEntries)}.`\n );\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: WebStorageErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n await this.appendLocked(record);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'WebStorageErrorAdapter',\n prefix: this.prefix,\n action: `to write to storage key \"${this.key}\"`,\n error,\n });\n }\n }\n\n /**\n * Appends under a cross-tab lock when one is available. If the lock itself\n * cannot be taken (for example a sandboxed frame), the entry is still written\n * without it rather than lost.\n */\n private async appendLocked(record: WebStorageErrorRecord): Promise<void> {\n if (this.locks === null) {\n this.append(record);\n return;\n }\n let appended = false;\n try {\n await this.locks.request(`error-log-adapter:${this.key}`, (): void => {\n appended = true;\n this.append(record);\n });\n } catch (error: unknown) {\n if (appended) {\n throw error;\n }\n this.append(record);\n }\n }\n\n /** Reads, appends, trims, and writes back. Runs without awaiting, so calls in one tab never interleave. */\n private append(record: WebStorageErrorRecord): void {\n // An entry logged again with the same id replaces its earlier record.\n const records: WebStorageErrorRecord[] = [\n ...this.readRecords().filter(\n (existing: WebStorageErrorRecord): boolean => existing?.id !== record.id\n ),\n record,\n ].slice(-this.maxEntries);\n for (;;) {\n try {\n this.storage.setItem(this.key, JSON.stringify(records));\n return;\n } catch (error: unknown) {\n if (!isQuotaError(error) || records.length <= 1) {\n throw error;\n }\n records.shift();\n }\n }\n }\n\n /** Returns the stored records, or none if the value is missing or unreadable. */\n private readRecords(): WebStorageErrorRecord[] {\n const stored: string | null = this.storage.getItem(this.key);\n if (stored === null) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(stored);\n if (Array.isArray(parsed)) {\n return parsed as WebStorageErrorRecord[];\n }\n this.setAside(stored, 'did not hold an array');\n } catch (error: unknown) {\n this.setAside(stored, `held invalid JSON (${describeError(error)})`);\n }\n return [];\n }\n\n /**\n * Copies an unusable value to `<key>.corrupt` before it is replaced, so data\n * the host app stored under the same key is not silently destroyed. Only the\n * first one is kept; a later unusable value would not overwrite it.\n */\n private setAside(stored: string, reason: string): void {\n const backupKey = `${this.key}${CORRUPT_KEY_SUFFIX}`;\n let savedTo = 'but it could not be copied aside';\n try {\n if (this.storage.getItem(backupKey) === null) {\n this.storage.setItem(backupKey, stored);\n savedTo = `and a copy was kept under \"${backupKey}\"`;\n } else {\n savedTo = `and \"${backupKey}\" already holds an earlier copy`;\n }\n } catch {\n // The store may be full; the original value is replaced regardless.\n }\n console.error(\n `${this.prefix} WebStorageErrorAdapter: storage key \"${this.key}\" ${reason}, so it was replaced, ${savedTo}.`\n );\n }\n}\n\n/** Suffix of the key an unusable value is copied to before being replaced. */\nexport const CORRUPT_KEY_SUFFIX = '.corrupt';\n\nfunction defaultLockManager(): LockManagerLike | null {\n const locks: unknown = (globalThis as { navigator?: { locks?: unknown } })\n .navigator?.locks;\n return typeof (locks as LockManagerLike | undefined)?.request === 'function'\n ? (locks as LockManagerLike)\n : null;\n}\n","/**\n * Runs tasks one after another, in the order they were queued. A task that\n * rejects does not stop the ones queued behind it.\n */\nexport class SerialQueue {\n private tail: Promise<unknown> = Promise.resolve();\n\n /** Starts `task` once every earlier task has finished, and resolves with its result. */\n run<T>(task: () => Promise<T>): Promise<T> {\n const result: Promise<T> = this.tail.then(task);\n this.tail = result.catch((): undefined => undefined);\n return result;\n }\n}\n","/**\n * The subset of `node:fs/promises` the file adapters use. Pass `fs.promises`\n * (or `node:fs/promises`) yourself, or leave `fs` unset in Node and Bun.\n */\nexport interface FileSystemLike {\n appendFile(path: string, data: string): Promise<unknown>;\n mkdir(path: string, options: { recursive: true }): Promise<unknown>;\n stat(path: string): Promise<{ size: number }>;\n rename(from: string, to: string): Promise<unknown>;\n rm(path: string, options: { force: true }): Promise<unknown>;\n}\n\n/** `FileSystemLike` plus the reading and replacing that whole-file writes need. */\nexport interface ReadWriteFileSystemLike extends FileSystemLike {\n readFile(path: string, encoding: 'utf8'): Promise<string>;\n writeFile(path: string, data: string): Promise<unknown>;\n}\n\n/** Loads `node:fs/promises`. Only call this from code that runs in Node or Bun. */\nexport async function loadNodeFileSystem(): Promise<ReadWriteFileSystemLike> {\n // A variable specifier keeps browser bundlers from resolving it.\n const moduleName: string = 'node:fs/promises';\n return (await import(moduleName)) as ReadWriteFileSystemLike;\n}\n\n/** A filesystem root: `/`, `\\\\`, `C:\\\\` or `C:/`, which always exists and cannot be created. */\nconst ROOT_PATTERN: RegExp = /^(?:[A-Za-z]:)?[\\\\/]+$/;\n\n/**\n * Returns the folder to create for a file path, or `null` when there is nothing\n * to create: the path has no folder part (`app.log`), or its folder is a\n * filesystem root that always exists (`/app.log`, `C:\\\\app.log`, `\\\\app.log`).\n * Calling `mkdir` on a Windows drive root fails with `EPERM`.\n */\nexport function parentDirectory(filePath: string): string | null {\n const lastSeparator: number = Math.max(\n filePath.lastIndexOf('/'),\n filePath.lastIndexOf('\\\\')\n );\n if (lastSeparator < 0) {\n return null;\n }\n const directory: string = filePath.slice(0, lastSeparator + 1);\n if (ROOT_PATTERN.test(directory)) {\n return null;\n }\n return filePath.slice(0, lastSeparator);\n}\n\n/** True for the error Node throws when a file or folder does not exist. */\nexport function isMissingFileError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { code?: unknown }).code === 'ENOENT'\n );\n}\n","import { SerialQueue } from '../queue.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport {\n isMissingFileError,\n loadNodeFileSystem,\n parentDirectory,\n} from '../file-system.js';\nimport type { FileSystemLike } from '../file-system.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default number of rotated files kept when `maxBytes` is set. */\nexport const DEFAULT_MAX_FILES = 3;\n\nexport interface FlatFileErrorOptions {\n /** File system to write to. Defaults to `node:fs/promises`, loaded on first use. */\n fs?: FileSystemLike;\n /** Turns an entry into one line of text. Defaults to `formatErrorLogLine`. */\n formatLine?: (entry: SerializedErrorLogEntry) => string;\n /** Rotate the file before it would grow past this many bytes. Off by default. */\n maxBytes?: number;\n /** Rotated files to keep (`app.log.1` is newest). Defaults to `3`. Needs `maxBytes`. */\n maxFiles?: number;\n /** Create missing parent directories on first use. Defaults to `true`. */\n createDirectory?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/** Keeps one entry on one line by escaping line breaks. */\nfunction escapeLineBreaks(text: string): string {\n return text.replace(/\\r?\\n/g, '\\\\n');\n}\n\nfunction describeCause(cause: unknown): string {\n if (typeof cause === 'string') {\n return cause;\n }\n if (typeof cause === 'object' && cause !== null) {\n const { name, message } = cause as { name?: unknown; message?: unknown };\n if (typeof name === 'string' && typeof message === 'string') {\n return `${name}: ${message}`;\n }\n }\n try {\n return JSON.stringify(cause) ?? String(cause);\n } catch {\n return String(cause);\n }\n}\n\n/**\n * The default line format:\n * `2026-01-02T03:04:05.000Z [CODE] source: message (cause: Error: details)`.\n * `source` and `cause` are left out when the entry has none.\n */\nexport function formatErrorLogLine(entry: SerializedErrorLogEntry): string {\n const source: string = entry.source === undefined ? '' : `${entry.source}: `;\n const cause: string =\n entry.cause === undefined ? '' : ` (cause: ${describeCause(entry.cause)})`;\n return escapeLineBreaks(\n `${entry.timestamp} [${entry.code}] ${source}${entry.message}${cause}`\n );\n}\n\nfunction assertPositiveInteger(value: number, name: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throw new RangeError(\n `FlatFileErrorAdapter: ${name} must be a positive integer, received ${String(value)}.`\n );\n }\n}\n\n/**\n * Appends one text line per entry to a file, optionally rotating it by size.\n * Writes are queued, so concurrent `log` calls never interleave or race a rotation.\n */\nexport class FlatFileErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly formatLine: (entry: SerializedErrorLogEntry) => string;\n private readonly maxBytes: number | undefined;\n private readonly maxFiles: number;\n private readonly createDirectory: boolean;\n private readonly prefix: string;\n private fs: FileSystemLike | undefined;\n private directoryReady: boolean = false;\n private readonly queue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `maxBytes` or `maxFiles` is not a positive integer. */\n constructor(\n private readonly filePath: string,\n options: FlatFileErrorOptions = {}\n ) {\n this.fs = options.fs;\n this.formatLine = options.formatLine ?? formatErrorLogLine;\n this.maxBytes = options.maxBytes;\n this.maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;\n this.createDirectory = options.createDirectory ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n if (this.maxBytes !== undefined) {\n assertPositiveInteger(this.maxBytes, 'maxBytes');\n }\n assertPositiveInteger(this.maxFiles, 'maxFiles');\n }\n\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n return this.queue.run((): Promise<ErrorLogResult> => this.write(entry));\n }\n\n private async write(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const fs: FileSystemLike = await this.getFileSystem();\n const formatted: string = this.formatLine(serializeEntry(entry));\n const line: string = formatted.endsWith('\\n')\n ? formatted\n : `${formatted}\\n`;\n await this.prepareDirectory(fs);\n await this.rotateIfNeeded(fs, new TextEncoder().encode(line).length);\n await fs.appendFile(this.filePath, line);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'FlatFileErrorAdapter',\n prefix: this.prefix,\n action: `to append to \"${this.filePath}\"`,\n error,\n });\n }\n }\n\n /** Uses the injected file system, or loads `node:fs/promises` on first use. */\n private async getFileSystem(): Promise<FileSystemLike> {\n if (this.fs === undefined) {\n this.fs = await loadNodeFileSystem();\n }\n return this.fs;\n }\n\n private async prepareDirectory(fs: FileSystemLike): Promise<void> {\n if (!this.createDirectory || this.directoryReady) {\n return;\n }\n const directory: string | null = parentDirectory(this.filePath);\n if (directory !== null) {\n await fs.mkdir(directory, { recursive: true });\n }\n this.directoryReady = true;\n }\n\n /**\n * Moves `app.log` to `app.log.1` (shifting older files up, dropping the\n * oldest) when the next line would push it past `maxBytes`. A failed rotation\n * is reported, but the entry is still written so it is not lost.\n */\n private async rotateIfNeeded(\n fs: FileSystemLike,\n lineBytes: number\n ): Promise<void> {\n if (this.maxBytes === undefined) {\n return;\n }\n try {\n let currentSize: number;\n try {\n currentSize = (await fs.stat(this.filePath)).size;\n } catch (error: unknown) {\n if (isMissingFileError(error)) {\n return;\n }\n throw error;\n }\n if (currentSize === 0 || currentSize + lineBytes <= this.maxBytes) {\n return;\n }\n await fs.rm(`${this.filePath}.${this.maxFiles}`, { force: true });\n for (let index = this.maxFiles - 1; index >= 1; index -= 1) {\n await this.renameIfPresent(\n fs,\n `${this.filePath}.${index}`,\n `${this.filePath}.${index + 1}`\n );\n }\n await fs.rename(this.filePath, `${this.filePath}.1`);\n } catch (error: unknown) {\n console.error(\n `${this.prefix} FlatFileErrorAdapter: could not rotate \"${this.filePath}\", so it will keep growing: ${describeError(error)}`\n );\n }\n }\n\n private async renameIfPresent(\n fs: FileSystemLike,\n from: string,\n to: string\n ): Promise<void> {\n try {\n await fs.rename(from, to);\n } catch (error: unknown) {\n if (!isMissingFileError(error)) {\n throw error;\n }\n }\n }\n}\n","import { SerialQueue } from '../queue.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport {\n isMissingFileError,\n loadNodeFileSystem,\n parentDirectory,\n} from '../file-system.js';\nimport type { ReadWriteFileSystemLike } from '../file-system.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default number of spaces used to indent array mode output. */\nexport const DEFAULT_INDENT = 2;\n\n/** A stored record: the serialized entry plus its generated UUID. */\nexport interface JsonFileErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\nexport interface JsonFileErrorOptions {\n /**\n * `ndjson` (default) appends one compact JSON object per line, which is cheap\n * and safe to tail. `array` keeps the whole file as one JSON array that is\n * rewritten on every entry.\n */\n mode?: 'ndjson' | 'array';\n /** Array mode only: most entries to keep. The oldest are dropped first. */\n maxEntries?: number;\n /** Array mode only: spaces of indentation. Defaults to `2`; `0` is compact. */\n indent?: number;\n /** File system to write to. Defaults to `node:fs/promises`, loaded on first use. */\n fs?: ReadWriteFileSystemLike;\n /** Create missing parent directories on first use. Defaults to `true`. */\n createDirectory?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Stores entries as JSON in a file (Node and Bun), either as newline-delimited\n * JSON or as one array. Writes are queued, so concurrent `log` calls stay in\n * order. Only one process should write to a given file.\n */\nexport class JsonFileErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly mode: 'ndjson' | 'array';\n private readonly maxEntries: number | undefined;\n private readonly indent: number;\n private readonly createDirectory: boolean;\n private readonly prefix: string;\n private fs: ReadWriteFileSystemLike | undefined;\n private directoryReady: boolean = false;\n private readonly queue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `maxEntries` or `indent` is invalid, or `maxEntries` is used outside array mode. */\n constructor(\n private readonly filePath: string,\n options: JsonFileErrorOptions = {}\n ) {\n this.mode = options.mode ?? 'ndjson';\n this.maxEntries = options.maxEntries;\n this.indent = options.indent ?? DEFAULT_INDENT;\n this.fs = options.fs;\n this.createDirectory = options.createDirectory ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n if (this.maxEntries !== undefined) {\n if (this.mode !== 'array') {\n throw new RangeError(\n `JsonFileErrorAdapter: maxEntries only applies to mode \"array\", but mode is \"${this.mode}\". Use mode \"array\" or remove maxEntries.`\n );\n }\n if (!Number.isInteger(this.maxEntries) || this.maxEntries < 1) {\n throw new RangeError(\n `JsonFileErrorAdapter: maxEntries must be a positive integer, received ${String(this.maxEntries)}.`\n );\n }\n }\n if (!Number.isInteger(this.indent) || this.indent < 0) {\n throw new RangeError(\n `JsonFileErrorAdapter: indent must be a non-negative integer, received ${String(this.indent)}.`\n );\n }\n }\n\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n return this.queue.run((): Promise<ErrorLogResult> => this.write(entry));\n }\n\n private async write(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: JsonFileErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const fs: ReadWriteFileSystemLike = await this.getFileSystem();\n await this.prepareDirectory(fs);\n if (this.mode === 'ndjson') {\n await fs.appendFile(this.filePath, `${JSON.stringify(record)}\\n`);\n } else {\n await this.rewriteArray(fs, record);\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'JsonFileErrorAdapter',\n prefix: this.prefix,\n action: `to write \"${this.filePath}\" in ${this.mode} mode`,\n error,\n });\n }\n }\n\n private async getFileSystem(): Promise<ReadWriteFileSystemLike> {\n if (this.fs === undefined) {\n this.fs = await loadNodeFileSystem();\n }\n return this.fs;\n }\n\n private async prepareDirectory(fs: ReadWriteFileSystemLike): Promise<void> {\n if (!this.createDirectory || this.directoryReady) {\n return;\n }\n const directory: string | null = parentDirectory(this.filePath);\n if (directory !== null) {\n await fs.mkdir(directory, { recursive: true });\n }\n this.directoryReady = true;\n }\n\n /**\n * Adds the record to the array and replaces the file. The new content is\n * written to a temporary file first and renamed into place, so a crash\n * mid-write cannot leave a half-written file.\n */\n private async rewriteArray(\n fs: ReadWriteFileSystemLike,\n record: JsonFileErrorRecord\n ): Promise<void> {\n // An entry logged again with the same id replaces its earlier record.\n const records: JsonFileErrorRecord[] = [\n ...(await this.readRecords(fs)).filter(\n (existing: JsonFileErrorRecord): boolean => existing?.id !== record.id\n ),\n record,\n ];\n const kept: JsonFileErrorRecord[] =\n this.maxEntries === undefined ? records : records.slice(-this.maxEntries);\n const temporaryPath: string = `${this.filePath}.tmp`;\n await fs.writeFile(\n temporaryPath,\n `${JSON.stringify(kept, null, this.indent)}\\n`\n );\n await fs.rename(temporaryPath, this.filePath);\n }\n\n /**\n * Returns the records already in the file. A missing or empty file has none.\n * A file that is not a JSON array is moved to `<path>.corrupt` (so nothing is\n * lost) and logging continues with a fresh file.\n */\n private async readRecords(\n fs: ReadWriteFileSystemLike\n ): Promise<JsonFileErrorRecord[]> {\n let content: string;\n try {\n content = await fs.readFile(this.filePath, 'utf8');\n } catch (error: unknown) {\n if (isMissingFileError(error)) {\n return [];\n }\n throw error;\n }\n if (content.trim() === '') {\n return [];\n }\n let problem: string;\n try {\n const parsed: unknown = JSON.parse(content);\n if (Array.isArray(parsed)) {\n return parsed as JsonFileErrorRecord[];\n }\n problem = 'did not hold a JSON array';\n } catch (error: unknown) {\n problem = `held invalid JSON (${describeError(error)})`;\n }\n const corruptPath: string = `${this.filePath}.corrupt`;\n await fs.rename(this.filePath, corruptPath);\n console.error(\n `${this.prefix} JsonFileErrorAdapter: \"${this.filePath}\" ${problem}, so it was moved to \"${corruptPath}\" and a new file was started.`\n );\n return [];\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * Longest allowed table name. The index name adds `_occurred_at_idx` (16\n * characters) and Postgres limits identifiers to 63.\n */\nexport const MAX_TABLE_NAME_LENGTH = 47;\n\nexport type SqlDialect = 'postgres' | 'mysql';\n\n/**\n * The only thing these adapters need from a database driver: run one\n * parameterized statement. `pg` (`Pool` / `Client`) and `mysql2/promise`\n * (`Pool` / `Connection`) both fit as they are.\n */\nexport interface SqlClientLike {\n query(sql: string, params: unknown[]): Promise<unknown>;\n}\n\nexport interface SqlErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only, up to 47 characters. */\n tableName?: string;\n /** Create the table (and index) on first use. Defaults to `true`. */\n createTable?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for each database statement before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Throws a descriptive error unless `tableName` is a short, plain SQL identifier. */\nfunction assertValidTableName(tableName: string, adapterName: string): void {\n assertIdentifier(tableName, 'table name', adapterName, MAX_TABLE_NAME_LENGTH);\n}\n\nfunction quoteIdentifier(dialect: SqlDialect, identifier: string): string {\n return dialect === 'postgres' ? `\"${identifier}\"` : `\\`${identifier}\\``;\n}\n\n/**\n * Returns the statements that create the error log table and its index for a\n * dialect. Use this to run your own migration when `createTable` is disabled.\n *\n * Postgres gets a native `UUID` key, `JSONB` for the cause, and `TIMESTAMPTZ`\n * columns. MySQL gets a `CHAR(36)` UUID key (it has no UUID type), a `JSON`\n * cause, and `DATETIME(3)` columns holding UTC times.\n */\nexport function buildErrorLogTableDdl(\n dialect: SqlDialect,\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName, 'buildErrorLogTableDdl');\n const table: string = quoteIdentifier(dialect, tableName);\n const index: string = quoteIdentifier(\n dialect,\n `${tableName}_occurred_at_idx`\n );\n if (dialect === 'postgres') {\n return [\n `CREATE TABLE IF NOT EXISTS ${table} (` +\n 'id UUID PRIMARY KEY, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause JSONB, ' +\n 'source TEXT, ' +\n 'occurred_at TIMESTAMPTZ NOT NULL, ' +\n 'created_at TIMESTAMPTZ NOT NULL DEFAULT now()' +\n ')',\n `CREATE INDEX IF NOT EXISTS ${index} ON ${table} (occurred_at)`,\n ];\n }\n // MySQL has no CREATE INDEX IF NOT EXISTS, so the index is part of the table.\n return [\n `CREATE TABLE IF NOT EXISTS ${table} (` +\n 'id CHAR(36) NOT NULL PRIMARY KEY, ' +\n 'code TEXT NOT NULL, ' +\n 'message TEXT NOT NULL, ' +\n 'cause JSON NULL, ' +\n 'source TEXT NULL, ' +\n 'occurred_at DATETIME(3) NOT NULL, ' +\n 'created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), ' +\n `INDEX ${index} (occurred_at)` +\n ')',\n ];\n}\n\n/**\n * Returns the statement that widens the `code` and `source` columns of a MySQL\n * table created by `buildErrorLogTableDdl` in 0.4.0 or earlier, where they were\n * `VARCHAR(255)` and a longer value made the insert fail. Run it once.\n */\nexport function buildMySqlWidenColumnsSql(\n tableName: string = DEFAULT_TABLE_NAME\n): string[] {\n assertValidTableName(tableName, 'buildMySqlWidenColumnsSql');\n return [\n `ALTER TABLE ${quoteIdentifier('mysql', tableName)} MODIFY code TEXT NOT NULL, MODIFY source TEXT NULL`,\n ];\n}\n\n/** `2026-01-02T03:04:05.678Z` becomes `2026-01-02 03:04:05.678`, which MySQL accepts. */\nfunction toMysqlDateTime(date: Date): string {\n return date.toISOString().replace('T', ' ').replace('Z', '');\n}\n\n/** Shared by the Postgres and MySQL adapters: only the SQL text and time format differ. */\nabstract class SqlErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly createTable: boolean;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private tableReady: Promise<void> | null = null;\n\n protected constructor(\n private readonly client: SqlClientLike,\n private readonly dialect: SqlDialect,\n private readonly adapterName: string,\n options: SqlErrorOptions\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertValidTableName(this.tableName, adapterName);\n this.createTable = options.createTable ?? true;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, adapterName);\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const serialized = serializeEntry(entry);\n const occurredAt: Date = new Date(serialized.timestamp);\n if (Number.isNaN(occurredAt.getTime())) {\n throw new RangeError(\n `timestamp \"${serialized.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n const params: unknown[] = [\n resolveEntryId(entry),\n serialized.code,\n serialized.message,\n serialized.cause === undefined\n ? null\n : JSON.stringify(serialized.cause),\n serialized.source ?? null,\n this.dialect === 'postgres'\n ? occurredAt.toISOString()\n : toMysqlDateTime(occurredAt),\n ];\n await this.ensureTable();\n await this.query(this.insertSql(), params);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: this.adapterName,\n prefix: this.prefix,\n action: `to write to table \"${this.tableName}\"`,\n error,\n });\n }\n }\n\n private insertSql(): string {\n const placeholders: string =\n this.dialect === 'postgres'\n ? '$1, $2, $3, $4, $5, $6'\n : '?, ?, ?, ?, ?, ?';\n // A repeated entry id is a retry: keep the stored row instead of failing on the key.\n const onRepeat: string =\n this.dialect === 'postgres'\n ? 'ON CONFLICT (id) DO NOTHING'\n : 'ON DUPLICATE KEY UPDATE id = id';\n return `INSERT INTO ${quoteIdentifier(this.dialect, this.tableName)} (id, code, message, cause, source, occurred_at) VALUES (${placeholders}) ${onRepeat}`;\n }\n\n /** Creates the table once. A failed attempt is retried on the next log call. */\n private ensureTable(): Promise<void> {\n if (!this.createTable) {\n return Promise.resolve();\n }\n if (this.tableReady === null) {\n this.tableReady = this.runCreateStatements().catch(\n (error: unknown): never => {\n this.tableReady = null;\n throw error;\n }\n );\n }\n return this.tableReady;\n }\n\n private async runCreateStatements(): Promise<void> {\n for (const sql of buildErrorLogTableDdl(this.dialect, this.tableName)) {\n await this.query(sql, []);\n }\n }\n\n /** Runs one statement, giving up waiting after `timeoutMs`. */\n private query(sql: string, params: unknown[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> => this.client.query(sql, params),\n this.timeoutMs,\n timeoutMessage(\n this.dialect === 'postgres' ? 'Postgres' : 'MySQL',\n this.timeoutMs\n )\n );\n }\n}\n\n/**\n * Writes entries to Postgres. Pass a `pg` `Pool` or `Client`.\n *\n * ```ts\n * const adapter = new PostgresErrorAdapter(new Pool({ connectionString }));\n * ```\n */\nexport class PostgresErrorAdapter<\n TCode extends string = string,\n> extends SqlErrorAdapter<TCode> {\n /** @throws {RangeError} if `options.tableName` is not a plain identifier of 47 characters or fewer. */\n constructor(client: SqlClientLike, options: SqlErrorOptions = {}) {\n super(client, 'postgres', 'PostgresErrorAdapter', options);\n }\n}\n\n/**\n * Writes entries to MySQL or MariaDB. Pass a `mysql2/promise` `Pool` or `Connection`.\n *\n * ```ts\n * const adapter = new MySqlErrorAdapter(mysql.createPool(connectionUri));\n * ```\n */\nexport class MySqlErrorAdapter<\n TCode extends string = string,\n> extends SqlErrorAdapter<TCode> {\n /** @throws {RangeError} if `options.tableName` is not a plain identifier of 47 characters or fewer. */\n constructor(client: SqlClientLike, options: SqlErrorOptions = {}) {\n super(client, 'mysql', 'MySqlErrorAdapter', options);\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default Redis key. */\nexport const DEFAULT_REDIS_KEY = 'error_log';\n\n/**\n * The only thing this adapter needs from a Redis client: send one raw command\n * as an array of strings. node-redis (`client.sendCommand`) already matches.\n * ioredis needs a one-line wrapper:\n *\n * ```ts\n * const client: RedisClientLike = {\n * sendCommand: (args) => redis.call(args[0] as string, ...args.slice(1)),\n * };\n * ```\n */\nexport interface RedisClientLike {\n sendCommand(args: string[]): Promise<unknown>;\n}\n\nexport interface RedisErrorOptions {\n /**\n * `list` (default) pushes each entry onto a Redis list as one JSON string.\n * `stream` appends each entry to a Redis stream (`XADD`) as separate fields.\n */\n mode?: 'list' | 'stream';\n /** Key to write to. Defaults to `error_log`. */\n key?: string;\n /**\n * Most entries to keep. The oldest are dropped first. Unlimited by default.\n * Lists are trimmed with `LTRIM` after the push; streams use an exact `MAXLEN`.\n */\n maxEntries?: number;\n /**\n * List mode only: which end to push onto. `right` (default, `RPUSH`) keeps\n * the newest entry last; `left` (`LPUSH`) keeps the newest entry first.\n */\n side?: 'left' | 'right';\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for each Redis command before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/**\n * Writes entries to Redis, either as a capped list of JSON strings or as a\n * stream. Each record holds a UUID `id` plus the serialized entry. In stream\n * mode `cause` and `source` fields are left out when the entry has none.\n */\nexport class RedisErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly mode: 'list' | 'stream';\n private readonly key: string;\n private readonly maxEntries: number | undefined;\n private readonly side: 'left' | 'right';\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `key` is empty, `maxEntries` is not a positive integer, or `mode` / `side` is unknown. */\n constructor(\n private readonly client: RedisClientLike,\n options: RedisErrorOptions = {}\n ) {\n this.mode = options.mode ?? 'list';\n this.key = options.key ?? DEFAULT_REDIS_KEY;\n this.maxEntries = options.maxEntries;\n this.side = options.side ?? 'right';\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'RedisErrorAdapter');\n if (this.mode !== 'list' && this.mode !== 'stream') {\n throw new RangeError(\n `RedisErrorAdapter: mode must be \"list\" or \"stream\", received \"${String(options.mode)}\".`\n );\n }\n if (this.side !== 'left' && this.side !== 'right') {\n throw new RangeError(\n `RedisErrorAdapter: side must be \"left\" or \"right\", received \"${String(options.side)}\".`\n );\n }\n if (this.key === '') {\n throw new RangeError('RedisErrorAdapter: key must not be empty.');\n }\n if (\n this.maxEntries !== undefined &&\n (!Number.isInteger(this.maxEntries) || this.maxEntries < 1)\n ) {\n throw new RangeError(\n `RedisErrorAdapter: maxEntries must be a positive integer, received ${String(this.maxEntries)}.`\n );\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record = { id: resolveEntryId(entry), ...serializeEntry(entry) };\n if (this.mode === 'stream') {\n await this.send(this.buildStreamCommand(record));\n } else {\n await this.send([\n this.side === 'left' ? 'LPUSH' : 'RPUSH',\n this.key,\n JSON.stringify(record),\n ]);\n await this.trimList();\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'RedisErrorAdapter',\n prefix: this.prefix,\n action: `to write to ${this.mode} \"${this.key}\"`,\n error,\n });\n }\n }\n\n private buildStreamCommand(record: {\n id: string;\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n }): string[] {\n const command: string[] = ['XADD', this.key];\n if (this.maxEntries !== undefined) {\n command.push('MAXLEN', '=', String(this.maxEntries));\n }\n command.push(\n '*',\n 'id',\n record.id,\n 'code',\n record.code,\n 'message',\n record.message,\n 'timestamp',\n record.timestamp\n );\n if (record.cause !== undefined) {\n command.push('cause', JSON.stringify(record.cause));\n }\n if (record.source !== undefined) {\n command.push('source', record.source);\n }\n return command;\n }\n\n /**\n * Drops the oldest list entries beyond `maxEntries`. The entry is already\n * stored by now, so a trim failure is reported but does not fail the call;\n * the next successful write trims the list again.\n */\n /** Sends one command, giving up waiting after `timeoutMs`. */\n private send(args: string[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> => this.client.sendCommand(args),\n this.timeoutMs,\n timeoutMessage('Redis', this.timeoutMs)\n );\n }\n\n private async trimList(): Promise<void> {\n if (this.maxEntries === undefined) {\n return;\n }\n const keep: number = this.maxEntries;\n const [start, stop]: [string, string] =\n this.side === 'right' ? [String(-keep), '-1'] : ['0', String(keep - 1)];\n try {\n await this.send(['LTRIM', this.key, start, stop]);\n } catch (error: unknown) {\n console.error(\n `${this.prefix} RedisErrorAdapter: stored the entry but could not trim list \"${this.key}\" to ${keep} entries: ${describeError(error)}`\n );\n }\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { SerialQueue } from '../queue.js';\nimport { resolveEntryId } from '../entry-id.js';\nimport { generateId } from '../uuid.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default key prefix for stored objects. */\nexport const DEFAULT_S3_PREFIX = 'error_log/';\n\n/** Buffered entries kept after failed uploads: this many times `batchSize`, oldest dropped first. */\nexport const BUFFER_LIMIT_MULTIPLIER = 10;\n\n/** The parameters this adapter passes to `putObject`. */\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: string;\n ContentType: string;\n}\n\n/**\n * The only thing this adapter needs from an S3 client: `putObject`. The AWS\n * SDK v3 aggregated client (`new S3({ ... })` from `@aws-sdk/client-s3`) fits as\n * it is, and so does anything else that follows the S3 API, including\n * DigitalOcean Spaces, Cloudflare R2 and MinIO when you point the client at\n * their endpoint.\n *\n * Some S3-compatible providers reject the checksum headers that recent AWS SDK\n * v3 versions add by default. If uploads fail with a checksum or header error,\n * create the client with `requestChecksumCalculation: 'WHEN_REQUIRED'` and\n * `responseChecksumValidation: 'WHEN_REQUIRED'`.\n */\nexport interface S3ClientLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n}\n\nexport interface S3BatchOptions {\n /** Upload once this many entries are buffered. */\n batchSize: number;\n /**\n * Also upload when the oldest buffered entry has waited this long (ms). The\n * timer does not keep the process alive, so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n}\n\nexport interface S3ErrorOptions {\n /**\n * Key prefix, for example `logs/app/`. A trailing slash is added if missing.\n * Defaults to `error_log/`. Use `''` for no prefix.\n */\n keyPrefix?: string;\n /**\n * Content type of the uploaded objects. Defaults to `application/json`, or\n * `application/x-ndjson` when batching.\n */\n contentType?: string;\n /**\n * Buffer entries and upload them together as one newline-delimited JSON\n * object. Off by default: one object per entry.\n */\n batch?: S3BatchOptions;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long an upload may take before `log` (or `flush`) gives up waiting, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The\n * upload cannot be cancelled, so it may still finish afterwards; in batch mode\n * the entries stay buffered and are uploaded again by the next flush.\n */\n timeoutMs?: number;\n}\n\ninterface S3Record {\n id: string;\n code: string;\n message: string;\n cause?: unknown;\n source?: string;\n timestamp: string;\n}\n\n/** Turns `2026-01-02T03:04:05.678Z` into `2026/01/02/20260102T030405678Z`. */\nfunction datePath(date: Date): string {\n const compact: string = date.toISOString().replace(/[-:.]/g, '');\n return `${compact.slice(0, 4)}/${compact.slice(4, 6)}/${compact.slice(6, 8)}/${compact}`;\n}\n\nfunction normalizePrefix(prefix: string): string {\n const trimmed: string = prefix.replace(/^\\/+/, '');\n return trimmed === '' || trimmed.endsWith('/') ? trimmed : `${trimmed}/`;\n}\n\n/**\n * Stores entries as objects in an S3-compatible bucket, keyed by date and UUID:\n * `error_log/2026/01/02/20260102T030405678Z-<uuid>.json`.\n *\n * By default each entry becomes its own JSON object and `log` resolves once the\n * upload succeeds. With `batch`, entries are buffered and uploaded together as\n * one `.ndjson` object; `log` then resolves ok as soon as the entry is\n * buffered (it is not stored yet), except for the call that fills the batch,\n * which resolves with the upload result. Call `flush()` before shutting down.\n */\nexport class S3ErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly keyPrefix: string;\n private readonly contentType: string;\n private readonly batch: S3BatchOptions | undefined;\n private readonly logPrefix: string;\n private readonly timeoutMs: number;\n private buffer: S3Record[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushQueue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if `bucket` is empty or the batch options are invalid. */\n constructor(\n private readonly client: S3ClientLike,\n private readonly bucket: string,\n options: S3ErrorOptions = {}\n ) {\n this.keyPrefix = normalizePrefix(options.keyPrefix ?? DEFAULT_S3_PREFIX);\n this.batch = options.batch;\n this.contentType =\n options.contentType ??\n (this.batch === undefined ? 'application/json' : 'application/x-ndjson');\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(options.timeoutMs, 'S3ErrorAdapter');\n if (bucket === '') {\n throw new RangeError('S3ErrorAdapter: bucket must not be empty.');\n }\n if (this.batch !== undefined) {\n if (!Number.isInteger(this.batch.batchSize) || this.batch.batchSize < 1) {\n throw new RangeError(\n `S3ErrorAdapter: batch.batchSize must be a positive integer, received ${String(this.batch.batchSize)}.`\n );\n }\n if (\n this.batch.maxWaitMs !== undefined &&\n (!Number.isFinite(this.batch.maxWaitMs) || this.batch.maxWaitMs <= 0)\n ) {\n throw new RangeError(\n `S3ErrorAdapter: batch.maxWaitMs must be a positive number, received ${String(this.batch.maxWaitMs)}.`\n );\n }\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: S3Record = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const occurredAt: Date = new Date(record.timestamp);\n if (Number.isNaN(occurredAt.getTime())) {\n throw new RangeError(\n `timestamp \"${record.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n if (this.batch === undefined) {\n const key: string = `${this.keyPrefix}${datePath(occurredAt)}-${record.id}.json`;\n await this.upload(key, `${JSON.stringify(record)}\\n`);\n return { ok: true };\n }\n return await this.addToBatch(record, this.batch);\n } catch (error: unknown) {\n return this.fail(error);\n }\n }\n\n /**\n * Uploads any buffered entries now. Resolves ok when nothing is buffered.\n * Entries from a failed upload stay buffered and are retried by the next flush.\n */\n flush(): Promise<ErrorLogResult> {\n return this.flushQueue.run((): Promise<ErrorLogResult> =>\n this.uploadBuffered()\n );\n }\n\n private async addToBatch(\n record: S3Record,\n batch: S3BatchOptions\n ): Promise<ErrorLogResult> {\n this.buffer.push(record);\n const limit: number = batch.batchSize * BUFFER_LIMIT_MULTIPLIER;\n if (this.buffer.length > limit) {\n const dropped: number = this.buffer.length - limit;\n this.buffer = this.buffer.slice(dropped);\n console.error(\n `${this.logPrefix} S3ErrorAdapter: dropped the ${dropped} oldest buffered entries because uploads to bucket \"${this.bucket}\" keep failing and the buffer is full (${limit} entries).`\n );\n }\n if (this.buffer.length >= batch.batchSize) {\n return this.flush();\n }\n this.scheduleFlush(batch);\n return { ok: true };\n }\n\n private scheduleFlush(batch: S3BatchOptions): void {\n if (batch.maxWaitMs === undefined || this.timer !== null) {\n return;\n }\n const timer = setTimeout((): void => {\n this.timer = null;\n void this.flush();\n }, batch.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.timer = timer;\n }\n\n private async uploadBuffered(): Promise<ErrorLogResult> {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const pending: S3Record[] = this.buffer.slice();\n if (pending.length === 0) {\n return { ok: true };\n }\n try {\n const key: string = `${this.keyPrefix}${datePath(new Date())}-${generateId()}.ndjson`;\n const body: string = pending\n .map((record: S3Record): string => JSON.stringify(record))\n .join('\\n');\n await this.upload(key, `${body}\\n`);\n const uploaded: Set<S3Record> = new Set(pending);\n this.buffer = this.buffer.filter(\n (record: S3Record): boolean => !uploaded.has(record)\n );\n return { ok: true };\n } catch (error: unknown) {\n if (this.batch !== undefined) {\n this.scheduleFlush(this.batch);\n }\n return this.fail(error, pending.length);\n }\n }\n\n private upload(key: string, body: string): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> =>\n this.client.putObject({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: this.contentType,\n }),\n this.timeoutMs,\n timeoutMessage('S3', this.timeoutMs)\n );\n }\n\n private fail(error: unknown, bufferedCount?: number): ErrorLogResult {\n const retry: string =\n bufferedCount === undefined\n ? ''\n : ` (${bufferedCount} buffered ${bufferedCount === 1 ? 'entry was' : 'entries were'} kept for the next flush)`;\n return reportFailure({\n adapterName: 'S3ErrorAdapter',\n prefix: this.logPrefix,\n action: `to upload to bucket \"${this.bucket}\"`,\n error,\n reason: `${describeError(error)}${retry}`,\n });\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport {\n DEFAULT_TIMEOUT_MS,\n resolveTimeoutMs,\n withTimeout,\n} from '../timeout.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** The fields stored for each entry, in Firestore and in the Realtime Database. */\nexport interface FirebaseErrorRecord {\n /** UUID. In Firestore it is also the document id. */\n id: string;\n code: string;\n message: string;\n /** ISO 8601 timestamp of the entry. */\n timestamp: string;\n source?: string;\n /**\n * The entry's cause as a JSON string. Plain text avoids Firestore's limits on\n * custom objects and nested arrays, and the Realtime Database's limits on key\n * characters, so an odd cause can never make the write fail.\n */\n cause?: string;\n}\n\n/**\n * The only thing the Firestore adapter needs from a collection: `doc(id).set(data)`.\n * A `firebase-admin` collection (`getFirestore().collection('error_log')`) fits\n * as it is. With the web SDK, wrap it:\n *\n * ```ts\n * const collectionLike: FirestoreCollectionLike = {\n * doc: (id) => ({ set: (data) => setDoc(doc(db, 'error_log', id), data) }),\n * };\n * ```\n */\nexport interface FirestoreCollectionLike {\n doc(documentId: string): {\n set(data: FirestoreErrorRecord): Promise<unknown>;\n };\n}\n\n/**\n * What the Firestore adapter writes: the usual fields, plus the expiry field\n * (a `Date`, which Firestore stores as a timestamp) when `expireAfterMs` is set.\n */\nexport type FirestoreErrorRecord = FirebaseErrorRecord & {\n [field: string]: unknown;\n};\n\n/**\n * The only thing the Realtime Database adapter needs from a reference:\n * `push(value)`, which resolves once the write is saved. A `firebase-admin`\n * reference (`getDatabase().ref('error_log')`) fits as it is. With the web SDK, wrap it:\n *\n * ```ts\n * const refLike: RealtimeDatabaseRefLike = {\n * push: (value) => push(ref(db, 'error_log'), value),\n * child: (key) => ({ set: (value) => set(ref(db, `error_log/${key}`), value) }),\n * };\n * ```\n *\n * `child(key).set(value)` is only needed for entries that carry their own `id`\n * (see `ErrorLogEntry.id`): they are written under that key, so logging the same\n * entry again overwrites the record instead of adding a second one.\n */\nexport interface RealtimeDatabaseRefLike {\n push(value: FirebaseErrorRecord): PromiseLike<unknown>;\n child?(key: string): {\n set(value: FirebaseErrorRecord): PromiseLike<unknown>;\n };\n}\n\n/** Default time to wait for a write to be saved, in milliseconds. */\nexport const DEFAULT_WRITE_TIMEOUT_MS: number = DEFAULT_TIMEOUT_MS;\n\nexport interface FirebaseErrorOptions {\n /**\n * How long `log` waits for the write to be saved before giving up, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit.\n *\n * The Firebase SDKs keep writes queued while the connection is down, and\n * their promises stay pending until it returns, so without a limit `log`\n * could hang for as long as an outage lasts. A write that timed out is still\n * queued by the SDK and may be saved later. Retry a timeout only with the same\n * `ErrorLogEntry.id`, so the retry overwrites the first write instead of\n * adding a second one.\n */\n timeoutMs?: number;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/** Default name of the field that holds a document's expiry time. */\nexport const DEFAULT_EXPIRE_FIELD = 'expire_at';\n\nconst RESERVED_FIELDS: ReadonlySet<string> = new Set([\n 'id',\n 'code',\n 'message',\n 'timestamp',\n 'source',\n 'cause',\n]);\n\nexport interface FirestoreErrorOptions extends FirebaseErrorOptions {\n /**\n * Add an expiry time to every document: the entry's `timestamp` plus this\n * many milliseconds, stored as a Firestore timestamp in `expireField`. This\n * only writes the field. Firestore deletes nothing until you turn on a TTL\n * policy for that field, for example:\n *\n * ```sh\n * gcloud firestore fields ttls update expire_at \\\n * --collection-group=error_log --enable-ttl\n * ```\n *\n * Firestore then removes documents some time after they expire (typically\n * within a day), and the deletes are billed like any other. Off by default.\n */\n expireAfterMs?: number;\n /** Name of the expiry field. Defaults to `expire_at`. Only used with `expireAfterMs`. */\n expireField?: string;\n}\n\n/** Waits for the write, but rejects with a descriptive error if it takes longer than `timeoutMs`. */\nfunction waitForWrite(\n start: () => PromiseLike<unknown>,\n timeoutMs: number\n): Promise<unknown> {\n return withTimeout(\n start,\n timeoutMs,\n `the write was not saved within ${timeoutMs} ms (the connection may be down; the SDK keeps the write queued and may still save it later, so logging the entry again could store it twice unless it carries an id)`\n );\n}\n\nfunction buildRecord<TCode extends string>(\n entry: ErrorLogEntry<TCode>\n): FirebaseErrorRecord {\n const serialized = serializeEntry(entry);\n const record: FirebaseErrorRecord = {\n id: resolveEntryId(entry),\n code: serialized.code,\n message: serialized.message,\n timestamp: serialized.timestamp,\n };\n if (serialized.source !== undefined) {\n record.source = serialized.source;\n }\n if (serialized.cause !== undefined) {\n record.cause = JSON.stringify(serialized.cause);\n }\n return record;\n}\n\n/**\n * Writes each entry as a document in a Firestore collection. The document id is\n * the entry's UUID, so the SDK finishing one queued write twice cannot create two\n * documents. Calling `log` again without an `id` on the entry is a new write\n * with a new UUID: if a call reported a timeout, the first write may still be\n * saved later, and logging the same entry again can store it twice. Give the\n * entry an `id` and the retry overwrites the same document.\n */\nexport class FirestoreErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n private readonly expireAfterMs: number | undefined;\n private readonly expireField: string;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`, or the expiry options are invalid. */\n constructor(\n private readonly collection: FirestoreCollectionLike,\n options: FirestoreErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'FirestoreErrorAdapter'\n );\n this.expireAfterMs = options.expireAfterMs;\n this.expireField = options.expireField ?? DEFAULT_EXPIRE_FIELD;\n if (\n this.expireAfterMs !== undefined &&\n (!Number.isFinite(this.expireAfterMs) || this.expireAfterMs <= 0)\n ) {\n throw new RangeError(\n `FirestoreErrorAdapter: expireAfterMs must be a positive number of milliseconds, received ${String(options.expireAfterMs)}.`\n );\n }\n if (\n this.expireField === '' ||\n /[./]/.test(this.expireField) ||\n RESERVED_FIELDS.has(this.expireField)\n ) {\n throw new RangeError(\n `FirestoreErrorAdapter: expireField must be a name other than ${[...RESERVED_FIELDS].join(', ')}, without \".\" or \"/\", received ${JSON.stringify(this.expireField)}.`\n );\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: FirestoreErrorRecord = { ...buildRecord(entry) };\n if (this.expireAfterMs !== undefined) {\n const occurredAt: number = Date.parse(record.timestamp);\n if (Number.isNaN(occurredAt)) {\n throw new RangeError(\n `timestamp \"${record.timestamp}\" is not a valid ISO 8601 date, so the expiry time cannot be worked out`\n );\n }\n record[this.expireField] = new Date(occurredAt + this.expireAfterMs);\n }\n await waitForWrite(\n () => this.collection.doc(record.id).set(record),\n this.timeoutMs\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'FirestoreErrorAdapter',\n prefix: this.prefix,\n action: `to write the document`,\n error,\n });\n }\n }\n}\n\n/**\n * Writes each entry under a Realtime Database reference with `push`, which\n * generates a chronologically ordered key.\n */\nexport class RealtimeDatabaseErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.timeoutMs` is not a positive number or `Infinity`. */\n constructor(\n private readonly reference: RealtimeDatabaseRefLike,\n options: FirebaseErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'RealtimeDatabaseErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: FirebaseErrorRecord = buildRecord(entry);\n let write: PromiseLike<unknown>;\n if (hasCallerId(entry)) {\n if (this.reference.child === undefined) {\n throw new TypeError(\n 'the entry has an id, which needs a reference with child(key).set(value) so a repeat overwrites the first write; add child to the reference or log without an id'\n );\n }\n write = this.reference.child(record.id).set(record);\n } else {\n write = this.reference.push(record);\n }\n await waitForWrite(() => write, this.timeoutMs);\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'RealtimeDatabaseErrorAdapter',\n prefix: this.prefix,\n action: `to write the entry`,\n error,\n });\n }\n }\n}\n","import { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { DEFAULT_TABLE_NAME, assertIdentifier } from '../sql-ident.js';\nimport { hasCallerId, resolveEntryId } from '../entry-id.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport { describeError, serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\nimport { buildErrorLogTableDdl } from './sql-error-adapter.js';\n\n/** The row inserted for each entry. The columns match the Postgres adapter's table. */\nexport interface SupabaseErrorRow {\n /** UUID primary key. */\n id: string;\n code: string;\n message: string;\n /** The cause as JSON, or `null` when the entry has none. */\n cause: unknown;\n source: string | null;\n /** ISO 8601 timestamp of the entry. */\n occurred_at: string;\n}\n\n/** The error object `supabase-js` returns (a `PostgrestError`) instead of throwing. */\nexport interface SupabaseErrorLike {\n message: string;\n code?: string;\n details?: string | null;\n hint?: string | null;\n}\n\n/** What `insert` resolves to. The status fields are used when the error has no message. */\nexport interface SupabaseInsertResult {\n error: SupabaseErrorLike | null;\n status?: number;\n statusText?: string;\n}\n\n/**\n * The only thing the Supabase adapter needs from a client: `from(table).insert(row)`.\n * A `supabase-js` client (`createClient(url, key)`) fits as it is.\n */\nexport interface SupabaseClientLike {\n from(table: string): {\n insert(row: SupabaseErrorRow): PromiseLike<SupabaseInsertResult>;\n };\n}\n\nexport interface SupabaseErrorOptions {\n /** Table to write to. Defaults to `error_log`. Letters, digits and `_` only. */\n tableName?: string;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long `log` waits for Supabase to answer before giving up, in milliseconds. Defaults\n * to 10000. Pass `Infinity` to wait without limit. The call cannot be\n * cancelled, so it may still finish after `log` has returned a failure.\n */\n timeoutMs?: number;\n}\n\n/** Longest `code` or `source` an insert policy accepts, in characters. */\nexport const POLICY_MAX_CODE_LENGTH = 255;\n/** Longest `message` an insert policy accepts, in characters. */\nexport const POLICY_MAX_MESSAGE_LENGTH = 4096;\n/** Largest `cause` an insert policy accepts, in bytes of stored JSON. */\nexport const POLICY_MAX_CAUSE_BYTES = 8192;\n\nexport interface SupabaseSetupOptions {\n /**\n * Postgres roles allowed to insert through the API, for example\n * `['authenticated']`. Leave empty when only a server using the service role\n * key logs: that role bypasses row level security, so it needs no policy, and\n * its key must never be shipped to a browser.\n *\n * The generated policy only checks the size of each field. It does not tie a\n * row to a user, so any holder of an allowed role's key can still add entries\n * of their own making: forged entries can hide real incidents, and a flood of\n * them uses up your database quota. Prefer `authenticated` over `anon`.\n */\n insertRoles?: string[];\n /**\n * Required to allow `anon` (or `public`) to insert. The `anon` key is part of\n * your frontend, so this lets anyone who loads the page write to the log.\n * Defaults to `false`.\n */\n allowPublicInsert?: boolean;\n}\n\n/** The `WITH CHECK` condition of the insert policy: every field must be a reasonable size. */\nconst INSERT_POLICY_CHECK: string =\n `char_length(code) <= ${POLICY_MAX_CODE_LENGTH} ` +\n `AND char_length(message) <= ${POLICY_MAX_MESSAGE_LENGTH} ` +\n `AND (source IS NULL OR char_length(source) <= ${POLICY_MAX_CODE_LENGTH}) ` +\n `AND (cause IS NULL OR pg_column_size(cause) <= ${POLICY_MAX_CAUSE_BYTES})`;\n\n/**\n * Returns the SQL to run once in the Supabase SQL editor: the error log table,\n * with row level security turned on (as Supabase recommends for every table in\n * an exposed schema) and, optionally, an insert-only policy for the given roles.\n * The policy limits the size of each field. Nobody but the service role can read\n * the log through the API.\n *\n * @throws {RangeError} if the table name or a role is not a plain identifier, or\n * `anon` / `public` is listed without `allowPublicInsert: true`.\n */\nexport function buildSupabaseSetupSql(\n tableName: string = DEFAULT_TABLE_NAME,\n options: SupabaseSetupOptions = {}\n): string[] {\n const insertRoles: string[] = options.insertRoles ?? [];\n for (const role of insertRoles) {\n assertIdentifier(role, 'role name', 'buildSupabaseSetupSql');\n if (\n (role.toLowerCase() === 'anon' || role.toLowerCase() === 'public') &&\n options.allowPublicInsert !== true\n ) {\n throw new RangeError(\n `buildSupabaseSetupSql: role \"${role}\" would let anyone who loads your page write to the log, because the anon key is public. Use \"authenticated\" instead, or pass allowPublicInsert: true to accept the risk of forged entries and quota use.`\n );\n }\n }\n const statements: string[] = [\n ...buildErrorLogTableDdl('postgres', tableName),\n `ALTER TABLE \"${tableName}\" ENABLE ROW LEVEL SECURITY`,\n ];\n if (insertRoles.length > 0) {\n const policy: string = `${tableName}_insert_only`;\n statements.push(\n `DROP POLICY IF EXISTS \"${policy}\" ON \"${tableName}\"`,\n `CREATE POLICY \"${policy}\" ON \"${tableName}\" FOR INSERT TO ${insertRoles.map((role: string): string => `\"${role}\"`).join(', ')} WITH CHECK (${INSERT_POLICY_CHECK})`\n );\n }\n return statements;\n}\n\n/**\n * Joins the parts of a Supabase error that are present into one message.\n * `supabase-js` can return an error with no message at all (an empty-bodied\n * 404 for a table the API cannot see, for example), so the HTTP status stands\n * in for it. Only the first line of `details` is kept, since network failures\n * put a full stack trace there.\n */\nfunction describeSupabaseError(result: SupabaseInsertResult): string {\n const error: SupabaseErrorLike = result.error ?? { message: '' };\n const parts: string[] = [];\n if (error.message) {\n parts.push(error.message);\n } else if (result.status) {\n parts.push(\n `HTTP ${result.status}${result.statusText ? ` ${result.statusText}` : ''} with no error message (a 404 usually means the table does not exist or is not exposed by the API)`\n );\n } else {\n parts.push('unknown error with no message');\n }\n if (error.code) {\n parts.push(`code ${error.code}`);\n }\n const detailsLine: string = (error.details ?? '').split('\\n')[0].trim();\n if (detailsLine && detailsLine !== error.message) {\n parts.push(`details: ${detailsLine}`);\n }\n if (error.hint) {\n parts.push(`hint: ${error.hint}`);\n }\n return parts.join('; ');\n}\n\n/**\n * Writes entries to a Supabase table through `supabase-js`. Create the table\n * first with `buildSupabaseSetupSql` (or `buildErrorLogTableDdl('postgres')`).\n *\n * ```ts\n * const adapter = new SupabaseErrorAdapter(createClient(url, serviceRoleKey));\n * ```\n *\n * `supabase-js` reports failures in a returned `error` field rather than\n * throwing; the adapter turns both that and a thrown error into `{ ok: false }`.\n */\nexport class SupabaseErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly tableName: string;\n private readonly prefix: string;\n private readonly timeoutMs: number;\n\n /** @throws {RangeError} if `options.tableName` is not a plain identifier or `options.timeoutMs` is invalid. */\n constructor(\n private readonly client: SupabaseClientLike,\n options: SupabaseErrorOptions = {}\n ) {\n this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;\n assertIdentifier(this.tableName, 'table name', 'SupabaseErrorAdapter');\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'SupabaseErrorAdapter'\n );\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const serialized = serializeEntry(entry);\n const row: SupabaseErrorRow = {\n id: resolveEntryId(entry),\n code: serialized.code,\n message: serialized.message,\n cause: serialized.cause === undefined ? null : serialized.cause,\n source: serialized.source ?? null,\n occurred_at: serialized.timestamp,\n };\n const result: SupabaseInsertResult = await withTimeout(\n (): PromiseLike<SupabaseInsertResult> =>\n this.client.from(this.tableName).insert(row),\n this.timeoutMs,\n timeoutMessage('Supabase', this.timeoutMs)\n );\n if (result.error) {\n if (hasCallerId(entry) && result.error.code === '23505') {\n // Unique violation on the id: this entry was stored by an earlier call.\n return { ok: true };\n }\n return this.fail(result, describeSupabaseError(result));\n }\n return { ok: true };\n } catch (error: unknown) {\n return this.fail(error, describeError(error));\n }\n }\n\n private fail(logged: unknown, reason: string): ErrorLogResult {\n return reportFailure({\n adapterName: 'SupabaseErrorAdapter',\n prefix: this.prefix,\n action: `to insert into table \"${this.tableName}\"`,\n error: logged,\n reason,\n });\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { serializeEntry } from '../serialize.js';\nimport type { SerializedErrorLogEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Default event name. It is not `error`, which Node's `EventEmitter` throws on when nobody listens. */\nexport const DEFAULT_EVENT_NAME = 'error-log';\n\n/** What listeners receive: the serialized entry plus its UUID. */\nexport interface EventEmitterErrorRecord extends SerializedErrorLogEntry {\n id: string;\n}\n\n/**\n * The only thing this adapter needs from an emitter: `emit(eventName, payload)`.\n * Node's `EventEmitter` and mitt-style emitters fit as they are. A browser\n * `EventTarget` needs a one-line wrapper:\n *\n * ```ts\n * const target = new EventTarget();\n * const emitter: EmitterLike = {\n * emit: (name, payload) =>\n * target.dispatchEvent(new CustomEvent(name, { detail: payload })),\n * };\n * ```\n */\nexport interface EmitterLike {\n emit(eventName: string, payload: EventEmitterErrorRecord): unknown;\n}\n\nexport interface EventEmitterErrorOptions {\n /**\n * Name of the event to emit. Defaults to `error-log`. Avoid `error` on a Node\n * `EventEmitter`: it throws when no listener is attached, and `log` would then\n * return `ok: false`.\n */\n eventName?: string;\n /**\n * Treat an emit that reached nobody as a failure. Node's `EventEmitter.emit`\n * returns `false` when there are no listeners; with this set to `true`, `log`\n * then returns `ok: false` instead of letting the entry vanish. Emitters whose\n * `emit` returns anything else (mitt, an `EventTarget` wrapper) cannot tell,\n * so they always count as delivered. Defaults to `false`.\n */\n requireListener?: boolean;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Emits each entry as an event on an emitter you own, for in-process hooks such\n * as a toast, a metrics counter, or a test assertion. Listeners run\n * synchronously inside `emit`, so a listener that throws makes `log` return\n * `ok: false` with the listener's message; the application is not affected.\n *\n * ```ts\n * const bus = new EventEmitter();\n * bus.on('error-log', (record) => showToast(record.message));\n * const adapter = new EventEmitterErrorAdapter(bus);\n * ```\n */\nexport class EventEmitterErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly eventName: string;\n private readonly requireListener: boolean;\n private readonly prefix: string;\n\n /** @throws {RangeError} if `options.eventName` is empty. */\n constructor(\n private readonly emitter: EmitterLike,\n options: EventEmitterErrorOptions = {}\n ) {\n this.eventName = options.eventName ?? DEFAULT_EVENT_NAME;\n if (this.eventName === '') {\n throw new RangeError(\n 'EventEmitterErrorAdapter: eventName must not be empty.'\n );\n }\n this.requireListener = options.requireListener ?? false;\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const record: EventEmitterErrorRecord = {\n id: resolveEntryId(entry),\n ...serializeEntry(entry),\n };\n const delivered: unknown = this.emitter.emit(this.eventName, record);\n if (this.requireListener && delivered === false) {\n throw new Error(\n `no listener is attached to \"${this.eventName}\", so nothing received the entry`\n );\n }\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'EventEmitterErrorAdapter',\n prefix: this.prefix,\n action: `to emit \"${this.eventName}\"`,\n error,\n });\n }\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { serializeEntry } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** `readyState` of an open socket, the same in browsers, Node, Deno, Bun, and the `ws` package. */\nconst OPEN = 1;\n\nconst STATE_NAMES: Readonly<Record<number, string>> = {\n 0: 'still connecting',\n 1: 'open',\n 2: 'closing',\n 3: 'closed',\n};\n\n/**\n * The only things this adapter needs from a socket: `send` and `readyState`. The\n * browser `WebSocket`, Node's global `WebSocket`, Deno, Bun, and the `ws` package\n * all fit as they are.\n */\nexport interface WebSocketLike {\n send(data: string): unknown;\n readonly readyState: number;\n}\n\nexport interface WebSocketErrorOptions {\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\n/**\n * Sends each entry as one JSON text message (the serialized entry plus its UUID\n * `id`) over a socket you own. You connect, and reconnect, the socket; this\n * adapter never opens one and keeps no buffer. If the socket is not open when\n * `log` is called, the entry is not sent and `log` returns `ok: false`, so\n * combine it with a durable adapter in a `CompositeErrorAdapter` when entries\n * must not be lost while the socket is down.\n *\n * `ok: true` means the socket accepted the message, not that the server\n * received it: a WebSocket gives no acknowledgement, and a connection that\n * drops after `send` loses the message.\n *\n * ```ts\n * const socket = new WebSocket('wss://logs.example.com/errors');\n * const adapter = new WebSocketErrorAdapter(socket);\n * ```\n */\nexport class WebSocketErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly prefix: string;\n\n constructor(\n private readonly socket: WebSocketLike,\n options: WebSocketErrorOptions = {}\n ) {\n this.prefix = options.prefix ?? DEFAULT_PREFIX;\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const state: number = this.socket.readyState;\n if (state !== OPEN) {\n throw new Error(\n `the socket is ${STATE_NAMES[state] ?? `in unknown state ${String(state)}`} (readyState ${String(state)}), so the entry was not sent; connect or reconnect it first`\n );\n }\n this.socket.send(\n JSON.stringify({ id: resolveEntryId(entry), ...serializeEntry(entry) })\n );\n return { ok: true };\n } catch (error: unknown) {\n return reportFailure({\n adapterName: 'WebSocketErrorAdapter',\n prefix: this.prefix,\n action: 'to send over the socket',\n error,\n });\n }\n }\n}\n","import { resolveEntryId } from '../entry-id.js';\nimport { DEFAULT_PREFIX, reportFailure } from '../fail.js';\nimport { SerialQueue } from '../queue.js';\nimport { serializeEntry } from '../serialize.js';\nimport { resolveTimeoutMs, timeoutMessage, withTimeout } from '../timeout.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/** Largest event CloudWatch accepts: 256 KiB, counting 26 bytes of overhead per event. */\nexport const MAX_EVENT_BYTES = 262_144;\n/** Overhead CloudWatch adds to every event when it measures size. */\nexport const EVENT_OVERHEAD_BYTES = 26;\n/** Largest `PutLogEvents` request: 1 MiB of events. */\nexport const MAX_BATCH_BYTES = 1_048_576;\n/** Most events in one `PutLogEvents` request. */\nexport const MAX_BATCH_EVENTS = 10_000;\n/** The events of one request may not span more than this. */\nconst MAX_BATCH_SPAN_MS = 24 * 60 * 60 * 1000;\n\n/** Buffered events kept after failed sends: this many times `batchSize`, oldest dropped first. */\nexport const BUFFER_LIMIT_MULTIPLIER = 10;\n\n/** One event as `PutLogEvents` takes it. */\nexport interface CloudWatchLogEvent {\n /** Milliseconds since the epoch. */\n timestamp: number;\n message: string;\n}\n\n/** The parameters this adapter passes to `putLogEvents`. */\nexport interface CloudWatchPutLogEventsInput {\n logGroupName: string;\n logStreamName: string;\n logEvents: CloudWatchLogEvent[];\n}\n\n/**\n * What an AWS SDK client needs to offer. The AWS SDK v3 aggregated client\n * (`new CloudWatchLogs({ ... })` from `@aws-sdk/client-cloudwatch-logs`) fits as\n * it is. `createLogGroup` and `createLogStream` are only used when\n * `createMissing` is on (the default); leave them out of a hand-written client\n * that cannot create them.\n */\nexport interface CloudWatchLogsClientLike {\n putLogEvents(input: CloudWatchPutLogEventsInput): Promise<unknown>;\n createLogGroup?(input: { logGroupName: string }): Promise<unknown>;\n createLogStream?(input: {\n logGroupName: string;\n logStreamName: string;\n }): Promise<unknown>;\n}\n\nexport interface CloudWatchBatchOptions {\n /** Send once this many events are buffered. */\n batchSize: number;\n /**\n * Also send when the oldest buffered event has waited this long (ms). The\n * timer does not keep the process alive, so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n}\n\nexport interface CloudWatchLogsErrorOptions {\n /**\n * Create the log stream, and the log group if that is missing too, when\n * `PutLogEvents` says they do not exist, then try again once. This needs the\n * `logs:CreateLogStream` (and `logs:CreateLogGroup`) permissions and only runs\n * after a \"not found\" answer, so a stream that exists costs nothing. A group\n * created this way never expires its logs; set a retention policy on it.\n * Defaults to `true`.\n */\n createMissing?: boolean;\n /**\n * Buffer events and send them together, up to the 10,000 events and 1 MiB\n * that one request allows. Off by default: one request per entry.\n */\n batch?: CloudWatchBatchOptions;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n /**\n * How long a request may take before `log` (or `flush`) gives up waiting, in\n * milliseconds. Defaults to 10000. Pass `Infinity` to wait without limit. The\n * request cannot be cancelled, so it may still finish afterwards; in batch\n * mode the events stay buffered and are sent again by the next flush.\n */\n timeoutMs?: number;\n}\n\ninterface BufferedEvent extends CloudWatchLogEvent {\n /** Entry id, for failure messages. */\n id: string;\n}\n\ninterface RejectedLogEventsInfo {\n tooNewLogEventStartIndex?: number;\n tooOldLogEventEndIndex?: number;\n expiredLogEventEndIndex?: number;\n}\n\nconst encoder = new TextEncoder();\n\nfunction eventBytes(message: string): number {\n return encoder.encode(message).length + EVENT_OVERHEAD_BYTES;\n}\n\n/** The AWS SDK names its errors; this works for both SDK v3 errors and hand-made ones. */\nfunction errorName(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null\n ? (error as { name?: string }).name\n : undefined;\n}\n\n/**\n * Splits events (oldest first) into groups that fit one `PutLogEvents` request:\n * at most 10,000 events, 1 MiB, and 24 hours between the first and the last.\n */\nexport function splitIntoRequests<T extends CloudWatchLogEvent>(\n events: readonly T[]\n): T[][] {\n const requests: T[][] = [];\n let current: T[] = [];\n let bytes = 0;\n for (const event of events) {\n const size: number = eventBytes(event.message);\n const first: T | undefined = current[0];\n if (\n first !== undefined &&\n (current.length >= MAX_BATCH_EVENTS ||\n bytes + size > MAX_BATCH_BYTES ||\n event.timestamp - first.timestamp > MAX_BATCH_SPAN_MS)\n ) {\n requests.push(current);\n current = [];\n bytes = 0;\n }\n current.push(event);\n bytes += size;\n }\n if (current.length > 0) {\n requests.push(current);\n }\n return requests;\n}\n\n/**\n * Sends entries to one CloudWatch Logs stream through `PutLogEvents`, each as a\n * JSON event (the serialized entry plus its UUID `id`) stamped with the entry's\n * time, so Logs Insights can query its fields. AWS no longer asks for sequence\n * tokens, so none are tracked.\n *\n * By default each entry is its own request and `log` resolves once CloudWatch\n * accepts it. With `batch`, events are buffered and sent together; `log` then\n * resolves ok as soon as the event is buffered (it is not stored yet), except\n * for the call that fills the batch, which resolves with the send result. Call\n * `flush()` before shutting down.\n *\n * CloudWatch can answer ok and still reject events it considers too old (over\n * 14 days) or too new (over 2 hours ahead); that is reported as `ok: false`. An\n * event over 256 KiB is refused before sending, since CloudWatch would reject\n * the whole request.\n *\n * In AWS Lambda, ECS and similar runtimes, anything written to stdout already\n * reaches CloudWatch; this adapter is for apps running elsewhere.\n */\nexport class CloudWatchLogsErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly createMissing: boolean;\n private readonly batch: CloudWatchBatchOptions | undefined;\n private readonly logPrefix: string;\n private readonly timeoutMs: number;\n private buffer: BufferedEvent[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private readonly flushQueue: SerialQueue = new SerialQueue();\n\n /** @throws {RangeError} if a name is empty, the batch or timeout options are invalid, or `createMissing` is on but the client cannot create streams. */\n constructor(\n private readonly client: CloudWatchLogsClientLike,\n private readonly logGroupName: string,\n private readonly logStreamName: string,\n options: CloudWatchLogsErrorOptions = {}\n ) {\n this.createMissing = options.createMissing ?? true;\n this.batch = options.batch;\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n this.timeoutMs = resolveTimeoutMs(\n options.timeoutMs,\n 'CloudWatchLogsErrorAdapter'\n );\n if (logGroupName === '') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: logGroupName must not be empty.'\n );\n }\n if (logStreamName === '') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: logStreamName must not be empty.'\n );\n }\n if (this.createMissing && typeof client.createLogStream !== 'function') {\n throw new RangeError(\n 'CloudWatchLogsErrorAdapter: createMissing is on, but the client has no createLogStream method. Pass createMissing: false, or use an AWS SDK CloudWatchLogs client.'\n );\n }\n if (this.batch !== undefined) {\n if (!Number.isInteger(this.batch.batchSize) || this.batch.batchSize < 1) {\n throw new RangeError(\n `CloudWatchLogsErrorAdapter: batch.batchSize must be a positive integer, received ${String(this.batch.batchSize)}.`\n );\n }\n if (\n this.batch.maxWaitMs !== undefined &&\n (!Number.isFinite(this.batch.maxWaitMs) || this.batch.maxWaitMs <= 0)\n ) {\n throw new RangeError(\n `CloudWatchLogsErrorAdapter: batch.maxWaitMs must be a positive number, received ${String(this.batch.maxWaitMs)}.`\n );\n }\n }\n }\n\n async log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n try {\n const id: string = resolveEntryId(entry);\n const record = { id, ...serializeEntry(entry) };\n const timestamp: number = Date.parse(record.timestamp);\n if (Number.isNaN(timestamp)) {\n throw new RangeError(\n `timestamp \"${record.timestamp}\" is not a valid ISO 8601 date`\n );\n }\n const message: string = JSON.stringify(record);\n const size: number = eventBytes(message);\n if (size > MAX_EVENT_BYTES) {\n throw new RangeError(\n `the event is ${size} bytes, over the ${MAX_EVENT_BYTES} byte limit CloudWatch Logs puts on one event, so it was not sent; shorten the message or cause`\n );\n }\n const event: BufferedEvent = { id, timestamp, message };\n if (this.batch === undefined) {\n const rejection: string | undefined = await this.send([event]);\n if (rejection !== undefined) {\n throw new Error(rejection);\n }\n return { ok: true };\n }\n return await this.addToBatch(event, this.batch);\n } catch (error: unknown) {\n return this.fail(error);\n }\n }\n\n /**\n * Sends any buffered events now. Resolves ok when nothing is buffered. Events\n * from a failed send stay buffered and are retried by the next flush.\n */\n flush(): Promise<ErrorLogResult> {\n return this.flushQueue.run((): Promise<ErrorLogResult> =>\n this.sendBuffered()\n );\n }\n\n private async addToBatch(\n event: BufferedEvent,\n batch: CloudWatchBatchOptions\n ): Promise<ErrorLogResult> {\n this.buffer.push(event);\n const limit: number = batch.batchSize * BUFFER_LIMIT_MULTIPLIER;\n if (this.buffer.length > limit) {\n const dropped: number = this.buffer.length - limit;\n this.buffer = this.buffer.slice(dropped);\n console.error(\n `${this.logPrefix} CloudWatchLogsErrorAdapter: dropped the ${dropped} oldest buffered events because sends to log stream \"${this.logStreamName}\" keep failing and the buffer is full (${limit} events).`\n );\n }\n if (this.buffer.length >= batch.batchSize) {\n return this.flush();\n }\n this.scheduleFlush(batch);\n return { ok: true };\n }\n\n private scheduleFlush(batch: CloudWatchBatchOptions): void {\n if (batch.maxWaitMs === undefined || this.timer !== null) {\n return;\n }\n const timer = setTimeout((): void => {\n this.timer = null;\n void this.flush();\n }, batch.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.timer = timer;\n }\n\n private async sendBuffered(): Promise<ErrorLogResult> {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const pending: BufferedEvent[] = this.buffer.slice();\n if (pending.length === 0) {\n return { ok: true };\n }\n // PutLogEvents wants events oldest first. The sort is stable, so events\n // with the same time keep the order they were logged in.\n pending.sort(\n (a: BufferedEvent, b: BufferedEvent): number => a.timestamp - b.timestamp\n );\n let rejectedTotal = 0;\n let lastRejection = '';\n try {\n for (const request of splitIntoRequests(pending)) {\n const rejection: string | undefined = await this.send(request);\n // Accepted or rejected for good (too old will never be accepted), the\n // events leave the buffer; only a thrown error keeps them for a retry.\n const sent: Set<BufferedEvent> = new Set(request);\n this.buffer = this.buffer.filter(\n (event: BufferedEvent): boolean => !sent.has(event)\n );\n if (rejection !== undefined) {\n rejectedTotal += request.length;\n lastRejection = rejection;\n }\n }\n } catch (error: unknown) {\n if (this.batch !== undefined) {\n this.scheduleFlush(this.batch);\n }\n return this.fail(error, this.buffer.length);\n }\n if (rejectedTotal > 0) {\n return this.fail(new Error(lastRejection), rejectedTotal);\n }\n return { ok: true };\n }\n\n /**\n * Sends one request, creating the stream first if CloudWatch says it is\n * missing. Resolves to a description of any events CloudWatch refused, or\n * undefined when it took them all.\n */\n private async send(events: BufferedEvent[]): Promise<string | undefined> {\n let output: unknown;\n try {\n output = await this.putLogEvents(events);\n } catch (error: unknown) {\n if (\n !this.createMissing ||\n errorName(error) !== 'ResourceNotFoundException'\n ) {\n throw error;\n }\n await this.createStream();\n output = await this.putLogEvents(events);\n }\n return describeRejection(output, events.length);\n }\n\n private putLogEvents(events: BufferedEvent[]): Promise<unknown> {\n return withTimeout(\n (): Promise<unknown> =>\n this.client.putLogEvents({\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n logEvents: events.map(\n ({ timestamp, message }: BufferedEvent): CloudWatchLogEvent => ({\n timestamp,\n message,\n })\n ),\n }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs)\n );\n }\n\n private async createStream(): Promise<void> {\n const createStream = (): Promise<unknown> =>\n withTimeout(\n (): Promise<unknown> =>\n (\n this.client.createLogStream as NonNullable<\n CloudWatchLogsClientLike['createLogStream']\n >\n )({\n logGroupName: this.logGroupName,\n logStreamName: this.logStreamName,\n }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs, 'create the stream')\n );\n try {\n await createStream();\n } catch (error: unknown) {\n const name: string | undefined = errorName(error);\n if (name === 'ResourceAlreadyExistsException') {\n return;\n }\n if (\n name !== 'ResourceNotFoundException' ||\n typeof this.client.createLogGroup !== 'function'\n ) {\n throw error;\n }\n // The group is missing too.\n const createGroup = this.client.createLogGroup.bind(this.client);\n try {\n await withTimeout(\n (): Promise<unknown> =>\n createGroup({ logGroupName: this.logGroupName }),\n this.timeoutMs,\n timeoutMessage('CloudWatch Logs', this.timeoutMs, 'create the group')\n );\n } catch (groupError: unknown) {\n if (errorName(groupError) !== 'ResourceAlreadyExistsException') {\n throw groupError;\n }\n }\n try {\n await createStream();\n } catch (retryError: unknown) {\n if (errorName(retryError) !== 'ResourceAlreadyExistsException') {\n throw retryError;\n }\n }\n }\n }\n\n private fail(error: unknown, count?: number): ErrorLogResult {\n return reportFailure({\n adapterName: 'CloudWatchLogsErrorAdapter',\n prefix: this.logPrefix,\n action: `to send to log stream \"${this.logStreamName}\" in log group \"${this.logGroupName}\"${count === undefined ? '' : ` (${count} event${count === 1 ? '' : 's'} affected)`}`,\n error,\n });\n }\n}\n\n/** Explains `rejectedLogEventsInfo`, which CloudWatch returns with an otherwise successful answer. */\nfunction describeRejection(\n output: unknown,\n eventCount: number\n): string | undefined {\n const info: RejectedLogEventsInfo | undefined =\n typeof output === 'object' && output !== null\n ? (output as { rejectedLogEventsInfo?: RejectedLogEventsInfo })\n .rejectedLogEventsInfo\n : undefined;\n if (info === undefined || info === null) {\n return undefined;\n }\n const reasons: string[] = [];\n if (info.tooOldLogEventEndIndex !== undefined) {\n reasons.push(\n `${info.tooOldLogEventEndIndex} event(s) were older than the 14 days CloudWatch accepts`\n );\n }\n if (info.expiredLogEventEndIndex !== undefined) {\n reasons.push(\n `${info.expiredLogEventEndIndex} event(s) were older than the log group's retention period`\n );\n }\n if (info.tooNewLogEventStartIndex !== undefined) {\n reasons.push(\n `${Math.max(0, eventCount - info.tooNewLogEventStartIndex)} event(s) were more than 2 hours ahead of the current time`\n );\n }\n return reasons.length === 0\n ? undefined\n : `CloudWatch Logs accepted the request but rejected events: ${reasons.join('; ')}`;\n}\n","import { backoffDelayMs } from '../backoff.js';\nimport { DEFAULT_PREFIX } from '../fail.js';\nimport { describeError } from '../serialize.js';\nimport type {\n ErrorLogAdapter,\n ErrorLogEntry,\n ErrorLogResult,\n} from '../types.js';\n\n/**\n * An adapter that can send several entries in one go, such as\n * `WebhookErrorAdapter` (one request carrying a JSON array).\n * `BatchingErrorAdapter` uses it when it is there. The whole batch succeeds or\n * fails together.\n */\nexport interface BatchCapableAdapter<\n TCode extends string = string,\n> extends ErrorLogAdapter<TCode> {\n logBatch(entries: ErrorLogEntry<TCode>[]): Promise<ErrorLogResult>;\n}\n\nexport interface BatchingErrorOptions<TCode extends string = string> {\n /** Deliver once this many entries are waiting. Defaults to 20. */\n batchSize?: number;\n /**\n * Also deliver once the oldest waiting entry has waited this long (ms).\n * Defaults to 5000. Pass `Infinity` to deliver only when `batchSize` is\n * reached or `flush()` is called. The timer does not keep the process alive,\n * so call `flush()` before exiting.\n */\n maxWaitMs?: number;\n /**\n * How many times to try an entry again after a failed delivery before giving\n * up on it. Defaults to 3 (four attempts in all).\n */\n retries?: number;\n /** Wait before the first retry, in milliseconds; it doubles after that. Defaults to 500. */\n retryDelayMs?: number;\n /** Longest wait between attempts, in milliseconds. Defaults to 30000. */\n maxRetryDelayMs?: number;\n /**\n * Most entries kept while the sink is slow or down. Past this, the oldest\n * are dropped. Defaults to 1000. Must be at least `batchSize`.\n */\n maxBuffered?: number;\n /**\n * Use the wrapped adapter's `logBatch` when it has one. Defaults to `true`.\n * Pass `false` to deliver one entry at a time even then (a wrapped\n * `WebhookErrorAdapter` then sends one request per entry, not an array).\n */\n useBatchMethod?: boolean;\n /**\n * Called when entries are given up on, either because they failed every\n * attempt or because the buffer was full. Errors it throws are ignored.\n */\n onDrop?: (entries: ErrorLogEntry<TCode>[], reason: string) => void;\n /** Text printed before failure messages. Defaults to `[error-log-adapter]`. */\n prefix?: string;\n}\n\ninterface Pending<TCode extends string> {\n entry: ErrorLogEntry<TCode>;\n /** Failed deliveries so far. */\n failures: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 20;\nconst DEFAULT_MAX_WAIT_MS = 5000;\nconst DEFAULT_RETRIES = 3;\nconst DEFAULT_RETRY_DELAY_MS = 500;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst DEFAULT_MAX_BUFFERED = 1000;\n\nfunction hasLogBatch<TCode extends string>(\n adapter: ErrorLogAdapter<TCode>\n): adapter is BatchCapableAdapter<TCode> {\n return (\n typeof (adapter as Partial<BatchCapableAdapter<TCode>>).logBatch ===\n 'function'\n );\n}\n\n/**\n * Puts a queue in front of any other adapter. `log()` returns `ok: true` as\n * soon as the entry is queued, so a slow or failing sink never holds up the\n * application; entries are then delivered in order, in batches, and a failed\n * delivery is tried again with exponential backoff before the entry is given up\n * on (see `onDrop`).\n *\n * Delivery uses the wrapped adapter's `logBatch` when it has one (the webhook\n * sends one request for the whole batch); otherwise the entries go one at a\n * time through `log`, stopping at the first failure so a sink that is down is\n * not hammered. Because of that, `ok: true` from `log()` means \"queued\", not\n * \"stored\": call `flush()` before the process exits, and watch `onDrop` or the\n * console for entries that could not be delivered. The queue is in memory, so\n * entries still waiting when the process dies are lost; put a durable adapter\n * beside it in a `CompositeErrorAdapter` when that matters.\n *\n * ```ts\n * const errorLog = new BatchingErrorAdapter(\n * new WebhookErrorAdapter('https://example.com/errors'),\n * { batchSize: 50, maxWaitMs: 2000 }\n * );\n * process.on('beforeExit', () => void errorLog.flush());\n * ```\n *\n * Do not also turn on the wrapped adapter's own retries (for example the\n * webhook's `retries`): the two multiply.\n */\nexport class BatchingErrorAdapter<\n TCode extends string = string,\n> implements ErrorLogAdapter<TCode> {\n private readonly batchSize: number;\n private readonly maxWaitMs: number;\n private readonly retries: number;\n private readonly retryDelayMs: number;\n private readonly maxRetryDelayMs: number;\n private readonly maxBuffered: number;\n private readonly useBatchMethod: boolean;\n private readonly onDrop: BatchingErrorOptions<TCode>['onDrop'];\n private readonly logPrefix: string;\n\n private queue: Pending<TCode>[] = [];\n private waitTimer: ReturnType<typeof setTimeout> | null = null;\n private backoffTimer: ReturnType<typeof setTimeout> | null = null;\n private draining: Promise<void> | null = null;\n private flushers = 0;\n private droppedSinceFlush = 0;\n private lastDropReason = '';\n\n /** @throws {RangeError} if an option is out of range. */\n constructor(\n private readonly inner: ErrorLogAdapter<TCode>,\n options: BatchingErrorOptions<TCode> = {}\n ) {\n this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;\n this.maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;\n this.retries = options.retries ?? DEFAULT_RETRIES;\n this.retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n this.maxRetryDelayMs =\n options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n this.maxBuffered = options.maxBuffered ?? DEFAULT_MAX_BUFFERED;\n this.useBatchMethod = options.useBatchMethod ?? true;\n this.onDrop = options.onDrop;\n this.logPrefix = options.prefix ?? DEFAULT_PREFIX;\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new RangeError(\n `BatchingErrorAdapter: batchSize must be a positive integer, received ${String(options.batchSize)}.`\n );\n }\n if (Number.isNaN(this.maxWaitMs) || this.maxWaitMs <= 0) {\n throw new RangeError(\n `BatchingErrorAdapter: maxWaitMs must be a positive number or Infinity, received ${String(options.maxWaitMs)}.`\n );\n }\n if (!Number.isInteger(this.retries) || this.retries < 0) {\n throw new RangeError(\n `BatchingErrorAdapter: retries must be a whole number of 0 or more, received ${String(options.retries)}.`\n );\n }\n if (!Number.isFinite(this.retryDelayMs) || this.retryDelayMs <= 0) {\n throw new RangeError(\n `BatchingErrorAdapter: retryDelayMs must be a positive number, received ${String(options.retryDelayMs)}.`\n );\n }\n if (\n !Number.isFinite(this.maxRetryDelayMs) ||\n this.maxRetryDelayMs < this.retryDelayMs\n ) {\n throw new RangeError(\n `BatchingErrorAdapter: maxRetryDelayMs must be a number no smaller than retryDelayMs (${this.retryDelayMs}), received ${String(options.maxRetryDelayMs)}.`\n );\n }\n if (\n !Number.isInteger(this.maxBuffered) ||\n this.maxBuffered < this.batchSize\n ) {\n throw new RangeError(\n `BatchingErrorAdapter: maxBuffered must be a whole number no smaller than batchSize (${this.batchSize}), received ${String(options.maxBuffered)}.`\n );\n }\n }\n\n /** Queues the entry. Resolves ok straight away; delivery happens in the background. */\n log(entry: ErrorLogEntry<TCode>): Promise<ErrorLogResult> {\n this.queue.push({ entry, failures: 0 });\n if (this.queue.length > this.maxBuffered) {\n const overflow: Pending<TCode>[] = this.queue.splice(\n 0,\n this.queue.length - this.maxBuffered\n );\n this.drop(\n overflow,\n `the buffer of ${this.maxBuffered} entries was full because the wrapped adapter keeps failing`\n );\n }\n if (this.queue.length >= this.batchSize) {\n this.startDraining();\n } else {\n this.scheduleDrain();\n }\n return Promise.resolve({ ok: true });\n }\n\n /**\n * Delivers everything queued, retrying as configured, and resolves once the\n * queue is empty. Resolves `ok: false` if any entries were given up on since\n * the last flush. With retries on and the sink down this can take a while\n * (the waits add up to well over a minute with the defaults).\n */\n async flush(): Promise<ErrorLogResult> {\n this.flushers += 1;\n // A process waiting in flush() must not exit during a backoff sleep.\n this.backoffTimer?.ref?.();\n try {\n this.startDraining();\n while (this.draining !== null) {\n await this.draining;\n }\n } finally {\n this.flushers -= 1;\n }\n if (this.droppedSinceFlush > 0) {\n const count: number = this.droppedSinceFlush;\n const reason: string = this.lastDropReason;\n this.droppedSinceFlush = 0;\n this.lastDropReason = '';\n return {\n ok: false,\n error: `BatchingErrorAdapter gave up on ${count} entr${count === 1 ? 'y' : 'ies'}: ${reason}`,\n };\n }\n return { ok: true };\n }\n\n private scheduleDrain(): void {\n if (this.waitTimer !== null || this.maxWaitMs === Infinity) {\n return;\n }\n const timer = setTimeout((): void => {\n this.waitTimer = null;\n this.startDraining();\n }, this.maxWaitMs);\n (timer as { unref?: () => void }).unref?.();\n this.waitTimer = timer;\n }\n\n private startDraining(): void {\n if (this.waitTimer !== null) {\n clearTimeout(this.waitTimer);\n this.waitTimer = null;\n }\n if (this.draining !== null || this.queue.length === 0) {\n return;\n }\n this.draining = this.drain()\n .catch((error: unknown): void => {\n // drain() handles every failure itself; this only guards a bug.\n console.error(`${this.logPrefix} BatchingErrorAdapter:`, error);\n })\n .finally((): void => {\n this.draining = null;\n // Entries logged after the last batch was taken still need a timer.\n if (this.queue.length > 0) {\n this.scheduleDrain();\n }\n });\n }\n\n /** Delivers batches until the queue is empty, waiting out a backoff after each failure. */\n private async drain(): Promise<void> {\n let failedRounds = 0;\n while (this.queue.length > 0) {\n const batch: Pending<TCode>[] = this.queue.slice(0, this.batchSize);\n const failure: string | undefined = await this.deliver(batch);\n if (failure === undefined) {\n failedRounds = 0;\n continue;\n }\n failedRounds += 1;\n const exhausted: Pending<TCode>[] = this.queue.filter(\n (item: Pending<TCode>): boolean => item.failures > this.retries\n );\n if (exhausted.length > 0) {\n this.queue = this.queue.filter(\n (item: Pending<TCode>): boolean => item.failures <= this.retries\n );\n this.drop(\n exhausted,\n `delivery failed ${this.retries + 1} time${this.retries === 0 ? '' : 's'}; last error: ${failure}`\n );\n }\n if (this.queue.length > 0) {\n await this.backoff(failedRounds);\n }\n }\n }\n\n /**\n * Tries to deliver `batch` (the front of the queue). Delivered entries leave\n * the queue; an entry that failed has its failure count raised and stays.\n * Resolves to the failure message, or undefined when everything went through.\n */\n private async deliver(batch: Pending<TCode>[]): Promise<string | undefined> {\n const remove = (delivered: Pending<TCode>[]): void => {\n const gone: Set<Pending<TCode>> = new Set(delivered);\n this.queue = this.queue.filter(\n (item: Pending<TCode>): boolean => !gone.has(item)\n );\n };\n\n if (this.useBatchMethod && hasLogBatch(this.inner)) {\n const result: ErrorLogResult = await this.safely(\n (): Promise<ErrorLogResult> =>\n (this.inner as BatchCapableAdapter<TCode>).logBatch(\n batch.map(\n (item: Pending<TCode>): ErrorLogEntry<TCode> => item.entry\n )\n )\n );\n if (result.ok) {\n remove(batch);\n return undefined;\n }\n for (const item of batch) {\n item.failures += 1;\n }\n return result.error ?? 'the wrapped adapter reported a failure';\n }\n\n for (const item of batch) {\n const result: ErrorLogResult = await this.safely(\n (): Promise<ErrorLogResult> => this.inner.log(item.entry)\n );\n if (result.ok) {\n remove([item]);\n continue;\n }\n // Stop here: the entries after this one stay queued without a failure\n // counted against them, and are tried after the backoff.\n item.failures += 1;\n return result.error ?? 'the wrapped adapter reported a failure';\n }\n return undefined;\n }\n\n /** The wrapped adapter should not throw, but a bug in one must not stop the queue. */\n private async safely(\n call: () => Promise<ErrorLogResult>\n ): Promise<ErrorLogResult> {\n try {\n return await call();\n } catch (error: unknown) {\n return { ok: false, error: describeError(error) };\n }\n }\n\n private backoff(failedRounds: number): Promise<void> {\n return new Promise<void>((resolve: () => void): void => {\n const timer = setTimeout(\n (): void => {\n this.backoffTimer = null;\n resolve();\n },\n backoffDelayMs(failedRounds, this.retryDelayMs, this.maxRetryDelayMs)\n );\n // Only a caller waiting in flush() should be kept alive by this wait.\n if (this.flushers === 0) {\n (timer as { unref?: () => void }).unref?.();\n }\n this.backoffTimer = timer;\n });\n }\n\n private drop(items: Pending<TCode>[], reason: string): void {\n if (items.length === 0) {\n return;\n }\n this.droppedSinceFlush += items.length;\n this.lastDropReason = reason;\n console.error(\n `${this.logPrefix} BatchingErrorAdapter: gave up on ${items.length} entr${items.length === 1 ? 'y' : 'ies'}: ${reason}`\n );\n try {\n this.onDrop?.(\n items.map((item: Pending<TCode>): ErrorLogEntry<TCode> => item.entry),\n reason\n );\n } catch {\n // A failing callback must not stop delivery.\n }\n }\n}\n"],"mappings":"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","names":["result","encoder","assertValidTableName","BUFFER_LIMIT_MULTIPLIER","DEFAULT_RETRY_DELAY_MS","DEFAULT_MAX_RETRY_DELAY_MS"]}