easy-ping 0.3.0 → 0.5.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 (127) hide show
  1. package/README.md +174 -82
  2. package/dist/adapter-B7R4rXtJ.d.cts +17 -0
  3. package/dist/adapter-B7R4rXtJ.d.ts +17 -0
  4. package/dist/{adapter-BfLe2J0t.d.cts → adapter-D3lTy-4E.d.cts} +6 -32
  5. package/dist/{adapter-BfLe2J0t.d.ts → adapter-D3lTy-4E.d.ts} +6 -32
  6. package/dist/adapters/drizzle.cjs +4 -4
  7. package/dist/adapters/drizzle.cjs.map +1 -1
  8. package/dist/adapters/drizzle.d.cts +35 -1
  9. package/dist/adapters/drizzle.d.ts +35 -1
  10. package/dist/adapters/drizzle.js +5 -5
  11. package/dist/adapters/drizzle.js.map +1 -1
  12. package/dist/adapters/mongodb.cjs +8 -7
  13. package/dist/adapters/mongodb.cjs.map +1 -1
  14. package/dist/adapters/mongodb.d.cts +4 -17
  15. package/dist/adapters/mongodb.d.ts +4 -17
  16. package/dist/adapters/mongodb.js +7 -7
  17. package/dist/adapters/mongodb.js.map +1 -1
  18. package/dist/adapters/mysql.cjs +605 -0
  19. package/dist/adapters/mysql.cjs.map +1 -0
  20. package/dist/adapters/mysql.d.cts +38 -0
  21. package/dist/adapters/mysql.d.ts +38 -0
  22. package/dist/adapters/mysql.js +51 -0
  23. package/dist/adapters/mysql.js.map +1 -0
  24. package/dist/adapters/postgres.cjs +19 -11
  25. package/dist/adapters/postgres.cjs.map +1 -1
  26. package/dist/adapters/postgres.d.cts +9 -54
  27. package/dist/adapters/postgres.d.ts +9 -54
  28. package/dist/adapters/postgres.js +6 -41
  29. package/dist/adapters/postgres.js.map +1 -1
  30. package/dist/adapters/sqlite.cjs +593 -0
  31. package/dist/adapters/sqlite.cjs.map +1 -0
  32. package/dist/adapters/sqlite.d.cts +37 -0
  33. package/dist/adapters/sqlite.d.ts +37 -0
  34. package/dist/adapters/sqlite.js +53 -0
  35. package/dist/adapters/sqlite.js.map +1 -0
  36. package/dist/browser.cjs.map +1 -1
  37. package/dist/browser.d.cts +2 -14
  38. package/dist/browser.d.ts +2 -14
  39. package/dist/browser.js.map +1 -1
  40. package/dist/chunk-2EYKPF5A.js +393 -0
  41. package/dist/chunk-2EYKPF5A.js.map +1 -0
  42. package/dist/{chunk-OZSOIX23.js → chunk-3VOQOGDR.js} +54 -12
  43. package/dist/chunk-3VOQOGDR.js.map +1 -0
  44. package/dist/{chunk-NYDVNMKG.js → chunk-AEYYTE5I.js} +4 -3
  45. package/dist/chunk-AEYYTE5I.js.map +1 -0
  46. package/dist/chunk-BECMTNK7.js +315 -0
  47. package/dist/chunk-BECMTNK7.js.map +1 -0
  48. package/dist/chunk-CK5UPVYP.js +48 -0
  49. package/dist/chunk-CK5UPVYP.js.map +1 -0
  50. package/dist/{chunk-BV3U542P.js → chunk-E4IZ2CQC.js} +2 -2
  51. package/dist/chunk-E4IZ2CQC.js.map +1 -0
  52. package/dist/chunk-GJNLOQJS.js +226 -0
  53. package/dist/chunk-GJNLOQJS.js.map +1 -0
  54. package/dist/chunk-HY47TQMY.js +52 -0
  55. package/dist/chunk-HY47TQMY.js.map +1 -0
  56. package/dist/chunk-M4R77BJD.js +64 -0
  57. package/dist/chunk-M4R77BJD.js.map +1 -0
  58. package/dist/{chunk-RBDW4S6F.js → chunk-XIDVLYNQ.js} +14 -4
  59. package/dist/chunk-XIDVLYNQ.js.map +1 -0
  60. package/dist/client.cjs.map +1 -1
  61. package/dist/client.js.map +1 -1
  62. package/dist/index.cjs +362 -64
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +56 -37
  65. package/dist/index.d.ts +56 -37
  66. package/dist/index.js +120 -359
  67. package/dist/index.js.map +1 -1
  68. package/dist/node.cjs +27 -6
  69. package/dist/node.cjs.map +1 -1
  70. package/dist/node.d.cts +8 -2
  71. package/dist/node.d.ts +8 -2
  72. package/dist/node.js +26 -6
  73. package/dist/node.js.map +1 -1
  74. package/dist/{plugin-CmPOFVmn.d.ts → plugin-CHd2ZiGC.d.cts} +37 -25
  75. package/dist/{plugin-f5KaFWSN.d.cts → plugin-Cg9usuUi.d.ts} +37 -25
  76. package/dist/plugins/digests.cjs +1 -4
  77. package/dist/plugins/digests.cjs.map +1 -1
  78. package/dist/plugins/digests.d.cts +6 -31
  79. package/dist/plugins/digests.d.ts +6 -31
  80. package/dist/plugins/digests.js +1 -4
  81. package/dist/plugins/digests.js.map +1 -1
  82. package/dist/plugins/preferences-client.cjs.map +1 -1
  83. package/dist/plugins/preferences-client.d.cts +2 -2
  84. package/dist/plugins/preferences-client.d.ts +2 -2
  85. package/dist/plugins/preferences-client.js +1 -1
  86. package/dist/plugins/preferences-react.cjs.map +1 -1
  87. package/dist/plugins/preferences-react.js.map +1 -1
  88. package/dist/plugins/preferences.cjs +101 -17
  89. package/dist/plugins/preferences.cjs.map +1 -1
  90. package/dist/plugins/preferences.d.cts +4 -2
  91. package/dist/plugins/preferences.d.ts +4 -2
  92. package/dist/plugins/preferences.js +36 -9
  93. package/dist/plugins/preferences.js.map +1 -1
  94. package/dist/plugins/push.cjs +198 -40
  95. package/dist/plugins/push.cjs.map +1 -1
  96. package/dist/plugins/push.d.cts +20 -18
  97. package/dist/plugins/push.d.ts +20 -18
  98. package/dist/plugins/push.js +141 -41
  99. package/dist/plugins/push.js.map +1 -1
  100. package/dist/providers/web-push.cjs +37 -11
  101. package/dist/providers/web-push.cjs.map +1 -1
  102. package/dist/providers/web-push.d.cts +10 -11
  103. package/dist/providers/web-push.d.ts +10 -11
  104. package/dist/providers/web-push.js +37 -12
  105. package/dist/providers/web-push.js.map +1 -1
  106. package/dist/react.cjs.map +1 -1
  107. package/dist/react.d.cts +1 -4
  108. package/dist/react.d.ts +1 -4
  109. package/dist/react.js.map +1 -1
  110. package/dist/schema.cjs +121 -2
  111. package/dist/schema.cjs.map +1 -1
  112. package/dist/schema.d.cts +25 -14
  113. package/dist/schema.d.ts +25 -14
  114. package/dist/schema.js +4 -2
  115. package/dist/testing.cjs +3 -19
  116. package/dist/testing.cjs.map +1 -1
  117. package/dist/testing.d.cts +4 -22
  118. package/dist/testing.d.ts +4 -22
  119. package/dist/testing.js +3 -19
  120. package/dist/testing.js.map +1 -1
  121. package/package.json +12 -1
  122. package/dist/chunk-BV3U542P.js.map +0 -1
  123. package/dist/chunk-NYDVNMKG.js.map +0 -1
  124. package/dist/chunk-OZSOIX23.js.map +0 -1
  125. package/dist/chunk-RBDW4S6F.js.map +0 -1
  126. package/dist/chunk-W3BMEPPQ.js +0 -50
  127. package/dist/chunk-W3BMEPPQ.js.map +0 -1
@@ -0,0 +1,393 @@
1
+ import { quoteWith, Statement } from './chunk-CK5UPVYP.js';
2
+ import { isOperator } from './chunk-3VOQOGDR.js';
3
+ import { toSnakeCase } from './chunk-AEYYTE5I.js';
4
+ import { decodeBase64Url, encodeBase64Url } from './chunk-PTGHMCCG.js';
5
+
6
+ // src/adapters/sql/adapter.ts
7
+ var str = (value) => String(value);
8
+ var nullableStr = (value) => value == null ? null : String(value);
9
+ var num = (value) => Number(value);
10
+ var encodeCursor = (createdAt, id) => encodeBase64Url(`${createdAt.toISOString()}|${id}`);
11
+ function decodeCursor(cursor) {
12
+ const decoded = decodeBase64Url(cursor);
13
+ if (!decoded) return null;
14
+ const [iso, id] = decoded.split("|");
15
+ if (!iso || !id) return null;
16
+ const createdAt = new Date(iso);
17
+ return Number.isNaN(createdAt.getTime()) ? null : { createdAt, id };
18
+ }
19
+ var NONE = { rows: [], affectedRows: 0 };
20
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
21
+ async function retryingLocks(dialect, attempt) {
22
+ for (let tries = 0; ; tries += 1) {
23
+ try {
24
+ return await attempt();
25
+ } catch (error) {
26
+ if (tries >= 5 || !dialect.isRetryableLock(error)) throw error;
27
+ await sleep(5 + Math.random() * 20 * (tries + 1));
28
+ }
29
+ }
30
+ }
31
+ function createSqlAdapter(dialect, exec, options = {}) {
32
+ const prefix = options.prefix ?? "";
33
+ const q = dialect.quote;
34
+ const NOTIFICATION = q(`${prefix}notification`);
35
+ const DELIVERY = q(`${prefix}notification_delivery`);
36
+ const stmt = () => new Statement({ placeholder: dialect.placeholder, bind: dialect.bind });
37
+ const run = (statement, on = exec) => on(statement.text, statement.params);
38
+ const atomically = (fn) => options.transaction ? options.transaction(fn) : fn(exec);
39
+ const toNotification = (row) => ({
40
+ id: str(row.id),
41
+ userId: str(row.user_id),
42
+ type: str(row.type),
43
+ payload: dialect.readJson(row.payload),
44
+ actorId: nullableStr(row.actor_id),
45
+ groupKey: nullableStr(row.group_key),
46
+ dedupeKey: nullableStr(row.dedupe_key),
47
+ seenAt: dialect.readNullableDate(row.seen_at),
48
+ readAt: dialect.readNullableDate(row.read_at),
49
+ archivedAt: dialect.readNullableDate(row.archived_at),
50
+ createdAt: dialect.readDate(row.created_at)
51
+ });
52
+ const toDelivery = (row) => ({
53
+ id: str(row.id),
54
+ notificationId: str(row.notification_id),
55
+ channel: str(row.channel),
56
+ status: str(row.status),
57
+ attempts: num(row.attempts),
58
+ maxAttempts: num(row.max_attempts),
59
+ notBefore: dialect.readDate(row.not_before),
60
+ claimedAt: dialect.readNullableDate(row.claimed_at),
61
+ claimedBy: nullableStr(row.claimed_by),
62
+ lastError: nullableStr(row.last_error),
63
+ updatedAt: dialect.readDate(row.updated_at)
64
+ });
65
+ function appendWhere(statement, where) {
66
+ const entries = Object.entries(where);
67
+ if (entries.length === 0) return;
68
+ statement.raw(" WHERE ");
69
+ entries.forEach(([field, condition], index) => {
70
+ if (index > 0) statement.raw(" AND ");
71
+ const column = q(toSnakeCase(field));
72
+ if (condition === null) {
73
+ statement.raw(`${column} IS NULL`);
74
+ return;
75
+ }
76
+ if (!isOperator(condition)) {
77
+ statement.raw(`${column} = `).value(condition);
78
+ return;
79
+ }
80
+ const operator = condition;
81
+ if ("in" in operator) {
82
+ const list = operator.in;
83
+ if (list.length === 0) statement.raw("1 = 0");
84
+ else statement.raw(`${column} IN (`).list(list).raw(")");
85
+ return;
86
+ }
87
+ if ("lt" in operator) statement.raw(`${column} < `).value(operator.lt);
88
+ else if ("lte" in operator) statement.raw(`${column} <= `).value(operator.lte);
89
+ else if ("gt" in operator) statement.raw(`${column} > `).value(operator.gt);
90
+ else if ("gte" in operator) statement.raw(`${column} >= `).value(operator.gte);
91
+ else if ("not" in operator) {
92
+ if (operator.not === null) statement.raw(`${column} IS NOT NULL`);
93
+ else dialect.distinctFrom(statement, column, operator.not);
94
+ } else statement.raw("1 = 1");
95
+ });
96
+ }
97
+ function eligible(statement, args, now, staleBefore) {
98
+ statement.raw("(status = 'pending' OR (status = 'claimed' AND claimed_at < ").value(staleBefore).raw(")) AND not_before <= ").value(now).raw(" AND attempts < max_attempts");
99
+ if (args.channels && args.channels.length > 0) {
100
+ statement.raw(" AND channel IN (").list(args.channels).raw(")");
101
+ }
102
+ if (args.ids && args.ids.length > 0) {
103
+ statement.raw(" AND id IN (").list(args.ids).raw(")");
104
+ }
105
+ }
106
+ const readClaimed = (token, on) => run(
107
+ stmt().raw(
108
+ `SELECT c.id, c.notification_id, c.channel, c.attempts, c.max_attempts, n.user_id, n.type, n.payload, n.actor_id FROM ${DELIVERY} c JOIN ${NOTIFICATION} n ON n.id = c.notification_id WHERE c.claimed_by = `
109
+ ).value(token).raw(" AND c.status = 'claimed' ORDER BY c.not_before ASC, c.id ASC"),
110
+ on
111
+ );
112
+ return {
113
+ name: dialect.name,
114
+ naming: "snake_case",
115
+ serializesJson: true,
116
+ async createNotifications(input) {
117
+ if (input.length === 0) return { created: [], deduped: [] };
118
+ const now = /* @__PURE__ */ new Date();
119
+ return atomically(async (on) => {
120
+ const created = [];
121
+ const deduped = [];
122
+ for (const row of input) {
123
+ if (row.dedupeKey) {
124
+ const existing = await run(
125
+ stmt().raw(`SELECT id FROM ${NOTIFICATION} WHERE user_id = `).value(row.userId).raw(" AND dedupe_key = ").value(row.dedupeKey),
126
+ on
127
+ );
128
+ if (existing.rows.length > 0) {
129
+ deduped.push(row.id);
130
+ continue;
131
+ }
132
+ }
133
+ try {
134
+ await run(
135
+ stmt().raw(
136
+ `INSERT INTO ${NOTIFICATION} (id, user_id, type, payload, actor_id, group_key, dedupe_key, created_at) VALUES (`
137
+ ).list([
138
+ row.id,
139
+ row.userId,
140
+ row.type,
141
+ JSON.stringify(row.payload ?? null),
142
+ row.actorId ?? null,
143
+ row.groupKey ?? null,
144
+ row.dedupeKey ?? null,
145
+ now
146
+ ]).raw(")"),
147
+ on
148
+ );
149
+ } catch (error) {
150
+ if (row.dedupeKey && dialect.isDuplicateKey(error)) {
151
+ deduped.push(row.id);
152
+ continue;
153
+ }
154
+ throw error;
155
+ }
156
+ created.push(row.id);
157
+ }
158
+ const createdSet = new Set(created);
159
+ const deliveries = input.filter((row) => createdSet.has(row.id)).flatMap((row) => row.deliveries.map((delivery) => ({ row, delivery })));
160
+ if (deliveries.length > 0) {
161
+ const insert = stmt().raw(
162
+ `INSERT INTO ${DELIVERY} (id, notification_id, channel, max_attempts, not_before, updated_at) VALUES `
163
+ );
164
+ deliveries.forEach(({ row, delivery }, index) => {
165
+ if (index > 0) insert.raw(", ");
166
+ insert.raw("(").list([
167
+ delivery.id,
168
+ row.id,
169
+ delivery.channel,
170
+ delivery.maxAttempts,
171
+ delivery.notBefore,
172
+ now
173
+ ]).raw(")");
174
+ });
175
+ await run(insert, on);
176
+ }
177
+ return { created, deduped };
178
+ });
179
+ },
180
+ async claimPendingDeliveries(args) {
181
+ const now = args.now ?? /* @__PURE__ */ new Date();
182
+ const staleBefore = new Date(now.getTime() - args.leaseMs);
183
+ const strategy = options.transaction ? dialect.claim.withTransaction : dialect.claim.withoutTransaction;
184
+ const claimSet = (statement) => statement.raw("status = 'claimed', claimed_at = ").value(now).raw(", claimed_by = ").value(args.claimToken);
185
+ let result;
186
+ if (strategy === "skip-locked") {
187
+ result = await atomically(async (on) => {
188
+ const pick = stmt().raw(`SELECT id FROM ${DELIVERY} WHERE `);
189
+ eligible(pick, args, now, staleBefore);
190
+ pick.raw(" ORDER BY not_before ASC, id ASC LIMIT ").value(args.limit).raw(" FOR UPDATE SKIP LOCKED");
191
+ const ids = (await run(pick, on)).rows.map((row) => str(row.id));
192
+ if (ids.length === 0) return NONE;
193
+ const update = stmt().raw(`UPDATE ${DELIVERY} SET `);
194
+ claimSet(update).raw(" WHERE id IN (").list(ids).raw(")");
195
+ await run(update, on);
196
+ return readClaimed(args.claimToken, on);
197
+ });
198
+ } else {
199
+ const update = stmt();
200
+ if (strategy === "derived-update") {
201
+ update.raw(`UPDATE ${DELIVERY} d JOIN (SELECT id FROM ${DELIVERY} WHERE `);
202
+ eligible(update, args, now, staleBefore);
203
+ update.raw(" ORDER BY not_before ASC, id ASC LIMIT ").value(args.limit).raw(") s ON s.id = d.id SET d.status = 'claimed', d.claimed_at = ").value(now).raw(", d.claimed_by = ").value(args.claimToken).raw(" WHERE d.status = 'pending' OR (d.status = 'claimed' AND d.claimed_at < ").value(staleBefore).raw(")");
204
+ } else {
205
+ update.raw(`UPDATE ${DELIVERY} SET `);
206
+ claimSet(update).raw(` WHERE id IN (SELECT id FROM ${DELIVERY} WHERE `);
207
+ eligible(update, args, now, staleBefore);
208
+ update.raw(" ORDER BY not_before ASC, id ASC LIMIT ").value(args.limit).raw(")");
209
+ }
210
+ const updated = await retryingLocks(dialect, () => run(update));
211
+ result = updated.affectedRows > 0 ? await readClaimed(args.claimToken, exec) : NONE;
212
+ }
213
+ return result.rows.map((row) => ({
214
+ id: str(row.id),
215
+ notificationId: str(row.notification_id),
216
+ channel: str(row.channel),
217
+ attempts: num(row.attempts),
218
+ maxAttempts: num(row.max_attempts),
219
+ notification: {
220
+ userId: str(row.user_id),
221
+ type: str(row.type),
222
+ payload: dialect.readJson(row.payload),
223
+ actorId: nullableStr(row.actor_id)
224
+ }
225
+ }));
226
+ },
227
+ async releaseDeliveries(releases) {
228
+ if (releases.length === 0) return;
229
+ const now = /* @__PURE__ */ new Date();
230
+ await atomically(async (on) => {
231
+ for (const { id, outcome, nextAttemptAt } of releases) {
232
+ const result = outcome.result;
233
+ const retryable = outcome.result === "failed" && outcome.retryable;
234
+ const error = outcome.result === "failed" ? outcome.error.slice(0, 2e3) : outcome.result === "skipped" ? outcome.reason.slice(0, 2e3) : null;
235
+ await run(
236
+ stmt().raw(`UPDATE ${DELIVERY} SET status = CASE WHEN `).value(result).raw(" = 'sent' THEN 'sent' WHEN ").value(result).raw(" = 'skipped' THEN 'skipped' WHEN ").value(retryable).raw(" = 1 AND attempts + 1 < max_attempts THEN 'pending' ELSE 'failed' END").raw(", attempts = CASE WHEN ").value(result).raw(" IN ('sent', 'skipped') THEN attempts ELSE attempts + 1 END").raw(", last_error = ").value(error).raw(", not_before = COALESCE(").value(nextAttemptAt ?? null).raw(", not_before), claimed_at = NULL, claimed_by = NULL, updated_at = ").value(now).raw(" WHERE id = ").value(id),
237
+ on
238
+ );
239
+ }
240
+ });
241
+ },
242
+ async listNotifications(feed) {
243
+ const cursor = feed.cursor ? decodeCursor(feed.cursor) : null;
244
+ const statement = stmt().raw(`SELECT * FROM ${NOTIFICATION} WHERE user_id = `).value(feed.userId).raw(" AND archived_at IS NULL");
245
+ if (feed.unreadOnly) statement.raw(" AND read_at IS NULL");
246
+ if (cursor) {
247
+ statement.raw(" AND (created_at, id) < (").value(cursor.createdAt).raw(", ").value(cursor.id).raw(")");
248
+ }
249
+ statement.raw(" ORDER BY created_at DESC, id DESC LIMIT ").value(feed.limit + 1);
250
+ const { rows } = await run(statement);
251
+ const page = rows.slice(0, feed.limit).map(toNotification);
252
+ const last = page.at(-1);
253
+ return {
254
+ notifications: page,
255
+ nextCursor: rows.length > feed.limit && last ? encodeCursor(last.createdAt, last.id) : null
256
+ };
257
+ },
258
+ async countUnseen(userId) {
259
+ const { rows } = await run(
260
+ stmt().raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `).value(userId).raw(" AND seen_at IS NULL AND archived_at IS NULL")
261
+ );
262
+ return num(rows[0]?.count ?? 0);
263
+ },
264
+ async markSeen(userId, before) {
265
+ await run(
266
+ stmt().raw(`UPDATE ${NOTIFICATION} SET seen_at = `).value(/* @__PURE__ */ new Date()).raw(" WHERE user_id = ").value(userId).raw(" AND seen_at IS NULL AND created_at <= ").value(before)
267
+ );
268
+ },
269
+ async markRead(userId, notificationIds) {
270
+ if (notificationIds.length === 0) return 0;
271
+ await run(
272
+ stmt().raw(`UPDATE ${NOTIFICATION} SET read_at = COALESCE(read_at, `).value(/* @__PURE__ */ new Date()).raw(") WHERE user_id = ").value(userId).raw(" AND id IN (").list(notificationIds).raw(")")
273
+ );
274
+ const { rows } = await run(
275
+ stmt().raw(`SELECT COUNT(*) AS count FROM ${NOTIFICATION} WHERE user_id = `).value(userId).raw(" AND id IN (").list(notificationIds).raw(")")
276
+ );
277
+ return num(rows[0]?.count ?? 0);
278
+ },
279
+ async markAllRead(userId) {
280
+ const { affectedRows } = await run(
281
+ stmt().raw(`UPDATE ${NOTIFICATION} SET read_at = `).value(/* @__PURE__ */ new Date()).raw(" WHERE user_id = ").value(userId).raw(" AND read_at IS NULL")
282
+ );
283
+ return affectedRows;
284
+ },
285
+ async getFailedDeliveries(args) {
286
+ const { rows } = await run(
287
+ stmt().raw(`SELECT * FROM ${DELIVERY} WHERE status = 'failed' AND updated_at >= `).value(args.since).raw(" ORDER BY updated_at DESC LIMIT ").value(args.limit)
288
+ );
289
+ return rows.map(toDelivery);
290
+ },
291
+ async queryTable(table, where, queryOptions) {
292
+ const statement = stmt().raw(`SELECT * FROM ${q(table)}`);
293
+ appendWhere(statement, where);
294
+ if (queryOptions.orderBy) {
295
+ const direction = queryOptions.orderBy.direction === "desc" ? "DESC" : "ASC";
296
+ statement.raw(` ORDER BY ${q(toSnakeCase(queryOptions.orderBy.field))} ${direction}`);
297
+ }
298
+ if (queryOptions.limit) statement.raw(" LIMIT ").value(queryOptions.limit);
299
+ return [...(await run(statement)).rows];
300
+ },
301
+ async insertRows(table, rows, onConflict) {
302
+ if (rows.length === 0) return 0;
303
+ const columns = Object.keys(rows[0] ?? {});
304
+ if (columns.length === 0) return 0;
305
+ const statement = stmt().raw(
306
+ `INSERT INTO ${q(table)} (${columns.map((c) => q(toSnakeCase(c))).join(", ")}) VALUES `
307
+ );
308
+ rows.forEach((row, index) => {
309
+ if (index > 0) statement.raw(", ");
310
+ statement.raw("(").list(columns.map((column) => row[column])).raw(")");
311
+ });
312
+ if (onConflict && onConflict.length > 0) {
313
+ statement.raw(
314
+ dialect.upsertClause(
315
+ onConflict.map((f) => q(toSnakeCase(f))),
316
+ columns.filter((c) => !onConflict.includes(c)).map((c) => q(toSnakeCase(c)))
317
+ )
318
+ );
319
+ await run(statement);
320
+ return rows.length;
321
+ }
322
+ return (await run(statement)).affectedRows;
323
+ },
324
+ async updateRows(table, where, set) {
325
+ const assignments = Object.entries(set);
326
+ if (assignments.length === 0) return 0;
327
+ const statement = stmt().raw(`UPDATE ${q(table)} SET `);
328
+ assignments.forEach(([field, value], index) => {
329
+ if (index > 0) statement.raw(", ");
330
+ statement.raw(`${q(toSnakeCase(field))} = `).value(value);
331
+ });
332
+ appendWhere(statement, where);
333
+ return (await run(statement)).affectedRows;
334
+ },
335
+ async deleteRows(table, where) {
336
+ const statement = stmt().raw(`DELETE FROM ${q(table)}`);
337
+ appendWhere(statement, where);
338
+ return (await run(statement)).affectedRows;
339
+ }
340
+ };
341
+ }
342
+
343
+ // src/adapters/sql/dialect.ts
344
+ var parseUtc = (value) => {
345
+ if (value instanceof Date) return value;
346
+ const text = String(value).trim();
347
+ return new Date(/^\d{4}-\d{2}-\d{2} /.test(text) ? `${text.replace(" ", "T")}Z` : text);
348
+ };
349
+ var readJson = (value) => typeof value === "string" ? JSON.parse(value) : value;
350
+ var bindCommon = (value) => {
351
+ if (typeof value === "boolean") return value ? 1 : 0;
352
+ if (value === void 0) return null;
353
+ return value;
354
+ };
355
+ var code = (error) => error?.code;
356
+ var errno = (error) => error?.errno;
357
+ var mysqlDialect = {
358
+ name: "mysql",
359
+ placeholder: "question",
360
+ quote: quoteWith("`"),
361
+ bind: (value) => value instanceof Date ? value.toISOString().slice(0, 23).replace("T", " ") : bindCommon(value),
362
+ readDate: parseUtc,
363
+ readNullableDate: (value) => value == null ? null : parseUtc(value),
364
+ readJson,
365
+ distinctFrom: (statement, column, value) => {
366
+ statement.raw(`NOT (${column} <=> `).value(value).raw(")");
367
+ },
368
+ // VALUES() rather than the 8.0.19 `AS new` alias: it also works on MariaDB.
369
+ upsertClause: (conflict, update) => ` ON DUPLICATE KEY UPDATE ${(update.length === 0 ? [`${conflict[0]} = ${conflict[0]}`] : update.map((column) => `${column} = VALUES(${column})`)).join(", ")}`,
370
+ isDuplicateKey: (error) => code(error) === "ER_DUP_ENTRY" || errno(error) === 1062,
371
+ isRetryableLock: (error) => code(error) === "ER_LOCK_DEADLOCK" || errno(error) === 1213 || code(error) === "ER_LOCK_WAIT_TIMEOUT" || errno(error) === 1205,
372
+ claim: { withTransaction: "skip-locked", withoutTransaction: "derived-update" }
373
+ };
374
+ var sqliteDialect = {
375
+ name: "sqlite",
376
+ placeholder: "question",
377
+ quote: quoteWith('"'),
378
+ bind: (value) => value instanceof Date ? value.toISOString() : bindCommon(value),
379
+ readDate: parseUtc,
380
+ readNullableDate: (value) => value == null ? null : parseUtc(value),
381
+ readJson,
382
+ distinctFrom: (statement, column, value) => {
383
+ statement.raw(`${column} IS NOT `).value(value);
384
+ },
385
+ upsertClause: (conflict, update) => ` ON CONFLICT (${conflict.join(", ")}) DO ${update.length === 0 ? "NOTHING" : `UPDATE SET ${update.map((column) => `${column} = excluded.${column}`).join(", ")}`}`,
386
+ isDuplicateKey: (error) => code(error) === "SQLITE_CONSTRAINT_UNIQUE" || /UNIQUE constraint failed/i.test(String(error?.message ?? "")),
387
+ isRetryableLock: (error) => code(error) === "SQLITE_BUSY" || /database is locked/i.test(String(error?.message ?? "")),
388
+ claim: { withTransaction: "subquery-update", withoutTransaction: "subquery-update" }
389
+ };
390
+
391
+ export { createSqlAdapter, mysqlDialect, sqliteDialect };
392
+ //# sourceMappingURL=chunk-2EYKPF5A.js.map
393
+ //# sourceMappingURL=chunk-2EYKPF5A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/sql/adapter.ts","../src/adapters/sql/dialect.ts"],"names":[],"mappings":"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","file":"chunk-2EYKPF5A.js","sourcesContent":["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"]}
@@ -1,4 +1,4 @@
1
- import { toSnakeCase } from './chunk-NYDVNMKG.js';
1
+ import { toSnakeCase } from './chunk-AEYYTE5I.js';
2
2
 
3
3
  // src/core/errors.ts
4
4
  var EasyPingError = class extends Error {
@@ -28,7 +28,32 @@ var consoleLogger = {
28
28
 
29
29
  // src/core/store.ts
30
30
  var OPERATORS = ["in", "lt", "lte", "gt", "gte", "not"];
31
- var isOperator = (value) => typeof value === "object" && value !== null && !(value instanceof Date) && OPERATORS.some((op) => op in value);
31
+ var isOperator = (value) => typeof value === "object" && value !== null && !(value instanceof Date) && OPERATORS.some((op) => Object.hasOwn(value, op));
32
+ var isScalar = (value) => value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
33
+ function assertCondition(pluginId, field, condition) {
34
+ if (isScalar(condition)) return;
35
+ if (isOperator(condition)) {
36
+ const operator = condition;
37
+ const keys = Object.keys(operator);
38
+ const [op] = keys;
39
+ if (keys.length !== 1 || op === void 0) {
40
+ throw new ConfigError(`plugin "${pluginId}" gave field "${field}" more than one operator.`);
41
+ }
42
+ const payload = operator[op];
43
+ const valid = op === "in" ? Array.isArray(payload) && payload.every((item) => typeof item === "string" || typeof item === "number") : isScalar(payload);
44
+ if (valid) return;
45
+ }
46
+ throw new ConfigError(
47
+ `plugin "${pluginId}" passed a non-scalar value for field "${field}"; validate request bodies before querying with them.`
48
+ );
49
+ }
50
+ function assertValue(pluginId, field, spec, value) {
51
+ if (value === void 0 || isScalar(value)) return;
52
+ if (spec.type === "json" && typeof value === "object") return;
53
+ throw new ConfigError(
54
+ `plugin "${pluginId}" passed a non-scalar value for ${spec.type} field "${field}".`
55
+ );
56
+ }
32
57
  function createPluginStore(pluginId, schema, storage, prefix) {
33
58
  const byTableName = /* @__PURE__ */ new Map();
34
59
  for (const declaration of Object.values(schema ?? {})) {
@@ -45,7 +70,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
45
70
  }
46
71
  function checkFields(table, fields, context) {
47
72
  for (const field of fields) {
48
- const spec = table.fields[field];
73
+ const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : void 0;
49
74
  if (!spec) {
50
75
  throw new ConfigError(
51
76
  `plugin "${pluginId}" used unknown field "${field}" on "${table.tableName}" (${context}).`
@@ -58,6 +83,19 @@ function createPluginStore(pluginId, schema, storage, prefix) {
58
83
  }
59
84
  }
60
85
  }
86
+ function checkWhere(table, where) {
87
+ checkFields(table, Object.keys(where), "where");
88
+ for (const [field, condition] of Object.entries(where)) {
89
+ assertCondition(pluginId, field, condition);
90
+ }
91
+ }
92
+ function checkRow(table, row, context) {
93
+ checkFields(table, Object.keys(row), context);
94
+ for (const [field, value] of Object.entries(row)) {
95
+ const spec = table.fields[field];
96
+ if (spec) assertValue(pluginId, field, spec, value);
97
+ }
98
+ }
61
99
  const qualified = (table) => prefix + table.tableName;
62
100
  const column = (field) => storage.naming === "preserve" ? field : toSnakeCase(field);
63
101
  const serialize = (table, row) => {
@@ -71,21 +109,25 @@ function createPluginStore(pluginId, schema, storage, prefix) {
71
109
  return {
72
110
  async find(table, where = {}, options = {}) {
73
111
  const declaration = resolve(table);
74
- checkFields(declaration, Object.keys(where), "where");
112
+ checkWhere(declaration, where);
75
113
  if (options.orderBy) checkFields(declaration, [options.orderBy.field], "orderBy");
76
114
  const rows = await storage.queryTable(qualified(declaration), where, options);
77
115
  return rows.map((row) => {
78
116
  const mapped = {};
79
117
  for (const field of Object.keys(declaration.fields)) {
80
118
  const value = row[column(field)];
81
- mapped[field] = declaration.fields[field]?.type === "json" && typeof value === "string" ? JSON.parse(value) : value;
119
+ const type = declaration.fields[field]?.type;
120
+ if (type === "json" && typeof value === "string") mapped[field] = JSON.parse(value);
121
+ else if (type === "date" && typeof value === "string") mapped[field] = new Date(value);
122
+ else if (type === "boolean" && typeof value === "number") mapped[field] = value !== 0;
123
+ else mapped[field] = value;
82
124
  }
83
125
  return mapped;
84
126
  });
85
127
  },
86
128
  async insert(table, rows) {
87
129
  const declaration = resolve(table);
88
- for (const row of rows) checkFields(declaration, Object.keys(row), "insert");
130
+ for (const row of rows) checkRow(declaration, row, "insert");
89
131
  return storage.insertRows(
90
132
  qualified(declaration),
91
133
  rows.map((row) => serialize(declaration, row))
@@ -93,7 +135,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
93
135
  },
94
136
  async upsert(table, rows, options) {
95
137
  const declaration = resolve(table);
96
- for (const row of rows) checkFields(declaration, Object.keys(row), "upsert");
138
+ for (const row of rows) checkRow(declaration, row, "upsert");
97
139
  checkFields(declaration, options.onConflict, "onConflict");
98
140
  return storage.insertRows(
99
141
  qualified(declaration),
@@ -103,18 +145,18 @@ function createPluginStore(pluginId, schema, storage, prefix) {
103
145
  },
104
146
  async update(table, where, set) {
105
147
  const declaration = resolve(table);
106
- checkFields(declaration, Object.keys(where), "where");
107
- checkFields(declaration, Object.keys(set), "update");
148
+ checkWhere(declaration, where);
149
+ checkRow(declaration, set, "update");
108
150
  return storage.updateRows(qualified(declaration), where, serialize(declaration, set));
109
151
  },
110
152
  async remove(table, where) {
111
153
  const declaration = resolve(table);
112
- checkFields(declaration, Object.keys(where), "where");
154
+ checkWhere(declaration, where);
113
155
  return storage.deleteRows(qualified(declaration), where);
114
156
  }
115
157
  };
116
158
  }
117
159
 
118
160
  export { ConfigError, EasyPingError, ValidationError, consoleLogger, createPluginStore, isOperator };
119
- //# sourceMappingURL=chunk-OZSOIX23.js.map
120
- //# sourceMappingURL=chunk-OZSOIX23.js.map
161
+ //# sourceMappingURL=chunk-3VOQOGDR.js.map
162
+ //# sourceMappingURL=chunk-3VOQOGDR.js.map