@rexezuge/d1 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +76 -0
  2. package/package.json +4 -3
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @rexezuge/d1
2
+
3
+ D1 retry engine, error classifier, `BaseDAO`, SQL splitter, and AES-GCM envelope.
4
+
5
+ Depends on `@rexezuge/errors` (failures surface as `DatabaseError` with the classifier's verdict in `retryable`) and `@rexezuge/shared` (base64 for the crypto envelope).
6
+
7
+ ```bash
8
+ pnpm add @rexezuge/d1
9
+ ```
10
+
11
+ ---
12
+
13
+ ## 1. Retry engine
14
+
15
+ One engine, one schedule — the seven consumer repos each spelled this differently, and divergent schedules were the drift:
16
+
17
+ ```ts
18
+ import { D1_RETRY_DEFAULTS, executeWithRetry } from '@rexezuge/d1';
19
+
20
+ const row = await executeWithRetry((db) => db.prepare('SELECT * FROM users WHERE id = ?').bind(id).first(), {
21
+ maxRetries: D1_RETRY_DEFAULTS.maxRetries, // 3, exponential backoff from 100ms via backoffMs()
22
+ });
23
+ ```
24
+
25
+ Both D1 failure shapes are handled: a resolved `success: false` result and a thrown transport error are classified from the message the same way (`isD1ErrorRetryable` / `RETRYABLE_PATTERNS` / `NON_RETRYABLE_PATTERNS`). `BaseDAO` is the intended caller; call it directly only for statements outside a DAO.
26
+
27
+ ## 2. `BaseDAO` and types
28
+
29
+ ```ts
30
+ import { BaseDAO, assertSqlIdentifier } from '@rexezuge/d1';
31
+
32
+ class UserDAO extends BaseDAO {
33
+ public getById(id: string) {
34
+ return this.first('SELECT * FROM users WHERE id = ?', [id]);
35
+ }
36
+ }
37
+ ```
38
+
39
+ `BaseDAO` wraps every statement in the retry engine. `D1Queryable` / `D1PreparedStatement` / `D1Result` are structural types, so fakes, Miniflare, and real D1 bindings all satisfy them. `assertSqlIdentifier` guards the one thing bindings cannot parameterize — table/column names.
40
+
41
+ ## 3. SQL splitting and schema errors
42
+
43
+ ```ts
44
+ import { executableStatements, isMissingSchemaError, splitSql } from '@rexezuge/d1';
45
+
46
+ const statements = executableStatements(migrationSql); // trigger-aware; comments/stripped, one batch per file
47
+ try {
48
+ await dao.get(normalized);
49
+ } catch (error: unknown) {
50
+ if (isMissingSchemaError(error)) return null; // migration hasn't run — the only safe fall-through
51
+ throw error; // every other failure is an outage, never a silent null
52
+ }
53
+ ```
54
+
55
+ `splitSql` / `isExecutable` power both migrations and `@rexezuge/tooling`'s integration-migration test helper, so a kit migration and a test suite can never disagree about where one statement ends.
56
+
57
+ ## 4. AES-GCM secrets
58
+
59
+ ```ts
60
+ import { decryptSecret, encryptSecret } from '@rexezuge/d1';
61
+
62
+ const { ciphertext, iv } = await encryptSecret(clientSecret, env.BACKUP_ENCRYPTION_KEY); // store the pair
63
+ const cleartext = await decryptSecret(ciphertext, iv, env.BACKUP_ENCRYPTION_KEY);
64
+ ```
65
+
66
+ Key material is base64 of exactly 32 bytes; a missing or wrong-sized key throws `AesGcmKeyError` rather than storing the secret in the clear. The IV is random per call — reusing one under a key destroys GCM's guarantees, which is why it is returned alongside the ciphertext instead of being internal.
67
+
68
+ ---
69
+
70
+ ## Verifying this package
71
+
72
+ ```bash
73
+ pnpm --filter @rexezuge/d1 exec tsc -p tsconfig.json
74
+ pnpm --filter @rexezuge/d1 typecheck
75
+ pnpm --filter @rexezuge/d1 test
76
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rexezuge/d1",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,14 +25,15 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@rexezuge/errors": "1.0.1",
29
- "@rexezuge/shared": "1.0.1"
28
+ "@rexezuge/errors": "1.0.2",
29
+ "@rexezuge/shared": "1.0.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "26.6.4",
33
33
  "typescript": "6.0.3",
34
34
  "vitest": "4.1.11"
35
35
  },
36
+ "description": "D1 retry engine, error classifier, BaseDAO, SQL splitter, and AES-GCM envelope.",
36
37
  "scripts": {
37
38
  "build": "tsc -p tsconfig.json",
38
39
  "typecheck": "tsc -p tsconfig.test.json --noEmit",