@rexezuge/d1 1.0.2 → 1.0.4

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,"file":"BaseDAO.js","sourceRoot":"","sources":["../src/BaseDAO.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseDAO.js","sourceRoot":"","sources":["../src/BaseDAO.ts"],"names":[],"mappings":"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","sourcesContent":["import { DatabaseError } from '@rexezuge/errors';\nimport { D1_RETRY_DEFAULTS, executeWithRetry } from './D1Retry';\nimport type { D1PreparedStatement, D1Queryable, D1Result } from './D1Types';\n\n/**\n * A statement result plus the payload it produced, so the retry engine (which\n * inspects `success`) also handles wrapped results like batch arrays.\n */\ninterface StatementEnvelope<T> extends D1Result {\n value: T;\n}\n\n/**\n * Canonical DAO base: retry is a policy, methods are a promise.\n *\n * Converged from the nine consumer repos' BaseDAOs. The API is the **sql-first**\n * shape CalDAV-Bridge and ChordDHT-Tracker used, not the closure-first shape\n * Durable-DAV's family used: a DAO states its SQL and bindings directly, and\n * the retry/normalization wrapper is invisible at the call site. The closure\n * form made every read site pay for a wrapper 90% of them never needed, and\n * each of the two shapes drifted its own method names (`withRetry` vs\n * `run(sql, bindings)`); the sql-first form is what survived.\n *\n * Non-DAO concerns stay out: cursor pagination lives in `@rexezuge/shared`\n * (`CursorCodec`), subrequest accounting is an injectable port. Retry policy\n * lives in `D1Retry` — one schedule, one classifier, one place the\n * `Failed to <context>` prefix is added.\n */\nabstract class BaseDAO {\n protected constructor(protected readonly database: D1Queryable) {}\n\n /** A prepared statement with its bindings applied. Bindings are always parameters, never interpolation. */\n protected statement(sql: string, bindings: unknown[] = []): D1PreparedStatement {\n return bindings.length > 0 ? this.database.prepare(sql).bind(...bindings) : this.database.prepare(sql);\n }\n\n /**\n * Run a statement. D1 reports a constraint violation or a missing table by\n * returning `success: false` rather than by throwing, so both forms are\n * normalized to a `DatabaseError` here — ignoring the result form is what\n * let a `UNIQUE` failure reach a caller as an opaque 500.\n */\n protected async run(sql: string, bindings: unknown[] = []): Promise<D1Result> {\n return executeWithRetry((): Promise<D1Result> => this.statement(sql, bindings).run(), 'run statement');\n }\n\n /**\n * Read one row. `first()` resolving to `null` is \"no row\", a success, not an\n * error; only a rejected statement is retried and normalized. Callers get\n * `undefined` — the shape every repo's row check (`row ? … : …`) already\n * treats as absent, unlike a null.\n */\n protected async first<T>(sql: string, bindings: unknown[] = []): Promise<T | undefined> {\n const envelope = await executeWithRetry(async (): Promise<StatementEnvelope<T | null>> => {\n const row: T | null = await this.statement(sql, bindings).first<T>();\n return { success: true, value: row };\n }, 'read a row');\n return envelope.value ?? undefined;\n }\n\n /** A list read. `.all()` returning `[]` is a success like `first()`'s null. */\n protected async all<T>(sql: string, bindings: unknown[] = []): Promise<T[]> {\n const envelope = await executeWithRetry(async (): Promise<StatementEnvelope<T[]>> => {\n const rows = await this.statement(sql, bindings).all<T>();\n return { success: true, value: rows.results ?? [] };\n }, 'read rows');\n return envelope.value;\n }\n\n /** A scalar count from a `SELECT count(*)` row; 0 when absent. */\n protected async count(sql: string, bindings: unknown[] = []): Promise<number> {\n const row = await this.first<{ count: number }>(sql, bindings);\n return row?.count ?? 0;\n }\n\n /**\n * A batch of prepared statements — one transaction and one subrequest,\n * however many statements it carries. Each result is checked, so a single\n * failure inside the batch raises rather than passing silently.\n */\n protected async batch(statements: D1PreparedStatement[]): Promise<D1Result[]> {\n if (statements.length === 0) return [];\n if (this.database.batch === undefined) {\n throw new DatabaseError('The D1 binding exposes no batch() — a multi-statement transaction is not available here');\n }\n const envelope = await executeWithRetry(async (): Promise<StatementEnvelope<D1Result[]>> => {\n const results: D1Result[] = await (this.database.batch as NonNullable<D1Queryable['batch']>)(statements);\n const failed: D1Result | undefined = results.find((result): boolean => !result.success);\n return { success: failed === undefined, error: failed?.error, value: results };\n }, 'run a batch');\n return envelope.value;\n }\n\n /**\n * Generic batched delete of rows older than a cutoff, in ID order, with\n * retry. Mirrors the per-DAO `DELETE ... WHERE id IN (SELECT ... LIMIT ?)`\n * pattern that every repo re-implemented per table. The cutoff is exclusive:\n * a delete never touches a row at exactly the boundary timestamp.\n */\n protected static async deleteOlderThan(\n db: D1Queryable,\n table: string,\n timeColumn: string,\n cutoff: number | string,\n limit: number,\n idColumn: string,\n ): Promise<number> {\n assertSqlIdentifier(table, 'table');\n assertSqlIdentifier(timeColumn, 'timeColumn');\n assertSqlIdentifier(idColumn, 'idColumn');\n const result: D1Result = await executeWithRetry(\n (): Promise<D1Result> =>\n db\n .prepare(\n `DELETE FROM ${table}\n WHERE ${idColumn} IN (\n SELECT ${idColumn} FROM ${table}\n WHERE ${timeColumn} < ?\n LIMIT ?\n )`,\n )\n .bind(cutoff, limit)\n .run(),\n `delete old rows from ${table}`,\n );\n return (result.meta as { changes?: number })?.changes ?? 0;\n }\n\n /** The retry schedule, for callers that surface it (health checks, tests). */\n protected static get retryDefaults(): typeof D1_RETRY_DEFAULTS {\n return D1_RETRY_DEFAULTS;\n }\n}\n\n/**\n * Allow-list one SQL identifier segment. Values never go through this — they\n * are always bound; this exists purely for the table/column names that\n * parameter binding cannot express.\n */\nfunction assertSqlIdentifier(value: string, label: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {\n throw new Error(`Unsafe SQL identifier for ${label}: ${JSON.stringify(value)}`);\n }\n}\n\nexport { BaseDAO, assertSqlIdentifier };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"D1ErrorClassifier.js","sourceRoot":"","sources":["../src/D1ErrorClassifier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAa;IACnC,OAAO;IACP,SAAS;IACT,UAAU;IACV,eAAe;IACf,+BAA+B;IAC/B,aAAa;IACb,UAAU;IACV,cAAc;IACd,UAAU;IACV,aAAa;IACb,QAAQ;IACR,WAAW;IACX,gBAAgB;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,sBAAsB,GAAa;IACvC,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,aAAa;IACb,gBAAgB;IAChB,mCAAmC;IACnC,kBAAkB;IAClB,uEAAuE;IACvE,gCAAgC;IAChC,uBAAuB;IACvB,qBAAqB;IACrB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;CACtB,CAAC;AAEF,SAAS,kBAAkB,CAAC,YAAoB;IAC9C,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,6EAA6E;IAC7E,uEAAuE;IACvE,+EAA+E;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,2EAA2E;IAC3E,yCAAyC;IACzC,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9C,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,sBAAsB,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"D1ErrorClassifier.js","sourceRoot":"","sources":["../src/D1ErrorClassifier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,kBAAkB,GAAa;IACnC,OAAO;IACP,SAAS;IACT,UAAU;IACV,eAAe;IACf,+BAA+B;IAC/B,aAAa;IACb,UAAU;IACV,cAAc;IACd,UAAU;IACV,aAAa;IACb,QAAQ;IACR,WAAW;IACX,gBAAgB;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,sBAAsB,GAAa;IACvC,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,aAAa;IACb,gBAAgB;IAChB,mCAAmC;IACnC,kBAAkB;IAClB,uEAAuE;IACvE,gCAAgC;IAChC,uBAAuB;IACvB,qBAAqB;IACrB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;CACtB,CAAC;AAEF,SAAS,kBAAkB,CAAC,YAAoB;IAC9C,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,6EAA6E;IAC7E,uEAAuE;IACvE,+EAA+E;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,2EAA2E;IAC3E,yCAAyC;IACzC,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9C,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,sBAAsB,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC","sourcesContent":["/**\n * Classify a raw D1 (or DO SQLite) error message as transient or permanent.\n *\n * Converged verbatim from Durable-DAV (and ChordDHT's byte-identical copy,\n * modulo the import scope) — the most-documented variant in the family.\n */\n\n/**\n * Patterns for a transient failure worth retrying.\n *\n * Checked **first**. A message matching both lists is treated as transient: one\n * wasted round trip against a real conflict is cheap, whereas mis-classifying a\n * blip as permanent turns a self-healing condition into a user-visible outage\n * that neither the DAO's retry nor the API's 503 mapping will cover.\n */\nconst RETRYABLE_PATTERNS: RegExp[] = [\n /busy/i,\n /locked/i,\n /timeout/i,\n /timed?\\s*out/i,\n /internal\\s+(server\\s+)?error/i,\n /connection/i,\n /network/i,\n /unavailable/i,\n /throttl/i,\n /too\\s+many/i,\n /retry/i,\n /deadlock/i,\n /serialization/i,\n];\n\n/**\n * Patterns for a failure that will not get better on its own.\n *\n * Every entry is deliberately **narrower than its obvious spelling**. This list\n * used to be consulted first and carried bare `/range/i` and `/not\\s+found/i`,\n * both of which match transient conditions:\n *\n * - `/range/i` matched `RangeError: Maximum call stack size exceeded` and\n * `byte range not satisfiable` — neither is a row-level conflict.\n * - `/not\\s+found/i` matched `host not found`, i.e. a DNS blip, which\n * `/connection/i` now correctly claims first.\n */\nconst NON_RETRYABLE_PATTERNS: RegExp[] = [\n /constraint/i,\n /\\bunique\\b/i,\n /primary\\s+key/i,\n /foreign\\s+key/i,\n /not\\s+found/i,\n /\\bsyntax\\b/i,\n /parse\\s+error/i,\n /no\\s+such\\s+(table|column|index)/i,\n /type\\s+mismatch/i,\n // Was `/range/i`, which also matched a stack-overflow `RangeError` and\n // \"byte range not satisfiable\".\n /\\bout\\s+of\\s+range\\b/i,\n /invalid\\s+argument/i,\n /\\bpermission\\b/i,\n /\\bauthorization\\b/i,\n /\\bauthentication\\b/i,\n];\n\nfunction isD1ErrorRetryable(errorMessage: string): boolean {\n if (!errorMessage) return false;\n\n // Retryable is checked first on purpose. An error can carry both a transient\n // signal and a permanent-sounding word — \"connection: host not found\",\n // \"retry: authentication backend unavailable\" — and retrying those is correct,\n // while a bare \"row not found\" or a UNIQUE/FOREIGN KEY violation still matches\n // nothing in the retryable list and is correctly refused. The ordering is\n // pinned by `hardening-round7.test.ts` in Durable-DAV; the kit carries the\n // equivalent expectations in d1.test.ts.\n for (const pattern of RETRYABLE_PATTERNS) {\n if (pattern.test(errorMessage)) return true;\n }\n\n for (const pattern of NON_RETRYABLE_PATTERNS) {\n if (pattern.test(errorMessage)) return false;\n }\n\n return false;\n}\n\nexport { isD1ErrorRetryable, RETRYABLE_PATTERNS, NON_RETRYABLE_PATTERNS };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"D1Retry.js","sourceRoot":"","sources":["../src/D1Retry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"D1Retry.js","sourceRoot":"","sources":["../src/D1Retry.ts"],"names":[],"mappings":"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","sourcesContent":["import { DatabaseError } from '@rexezuge/errors';\nimport { isD1ErrorRetryable } from './D1ErrorClassifier';\nimport type { D1Result } from './D1Types';\n\n/**\n * The single retry engine for D1 statements.\n *\n * Converged from Durable-DAV's `D1Utils.ts` (the richest, most-documented\n * variant) with ChordDHT's byte-identical copy and the straight-line port in\n * Mail-Meow. The seven consumer repos each spelled this differently\n * (`executeD1WithRetry`, `runWithRetry`, `firstWithRetry` with its own schedule);\n * the kit ships one engine plus the schedule constants, and `BaseDAO` is the\n * only intended caller.\n */\n\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_BASE_DELAY_MS = 100;\n\n/**\n * The retry schedule, exported so `BaseDAO` and any other caller cannot\n * hard-code a second, divergent one — re-deriving `baseDelayMs * 2 ** n` at a\n * call site is the same drift one level up.\n */\nconst D1_RETRY_DEFAULTS = { maxRetries: DEFAULT_MAX_RETRIES, baseDelayMs: DEFAULT_BASE_DELAY_MS } as const;\n\ninterface D1RetryOptions {\n maxRetries?: number;\n baseDelayMs?: number;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve: (value: void) => void): unknown => setTimeout(resolve, ms));\n}\n\n/**\n * Backoff before the next attempt. Exponential, and uncapped by design: the\n * default base (100 ms) tops out under a second by attempt 3, while an operator\n * who sets a larger base gets the longer wait they asked for. The point is to\n * ride out a busy/locked D1, not to implement a rate limiter.\n */\nfunction backoffMs(baseDelayMs: number, attempt: number): number {\n return baseDelayMs * 2 ** attempt;\n}\n\nfunction toDatabaseError(errorMessage: string, context: string): DatabaseError {\n return new DatabaseError(`Failed to ${context}: ${errorMessage}`, isD1ErrorRetryable(errorMessage));\n}\n\n/**\n * Run a D1 operation, retrying transient failures with exponential backoff.\n *\n * Two failure shapes are handled, because D1 reports both:\n *\n * 1. **A result with `success: false`.** The operation resolved; the database\n * said no. Retried when the error classifies as transient.\n * 2. **A thrown error.** A transport-level rejection, which never carries a\n * `D1Result` at all. Classified from the message the same way.\n *\n * A `DatabaseError` we raised ourselves in (1) arrives back through the `catch`\n * on the retry attempt, so it must be recognised and re-thrown rather than\n * re-wrapped into a second layer of `Failed to …`.\n */\nasync function executeWithRetry<T extends D1Result>(\n operation: () => Promise<T>,\n context: string,\n options?: D1RetryOptions,\n): Promise<T> {\n const maxRetries: number = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n const baseDelayMs: number = options?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const isLastAttempt = attempt === maxRetries;\n try {\n const result: T = await operation();\n if (result.success) return result;\n const errorMessage: string = result.error ?? 'Unknown database error';\n const failure = toDatabaseError(errorMessage, context);\n if (!isLastAttempt && failure.retryable) {\n await sleep(backoffMs(baseDelayMs, attempt));\n continue;\n }\n throw failure;\n } catch (error: unknown) {\n // Already classified above, or raised by the operation itself. Either way\n // re-wrapping would bury the original context.\n if (error instanceof DatabaseError) {\n if (!isLastAttempt && error.retryable) {\n await sleep(backoffMs(baseDelayMs, attempt));\n continue;\n }\n throw error;\n }\n if (error instanceof Error) {\n const failure = toDatabaseError(error.message, context);\n if (!isLastAttempt && failure.retryable) {\n await sleep(backoffMs(baseDelayMs, attempt));\n continue;\n }\n throw failure;\n }\n throw error;\n }\n }\n\n // Unreachable: the loop either returns a result or throws on its final\n // iteration. Present only so the function is total for a reader who has not\n // traced the `continue` paths.\n throw new DatabaseError(`Failed to ${context} after ${maxRetries + 1} attempts`);\n}\n\nexport { backoffMs, D1_RETRY_DEFAULTS, executeWithRetry, sleep };\nexport type { D1RetryOptions };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"D1Types.js","sourceRoot":"","sources":["../src/D1Types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"D1Types.js","sourceRoot":"","sources":["../src/D1Types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Minimal structural D1 types so `@rexezuge/d1` typechecks without\n * @cloudflare/workers-types. A real `D1Database` satisfies these structurally.\n *\n * Converged from the identical copies in the nine consumer repos.\n */\ninterface D1PreparedStatement {\n bind(...values: unknown[]): D1PreparedStatement;\n first<T = unknown>(): Promise<T | null>;\n all<T = unknown>(): Promise<{ results: T[] }>;\n run(): Promise<D1Result>;\n}\n\ninterface D1Result {\n success: boolean;\n error?: string;\n meta?: { changes?: number; [key: string]: unknown };\n}\n\ninterface D1Queryable {\n prepare(query: string): D1PreparedStatement;\n batch?(statements: D1PreparedStatement[]): Promise<D1Result[]>;\n}\n\nexport type { D1PreparedStatement, D1Queryable, D1Result };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"aes-gcm.js","sourceRoot":"","sources":["../src/aes-gcm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aes-gcm.js","sourceRoot":"","sources":["../src/aes-gcm.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64ToBytes, bytesToBase64 } from '@rexezuge/shared';\n\n/**\n * AES-GCM envelope for values that must be recoverable.\n *\n * Converged from the seven drifted copies of `backend-data/crypto/aes-gcm.ts`\n * (AWS, CalDAV, Durable-DAV, Edge-Git, Edge-Sonic, Mail-Meow, Mail-Otter).\n * Durable-DAV's is the canonical shape; names are de-scoped from\n * \"replication\" because every repo that encrypts a secret — replication remote\n * passwords, SES credentials, OAuth client secrets — has the same problem.\n *\n * Key material is base64 of exactly 32 bytes. A missing or wrong-sized key\n * throws rather than returning plaintext: the alternative — a helper that\n * quietly passes the value through when it cannot encrypt — would store the\n * secret in the clear on exactly the deployments that forgot to configure a\n * key, and nothing would report it.\n */\n\nconst KEY_BYTES = 32;\nconst IV_BYTES = 12;\n\nclass AesGcmKeyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AesGcmKeyError';\n }\n}\n\nasync function importKey(encodedKey: string | undefined | null): Promise<CryptoKey> {\n if (typeof encodedKey !== 'string' || encodedKey.trim() === '') {\n throw new AesGcmKeyError('Encryption key is not configured; the secret cannot be stored');\n }\n let raw: Uint8Array;\n try {\n raw = base64ToBytes(encodedKey.trim());\n } catch {\n throw new AesGcmKeyError('Encryption key is not valid base64');\n }\n if (raw.byteLength !== KEY_BYTES) {\n throw new AesGcmKeyError(`Encryption key must decode to ${KEY_BYTES} bytes, got ${raw.byteLength}`);\n }\n try {\n return await crypto.subtle.importKey('raw', raw as BufferSource, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);\n } catch {\n throw new AesGcmKeyError('Encryption key could not be imported as an AES-GCM key');\n }\n}\n\ninterface EncryptedEnvelope {\n ciphertext: string;\n iv: string;\n}\n\n/**\n * Encrypt one secret. The IV is random per call and returned alongside the\n * ciphertext so the caller can store the pair — reusing an IV under one key\n * destroys GCM's security guarantees outright, which is why it is not\n * internal to the ciphertext.\n */\nasync function encryptSecret(plaintext: string, encodedKey: string | undefined | null): Promise<EncryptedEnvelope> {\n const key = await importKey(encodedKey);\n const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(IV_BYTES));\n const sealed: ArrayBuffer = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n key,\n new TextEncoder().encode(plaintext),\n );\n return { ciphertext: bytesToBase64(new Uint8Array(sealed)), iv: bytesToBase64(iv) };\n}\n\n/**\n * Decrypt one envelope.\n *\n * Throws on a wrong key or tampered ciphertext rather than returning garbage:\n * a fail-closed decrypt turns a config mistake into a loud, immediate error\n * instead of corrupt data flowing into a downstream request.\n */\nasync function decryptSecret(envelope: EncryptedEnvelope, encodedKey: string | undefined | null): Promise<string> {\n const key = await importKey(encodedKey);\n const iv: Uint8Array = base64ToBytes(envelope.iv);\n const ciphertext: Uint8Array = base64ToBytes(envelope.ciphertext);\n const opened: ArrayBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv as BufferSource }, key, ciphertext as BufferSource);\n return new TextDecoder().decode(opened);\n}\n\n/** Round-trip helper for tests and one-shot migrations. */\nasync function roundTripSecret(plaintext: string, encodedKey: string): Promise<string> {\n const envelope: EncryptedEnvelope = await encryptSecret(plaintext, encodedKey);\n return decryptSecret(envelope, encodedKey);\n}\n\nexport { AesGcmKeyError, decryptSecret, encryptSecret, roundTripSecret, IV_BYTES, KEY_BYTES };\nexport type { EncryptedEnvelope };\n"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAGlF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC","sourcesContent":["export { BaseDAO, assertSqlIdentifier } from './BaseDAO';\nexport { isD1ErrorRetryable, RETRYABLE_PATTERNS, NON_RETRYABLE_PATTERNS } from './D1ErrorClassifier';\nexport { isMissingSchemaError, MISSING_SCHEMA_PATTERN } from './isMissingSchemaError';\nexport { executableStatements, isExecutable, splitSql } from './splitSql';\nexport { backoffMs, D1_RETRY_DEFAULTS, executeWithRetry, sleep } from './D1Retry';\nexport type { D1RetryOptions } from './D1Retry';\nexport type { D1PreparedStatement, D1Queryable, D1Result } from './D1Types';\nexport { AesGcmKeyError, decryptSecret, encryptSecret, roundTripSecret, IV_BYTES, KEY_BYTES } from './aes-gcm';\nexport type { EncryptedEnvelope } from './aes-gcm';\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"isMissingSchemaError.js","sourceRoot":"","sources":["../src/isMissingSchemaError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,sBAAsB,GAAG,qCAAqC,CAAC;AAErE,SAAS,oBAAoB,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9E,6EAA6E;IAC7E,uCAAuC;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"isMissingSchemaError.js","sourceRoot":"","sources":["../src/isMissingSchemaError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,sBAAsB,GAAG,qCAAqC,CAAC;AAErE,SAAS,oBAAoB,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9E,6EAA6E;IAC7E,uCAAuC;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,CAAC","sourcesContent":["/**\n * Distinguishes \"the table or column isn't there\" from every other way a SQL\n * statement can fail.\n *\n * Provenance: `shared/src/utils/SqlSchemaError.ts` in Durable-DAV (re-exported\n * from `backend-data`), Byte-comparable copies in Edge-Git, Edge-Sonic and\n * ChordDHT-Tracker. Durable-DAV's own note is the canonical home decision: both\n * SQL surfaces the family uses (D1 and DO SQLite) report a missing relation the\n * same way, so the predicate lives below the layers that degrade on it —\n * in this kit, that layer is `d1`.\n *\n * The rule it exists to enforce is fail-closed: a caller may degrade to `[]` or\n * a fallback query **only** on `true` here, and must rethrow everything else.\n * Catching broadly is how \"this resource has no dead properties\" comes to mean\n * \"the database is corrupt\" — a silent data-loss report, which is worse than an\n * error.\n */\nconst MISSING_SCHEMA_PATTERN = /no\\s+such\\s+(?:table|column|index)/i;\n\nfunction isMissingSchemaError(error: unknown): boolean {\n if (error instanceof Error) return MISSING_SCHEMA_PATTERN.test(error.message);\n // A thrown string is legal; anything else (a number, an object, `undefined`)\n // carries no message to match against.\n return typeof error === 'string' && MISSING_SCHEMA_PATTERN.test(error);\n}\n\nexport { isMissingSchemaError, MISSING_SCHEMA_PATTERN };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"splitSql.js","sourceRoot":"","sources":["../src/splitSql.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"splitSql.js","sourceRoot":"","sources":["../src/splitSql.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal SQL statement splitter for SQLite migration files.\n *\n * Provenance: `test/integration/helpers/migrations.ts`, byte-near-identical in\n * six consumer repos, ported here verbatim because its comment-level history is\n * the specification for the edge cases (`''` escapes, block comments, trigger\n * bodies with semicolons). The kit keeps one copy: `@rexezuge/tooling`'s\n * integration helper and `@rexezuge/identity`'s migration applier both delegate\n * to this module, so the splitter cannot drift between them.\n *\n * Handles: single/double/backtick quoted strings (incl. `''` escapes), `--`\n * line comments, block comments. Semicolons inside strings or comments do not\n * split. Statements come back trimmed, without the terminating semicolon, and\n * with their leading comments attached — use `executableStatements` to drop\n * comment-only runs before sending anything to D1.\n *\n * Trigger-aware: `CREATE TRIGGER ... BEGIN ...; ... END;` bodies contain\n * semicolons that must not split. The splitter tracks the outermost BEGIN/END\n * depth after a CREATE TRIGGER header and only splits on the terminating\n * semicolon after the final END.\n */\nfunction splitSql(sql: string): string[] {\n const statements: string[] = [];\n let current = '';\n let inString = false;\n let stringChar = '';\n let inLineComment = false;\n let inBlockComment = false;\n let inTrigger = false;\n let triggerDepth = 0;\n let word = '';\n const recentKeywords: string[] = [];\n\n const flushWord = (): void => {\n if (word.length === 0) return;\n const upper = word.toUpperCase();\n word = '';\n recentKeywords.push(upper);\n if (recentKeywords.length > 6) recentKeywords.shift();\n // `CREATE [TEMP|TEMPORARY] TRIGGER` opens a body; a table merely named\n // \"trigger\" (`CREATE TABLE trigger`) must not. The word before TRIGGER\n // disambiguates.\n if (upper === 'TRIGGER' && ['CREATE', 'TEMP', 'TEMPORARY'].includes(recentKeywords.at(-2) ?? '')) {\n inTrigger = true;\n triggerDepth = 0;\n } else if (inTrigger && upper === 'BEGIN') {\n triggerDepth += 1;\n } else if (inTrigger && upper === 'END') {\n triggerDepth -= 1;\n if (triggerDepth <= 0) {\n inTrigger = false;\n triggerDepth = 0;\n }\n }\n };\n\n let index = 0;\n while (index < sql.length) {\n const char = sql[index] as string;\n const next = sql[index + 1] ?? '';\n\n if (inLineComment) {\n current += char;\n if (char === '\\n') inLineComment = false;\n index += 1;\n continue;\n }\n if (inBlockComment) {\n current += char;\n if (char === '*' && next === '/') {\n current += next;\n index += 2;\n inBlockComment = false;\n continue;\n }\n index += 1;\n continue;\n }\n if (inString) {\n current += char;\n if (char === stringChar) {\n // SQL escapes a quote by doubling it ('it''s'); do not end the string.\n if (sql[index + 1] === stringChar) {\n current += sql[index + 1];\n index += 2;\n continue;\n }\n if (sql[index - 1] !== '\\\\') inString = false;\n }\n index += 1;\n continue;\n }\n // Buffer word characters for CREATE TRIGGER / BEGIN / END tracking.\n // Any other character ends the current word.\n if (/[\\w$]/.test(char)) {\n word += char;\n current += char;\n index += 1;\n continue;\n }\n flushWord();\n if (char === '-' && next === '-') {\n inLineComment = true;\n current += char;\n index += 1;\n continue;\n }\n if (char === '/' && next === '*') {\n inBlockComment = true;\n current += char;\n index += 1;\n continue;\n }\n if (char === \"'\" || char === '\"' || char === '`') {\n inString = true;\n stringChar = char;\n current += char;\n index += 1;\n continue;\n }\n if (char === ';') {\n // Semicolons inside a trigger body do not terminate the statement.\n if (inTrigger) {\n current += char;\n index += 1;\n continue;\n }\n const trimmed = current.trim();\n if (trimmed.length > 0) statements.push(trimmed);\n current = '';\n index += 1;\n continue;\n }\n current += char;\n index += 1;\n }\n const trimmed = current.trim();\n if (trimmed.length > 0) statements.push(trimmed);\n return statements;\n}\n\n/**\n * Whether a statement is executable SQL rather than a comment.\n *\n * Checked after the split, because the splitter has to keep comments attached to\n * the statement they sit above — dropping them there would change what an error\n * message quotes.\n */\nfunction isExecutable(statement: string): boolean {\n const withoutComments = statement\n .replaceAll(/--[^\\n]*/g, '')\n .replaceAll(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n return withoutComments.trim().length > 0;\n}\n\n/**\n * The executable statements of one migration file, in order: split first, then\n * filtered. A splitter that dropped comments itself would also drop the ones a\n * migration keeps beside the statement they describe.\n */\nfunction executableStatements(sql: string): string[] {\n return splitSql(sql).filter(isExecutable);\n}\n\nexport { executableStatements, isExecutable, splitSql };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rexezuge/d1",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,8 +25,8 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@rexezuge/errors": "1.0.2",
29
- "@rexezuge/shared": "1.0.2"
28
+ "@rexezuge/errors": "1.0.4",
29
+ "@rexezuge/shared": "1.0.4"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "26.6.4",