easy-ping 0.8.1 → 0.9.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.
Files changed (38) hide show
  1. package/README.md +17 -11
  2. package/dist/adapter-C3uFRpEN.d.cts +10 -0
  3. package/dist/adapter-DoCRPi9I.d.ts +10 -0
  4. package/dist/adapters/mysql.cjs +7 -4
  5. package/dist/adapters/mysql.cjs.map +1 -1
  6. package/dist/adapters/mysql.d.cts +3 -2
  7. package/dist/adapters/mysql.d.ts +3 -2
  8. package/dist/adapters/mysql.js +1 -1
  9. package/dist/adapters/postgres.js +3 -3
  10. package/dist/adapters/sqlite.cjs +8 -5
  11. package/dist/adapters/sqlite.cjs.map +1 -1
  12. package/dist/adapters/sqlite.d.cts +3 -2
  13. package/dist/adapters/sqlite.d.ts +3 -2
  14. package/dist/adapters/sqlite.js +1 -1
  15. package/dist/chunk-EROWLUL4.js +411 -0
  16. package/dist/chunk-EROWLUL4.js.map +1 -0
  17. package/dist/{chunk-HY47TQMY.js → chunk-HSB5T3LD.js} +11 -8
  18. package/dist/chunk-HSB5T3LD.js.map +1 -0
  19. package/dist/{chunk-M4R77BJD.js → chunk-UM2L26PV.js} +10 -7
  20. package/dist/chunk-UM2L26PV.js.map +1 -0
  21. package/dist/dialect-CvBJSydR.d.cts +10 -0
  22. package/dist/dialect-CvBJSydR.d.ts +10 -0
  23. package/dist/plugins/digests.cjs +19 -12
  24. package/dist/plugins/digests.cjs.map +1 -1
  25. package/dist/plugins/digests.js +5 -12
  26. package/dist/plugins/digests.js.map +1 -1
  27. package/dist/schema.cjs +334 -123
  28. package/dist/schema.cjs.map +1 -1
  29. package/dist/schema.d.cts +26 -1
  30. package/dist/schema.d.ts +26 -1
  31. package/dist/schema.js +3 -3
  32. package/package.json +1 -1
  33. package/dist/adapter-B7R4rXtJ.d.cts +0 -17
  34. package/dist/adapter-B7R4rXtJ.d.ts +0 -17
  35. package/dist/chunk-HY47TQMY.js.map +0 -1
  36. package/dist/chunk-M4R77BJD.js.map +0 -1
  37. package/dist/chunk-XIDVLYNQ.js +0 -209
  38. package/dist/chunk-XIDVLYNQ.js.map +0 -1
package/README.md CHANGED
@@ -55,12 +55,13 @@ of the others — they are good tools solving a bigger problem.
55
55
  **Scale target:** thousands to low-millions of notifications per month. Not Slack-scale fan-out.
56
56
  Every "no queue required" decision below follows from that.
57
57
 
58
- > **Status: published, pre-1.0.** The core pipeline, all three adapters, the React client and the
58
+ > **Status: published, pre-1.0.** The core pipeline, all five adapters, the React client and the
59
59
  > preferences, digests, push and telegram plugins are covered by tests against real databases. Web push is
60
60
  > verified end to end against Mozilla's production push service and cross-checked against
61
- > `http_ece`. The bell updates over a server-sent event stream with polling as the fallback;
62
- > batching is not built, and the Resend provider has only been exercised against a stub. Minor
63
- > versions may still move APIs before 1.0.
61
+ > `http_ece`; email is verified against Resend's live API, including idempotent retries and a full
62
+ > send-to-delivered pass. The bell updates over a server-sent event stream with polling as the
63
+ > fallback; batching is not built yet. Minor
64
+ > versions may still move APIs before 1.0; [what is stable and how changes are announced](https://easy-ping.teklumoges.dev/docs/stability).
64
65
 
65
66
  ---
66
67
 
@@ -311,8 +312,9 @@ return Response.json(data, { headers: notify.inboxHeaders(userId) });
311
312
  const fetchWithBell = client.instrument(fetch);
312
313
 
313
314
  // Push relay: if you already run the push plugin, the service worker wakes the bell too.
315
+ // easy-ping/sw is an ES module: bundle sw.js (esbuild/Vite) before serving it.
314
316
  import { handlePush } from "easy-ping/sw";
315
- self.addEventListener("push", (event) => handlePush(event));
317
+ self.addEventListener("push", (event) => event.waitUntil(handlePush(event)));
316
318
  ```
317
319
 
318
320
  **React Native (beta, under testing).** The same client ships as `easy-ping/react-native`: `useNotifications` wired to `AppState`, a live stream when you pass `fetch` from `expo/fetch`, polling otherwise, and `registerMobilePushDevice` for native push. `examples/expo` is a complete screen.
@@ -382,7 +384,7 @@ single replica.
382
384
  | `email` | ✅ Resend provider; the interface is open for others |
383
385
  | `push` | ✅ push plugin + `webPush()` — VAPID and aes128gcm on Web Crypto, so it runs on edge too |
384
386
  | `telegram` | ✅ telegram plugin + `telegramBot()` — one-tap linking through the bot, webhook or long-poll |
385
- | `mobilePush` | 🧪 **beta** — mobile-push plugin + `expoPush()`, iOS and Android through Expo's push service; under testing on real devices |
387
+ | `mobilePush` | 🧪 **beta** — mobile-push plugin + `expoPush()`, iOS and Android through Expo's push service; in testing |
386
388
  | `sms` | ⬜ not implemented |
387
389
  | `slack` | ⬜ not implemented |
388
390
 
@@ -412,6 +414,8 @@ A later version may add a column. How you pick it up depends on how you created
412
414
  | --- | --- |
413
415
  | `createSchema()` + drizzle-kit | `drizzle-kit` diffs it for you — nothing to do here |
414
416
  | `renderPostgresDdl()` | `planPostgresMigration()` — see below |
417
+ | `createMysqlTables()` | `planMysqlMigration(query, schema)` from `easy-ping/schema` |
418
+ | `createSqliteTables()` | `planSqliteMigration(query, schema)` from `easy-ping/schema`, in a transaction |
415
419
  | `createMongoIndexes()` | rerun it; `createIndex` is idempotent and additive |
416
420
 
417
421
  **`renderPostgresDdl` cannot upgrade you.** It emits `CREATE TABLE IF NOT EXISTS`, which is correct exactly once and a silent no-op afterwards. Rerunning it on an existing database does nothing at all.
@@ -485,7 +489,7 @@ Defaults to the last 24 hours, capped at 1000 rows. Wire it to an admin page or
485
489
  | ✅ MySQL 8 / MariaDB | `SKIP LOCKED` with a transaction, lock-free `UPDATE` without |
486
490
  | ✅ SQLite | `node:sqlite` or better-sqlite3; one writer, so the claim is one `UPDATE` |
487
491
  | ✅ Delivery runner, all four modes, retry + backoff | |
488
- | ✅ Resend provider | |
492
+ | ✅ Resend provider | verified against the live API: delivery, idempotent retries, end-to-end pipeline |
489
493
  | ✅ Route handler, session scoping, cron | |
490
494
  | ✅ React client, optimistic updates, one event stream per browser with polling fallback | `GET /events`, `navigator.locks` leader, `BroadcastChannel` mirror |
491
495
  | ✅ wake-ups: worker woken by `send()`, LISTEN/NOTIFY and change-stream signals, request-driven sweep | `signals`, `delivery.sweepOnRequest` |
@@ -494,9 +498,9 @@ Defaults to the last 24 hours, capped at 1000 rows. Wire it to an admin page or
494
498
  | ✅ push plugin + web-push provider | VAPID + RFC 8291, no node:crypto |
495
499
  | ✅ push verified against a live push service | Mozilla autopush, plus a cross-check against `http_ece` |
496
500
  | ✅ telegram plugin + bot provider | one-tap linking, webhook or long-poll, blocked chats pruned |
497
- | 🧪 mobile push plugin + Expo provider, React Native client entry (beta) | covered by tests, not yet proven on real devices at scale; APIs may change |
501
+ | 🧪 mobile push plugin + Expo provider, React Native client entry (beta) | in testing; APIs may change before stable |
498
502
  | ✅ scoped plugin storage, so plugins own their tables | |
499
- | ✅ additive schema migrations for the raw-SQL path | `planPostgresMigration()`; MySQL and SQLite are bootstrap-only for now |
503
+ | ✅ additive schema migrations on every SQL database | `planPostgresMigration()`, `planMysqlMigration()`, `planSqliteMigration()`; MongoDB reruns its index setup |
500
504
  | ✅ failed deliveries reachable from the instance | `notify.getFailedDeliveries()` |
501
505
  | ⬜ batching | |
502
506
  | ⬜ Prisma adapter, Vue / Svelte bindings | Kysely already works through `postgresAdapter` |
@@ -544,12 +548,14 @@ The push crypto is checked two ways. `web-push-reference.test.ts` decrypts our o
544
548
  EASY_PING_LIVE_PUSH=1 pnpm --filter easy-ping test web-push-live
545
549
  ```
546
550
 
547
- The Resend provider is checked against Resend's real API too: the rejection paths need no credentials — a bogus key coming back as a structured 401 rather than a 400 is what proves the request shape is right. The delivery leg needs your own key:
551
+ The Resend provider is checked against Resend's real API too. The rejection paths need no credentials: a bogus key coming back as a structured 401 rather than a 400 is what proves the request shape is right. With a key, three more cases run: a real send returns a message id, the same delivery sent twice is one email (the idempotency key a retry relies on), and a notification goes all the way through `send()` and the cron sweep to a delivery marked `sent`:
548
552
 
549
553
  ```bash
550
- RESEND_API_KEY=re_... RESEND_FROM="Acme <hi@acme.dev>" pnpm --filter easy-ping test resend-live
554
+ RESEND_API_KEY=re_... RESEND_FROM="Acme <hi@acme.dev>" RESEND_TO=you@acme.dev pnpm --filter easy-ping test resend-live
551
555
  ```
552
556
 
557
+ Without a verified domain, `RESEND_FROM="onboarding@resend.dev"` works but only delivers to your own Resend account's address.
558
+
553
559
  ## Releasing
554
560
 
555
561
  Every release is automatic. Open a PR with a changeset:
@@ -0,0 +1,10 @@
1
+ import { S as SqlExec } from './dialect-CvBJSydR.cjs';
2
+
3
+ type SqlAdapterOptions = {
4
+ /** Table-name prefix. Must match the instance's `tablePrefix`. */
5
+ prefix?: string;
6
+ /** Makes createNotifications atomic and, on MySQL, lets a claim use FOR UPDATE SKIP LOCKED. */
7
+ transaction?: <T>(fn: (exec: SqlExec) => Promise<T>) => Promise<T>;
8
+ };
9
+
10
+ export type { SqlAdapterOptions as S };
@@ -0,0 +1,10 @@
1
+ import { S as SqlExec } from './dialect-CvBJSydR.js';
2
+
3
+ type SqlAdapterOptions = {
4
+ /** Table-name prefix. Must match the instance's `tablePrefix`. */
5
+ prefix?: string;
6
+ /** Makes createNotifications atomic and, on MySQL, lets a claim use FOR UPDATE SKIP LOCKED. */
7
+ transaction?: <T>(fn: (exec: SqlExec) => Promise<T>) => Promise<T>;
8
+ };
9
+
10
+ export type { SqlAdapterOptions as S };
@@ -535,13 +535,16 @@ var defaultClause = (spec) => {
535
535
  if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
536
536
  return ` DEFAULT ${spec.default}`;
537
537
  };
538
+ function mysqlColumnDefinition(field, table, spec) {
539
+ const nullability = spec.required ? " NOT NULL" : "";
540
+ return `${quote2(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;
541
+ }
538
542
  function renderMysqlDdl(schema, prefix = "") {
539
543
  const statements = [];
540
544
  for (const table of Object.values(schema)) {
541
- const lines = Object.entries(table.fields).map(([field, spec]) => {
542
- const nullability = spec.required ? " NOT NULL" : "";
543
- return ` ${quote2(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;
544
- });
545
+ const lines = Object.entries(table.fields).map(
546
+ ([field, spec]) => ` ${mysqlColumnDefinition(field, table, spec)}`
547
+ );
545
548
  if (table.primaryKey?.length) {
546
549
  lines.push(
547
550
  ` PRIMARY KEY (${table.primaryKey.map((f) => quote2(toSnakeCase(f))).join(", ")})`
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/base64url.ts","../../src/schema/declaration.ts","../../src/core/store.ts","../../src/adapters/sql/statement.ts","../../src/adapters/sql/adapter.ts","../../src/adapters/sql/dialect.ts","../../src/adapters/mysql/adapter.ts","../../src/schema/render-mysql.ts","../../src/adapters/mysql/helpers.ts"],"names":["quote"],"mappings":"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","file":"mysql.cjs","sourcesContent":["// Web APIs, not Buffer: this runs on Workers and Vercel Edge.\n\nexport function encodeBase64Url(input: string | Uint8Array): string {\n const bytes = typeof input === \"string\" ? new TextEncoder().encode(input) : input;\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n// Uint8Array<ArrayBuffer>: bare Uint8Array widens and is not a BufferSource.\nexport function decodeBase64UrlBytes(value: string): Uint8Array<ArrayBuffer> | null {\n try {\n const binary = atob(value.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n } catch {\n return null;\n }\n}\n\nexport function decodeBase64Url(value: string): string | null {\n const bytes = decodeBase64UrlBytes(value);\n return bytes ? new TextDecoder().decode(bytes) : null;\n}\n","import type { SchemaDeclaration } from \"../core/plugin\";\n\nexport const toSnakeCase = (value: string) => value.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n\n/** Core tables in the plugin format. A drift test asserts it agrees with the Drizzle schema. */\nexport const coreSchema = {\n notification: {\n tableName: \"notification\",\n fields: {\n id: { type: \"string\", required: true },\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n payload: { type: \"json\", required: true },\n actorId: { type: \"string\" },\n groupKey: { type: \"string\" },\n dedupeKey: { type: \"string\" },\n seenAt: { type: \"date\" },\n readAt: { type: \"date\" },\n archivedAt: { type: \"date\" },\n createdAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"userId\", \"createdAt\"], name: \"notification_feed_idx\" },\n { on: [\"userId\", \"groupKey\"], name: \"notification_group_idx\" },\n { on: [\"userId\", \"dedupeKey\"], unique: true, name: \"notification_dedupe_idx\" },\n ],\n },\n\n notificationDelivery: {\n tableName: \"notification_delivery\",\n fields: {\n id: { type: \"string\", required: true },\n notificationId: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n status: { type: \"string\", required: true, default: \"pending\" },\n attempts: { type: \"number\", required: true, default: 0 },\n maxAttempts: { type: \"number\", required: true, default: 5 },\n notBefore: { type: \"date\", required: true, defaultNow: true },\n claimedAt: { type: \"date\" },\n claimedBy: { type: \"string\" },\n lastError: { type: \"string\" },\n updatedAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"status\", \"notBefore\"], name: \"delivery_claim_idx\" },\n { on: [\"notificationId\"], name: \"delivery_notification_idx\" },\n ],\n },\n\n notificationPreference: {\n tableName: \"notification_preference\",\n fields: {\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n enabled: { type: \"boolean\", required: true, default: true },\n frequency: { type: \"string\", required: true, default: \"instant\" },\n updatedAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"userId\", \"type\", \"channel\"],\n },\n} satisfies SchemaDeclaration;\n","import { toSnakeCase } from \"../schema/declaration\";\nimport { ConfigError } from \"./errors\";\nimport type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"./plugin\";\n\nexport type Scalar = string | number | boolean | Date | null;\n\n/** Operators are tagged objects so a bare value is always equality; json columns cannot be filtered. */\nexport type WhereCondition =\n | Scalar\n | { in: readonly (string | number)[] }\n | { lt: Date | number }\n | { lte: Date | number }\n | { gt: Date | number }\n | { gte: Date | number }\n | { not: Scalar };\n\nexport type WhereClause = Record<string, WhereCondition>;\n\nexport type QueryOptions = {\n limit?: number;\n orderBy?: { field: string; direction: \"asc\" | \"desc\" };\n};\n\nexport type UpsertOptions = { onConflict: readonly string[] };\n\n/** Table access for plugins, scoped to the tables the plugin declared. */\nexport type PluginStore = {\n find<T = Record<string, unknown>>(\n table: string,\n where?: WhereClause,\n options?: QueryOptions,\n ): Promise<T[]>;\n insert(table: string, rows: readonly Record<string, unknown>[]): Promise<number>;\n upsert(\n table: string,\n rows: readonly Record<string, unknown>[],\n options: UpsertOptions,\n ): Promise<number>;\n update(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n remove(table: string, where: WhereClause): Promise<number>;\n};\n\nexport type TableStorage = {\n readonly naming?: \"snake_case\" | \"preserve\";\n readonly serializesJson?: boolean;\n\n queryTable(\n table: string,\n where: WhereClause,\n options: QueryOptions,\n ): Promise<Record<string, unknown>[]>;\n insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ): Promise<number>;\n updateRows(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n deleteRows(table: string, where: WhereClause): Promise<number>;\n};\n\nconst OPERATORS = [\"in\", \"lt\", \"lte\", \"gt\", \"gte\", \"not\"] as const;\n\nexport const isOperator = (value: unknown): boolean =>\n typeof value === \"object\" &&\n value !== null &&\n !(value instanceof Date) &&\n OPERATORS.some((op) => Object.hasOwn(value, op));\n\nconst isScalar = (value: unknown): value is Scalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\n/** A forwarded JSON body can carry `{ $ne: … }`; refuse anything that is not a scalar or a known operator. */\nfunction assertCondition(pluginId: string, field: string, condition: unknown): void {\n if (isScalar(condition)) return;\n\n if (isOperator(condition)) {\n const operator = condition as Record<string, unknown>;\n const keys = Object.keys(operator);\n const [op] = keys;\n if (keys.length !== 1 || op === undefined) {\n throw new ConfigError(`plugin \"${pluginId}\" gave field \"${field}\" more than one operator.`);\n }\n const payload = operator[op];\n const valid =\n op === \"in\"\n ? Array.isArray(payload) &&\n payload.every((item) => typeof item === \"string\" || typeof item === \"number\")\n : isScalar(payload);\n if (valid) return;\n }\n\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for field \"${field}\"; ` +\n \"validate request bodies before querying with them.\",\n );\n}\n\nfunction assertValue(pluginId: string, field: string, spec: FieldDeclaration, value: unknown) {\n if (value === undefined || isScalar(value)) return;\n if (spec.type === \"json\" && typeof value === \"object\") return;\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for ${spec.type} field \"${field}\".`,\n );\n}\n\n/** Every table and column is checked against the plugin's own declaration. */\nexport function createPluginStore(\n pluginId: string,\n schema: SchemaDeclaration | undefined,\n storage: TableStorage,\n prefix: string,\n): PluginStore {\n const byTableName = new Map<string, TableDeclaration>();\n for (const declaration of Object.values(schema ?? {})) {\n byTableName.set(declaration.tableName, declaration);\n }\n\n function resolve(table: string): TableDeclaration {\n const declaration = byTableName.get(table);\n if (!declaration) {\n throw new ConfigError(\n `plugin \"${pluginId}\" accessed table \"${table}\", which it does not declare in schema().`,\n );\n }\n return declaration;\n }\n\n function checkFields(table: TableDeclaration, fields: Iterable<string>, context: string) {\n for (const field of fields) {\n // hasOwn, not a lookup: `constructor` and `__proto__` are not columns.\n const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : undefined;\n if (!spec) {\n throw new ConfigError(\n `plugin \"${pluginId}\" used unknown field \"${field}\" on \"${table.tableName}\" (${context}).`,\n );\n }\n if (context === \"where\" && spec.type === \"json\") {\n throw new ConfigError(\n `plugin \"${pluginId}\" cannot filter on json field \"${field}\"; ` +\n \"a bare object value is indistinguishable from an operator.\",\n );\n }\n }\n }\n\n function checkWhere(table: TableDeclaration, where: WhereClause) {\n checkFields(table, Object.keys(where), \"where\");\n for (const [field, condition] of Object.entries(where)) {\n assertCondition(pluginId, field, condition);\n }\n }\n\n function checkRow(table: TableDeclaration, row: Record<string, unknown>, context: string) {\n checkFields(table, Object.keys(row), context);\n for (const [field, value] of Object.entries(row)) {\n const spec = table.fields[field];\n if (spec) assertValue(pluginId, field, spec, value);\n }\n }\n\n const qualified = (table: TableDeclaration) => prefix + table.tableName;\n\n // A document store keeps the declared names; a SQL adapter wants columns.\n const column = (field: string) => (storage.naming === \"preserve\" ? field : toSnakeCase(field));\n\n // json must reach postgres-js as text.\n const serialize = (table: TableDeclaration, row: Record<string, unknown>) => {\n if (storage.serializesJson === false) return row;\n\n const out: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(row)) {\n out[field] =\n table.fields[field]?.type === \"json\" && value !== null && value !== undefined\n ? JSON.stringify(value)\n : value;\n }\n return out;\n };\n\n return {\n async find(table, where = {}, options = {}) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n if (options.orderBy) checkFields(declaration, [options.orderBy.field], \"orderBy\");\n\n const rows = await storage.queryTable(qualified(declaration), where, options);\n\n // SELECT * returns snake_case; plugins read camelCase.\n return rows.map((row) => {\n const mapped: Record<string, unknown> = {};\n for (const field of Object.keys(declaration.fields)) {\n const value = row[column(field)];\n const type = declaration.fields[field]?.type;\n // Drivers differ on jsonb (parsed or text) and timestamptz (Date or text).\n if (type === \"json\" && typeof value === \"string\") mapped[field] = JSON.parse(value);\n else if (type === \"date\" && typeof value === \"string\") mapped[field] = new Date(value);\n // MySQL and SQLite have no boolean column; they hand back 0 and 1.\n else if (type === \"boolean\" && typeof value === \"number\") mapped[field] = value !== 0;\n else mapped[field] = value;\n }\n return mapped;\n }) as never;\n },\n\n async insert(table, rows) {\n const declaration = resolve(table);\n for (const row of rows) checkRow(declaration, row, \"insert\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n );\n },\n\n async upsert(table, rows, options) {\n const declaration = resolve(table);\n for (const row of rows) checkRow(declaration, row, \"upsert\");\n checkFields(declaration, options.onConflict, \"onConflict\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n options.onConflict,\n );\n },\n\n async update(table, where, set) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n checkRow(declaration, set, \"update\");\n return storage.updateRows(qualified(declaration), where, serialize(declaration, set));\n },\n\n async remove(table, where) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n return storage.deleteRows(qualified(declaration), where);\n },\n };\n}\n","export type Placeholder = \"dollar\" | \"question\";\n\nexport type StatementOptions = {\n /** `$1, $2` for Postgres; `?` for MySQL and SQLite. */\n placeholder?: Placeholder;\n /** What the driver is handed for a JS value. Dates become ISO strings by default. */\n bind?: (value: unknown) => unknown;\n};\n\nconst defaultBind = (value: unknown) => (value instanceof Date ? value.toISOString() : value);\n\n/** SQL text plus bound values. Values are never interpolated; identifiers arrive pre-validated. */\nexport class Statement {\n #text = \"\";\n readonly params: unknown[] = [];\n readonly #placeholder: Placeholder;\n readonly #bind: (value: unknown) => unknown;\n\n constructor(options: StatementOptions = {}) {\n this.#placeholder = options.placeholder ?? \"dollar\";\n this.#bind = options.bind ?? defaultBind;\n }\n\n /** Raw SQL. Never user input; use `value`. */\n raw(fragment: string): this {\n this.#text += fragment;\n return this;\n }\n\n value(value: unknown): this {\n this.params.push(this.#bind(value));\n this.#text += this.#placeholder === \"dollar\" ? `$${this.params.length}` : \"?\";\n return this;\n }\n\n list(values: readonly unknown[], separator = \", \"): this {\n values.forEach((value, index) => {\n if (index > 0) this.raw(separator);\n this.value(value);\n });\n return this;\n }\n\n /** `($1::text, $2::timestamptz)`. Casts are Postgres-only and dropped elsewhere. */\n tuple(cells: readonly (readonly [unknown, string])[]): this {\n this.raw(\"(\");\n cells.forEach(([value, cast], index) => {\n if (index > 0) this.raw(\", \");\n this.value(value);\n if (this.#placeholder === \"dollar\") this.raw(`::${cast}`);\n });\n return this.raw(\")\");\n }\n\n get text(): string {\n return this.#text;\n }\n}\n\nexport const quoteWith =\n (quoteChar: '\"' | \"`\") =>\n (identifier: string): string =>\n `${quoteChar}${identifier.split(quoteChar).join(quoteChar + quoteChar)}${quoteChar}`;\n\n/** Double-quoted: Postgres and SQLite. */\nexport const quote = quoteWith('\"');\n","import type {\n ClaimArgs,\n ClaimedDelivery,\n DatabaseAdapter,\n DeliveryRelease,\n FeedPage,\n FeedQuery,\n InsertNotification,\n} from \"../../core/adapter\";\nimport { decodeBase64Url, encodeBase64Url } from \"../../core/base64url\";\nimport { isOperator, type QueryOptions, type WhereClause } from \"../../core/store\";\nimport type { Channel, DeliveryRecord, DeliveryStatus, NotificationRecord } from \"../../core/types\";\nimport { toSnakeCase } from \"../../schema/declaration\";\nimport type { Dialect, Row, SqlExec, SqlResult } from \"./dialect\";\nimport { Statement } from \"./statement\";\n\nexport type SqlAdapterOptions = {\n /** Table-name prefix. Must match the instance's `tablePrefix`. */\n prefix?: string;\n /** Makes createNotifications atomic and, on MySQL, lets a claim use FOR UPDATE SKIP LOCKED. */\n transaction?: <T>(fn: (exec: SqlExec) => Promise<T>) => Promise<T>;\n};\n\nconst str = (value: unknown): string => String(value);\nconst nullableStr = (value: unknown): string | null => (value == null ? null : String(value));\nconst num = (value: unknown): number => Number(value);\n\nconst encodeCursor = (createdAt: Date, id: string) =>\n encodeBase64Url(`${createdAt.toISOString()}|${id}`);\n\nfunction decodeCursor(cursor: string): { createdAt: Date; id: string } | null {\n const decoded = decodeBase64Url(cursor);\n if (!decoded) return null;\n const [iso, id] = decoded.split(\"|\");\n if (!iso || !id) return null;\n const createdAt = new Date(iso);\n return Number.isNaN(createdAt.getTime()) ? null : { createdAt, id };\n}\n\nconst NONE: SqlResult = { rows: [], affectedRows: 0 };\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** InnoDB rolls back the loser of a deadlock and asks for a retry; bounded and jittered. */\nasync function retryingLocks<T>(dialect: Dialect, attempt: () => Promise<T>): Promise<T> {\n for (let tries = 0; ; tries += 1) {\n try {\n return await attempt();\n } catch (error) {\n if (tries >= 5 || !dialect.isRetryableLock(error)) throw error;\n await sleep(5 + Math.random() * 20 * (tries + 1));\n }\n }\n}\n\n/**\n * Every SQL engine that is not Postgres, behind one dialect: the same queries\n * minus RETURNING, casts and IS DISTINCT FROM. RFC 0005.\n */\nexport function createSqlAdapter(\n dialect: Dialect,\n exec: SqlExec,\n options: SqlAdapterOptions = {},\n): DatabaseAdapter {\n const prefix = options.prefix ?? \"\";\n const q = dialect.quote;\n const NOTIFICATION = q(`${prefix}notification`);\n const DELIVERY = q(`${prefix}notification_delivery`);\n\n const stmt = () => new Statement({ placeholder: dialect.placeholder, bind: dialect.bind });\n const run = (statement: Statement, on: SqlExec = exec) => on(statement.text, statement.params);\n const atomically = <T>(fn: (on: SqlExec) => Promise<T>): Promise<T> =>\n options.transaction ? options.transaction(fn) : fn(exec);\n\n const toNotification = (row: Row): NotificationRecord => ({\n id: str(row.id),\n userId: str(row.user_id),\n type: str(row.type),\n payload: dialect.readJson(row.payload),\n actorId: nullableStr(row.actor_id),\n groupKey: nullableStr(row.group_key),\n dedupeKey: nullableStr(row.dedupe_key),\n seenAt: dialect.readNullableDate(row.seen_at),\n readAt: dialect.readNullableDate(row.read_at),\n archivedAt: dialect.readNullableDate(row.archived_at),\n createdAt: dialect.readDate(row.created_at),\n });\n\n const toDelivery = (row: Row): DeliveryRecord => ({\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n status: str(row.status) as DeliveryStatus,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notBefore: dialect.readDate(row.not_before),\n claimedAt: dialect.readNullableDate(row.claimed_at),\n claimedBy: nullableStr(row.claimed_by),\n lastError: nullableStr(row.last_error),\n updatedAt: dialect.readDate(row.updated_at),\n });\n\n function appendWhere(statement: Statement, where: WhereClause): void {\n const entries = Object.entries(where);\n if (entries.length === 0) return;\n\n statement.raw(\" WHERE \");\n\n entries.forEach(([field, condition], index) => {\n if (index > 0) statement.raw(\" AND \");\n const column = q(toSnakeCase(field));\n\n if (condition === null) {\n statement.raw(`${column} IS NULL`);\n return;\n }\n if (!isOperator(condition)) {\n statement.raw(`${column} = `).value(condition);\n return;\n }\n\n const operator = condition as Record<string, unknown>;\n if (\"in\" in operator) {\n const list = operator.in as readonly (string | number)[];\n if (list.length === 0) statement.raw(\"1 = 0\");\n else statement.raw(`${column} IN (`).list(list).raw(\")\");\n return;\n }\n if (\"lt\" in operator) statement.raw(`${column} < `).value(operator.lt);\n else if (\"lte\" in operator) statement.raw(`${column} <= `).value(operator.lte);\n else if (\"gt\" in operator) statement.raw(`${column} > `).value(operator.gt);\n else if (\"gte\" in operator) statement.raw(`${column} >= `).value(operator.gte);\n else if (\"not\" in operator) {\n if (operator.not === null) statement.raw(`${column} IS NOT NULL`);\n else dialect.distinctFrom(statement, column, operator.not);\n } else statement.raw(\"1 = 1\");\n });\n }\n\n function eligible(statement: Statement, args: ClaimArgs, now: Date, staleBefore: Date): void {\n statement\n .raw(\"(status = 'pending' OR (status = 'claimed' AND claimed_at < \")\n .value(staleBefore)\n .raw(\")) AND not_before <= \")\n .value(now)\n .raw(\" AND attempts < max_attempts\");\n if (args.channels && args.channels.length > 0) {\n statement.raw(\" AND channel IN (\").list(args.channels).raw(\")\");\n }\n if (args.ids && args.ids.length > 0) {\n statement.raw(\" AND id IN (\").list(args.ids).raw(\")\");\n }\n }\n\n /** The token is unique per call, so this is exactly what the call just claimed. */\n const readClaimed = (token: string, on: SqlExec) =>\n run(\n stmt()\n .raw(\n \"SELECT c.id, c.notification_id, c.channel, c.attempts, c.max_attempts,\" +\n ` n.user_id, n.type, n.payload, n.actor_id FROM ${DELIVERY} c` +\n ` JOIN ${NOTIFICATION} n ON n.id = c.notification_id WHERE c.claimed_by = `,\n )\n .value(token)\n .raw(\" AND c.status = 'claimed' ORDER BY c.not_before ASC, c.id ASC\"),\n on,\n );\n\n return {\n name: dialect.name,\n naming: \"snake_case\",\n serializesJson: true,\n\n async createNotifications(input: readonly InsertNotification[]) {\n if (input.length === 0) return { created: [], deduped: [] };\n const now = new Date();\n\n return atomically(async (on) => {\n const created: string[] = [];\n const deduped: string[] = [];\n\n // No RETURNING on MySQL and INSERT IGNORE hides real errors: look, insert, treat a race as dedupe.\n for (const row of input) {\n if (row.dedupeKey) {\n const existing = await run(\n stmt()\n .raw(`SELECT id FROM ${NOTIFICATION} WHERE user_id = `)\n .value(row.userId)\n .raw(\" AND dedupe_key = \")\n .value(row.dedupeKey),\n on,\n );\n if (existing.rows.length > 0) {\n deduped.push(row.id);\n continue;\n }\n }\n\n try {\n await run(\n stmt()\n .raw(\n `INSERT INTO ${NOTIFICATION} ` +\n \"(id, user_id, type, payload, actor_id, group_key, dedupe_key, created_at) VALUES (\",\n )\n .list([\n row.id,\n row.userId,\n row.type,\n JSON.stringify(row.payload ?? null),\n row.actorId ?? null,\n row.groupKey ?? null,\n row.dedupeKey ?? null,\n now,\n ])\n .raw(\")\"),\n on,\n );\n } catch (error) {\n if (row.dedupeKey && dialect.isDuplicateKey(error)) {\n deduped.push(row.id);\n continue;\n }\n throw error;\n }\n created.push(row.id);\n }\n\n const createdSet = new Set(created);\n const deliveries = input\n .filter((row) => createdSet.has(row.id))\n .flatMap((row) => row.deliveries.map((delivery) => ({ row, delivery })));\n\n if (deliveries.length > 0) {\n const insert = stmt().raw(\n `INSERT INTO ${DELIVERY} ` +\n \"(id, notification_id, channel, max_attempts, not_before, updated_at) VALUES \",\n );\n deliveries.forEach(({ row, delivery }, index) => {\n if (index > 0) insert.raw(\", \");\n insert\n .raw(\"(\")\n .list([\n delivery.id,\n row.id,\n delivery.channel,\n delivery.maxAttempts,\n delivery.notBefore,\n now,\n ])\n .raw(\")\");\n });\n await run(insert, on);\n }\n\n return { created, deduped };\n });\n },\n\n async claimPendingDeliveries(args: ClaimArgs): Promise<readonly ClaimedDelivery[]> {\n const now = args.now ?? new Date();\n const staleBefore = new Date(now.getTime() - args.leaseMs);\n const strategy = options.transaction\n ? dialect.claim.withTransaction\n : dialect.claim.withoutTransaction;\n\n const claimSet = (statement: Statement) =>\n statement\n .raw(\"status = 'claimed', claimed_at = \")\n .value(now)\n .raw(\", claimed_by = \")\n .value(args.claimToken);\n\n let result: SqlResult;\n\n if (strategy === \"skip-locked\") {\n result = await atomically(async (on) => {\n const pick = stmt().raw(`SELECT id FROM ${DELIVERY} WHERE `);\n eligible(pick, args, now, staleBefore);\n pick\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\" FOR UPDATE SKIP LOCKED\");\n const ids = (await run(pick, on)).rows.map((row) => str(row.id));\n if (ids.length === 0) return NONE;\n\n const update = stmt().raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(\" WHERE id IN (\").list(ids).raw(\")\");\n await run(update, on);\n return readClaimed(args.claimToken, on);\n });\n } else {\n const update = stmt();\n if (strategy === \"derived-update\") {\n // The derived table is materialised, which is what lets MySQL update the table it\n // selects from; the outer predicate is re-evaluated on the locked row.\n update.raw(`UPDATE ${DELIVERY} d JOIN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\") s ON s.id = d.id SET d.status = 'claimed', d.claimed_at = \")\n .value(now)\n .raw(\", d.claimed_by = \")\n .value(args.claimToken)\n .raw(\" WHERE d.status = 'pending' OR (d.status = 'claimed' AND d.claimed_at < \")\n .value(staleBefore)\n .raw(\")\");\n } else {\n update.raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(` WHERE id IN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update.raw(\" ORDER BY not_before ASC, id ASC LIMIT \").value(args.limit).raw(\")\");\n }\n const updated = await retryingLocks(dialect, () => run(update));\n result = updated.affectedRows > 0 ? await readClaimed(args.claimToken, exec) : NONE;\n }\n\n return result.rows.map((row) => ({\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notification: {\n userId: str(row.user_id),\n type: str(row.type),\n payload: dialect.readJson(row.payload),\n actorId: nullableStr(row.actor_id),\n },\n }));\n },\n\n async releaseDeliveries(releases: readonly DeliveryRelease[]) {\n if (releases.length === 0) return;\n const now = new Date();\n\n // No claimed_by predicate (RFC 0003 §6). `status` before `attempts`: MySQL evaluates SET\n // left to right with updated values, and the CASE needs the old count.\n await atomically(async (on) => {\n for (const { id, outcome, nextAttemptAt } of releases) {\n const result = outcome.result;\n const retryable = outcome.result === \"failed\" && outcome.retryable;\n const error =\n outcome.result === \"failed\"\n ? outcome.error.slice(0, 2000)\n : outcome.result === \"skipped\"\n ? outcome.reason.slice(0, 2000)\n : null;\n\n await run(\n stmt()\n .raw(`UPDATE ${DELIVERY} SET status = CASE WHEN `)\n .value(result)\n .raw(\" = 'sent' THEN 'sent' WHEN \")\n .value(result)\n .raw(\" = 'skipped' THEN 'skipped' WHEN \")\n .value(retryable)\n .raw(\" = 1 AND attempts + 1 < max_attempts THEN 'pending' ELSE 'failed' END\")\n .raw(\", attempts = CASE WHEN \")\n .value(result)\n .raw(\" IN ('sent', 'skipped') THEN attempts ELSE attempts + 1 END\")\n .raw(\", last_error = \")\n .value(error)\n .raw(\", not_before = COALESCE(\")\n .value(nextAttemptAt ?? null)\n .raw(\", not_before), claimed_at = NULL, claimed_by = NULL, updated_at = \")\n .value(now)\n .raw(\" WHERE id = \")\n .value(id),\n on,\n );\n }\n });\n },\n\n async listNotifications(feed: FeedQuery): Promise<FeedPage> {\n const cursor = feed.cursor ? decodeCursor(feed.cursor) : null;\n\n const statement = stmt()\n .raw(`SELECT * FROM ${NOTIFICATION} WHERE user_id = `)\n .value(feed.userId)\n .raw(\" AND archived_at IS NULL\");\n if (feed.unreadOnly) statement.raw(\" AND read_at IS NULL\");\n if (cursor) {\n statement\n .raw(\" AND (created_at, id) < (\")\n .value(cursor.createdAt)\n .raw(\", \")\n .value(cursor.id)\n .raw(\")\");\n }\n statement.raw(\" ORDER BY created_at DESC, id DESC LIMIT \").value(feed.limit + 1);\n\n const { rows } = await run(statement);\n const page = rows.slice(0, feed.limit).map(toNotification);\n const last = page.at(-1);\n\n return {\n notifications: page,\n nextCursor: rows.length > feed.limit && last ? encodeCursor(last.createdAt, last.id) : null,\n };\n },\n\n async countUnseen(userId: string) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" AND seen_at IS NULL AND archived_at IS NULL\"),\n );\n return num(rows[0]?.count ?? 0);\n },\n\n async markSeen(userId: string, before: Date) {\n await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET seen_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND seen_at IS NULL AND created_at <= \")\n .value(before),\n );\n },\n\n async markRead(userId: string, notificationIds: readonly string[]) {\n if (notificationIds.length === 0) return 0;\n\n // Idempotent, so the answer is rows matched; MySQL's affectedRows reports rows changed.\n await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = COALESCE(read_at, `)\n .value(new Date())\n .raw(\") WHERE user_id = \")\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n return num(rows[0]?.count ?? 0);\n },\n\n async markAllRead(userId: string) {\n const { affectedRows } = await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND read_at IS NULL\"),\n );\n return affectedRows;\n },\n\n async getFailedDeliveries(args: { since: Date; limit: number }) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT * FROM ${DELIVERY} WHERE status = 'failed' AND updated_at >= `)\n .value(args.since)\n .raw(\" ORDER BY updated_at DESC LIMIT \")\n .value(args.limit),\n );\n return rows.map(toDelivery);\n },\n\n async queryTable(table: string, where: WhereClause, queryOptions: QueryOptions) {\n const statement = stmt().raw(`SELECT * FROM ${q(table)}`);\n appendWhere(statement, where);\n if (queryOptions.orderBy) {\n const direction = queryOptions.orderBy.direction === \"desc\" ? \"DESC\" : \"ASC\";\n statement.raw(` ORDER BY ${q(toSnakeCase(queryOptions.orderBy.field))} ${direction}`);\n }\n if (queryOptions.limit) statement.raw(\" LIMIT \").value(queryOptions.limit);\n return [...(await run(statement)).rows];\n },\n\n async insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ) {\n if (rows.length === 0) return 0;\n const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) return 0;\n\n const statement = stmt().raw(\n `INSERT INTO ${q(table)} (${columns.map((c) => q(toSnakeCase(c))).join(\", \")}) VALUES `,\n );\n rows.forEach((row, index) => {\n if (index > 0) statement.raw(\", \");\n statement\n .raw(\"(\")\n .list(columns.map((column) => row[column]))\n .raw(\")\");\n });\n\n if (onConflict && onConflict.length > 0) {\n statement.raw(\n dialect.upsertClause(\n onConflict.map((f) => q(toSnakeCase(f))),\n columns.filter((c) => !onConflict.includes(c)).map((c) => q(toSnakeCase(c))),\n ),\n );\n await run(statement);\n // MySQL reports 2 for an updated row; the rows handed in is the honest count.\n return rows.length;\n }\n\n return (await run(statement)).affectedRows;\n },\n\n async updateRows(table: string, where: WhereClause, set: Record<string, unknown>) {\n const assignments = Object.entries(set);\n if (assignments.length === 0) return 0;\n\n const statement = stmt().raw(`UPDATE ${q(table)} SET `);\n assignments.forEach(([field, value], index) => {\n if (index > 0) statement.raw(\", \");\n statement.raw(`${q(toSnakeCase(field))} = `).value(value);\n });\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n\n async deleteRows(table: string, where: WhereClause) {\n const statement = stmt().raw(`DELETE FROM ${q(table)}`);\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n };\n}\n","import type { Placeholder, Statement } from \"./statement\";\nimport { quoteWith } from \"./statement\";\n\nexport type Row = Record<string, unknown>;\n\nexport type SqlResult = {\n rows: readonly Row[];\n /** Rows an INSERT/UPDATE/DELETE touched; drivers without RETURNING report through this. */\n affectedRows: number;\n};\n\n/** The whole driver contract for the MySQL and SQLite adapters. */\nexport type SqlExec = (text: string, params: readonly unknown[]) => Promise<SqlResult>;\n\n/** How a claim takes its rows. RFC 0005 §3. */\nexport type ClaimStrategy = \"skip-locked\" | \"derived-update\" | \"subquery-update\";\n\nexport type Dialect = {\n name: string;\n placeholder: Placeholder;\n quote: (identifier: string) => string;\n bind: (value: unknown) => unknown;\n readDate: (value: unknown) => Date;\n readNullableDate: (value: unknown) => Date | null;\n readJson: (value: unknown) => unknown;\n /** Appends `column IS DISTINCT FROM value` in the engine's spelling. */\n distinctFrom: (statement: Statement, column: string, value: unknown) => void;\n /** The clause after a multi-row INSERT that makes it an upsert. Columns arrive quoted. */\n upsertClause: (conflict: readonly string[], update: readonly string[]) => string;\n isDuplicateKey: (error: unknown) => boolean;\n /** A deadlock or busy lock the engine asks the caller to retry; the statement was rolled back. */\n isRetryableLock: (error: unknown) => boolean;\n claim: { withTransaction: ClaimStrategy; withoutTransaction: ClaimStrategy };\n};\n\nconst parseUtc = (value: unknown): Date => {\n if (value instanceof Date) return value;\n const text = String(value).trim();\n // \"2026-09-23 10:11:12.345\" from a dateStrings driver carries no zone; it was written as UTC.\n return new Date(/^\\d{4}-\\d{2}-\\d{2} /.test(text) ? `${text.replace(\" \", \"T\")}Z` : text);\n};\n\nconst readJson = (value: unknown) => (typeof value === \"string\" ? JSON.parse(value) : value);\n\nconst bindCommon = (value: unknown) => {\n if (typeof value === \"boolean\") return value ? 1 : 0;\n if (value === undefined) return null;\n return value;\n};\n\nconst code = (error: unknown) => (error as { code?: unknown } | null)?.code;\nconst errno = (error: unknown) => (error as { errno?: unknown } | null)?.errno;\n\nexport const mysqlDialect: Dialect = {\n name: \"mysql\",\n placeholder: \"question\",\n quote: quoteWith(\"`\"),\n bind: (value) =>\n value instanceof Date ? value.toISOString().slice(0, 23).replace(\"T\", \" \") : bindCommon(value),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`NOT (${column} <=> `).value(value).raw(\")\");\n },\n // VALUES() rather than the 8.0.19 `AS new` alias: it also works on MariaDB.\n upsertClause: (conflict, update) =>\n ` ON DUPLICATE KEY UPDATE ${(\n update.length === 0\n ? [`${conflict[0]} = ${conflict[0]}`]\n : update.map((column) => `${column} = VALUES(${column})`)\n ).join(\", \")}`,\n isDuplicateKey: (error) => code(error) === \"ER_DUP_ENTRY\" || errno(error) === 1062,\n isRetryableLock: (error) =>\n code(error) === \"ER_LOCK_DEADLOCK\" ||\n errno(error) === 1213 ||\n code(error) === \"ER_LOCK_WAIT_TIMEOUT\" ||\n errno(error) === 1205,\n claim: { withTransaction: \"skip-locked\", withoutTransaction: \"derived-update\" },\n};\n\nexport const sqliteDialect: Dialect = {\n name: \"sqlite\",\n placeholder: \"question\",\n quote: quoteWith('\"'),\n bind: (value) => (value instanceof Date ? value.toISOString() : bindCommon(value)),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`${column} IS NOT `).value(value);\n },\n upsertClause: (conflict, update) =>\n ` ON CONFLICT (${conflict.join(\", \")}) DO ${\n update.length === 0\n ? \"NOTHING\"\n : `UPDATE SET ${update.map((column) => `${column} = excluded.${column}`).join(\", \")}`\n }`,\n isDuplicateKey: (error) =>\n code(error) === \"SQLITE_CONSTRAINT_UNIQUE\" ||\n /UNIQUE constraint failed/i.test(String((error as Error | null)?.message ?? \"\")),\n isRetryableLock: (error) =>\n code(error) === \"SQLITE_BUSY\" ||\n /database is locked/i.test(String((error as Error | null)?.message ?? \"\")),\n claim: { withTransaction: \"subquery-update\", withoutTransaction: \"subquery-update\" },\n};\n","import type { DatabaseAdapter } from \"../../core/adapter\";\nimport { createSqlAdapter, type SqlAdapterOptions } from \"../sql/adapter\";\nimport { mysqlDialect, type SqlExec } from \"../sql/dialect\";\n\nexport type MysqlAdapterOptions = SqlAdapterOptions;\n\n/**\n * MySQL 8 or MariaDB through any driver. With `transaction` a claim uses\n * FOR UPDATE SKIP LOCKED; without it a lock-free UPDATE that never double-claims. RFC 0005 §3.\n */\nexport function mysqlAdapter(query: SqlExec, options: MysqlAdapterOptions = {}): DatabaseAdapter {\n return createSqlAdapter(mysqlDialect, query, options);\n}\n","import type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"../core/plugin\";\nimport { toSnakeCase } from \"./declaration\";\n\nconst quote = (value: string) => `\\`${value.split(\"`\").join(\"``\")}\\``;\n\n/** InnoDB caps a key at 3072 bytes: 768 utf8mb4 chars for one column, 255 each for three. TEXT cannot be a key or carry a default. */\nfunction stringType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n const keys = [\n ...(table.primaryKey ? [table.primaryKey] : []),\n ...(table.indexes ?? []).map((index) => index.on),\n ].filter((columns) => columns.includes(field));\n\n if (keys.some((columns) => columns.length > 1)) return \"VARCHAR(255)\";\n if (keys.length > 0) return \"VARCHAR(768)\";\n if (spec.default !== undefined) return \"VARCHAR(255)\";\n return \"TEXT\";\n}\n\nfunction columnType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n switch (spec.type) {\n case \"string\":\n return stringType(field, table, spec);\n case \"number\":\n return \"INT\";\n case \"boolean\":\n return \"TINYINT(1)\";\n case \"date\":\n return \"DATETIME(3)\";\n case \"json\":\n return \"JSON\";\n }\n}\n\nconst defaultClause = (spec: FieldDeclaration): string => {\n if (spec.defaultNow) return \" DEFAULT CURRENT_TIMESTAMP(3)\";\n if (spec.default === undefined) return \"\";\n if (typeof spec.default === \"string\") return ` DEFAULT '${spec.default.replace(/'/g, \"''\")}'`;\n if (typeof spec.default === \"boolean\") return ` DEFAULT ${spec.default ? 1 : 0}`;\n return ` DEFAULT ${spec.default}`;\n};\n\n/** MySQL DDL, one CREATE TABLE IF NOT EXISTS per table with indexes inline (no CREATE INDEX IF NOT EXISTS). */\nexport function renderMysqlDdl(schema: SchemaDeclaration, prefix = \"\"): string[] {\n const statements: string[] = [];\n\n for (const table of Object.values(schema)) {\n const lines = Object.entries(table.fields).map(([field, spec]) => {\n const nullability = spec.required ? \" NOT NULL\" : \"\";\n return ` ${quote(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;\n });\n\n if (table.primaryKey?.length) {\n lines.push(\n ` PRIMARY KEY (${table.primaryKey.map((f) => quote(toSnakeCase(f))).join(\", \")})`,\n );\n }\n\n for (const index of table.indexes ?? []) {\n const name = quote(prefix + (index.name ?? `${table.tableName}_${index.on.join(\"_\")}_idx`));\n const columns = index.on.map((f) => quote(toSnakeCase(f))).join(\", \");\n lines.push(` ${index.unique ? \"UNIQUE \" : \"\"}INDEX ${name} (${columns})`);\n }\n\n statements.push(\n `CREATE TABLE IF NOT EXISTS ${quote(prefix + table.tableName)} (\\n${lines.join(\",\\n\")}\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,\n );\n }\n\n return statements;\n}\n","import type { SchemaDeclaration } from \"../../core/plugin\";\nimport { coreSchema } from \"../../schema/declaration\";\nimport { renderMysqlDdl } from \"../../schema/render-mysql\";\nimport type { Row, SqlExec, SqlResult } from \"../sql/dialect\";\n\n/** The slice of a mysql2 pool or connection this needs, structurally. */\nexport type Mysql2Queryable = {\n // biome-ignore lint/suspicious/noExplicitAny: must accept mysql2's own overloaded signature\n query(sql: string, values?: any): Promise<[unknown, unknown]>;\n};\n\nexport type Mysql2PoolLike = Mysql2Queryable & {\n getConnection(): Promise<\n Mysql2Queryable & {\n beginTransaction(): Promise<void>;\n commit(): Promise<void>;\n rollback(): Promise<void>;\n release(): void;\n }\n >;\n};\n\n/** mysql2 returns rows for a SELECT and a ResultSetHeader for anything else. */\nfunction toResult([result]: [unknown, unknown]): SqlResult {\n if (Array.isArray(result)) return { rows: result as Row[], affectedRows: 0 };\n const header = result as { affectedRows?: unknown } | null;\n return { rows: [], affectedRows: Number(header?.affectedRows ?? 0) };\n}\n\n/** The SqlExec for a mysql2 pool. Create the pool with `timezone: \"Z\"`, or DATETIME comes back shifted. */\nexport const mysql2Query =\n (pool: Mysql2Queryable): SqlExec =>\n async (text, params) =>\n toResult(await pool.query(text, [...params]));\n\n/** The `transaction` option for mysql2. */\nexport function mysqlTransaction(pool: Mysql2PoolLike) {\n return async <T>(fn: (exec: SqlExec) => Promise<T>): Promise<T> => {\n const connection = await pool.getConnection();\n try {\n await connection.beginTransaction();\n const result = await fn(async (text, params) =>\n toResult(await connection.query(text, [...params])),\n );\n await connection.commit();\n return result;\n } catch (error) {\n await connection.rollback();\n throw error;\n } finally {\n connection.release();\n }\n };\n}\n\nexport type CreateMysqlTablesOptions = {\n /** Must match the prefix given to the adapter. */\n prefix?: string;\n /** Plugin schemas, e.g. `pushSchema`. */\n plugins?: readonly SchemaDeclaration[];\n};\n\n/** Creates core and plugin tables, IF NOT EXISTS. Bootstrap, not migration. */\nexport async function createMysqlTables(\n query: SqlExec,\n options: CreateMysqlTablesOptions = {},\n): Promise<number> {\n let count = 0;\n for (const schema of [coreSchema, ...(options.plugins ?? [])]) {\n for (const statement of renderMysqlDdl(schema, options.prefix)) {\n await query(statement, []);\n count += 1;\n }\n }\n return count;\n}\n"]}
1
+ {"version":3,"sources":["../../src/core/base64url.ts","../../src/schema/declaration.ts","../../src/core/store.ts","../../src/adapters/sql/statement.ts","../../src/adapters/sql/adapter.ts","../../src/adapters/sql/dialect.ts","../../src/adapters/mysql/adapter.ts","../../src/schema/render-mysql.ts","../../src/adapters/mysql/helpers.ts"],"names":["quote"],"mappings":"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","file":"mysql.cjs","sourcesContent":["// Web APIs, not Buffer: this runs on Workers and Vercel Edge.\n\nexport function encodeBase64Url(input: string | Uint8Array): string {\n const bytes = typeof input === \"string\" ? new TextEncoder().encode(input) : input;\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n// Uint8Array<ArrayBuffer>: bare Uint8Array widens and is not a BufferSource.\nexport function decodeBase64UrlBytes(value: string): Uint8Array<ArrayBuffer> | null {\n try {\n const binary = atob(value.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n } catch {\n return null;\n }\n}\n\nexport function decodeBase64Url(value: string): string | null {\n const bytes = decodeBase64UrlBytes(value);\n return bytes ? new TextDecoder().decode(bytes) : null;\n}\n","import type { SchemaDeclaration } from \"../core/plugin\";\n\nexport const toSnakeCase = (value: string) => value.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n\n/** Core tables in the plugin format. A drift test asserts it agrees with the Drizzle schema. */\nexport const coreSchema = {\n notification: {\n tableName: \"notification\",\n fields: {\n id: { type: \"string\", required: true },\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n payload: { type: \"json\", required: true },\n actorId: { type: \"string\" },\n groupKey: { type: \"string\" },\n dedupeKey: { type: \"string\" },\n seenAt: { type: \"date\" },\n readAt: { type: \"date\" },\n archivedAt: { type: \"date\" },\n createdAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"userId\", \"createdAt\"], name: \"notification_feed_idx\" },\n { on: [\"userId\", \"groupKey\"], name: \"notification_group_idx\" },\n { on: [\"userId\", \"dedupeKey\"], unique: true, name: \"notification_dedupe_idx\" },\n ],\n },\n\n notificationDelivery: {\n tableName: \"notification_delivery\",\n fields: {\n id: { type: \"string\", required: true },\n notificationId: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n status: { type: \"string\", required: true, default: \"pending\" },\n attempts: { type: \"number\", required: true, default: 0 },\n maxAttempts: { type: \"number\", required: true, default: 5 },\n notBefore: { type: \"date\", required: true, defaultNow: true },\n claimedAt: { type: \"date\" },\n claimedBy: { type: \"string\" },\n lastError: { type: \"string\" },\n updatedAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"id\"],\n indexes: [\n { on: [\"status\", \"notBefore\"], name: \"delivery_claim_idx\" },\n { on: [\"notificationId\"], name: \"delivery_notification_idx\" },\n ],\n },\n\n notificationPreference: {\n tableName: \"notification_preference\",\n fields: {\n userId: { type: \"string\", required: true },\n type: { type: \"string\", required: true },\n channel: { type: \"string\", required: true },\n enabled: { type: \"boolean\", required: true, default: true },\n frequency: { type: \"string\", required: true, default: \"instant\" },\n updatedAt: { type: \"date\", required: true, defaultNow: true },\n },\n primaryKey: [\"userId\", \"type\", \"channel\"],\n },\n} satisfies SchemaDeclaration;\n","import { toSnakeCase } from \"../schema/declaration\";\nimport { ConfigError } from \"./errors\";\nimport type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"./plugin\";\n\nexport type Scalar = string | number | boolean | Date | null;\n\n/** Operators are tagged objects so a bare value is always equality; json columns cannot be filtered. */\nexport type WhereCondition =\n | Scalar\n | { in: readonly (string | number)[] }\n | { lt: Date | number }\n | { lte: Date | number }\n | { gt: Date | number }\n | { gte: Date | number }\n | { not: Scalar };\n\nexport type WhereClause = Record<string, WhereCondition>;\n\nexport type QueryOptions = {\n limit?: number;\n orderBy?: { field: string; direction: \"asc\" | \"desc\" };\n};\n\nexport type UpsertOptions = { onConflict: readonly string[] };\n\n/** Table access for plugins, scoped to the tables the plugin declared. */\nexport type PluginStore = {\n find<T = Record<string, unknown>>(\n table: string,\n where?: WhereClause,\n options?: QueryOptions,\n ): Promise<T[]>;\n insert(table: string, rows: readonly Record<string, unknown>[]): Promise<number>;\n upsert(\n table: string,\n rows: readonly Record<string, unknown>[],\n options: UpsertOptions,\n ): Promise<number>;\n update(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n remove(table: string, where: WhereClause): Promise<number>;\n};\n\nexport type TableStorage = {\n readonly naming?: \"snake_case\" | \"preserve\";\n readonly serializesJson?: boolean;\n\n queryTable(\n table: string,\n where: WhereClause,\n options: QueryOptions,\n ): Promise<Record<string, unknown>[]>;\n insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ): Promise<number>;\n updateRows(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n deleteRows(table: string, where: WhereClause): Promise<number>;\n};\n\nconst OPERATORS = [\"in\", \"lt\", \"lte\", \"gt\", \"gte\", \"not\"] as const;\n\nexport const isOperator = (value: unknown): boolean =>\n typeof value === \"object\" &&\n value !== null &&\n !(value instanceof Date) &&\n OPERATORS.some((op) => Object.hasOwn(value, op));\n\nconst isScalar = (value: unknown): value is Scalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\n/** A forwarded JSON body can carry `{ $ne: … }`; refuse anything that is not a scalar or a known operator. */\nfunction assertCondition(pluginId: string, field: string, condition: unknown): void {\n if (isScalar(condition)) return;\n\n if (isOperator(condition)) {\n const operator = condition as Record<string, unknown>;\n const keys = Object.keys(operator);\n const [op] = keys;\n if (keys.length !== 1 || op === undefined) {\n throw new ConfigError(`plugin \"${pluginId}\" gave field \"${field}\" more than one operator.`);\n }\n const payload = operator[op];\n const valid =\n op === \"in\"\n ? Array.isArray(payload) &&\n payload.every((item) => typeof item === \"string\" || typeof item === \"number\")\n : isScalar(payload);\n if (valid) return;\n }\n\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for field \"${field}\"; ` +\n \"validate request bodies before querying with them.\",\n );\n}\n\nfunction assertValue(pluginId: string, field: string, spec: FieldDeclaration, value: unknown) {\n if (value === undefined || isScalar(value)) return;\n if (spec.type === \"json\" && typeof value === \"object\") return;\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for ${spec.type} field \"${field}\".`,\n );\n}\n\n/** Every table and column is checked against the plugin's own declaration. */\nexport function createPluginStore(\n pluginId: string,\n schema: SchemaDeclaration | undefined,\n storage: TableStorage,\n prefix: string,\n): PluginStore {\n const byTableName = new Map<string, TableDeclaration>();\n for (const declaration of Object.values(schema ?? {})) {\n byTableName.set(declaration.tableName, declaration);\n }\n\n function resolve(table: string): TableDeclaration {\n const declaration = byTableName.get(table);\n if (!declaration) {\n throw new ConfigError(\n `plugin \"${pluginId}\" accessed table \"${table}\", which it does not declare in schema().`,\n );\n }\n return declaration;\n }\n\n function checkFields(table: TableDeclaration, fields: Iterable<string>, context: string) {\n for (const field of fields) {\n // hasOwn, not a lookup: `constructor` and `__proto__` are not columns.\n const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : undefined;\n if (!spec) {\n throw new ConfigError(\n `plugin \"${pluginId}\" used unknown field \"${field}\" on \"${table.tableName}\" (${context}).`,\n );\n }\n if (context === \"where\" && spec.type === \"json\") {\n throw new ConfigError(\n `plugin \"${pluginId}\" cannot filter on json field \"${field}\"; ` +\n \"a bare object value is indistinguishable from an operator.\",\n );\n }\n }\n }\n\n function checkWhere(table: TableDeclaration, where: WhereClause) {\n checkFields(table, Object.keys(where), \"where\");\n for (const [field, condition] of Object.entries(where)) {\n assertCondition(pluginId, field, condition);\n }\n }\n\n function checkRow(table: TableDeclaration, row: Record<string, unknown>, context: string) {\n checkFields(table, Object.keys(row), context);\n for (const [field, value] of Object.entries(row)) {\n const spec = table.fields[field];\n if (spec) assertValue(pluginId, field, spec, value);\n }\n }\n\n const qualified = (table: TableDeclaration) => prefix + table.tableName;\n\n // A document store keeps the declared names; a SQL adapter wants columns.\n const column = (field: string) => (storage.naming === \"preserve\" ? field : toSnakeCase(field));\n\n // json must reach postgres-js as text.\n const serialize = (table: TableDeclaration, row: Record<string, unknown>) => {\n if (storage.serializesJson === false) return row;\n\n const out: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(row)) {\n out[field] =\n table.fields[field]?.type === \"json\" && value !== null && value !== undefined\n ? JSON.stringify(value)\n : value;\n }\n return out;\n };\n\n return {\n async find(table, where = {}, options = {}) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n if (options.orderBy) checkFields(declaration, [options.orderBy.field], \"orderBy\");\n\n const rows = await storage.queryTable(qualified(declaration), where, options);\n\n // SELECT * returns snake_case; plugins read camelCase.\n return rows.map((row) => {\n const mapped: Record<string, unknown> = {};\n for (const field of Object.keys(declaration.fields)) {\n const value = row[column(field)];\n const type = declaration.fields[field]?.type;\n // Drivers differ on jsonb (parsed or text) and timestamptz (Date or text).\n if (type === \"json\" && typeof value === \"string\") mapped[field] = JSON.parse(value);\n else if (type === \"date\" && typeof value === \"string\") mapped[field] = new Date(value);\n // MySQL and SQLite have no boolean column; they hand back 0 and 1.\n else if (type === \"boolean\" && typeof value === \"number\") mapped[field] = value !== 0;\n else mapped[field] = value;\n }\n return mapped;\n }) as never;\n },\n\n async insert(table, rows) {\n const declaration = resolve(table);\n for (const row of rows) checkRow(declaration, row, \"insert\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n );\n },\n\n async upsert(table, rows, options) {\n const declaration = resolve(table);\n for (const row of rows) checkRow(declaration, row, \"upsert\");\n checkFields(declaration, options.onConflict, \"onConflict\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n options.onConflict,\n );\n },\n\n async update(table, where, set) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n checkRow(declaration, set, \"update\");\n return storage.updateRows(qualified(declaration), where, serialize(declaration, set));\n },\n\n async remove(table, where) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n return storage.deleteRows(qualified(declaration), where);\n },\n };\n}\n","export type Placeholder = \"dollar\" | \"question\";\n\nexport type StatementOptions = {\n /** `$1, $2` for Postgres; `?` for MySQL and SQLite. */\n placeholder?: Placeholder;\n /** What the driver is handed for a JS value. Dates become ISO strings by default. */\n bind?: (value: unknown) => unknown;\n};\n\nconst defaultBind = (value: unknown) => (value instanceof Date ? value.toISOString() : value);\n\n/** SQL text plus bound values. Values are never interpolated; identifiers arrive pre-validated. */\nexport class Statement {\n #text = \"\";\n readonly params: unknown[] = [];\n readonly #placeholder: Placeholder;\n readonly #bind: (value: unknown) => unknown;\n\n constructor(options: StatementOptions = {}) {\n this.#placeholder = options.placeholder ?? \"dollar\";\n this.#bind = options.bind ?? defaultBind;\n }\n\n /** Raw SQL. Never user input; use `value`. */\n raw(fragment: string): this {\n this.#text += fragment;\n return this;\n }\n\n value(value: unknown): this {\n this.params.push(this.#bind(value));\n this.#text += this.#placeholder === \"dollar\" ? `$${this.params.length}` : \"?\";\n return this;\n }\n\n list(values: readonly unknown[], separator = \", \"): this {\n values.forEach((value, index) => {\n if (index > 0) this.raw(separator);\n this.value(value);\n });\n return this;\n }\n\n /** `($1::text, $2::timestamptz)`. Casts are Postgres-only and dropped elsewhere. */\n tuple(cells: readonly (readonly [unknown, string])[]): this {\n this.raw(\"(\");\n cells.forEach(([value, cast], index) => {\n if (index > 0) this.raw(\", \");\n this.value(value);\n if (this.#placeholder === \"dollar\") this.raw(`::${cast}`);\n });\n return this.raw(\")\");\n }\n\n get text(): string {\n return this.#text;\n }\n}\n\nexport const quoteWith =\n (quoteChar: '\"' | \"`\") =>\n (identifier: string): string =>\n `${quoteChar}${identifier.split(quoteChar).join(quoteChar + quoteChar)}${quoteChar}`;\n\n/** Double-quoted: Postgres and SQLite. */\nexport const quote = quoteWith('\"');\n","import type {\n ClaimArgs,\n ClaimedDelivery,\n DatabaseAdapter,\n DeliveryRelease,\n FeedPage,\n FeedQuery,\n InsertNotification,\n} from \"../../core/adapter\";\nimport { decodeBase64Url, encodeBase64Url } from \"../../core/base64url\";\nimport { isOperator, type QueryOptions, type WhereClause } from \"../../core/store\";\nimport type { Channel, DeliveryRecord, DeliveryStatus, NotificationRecord } from \"../../core/types\";\nimport { toSnakeCase } from \"../../schema/declaration\";\nimport type { Dialect, Row, SqlExec, SqlResult } from \"./dialect\";\nimport { Statement } from \"./statement\";\n\nexport type SqlAdapterOptions = {\n /** Table-name prefix. Must match the instance's `tablePrefix`. */\n prefix?: string;\n /** Makes createNotifications atomic and, on MySQL, lets a claim use FOR UPDATE SKIP LOCKED. */\n transaction?: <T>(fn: (exec: SqlExec) => Promise<T>) => Promise<T>;\n};\n\nconst str = (value: unknown): string => String(value);\nconst nullableStr = (value: unknown): string | null => (value == null ? null : String(value));\nconst num = (value: unknown): number => Number(value);\n\nconst encodeCursor = (createdAt: Date, id: string) =>\n encodeBase64Url(`${createdAt.toISOString()}|${id}`);\n\nfunction decodeCursor(cursor: string): { createdAt: Date; id: string } | null {\n const decoded = decodeBase64Url(cursor);\n if (!decoded) return null;\n const [iso, id] = decoded.split(\"|\");\n if (!iso || !id) return null;\n const createdAt = new Date(iso);\n return Number.isNaN(createdAt.getTime()) ? null : { createdAt, id };\n}\n\nconst NONE: SqlResult = { rows: [], affectedRows: 0 };\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** InnoDB rolls back the loser of a deadlock and asks for a retry; bounded and jittered. */\nasync function retryingLocks<T>(dialect: Dialect, attempt: () => Promise<T>): Promise<T> {\n for (let tries = 0; ; tries += 1) {\n try {\n return await attempt();\n } catch (error) {\n if (tries >= 5 || !dialect.isRetryableLock(error)) throw error;\n await sleep(5 + Math.random() * 20 * (tries + 1));\n }\n }\n}\n\n/**\n * Every SQL engine that is not Postgres, behind one dialect: the same queries\n * minus RETURNING, casts and IS DISTINCT FROM. RFC 0005.\n */\nexport function createSqlAdapter(\n dialect: Dialect,\n exec: SqlExec,\n options: SqlAdapterOptions = {},\n): DatabaseAdapter {\n const prefix = options.prefix ?? \"\";\n const q = dialect.quote;\n const NOTIFICATION = q(`${prefix}notification`);\n const DELIVERY = q(`${prefix}notification_delivery`);\n\n const stmt = () => new Statement({ placeholder: dialect.placeholder, bind: dialect.bind });\n const run = (statement: Statement, on: SqlExec = exec) => on(statement.text, statement.params);\n const atomically = <T>(fn: (on: SqlExec) => Promise<T>): Promise<T> =>\n options.transaction ? options.transaction(fn) : fn(exec);\n\n const toNotification = (row: Row): NotificationRecord => ({\n id: str(row.id),\n userId: str(row.user_id),\n type: str(row.type),\n payload: dialect.readJson(row.payload),\n actorId: nullableStr(row.actor_id),\n groupKey: nullableStr(row.group_key),\n dedupeKey: nullableStr(row.dedupe_key),\n seenAt: dialect.readNullableDate(row.seen_at),\n readAt: dialect.readNullableDate(row.read_at),\n archivedAt: dialect.readNullableDate(row.archived_at),\n createdAt: dialect.readDate(row.created_at),\n });\n\n const toDelivery = (row: Row): DeliveryRecord => ({\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n status: str(row.status) as DeliveryStatus,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notBefore: dialect.readDate(row.not_before),\n claimedAt: dialect.readNullableDate(row.claimed_at),\n claimedBy: nullableStr(row.claimed_by),\n lastError: nullableStr(row.last_error),\n updatedAt: dialect.readDate(row.updated_at),\n });\n\n function appendWhere(statement: Statement, where: WhereClause): void {\n const entries = Object.entries(where);\n if (entries.length === 0) return;\n\n statement.raw(\" WHERE \");\n\n entries.forEach(([field, condition], index) => {\n if (index > 0) statement.raw(\" AND \");\n const column = q(toSnakeCase(field));\n\n if (condition === null) {\n statement.raw(`${column} IS NULL`);\n return;\n }\n if (!isOperator(condition)) {\n statement.raw(`${column} = `).value(condition);\n return;\n }\n\n const operator = condition as Record<string, unknown>;\n if (\"in\" in operator) {\n const list = operator.in as readonly (string | number)[];\n if (list.length === 0) statement.raw(\"1 = 0\");\n else statement.raw(`${column} IN (`).list(list).raw(\")\");\n return;\n }\n if (\"lt\" in operator) statement.raw(`${column} < `).value(operator.lt);\n else if (\"lte\" in operator) statement.raw(`${column} <= `).value(operator.lte);\n else if (\"gt\" in operator) statement.raw(`${column} > `).value(operator.gt);\n else if (\"gte\" in operator) statement.raw(`${column} >= `).value(operator.gte);\n else if (\"not\" in operator) {\n if (operator.not === null) statement.raw(`${column} IS NOT NULL`);\n else dialect.distinctFrom(statement, column, operator.not);\n } else statement.raw(\"1 = 1\");\n });\n }\n\n function eligible(statement: Statement, args: ClaimArgs, now: Date, staleBefore: Date): void {\n statement\n .raw(\"(status = 'pending' OR (status = 'claimed' AND claimed_at < \")\n .value(staleBefore)\n .raw(\")) AND not_before <= \")\n .value(now)\n .raw(\" AND attempts < max_attempts\");\n if (args.channels && args.channels.length > 0) {\n statement.raw(\" AND channel IN (\").list(args.channels).raw(\")\");\n }\n if (args.ids && args.ids.length > 0) {\n statement.raw(\" AND id IN (\").list(args.ids).raw(\")\");\n }\n }\n\n /** The token is unique per call, so this is exactly what the call just claimed. */\n const readClaimed = (token: string, on: SqlExec) =>\n run(\n stmt()\n .raw(\n \"SELECT c.id, c.notification_id, c.channel, c.attempts, c.max_attempts,\" +\n ` n.user_id, n.type, n.payload, n.actor_id FROM ${DELIVERY} c` +\n ` JOIN ${NOTIFICATION} n ON n.id = c.notification_id WHERE c.claimed_by = `,\n )\n .value(token)\n .raw(\" AND c.status = 'claimed' ORDER BY c.not_before ASC, c.id ASC\"),\n on,\n );\n\n return {\n name: dialect.name,\n naming: \"snake_case\",\n serializesJson: true,\n\n async createNotifications(input: readonly InsertNotification[]) {\n if (input.length === 0) return { created: [], deduped: [] };\n const now = new Date();\n\n return atomically(async (on) => {\n const created: string[] = [];\n const deduped: string[] = [];\n\n // No RETURNING on MySQL and INSERT IGNORE hides real errors: look, insert, treat a race as dedupe.\n for (const row of input) {\n if (row.dedupeKey) {\n const existing = await run(\n stmt()\n .raw(`SELECT id FROM ${NOTIFICATION} WHERE user_id = `)\n .value(row.userId)\n .raw(\" AND dedupe_key = \")\n .value(row.dedupeKey),\n on,\n );\n if (existing.rows.length > 0) {\n deduped.push(row.id);\n continue;\n }\n }\n\n try {\n await run(\n stmt()\n .raw(\n `INSERT INTO ${NOTIFICATION} ` +\n \"(id, user_id, type, payload, actor_id, group_key, dedupe_key, created_at) VALUES (\",\n )\n .list([\n row.id,\n row.userId,\n row.type,\n JSON.stringify(row.payload ?? null),\n row.actorId ?? null,\n row.groupKey ?? null,\n row.dedupeKey ?? null,\n now,\n ])\n .raw(\")\"),\n on,\n );\n } catch (error) {\n if (row.dedupeKey && dialect.isDuplicateKey(error)) {\n deduped.push(row.id);\n continue;\n }\n throw error;\n }\n created.push(row.id);\n }\n\n const createdSet = new Set(created);\n const deliveries = input\n .filter((row) => createdSet.has(row.id))\n .flatMap((row) => row.deliveries.map((delivery) => ({ row, delivery })));\n\n if (deliveries.length > 0) {\n const insert = stmt().raw(\n `INSERT INTO ${DELIVERY} ` +\n \"(id, notification_id, channel, max_attempts, not_before, updated_at) VALUES \",\n );\n deliveries.forEach(({ row, delivery }, index) => {\n if (index > 0) insert.raw(\", \");\n insert\n .raw(\"(\")\n .list([\n delivery.id,\n row.id,\n delivery.channel,\n delivery.maxAttempts,\n delivery.notBefore,\n now,\n ])\n .raw(\")\");\n });\n await run(insert, on);\n }\n\n return { created, deduped };\n });\n },\n\n async claimPendingDeliveries(args: ClaimArgs): Promise<readonly ClaimedDelivery[]> {\n const now = args.now ?? new Date();\n const staleBefore = new Date(now.getTime() - args.leaseMs);\n const strategy = options.transaction\n ? dialect.claim.withTransaction\n : dialect.claim.withoutTransaction;\n\n const claimSet = (statement: Statement) =>\n statement\n .raw(\"status = 'claimed', claimed_at = \")\n .value(now)\n .raw(\", claimed_by = \")\n .value(args.claimToken);\n\n let result: SqlResult;\n\n if (strategy === \"skip-locked\") {\n result = await atomically(async (on) => {\n const pick = stmt().raw(`SELECT id FROM ${DELIVERY} WHERE `);\n eligible(pick, args, now, staleBefore);\n pick\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\" FOR UPDATE SKIP LOCKED\");\n const ids = (await run(pick, on)).rows.map((row) => str(row.id));\n if (ids.length === 0) return NONE;\n\n const update = stmt().raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(\" WHERE id IN (\").list(ids).raw(\")\");\n await run(update, on);\n return readClaimed(args.claimToken, on);\n });\n } else {\n const update = stmt();\n if (strategy === \"derived-update\") {\n // The derived table is materialised, which is what lets MySQL update the table it\n // selects from; the outer predicate is re-evaluated on the locked row.\n update.raw(`UPDATE ${DELIVERY} d JOIN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update\n .raw(\" ORDER BY not_before ASC, id ASC LIMIT \")\n .value(args.limit)\n .raw(\") s ON s.id = d.id SET d.status = 'claimed', d.claimed_at = \")\n .value(now)\n .raw(\", d.claimed_by = \")\n .value(args.claimToken)\n .raw(\" WHERE d.status = 'pending' OR (d.status = 'claimed' AND d.claimed_at < \")\n .value(staleBefore)\n .raw(\")\");\n } else {\n update.raw(`UPDATE ${DELIVERY} SET `);\n claimSet(update).raw(` WHERE id IN (SELECT id FROM ${DELIVERY} WHERE `);\n eligible(update, args, now, staleBefore);\n update.raw(\" ORDER BY not_before ASC, id ASC LIMIT \").value(args.limit).raw(\")\");\n }\n const updated = await retryingLocks(dialect, () => run(update));\n result = updated.affectedRows > 0 ? await readClaimed(args.claimToken, exec) : NONE;\n }\n\n return result.rows.map((row) => ({\n id: str(row.id),\n notificationId: str(row.notification_id),\n channel: str(row.channel) as Channel,\n attempts: num(row.attempts),\n maxAttempts: num(row.max_attempts),\n notification: {\n userId: str(row.user_id),\n type: str(row.type),\n payload: dialect.readJson(row.payload),\n actorId: nullableStr(row.actor_id),\n },\n }));\n },\n\n async releaseDeliveries(releases: readonly DeliveryRelease[]) {\n if (releases.length === 0) return;\n const now = new Date();\n\n // No claimed_by predicate (RFC 0003 §6). `status` before `attempts`: MySQL evaluates SET\n // left to right with updated values, and the CASE needs the old count.\n await atomically(async (on) => {\n for (const { id, outcome, nextAttemptAt } of releases) {\n const result = outcome.result;\n const retryable = outcome.result === \"failed\" && outcome.retryable;\n const error =\n outcome.result === \"failed\"\n ? outcome.error.slice(0, 2000)\n : outcome.result === \"skipped\"\n ? outcome.reason.slice(0, 2000)\n : null;\n\n await run(\n stmt()\n .raw(`UPDATE ${DELIVERY} SET status = CASE WHEN `)\n .value(result)\n .raw(\" = 'sent' THEN 'sent' WHEN \")\n .value(result)\n .raw(\" = 'skipped' THEN 'skipped' WHEN \")\n .value(retryable)\n .raw(\" = 1 AND attempts + 1 < max_attempts THEN 'pending' ELSE 'failed' END\")\n .raw(\", attempts = CASE WHEN \")\n .value(result)\n .raw(\" IN ('sent', 'skipped') THEN attempts ELSE attempts + 1 END\")\n .raw(\", last_error = \")\n .value(error)\n .raw(\", not_before = COALESCE(\")\n .value(nextAttemptAt ?? null)\n .raw(\", not_before), claimed_at = NULL, claimed_by = NULL, updated_at = \")\n .value(now)\n .raw(\" WHERE id = \")\n .value(id),\n on,\n );\n }\n });\n },\n\n async listNotifications(feed: FeedQuery): Promise<FeedPage> {\n const cursor = feed.cursor ? decodeCursor(feed.cursor) : null;\n\n const statement = stmt()\n .raw(`SELECT * FROM ${NOTIFICATION} WHERE user_id = `)\n .value(feed.userId)\n .raw(\" AND archived_at IS NULL\");\n if (feed.unreadOnly) statement.raw(\" AND read_at IS NULL\");\n if (cursor) {\n statement\n .raw(\" AND (created_at, id) < (\")\n .value(cursor.createdAt)\n .raw(\", \")\n .value(cursor.id)\n .raw(\")\");\n }\n statement.raw(\" ORDER BY created_at DESC, id DESC LIMIT \").value(feed.limit + 1);\n\n const { rows } = await run(statement);\n const page = rows.slice(0, feed.limit).map(toNotification);\n const last = page.at(-1);\n\n return {\n notifications: page,\n nextCursor: rows.length > feed.limit && last ? encodeCursor(last.createdAt, last.id) : null,\n };\n },\n\n async countUnseen(userId: string) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" AND seen_at IS NULL AND archived_at IS NULL\"),\n );\n return num(rows[0]?.count ?? 0);\n },\n\n async markSeen(userId: string, before: Date) {\n await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET seen_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND seen_at IS NULL AND created_at <= \")\n .value(before),\n );\n },\n\n async markRead(userId: string, notificationIds: readonly string[]) {\n if (notificationIds.length === 0) return 0;\n\n // Idempotent, so the answer is rows matched; MySQL's affectedRows reports rows changed.\n await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = COALESCE(read_at, `)\n .value(new Date())\n .raw(\") WHERE user_id = \")\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n const { rows } = await run(\n stmt()\n .raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `)\n .value(userId)\n .raw(\" AND id IN (\")\n .list(notificationIds)\n .raw(\")\"),\n );\n return num(rows[0]?.count ?? 0);\n },\n\n async markAllRead(userId: string) {\n const { affectedRows } = await run(\n stmt()\n .raw(`UPDATE ${NOTIFICATION} SET read_at = `)\n .value(new Date())\n .raw(\" WHERE user_id = \")\n .value(userId)\n .raw(\" AND read_at IS NULL\"),\n );\n return affectedRows;\n },\n\n async getFailedDeliveries(args: { since: Date; limit: number }) {\n const { rows } = await run(\n stmt()\n .raw(`SELECT * FROM ${DELIVERY} WHERE status = 'failed' AND updated_at >= `)\n .value(args.since)\n .raw(\" ORDER BY updated_at DESC LIMIT \")\n .value(args.limit),\n );\n return rows.map(toDelivery);\n },\n\n async queryTable(table: string, where: WhereClause, queryOptions: QueryOptions) {\n const statement = stmt().raw(`SELECT * FROM ${q(table)}`);\n appendWhere(statement, where);\n if (queryOptions.orderBy) {\n const direction = queryOptions.orderBy.direction === \"desc\" ? \"DESC\" : \"ASC\";\n statement.raw(` ORDER BY ${q(toSnakeCase(queryOptions.orderBy.field))} ${direction}`);\n }\n if (queryOptions.limit) statement.raw(\" LIMIT \").value(queryOptions.limit);\n return [...(await run(statement)).rows];\n },\n\n async insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ) {\n if (rows.length === 0) return 0;\n const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) return 0;\n\n const statement = stmt().raw(\n `INSERT INTO ${q(table)} (${columns.map((c) => q(toSnakeCase(c))).join(\", \")}) VALUES `,\n );\n rows.forEach((row, index) => {\n if (index > 0) statement.raw(\", \");\n statement\n .raw(\"(\")\n .list(columns.map((column) => row[column]))\n .raw(\")\");\n });\n\n if (onConflict && onConflict.length > 0) {\n statement.raw(\n dialect.upsertClause(\n onConflict.map((f) => q(toSnakeCase(f))),\n columns.filter((c) => !onConflict.includes(c)).map((c) => q(toSnakeCase(c))),\n ),\n );\n await run(statement);\n // MySQL reports 2 for an updated row; the rows handed in is the honest count.\n return rows.length;\n }\n\n return (await run(statement)).affectedRows;\n },\n\n async updateRows(table: string, where: WhereClause, set: Record<string, unknown>) {\n const assignments = Object.entries(set);\n if (assignments.length === 0) return 0;\n\n const statement = stmt().raw(`UPDATE ${q(table)} SET `);\n assignments.forEach(([field, value], index) => {\n if (index > 0) statement.raw(\", \");\n statement.raw(`${q(toSnakeCase(field))} = `).value(value);\n });\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n\n async deleteRows(table: string, where: WhereClause) {\n const statement = stmt().raw(`DELETE FROM ${q(table)}`);\n appendWhere(statement, where);\n return (await run(statement)).affectedRows;\n },\n };\n}\n","import type { Placeholder, Statement } from \"./statement\";\nimport { quoteWith } from \"./statement\";\n\nexport type Row = Record<string, unknown>;\n\nexport type SqlResult = {\n rows: readonly Row[];\n /** Rows an INSERT/UPDATE/DELETE touched; drivers without RETURNING report through this. */\n affectedRows: number;\n};\n\n/** The whole driver contract for the MySQL and SQLite adapters. */\nexport type SqlExec = (text: string, params: readonly unknown[]) => Promise<SqlResult>;\n\n/** How a claim takes its rows. RFC 0005 §3. */\nexport type ClaimStrategy = \"skip-locked\" | \"derived-update\" | \"subquery-update\";\n\nexport type Dialect = {\n name: string;\n placeholder: Placeholder;\n quote: (identifier: string) => string;\n bind: (value: unknown) => unknown;\n readDate: (value: unknown) => Date;\n readNullableDate: (value: unknown) => Date | null;\n readJson: (value: unknown) => unknown;\n /** Appends `column IS DISTINCT FROM value` in the engine's spelling. */\n distinctFrom: (statement: Statement, column: string, value: unknown) => void;\n /** The clause after a multi-row INSERT that makes it an upsert. Columns arrive quoted. */\n upsertClause: (conflict: readonly string[], update: readonly string[]) => string;\n isDuplicateKey: (error: unknown) => boolean;\n /** A deadlock or busy lock the engine asks the caller to retry; the statement was rolled back. */\n isRetryableLock: (error: unknown) => boolean;\n claim: { withTransaction: ClaimStrategy; withoutTransaction: ClaimStrategy };\n};\n\nconst parseUtc = (value: unknown): Date => {\n if (value instanceof Date) return value;\n const text = String(value).trim();\n // \"2026-09-23 10:11:12.345\" from a dateStrings driver carries no zone; it was written as UTC.\n return new Date(/^\\d{4}-\\d{2}-\\d{2} /.test(text) ? `${text.replace(\" \", \"T\")}Z` : text);\n};\n\nconst readJson = (value: unknown) => (typeof value === \"string\" ? JSON.parse(value) : value);\n\nconst bindCommon = (value: unknown) => {\n if (typeof value === \"boolean\") return value ? 1 : 0;\n if (value === undefined) return null;\n return value;\n};\n\nconst code = (error: unknown) => (error as { code?: unknown } | null)?.code;\nconst errno = (error: unknown) => (error as { errno?: unknown } | null)?.errno;\n\nexport const mysqlDialect: Dialect = {\n name: \"mysql\",\n placeholder: \"question\",\n quote: quoteWith(\"`\"),\n bind: (value) =>\n value instanceof Date ? value.toISOString().slice(0, 23).replace(\"T\", \" \") : bindCommon(value),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`NOT (${column} <=> `).value(value).raw(\")\");\n },\n // VALUES() rather than the 8.0.19 `AS new` alias: it also works on MariaDB.\n upsertClause: (conflict, update) =>\n ` ON DUPLICATE KEY UPDATE ${(\n update.length === 0\n ? [`${conflict[0]} = ${conflict[0]}`]\n : update.map((column) => `${column} = VALUES(${column})`)\n ).join(\", \")}`,\n isDuplicateKey: (error) => code(error) === \"ER_DUP_ENTRY\" || errno(error) === 1062,\n isRetryableLock: (error) =>\n code(error) === \"ER_LOCK_DEADLOCK\" ||\n errno(error) === 1213 ||\n code(error) === \"ER_LOCK_WAIT_TIMEOUT\" ||\n errno(error) === 1205,\n claim: { withTransaction: \"skip-locked\", withoutTransaction: \"derived-update\" },\n};\n\nexport const sqliteDialect: Dialect = {\n name: \"sqlite\",\n placeholder: \"question\",\n quote: quoteWith('\"'),\n bind: (value) => (value instanceof Date ? value.toISOString() : bindCommon(value)),\n readDate: parseUtc,\n readNullableDate: (value) => (value == null ? null : parseUtc(value)),\n readJson,\n distinctFrom: (statement, column, value) => {\n statement.raw(`${column} IS NOT `).value(value);\n },\n upsertClause: (conflict, update) =>\n ` ON CONFLICT (${conflict.join(\", \")}) DO ${\n update.length === 0\n ? \"NOTHING\"\n : `UPDATE SET ${update.map((column) => `${column} = excluded.${column}`).join(\", \")}`\n }`,\n isDuplicateKey: (error) =>\n code(error) === \"SQLITE_CONSTRAINT_UNIQUE\" ||\n /UNIQUE constraint failed/i.test(String((error as Error | null)?.message ?? \"\")),\n isRetryableLock: (error) =>\n code(error) === \"SQLITE_BUSY\" ||\n /database is locked/i.test(String((error as Error | null)?.message ?? \"\")),\n claim: { withTransaction: \"subquery-update\", withoutTransaction: \"subquery-update\" },\n};\n","import type { DatabaseAdapter } from \"../../core/adapter\";\nimport { createSqlAdapter, type SqlAdapterOptions } from \"../sql/adapter\";\nimport { mysqlDialect, type SqlExec } from \"../sql/dialect\";\n\nexport type MysqlAdapterOptions = SqlAdapterOptions;\n\n/**\n * MySQL 8 or MariaDB through any driver. With `transaction` a claim uses\n * FOR UPDATE SKIP LOCKED; without it a lock-free UPDATE that never double-claims. RFC 0005 §3.\n */\nexport function mysqlAdapter(query: SqlExec, options: MysqlAdapterOptions = {}): DatabaseAdapter {\n return createSqlAdapter(mysqlDialect, query, options);\n}\n","import type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"../core/plugin\";\nimport { toSnakeCase } from \"./declaration\";\n\nconst quote = (value: string) => `\\`${value.split(\"`\").join(\"``\")}\\``;\n\n/** InnoDB caps a key at 3072 bytes: 768 utf8mb4 chars for one column, 255 each for three. TEXT cannot be a key or carry a default. */\nfunction stringType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n const keys = [\n ...(table.primaryKey ? [table.primaryKey] : []),\n ...(table.indexes ?? []).map((index) => index.on),\n ].filter((columns) => columns.includes(field));\n\n if (keys.some((columns) => columns.length > 1)) return \"VARCHAR(255)\";\n if (keys.length > 0) return \"VARCHAR(768)\";\n if (spec.default !== undefined) return \"VARCHAR(255)\";\n return \"TEXT\";\n}\n\nfunction columnType(field: string, table: TableDeclaration, spec: FieldDeclaration): string {\n switch (spec.type) {\n case \"string\":\n return stringType(field, table, spec);\n case \"number\":\n return \"INT\";\n case \"boolean\":\n return \"TINYINT(1)\";\n case \"date\":\n return \"DATETIME(3)\";\n case \"json\":\n return \"JSON\";\n }\n}\n\nconst defaultClause = (spec: FieldDeclaration): string => {\n if (spec.defaultNow) return \" DEFAULT CURRENT_TIMESTAMP(3)\";\n if (spec.default === undefined) return \"\";\n if (typeof spec.default === \"string\") return ` DEFAULT '${spec.default.replace(/'/g, \"''\")}'`;\n if (typeof spec.default === \"boolean\") return ` DEFAULT ${spec.default ? 1 : 0}`;\n return ` DEFAULT ${spec.default}`;\n};\n\n/** One column's definition, as CREATE TABLE and ALTER TABLE ... ADD COLUMN both need it. */\nexport function mysqlColumnDefinition(\n field: string,\n table: TableDeclaration,\n spec: FieldDeclaration,\n): string {\n const nullability = spec.required ? \" NOT NULL\" : \"\";\n return `${quote(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;\n}\n\n/** MySQL DDL, one CREATE TABLE IF NOT EXISTS per table with indexes inline (no CREATE INDEX IF NOT EXISTS). */\nexport function renderMysqlDdl(schema: SchemaDeclaration, prefix = \"\"): string[] {\n const statements: string[] = [];\n\n for (const table of Object.values(schema)) {\n const lines = Object.entries(table.fields).map(\n ([field, spec]) => ` ${mysqlColumnDefinition(field, table, spec)}`,\n );\n\n if (table.primaryKey?.length) {\n lines.push(\n ` PRIMARY KEY (${table.primaryKey.map((f) => quote(toSnakeCase(f))).join(\", \")})`,\n );\n }\n\n for (const index of table.indexes ?? []) {\n const name = quote(prefix + (index.name ?? `${table.tableName}_${index.on.join(\"_\")}_idx`));\n const columns = index.on.map((f) => quote(toSnakeCase(f))).join(\", \");\n lines.push(` ${index.unique ? \"UNIQUE \" : \"\"}INDEX ${name} (${columns})`);\n }\n\n statements.push(\n `CREATE TABLE IF NOT EXISTS ${quote(prefix + table.tableName)} (\\n${lines.join(\",\\n\")}\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,\n );\n }\n\n return statements;\n}\n","import type { SchemaDeclaration } from \"../../core/plugin\";\nimport { coreSchema } from \"../../schema/declaration\";\nimport { renderMysqlDdl } from \"../../schema/render-mysql\";\nimport type { Row, SqlExec, SqlResult } from \"../sql/dialect\";\n\n/** The slice of a mysql2 pool or connection this needs, structurally. */\nexport type Mysql2Queryable = {\n // biome-ignore lint/suspicious/noExplicitAny: must accept mysql2's own overloaded signature\n query(sql: string, values?: any): Promise<[unknown, unknown]>;\n};\n\nexport type Mysql2PoolLike = Mysql2Queryable & {\n getConnection(): Promise<\n Mysql2Queryable & {\n beginTransaction(): Promise<void>;\n commit(): Promise<void>;\n rollback(): Promise<void>;\n release(): void;\n }\n >;\n};\n\n/** mysql2 returns rows for a SELECT and a ResultSetHeader for anything else. */\nfunction toResult([result]: [unknown, unknown]): SqlResult {\n if (Array.isArray(result)) return { rows: result as Row[], affectedRows: 0 };\n const header = result as { affectedRows?: unknown } | null;\n return { rows: [], affectedRows: Number(header?.affectedRows ?? 0) };\n}\n\n/** The SqlExec for a mysql2 pool. Create the pool with `timezone: \"Z\"`, or DATETIME comes back shifted. */\nexport const mysql2Query =\n (pool: Mysql2Queryable): SqlExec =>\n async (text, params) =>\n toResult(await pool.query(text, [...params]));\n\n/** The `transaction` option for mysql2. */\nexport function mysqlTransaction(pool: Mysql2PoolLike) {\n return async <T>(fn: (exec: SqlExec) => Promise<T>): Promise<T> => {\n const connection = await pool.getConnection();\n try {\n await connection.beginTransaction();\n const result = await fn(async (text, params) =>\n toResult(await connection.query(text, [...params])),\n );\n await connection.commit();\n return result;\n } catch (error) {\n await connection.rollback();\n throw error;\n } finally {\n connection.release();\n }\n };\n}\n\nexport type CreateMysqlTablesOptions = {\n /** Must match the prefix given to the adapter. */\n prefix?: string;\n /** Plugin schemas, e.g. `pushSchema`. */\n plugins?: readonly SchemaDeclaration[];\n};\n\n/** Creates core and plugin tables, IF NOT EXISTS. Bootstrap, not migration. */\nexport async function createMysqlTables(\n query: SqlExec,\n options: CreateMysqlTablesOptions = {},\n): Promise<number> {\n let count = 0;\n for (const schema of [coreSchema, ...(options.plugins ?? [])]) {\n for (const statement of renderMysqlDdl(schema, options.prefix)) {\n await query(statement, []);\n count += 1;\n }\n }\n return count;\n}\n"]}
@@ -1,6 +1,7 @@
1
- import { S as SqlAdapterOptions, a as SqlExec } from '../adapter-B7R4rXtJ.cjs';
2
- export { b as MysqlResult } from '../adapter-B7R4rXtJ.cjs';
1
+ import { S as SqlExec } from '../dialect-CvBJSydR.cjs';
2
+ export { a as MysqlResult } from '../dialect-CvBJSydR.cjs';
3
3
  import { D as DatabaseAdapter } from '../adapter-CV5Yt2c6.cjs';
4
+ import { S as SqlAdapterOptions } from '../adapter-C3uFRpEN.cjs';
4
5
  import { S as SchemaDeclaration } from '../plugin-6S8tpH0n.cjs';
5
6
 
6
7
  type MysqlAdapterOptions = SqlAdapterOptions;
@@ -1,6 +1,7 @@
1
- import { S as SqlAdapterOptions, a as SqlExec } from '../adapter-B7R4rXtJ.js';
2
- export { b as MysqlResult } from '../adapter-B7R4rXtJ.js';
1
+ import { S as SqlExec } from '../dialect-CvBJSydR.js';
2
+ export { a as MysqlResult } from '../dialect-CvBJSydR.js';
3
3
  import { D as DatabaseAdapter } from '../adapter-CV5Yt2c6.js';
4
+ import { S as SqlAdapterOptions } from '../adapter-DoCRPi9I.js';
4
5
  import { S as SchemaDeclaration } from '../plugin-C4fikLl7.js';
5
6
 
6
7
  type MysqlAdapterOptions = SqlAdapterOptions;
@@ -1,6 +1,6 @@
1
1
  import { createSqlAdapter, mysqlDialect } from '../chunk-2EYKPF5A.js';
2
2
  import '../chunk-CK5UPVYP.js';
3
- import { renderMysqlDdl } from '../chunk-M4R77BJD.js';
3
+ import { renderMysqlDdl } from '../chunk-UM2L26PV.js';
4
4
  import '../chunk-3VOQOGDR.js';
5
5
  import { coreSchema } from '../chunk-AEYYTE5I.js';
6
6
  import '../chunk-PTGHMCCG.js';
@@ -1,7 +1,7 @@
1
1
  import { quote, Statement } from '../chunk-CK5UPVYP.js';
2
- import { renderPostgresDdl } from '../chunk-XIDVLYNQ.js';
3
- import '../chunk-M4R77BJD.js';
4
- import '../chunk-HY47TQMY.js';
2
+ import { renderPostgresDdl } from '../chunk-EROWLUL4.js';
3
+ import '../chunk-UM2L26PV.js';
4
+ import '../chunk-HSB5T3LD.js';
5
5
  import { createBridgedSignals } from '../chunk-ORZKH224.js';
6
6
  import { isOperator } from '../chunk-3VOQOGDR.js';
7
7
  import { toSnakeCase, coreSchema } from '../chunk-AEYYTE5I.js';
@@ -501,7 +501,7 @@ function sqliteAdapter(query, options = {}) {
501
501
  }
502
502
 
503
503
  // src/schema/render-sqlite.ts
504
- var SQL_TYPE = {
504
+ var SQLITE_TYPE = {
505
505
  string: "TEXT",
506
506
  number: "INTEGER",
507
507
  boolean: "INTEGER",
@@ -518,14 +518,17 @@ var defaultClause = (spec) => {
518
518
  if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
519
519
  return ` DEFAULT ${spec.default}`;
520
520
  };
521
+ function sqliteColumnDefinition(field, spec) {
522
+ const nullability = spec.required ? " NOT NULL" : "";
523
+ return `${quote2(toSnakeCase(field))} ${SQLITE_TYPE[spec.type]}${nullability}${defaultClause(spec)}`;
524
+ }
521
525
  function renderSqliteDdl(schema, prefix = "") {
522
526
  const statements = [];
523
527
  for (const table of Object.values(schema)) {
524
528
  const name = quote2(prefix + table.tableName);
525
- const columns = Object.entries(table.fields).map(([field, spec]) => {
526
- const nullability = spec.required ? " NOT NULL" : "";
527
- return ` ${quote2(toSnakeCase(field))} ${SQL_TYPE[spec.type]}${nullability}${defaultClause(spec)}`;
528
- });
529
+ const columns = Object.entries(table.fields).map(
530
+ ([field, spec]) => ` ${sqliteColumnDefinition(field, spec)}`
531
+ );
529
532
  if (table.primaryKey?.length) {
530
533
  columns.push(
531
534
  ` PRIMARY KEY (${table.primaryKey.map((f) => quote2(toSnakeCase(f))).join(", ")})`