@delali/sirannon-db 0.3.4-next.62 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +79 -56
  2. package/dist/backup/index.d.ts +44 -45
  3. package/dist/backup/index.mjs +7 -7
  4. package/dist/backup-scheduler/index.d.ts +42 -41
  5. package/dist/backup-scheduler/index.mjs +4 -4
  6. package/dist/baseline-Co3iIsN_.d.ts +19 -0
  7. package/dist/change-tracker-DqCdeEyx.d.ts +128 -0
  8. package/dist/{chunk-VOSJBZ6Q.mjs → chunk-2ES3DT4A.mjs} +71 -33
  9. package/dist/{chunk-6CMCO7S4.mjs → chunk-2O3PP4CF.mjs} +1 -1
  10. package/dist/{chunk-E7Q7HE46.mjs → chunk-3BZLZ47R.mjs} +26 -26
  11. package/dist/chunk-4KASJKKK.mjs +2501 -0
  12. package/dist/{chunk-MYNI2WFS.mjs → chunk-52XW3Z76.mjs} +40 -8
  13. package/dist/chunk-55YRLSM2.mjs +57 -0
  14. package/dist/{chunk-KDAHYFW7.mjs → chunk-6AOJCNRU.mjs} +5 -5
  15. package/dist/{chunk-KXTYR2OQ.mjs → chunk-A3PHCO6W.mjs} +39 -111
  16. package/dist/chunk-ADNWEHGZ.mjs +299 -0
  17. package/dist/{chunk-2QLXDHAP.mjs → chunk-BLMRNSUG.mjs} +11 -2
  18. package/dist/chunk-BVMZJOVM.mjs +5 -0
  19. package/dist/{chunk-IWGIYDMZ.mjs → chunk-BZWDPF4G.mjs} +1 -1
  20. package/dist/{chunk-YVF2ZJTE.mjs → chunk-CZL4D3BM.mjs} +1 -1
  21. package/dist/{chunk-7SC2TZAS.mjs → chunk-DX4WGJL7.mjs} +2 -2
  22. package/dist/{chunk-45MKELYA.mjs → chunk-ESTZZPT5.mjs} +132 -13
  23. package/dist/{chunk-QP5L5DFJ.mjs → chunk-FJPR2CJX.mjs} +3 -3
  24. package/dist/chunk-HR7K3GZW.mjs +36 -0
  25. package/dist/{chunk-LXCGPZPA.mjs → chunk-IEKIYO4V.mjs} +1 -1
  26. package/dist/{chunk-TUD5CJ76.mjs → chunk-KB4OLJAD.mjs} +26 -19
  27. package/dist/{chunk-PBRXXISQ.mjs → chunk-KINDFBQP.mjs} +3 -2
  28. package/dist/{chunk-E6IMX7RS.mjs → chunk-KJLNYOOL.mjs} +2 -2
  29. package/dist/{chunk-BTTFW4Z4.mjs → chunk-KU32OT4H.mjs} +2 -10
  30. package/dist/{chunk-JQWGHSEH.mjs → chunk-NVSQ2T5Q.mjs} +71 -70
  31. package/dist/{chunk-W4EUTJNT.mjs → chunk-NZGSDALX.mjs} +94 -96
  32. package/dist/{chunk-U445A3C4.mjs → chunk-QGVRRKB2.mjs} +1 -1
  33. package/dist/{chunk-GYCEJNU4.mjs → chunk-R7GBXNTO.mjs} +1 -1
  34. package/dist/{chunk-7C36BCSN.mjs → chunk-VDN6FHU4.mjs} +1 -1
  35. package/dist/{chunk-6IYS4WLH.mjs → chunk-VO3ZWA2G.mjs} +142 -115
  36. package/dist/chunk-WOMADPM4.mjs +12 -0
  37. package/dist/{chunk-OUSWVNWT.mjs → chunk-YCMB6GR4.mjs} +1 -1
  38. package/dist/{chunk-YNTDOFTN.mjs → chunk-YW7IKQ7T.mjs} +1 -1
  39. package/dist/{chunk-M2BEAH5I.mjs → chunk-ZN4HI5FK.mjs} +126 -72
  40. package/dist/{chunk-SBL6GN43.mjs → chunk-ZYWI45EN.mjs} +1 -1
  41. package/dist/client/index.d.ts +135 -137
  42. package/dist/client/index.mjs +177 -82
  43. package/dist/client/topology.d.ts +31 -31
  44. package/dist/client/topology.mjs +42 -23
  45. package/dist/client-base-BxTocU1x.d.ts +398 -0
  46. package/dist/client-streams-YKANXMWO.mjs +112 -0
  47. package/dist/codegen/cli.mjs +4 -4
  48. package/dist/codegen/index.d.ts +36 -29
  49. package/dist/codegen/index.mjs +4 -4
  50. package/dist/core/index.d.ts +130 -115
  51. package/dist/core/index.mjs +387 -297
  52. package/dist/core/writer-worker.mjs +3 -3
  53. package/dist/database-CGDsXW1K.d.ts +959 -0
  54. package/dist/database-backup-DcLhxoa1.d.ts +143 -0
  55. package/dist/driver/better-sqlite3.d.ts +11 -10
  56. package/dist/driver/better-sqlite3.mjs +11 -11
  57. package/dist/driver/bun.d.ts +9 -8
  58. package/dist/driver/expo.d.ts +6 -7
  59. package/dist/driver/node.d.ts +11 -10
  60. package/dist/driver/node.mjs +13 -11
  61. package/dist/driver/wa-sqlite.d.ts +6 -6
  62. package/dist/errors-CJUkQ-KL.d.ts +199 -0
  63. package/dist/file-migrations/index.d.ts +20 -18
  64. package/dist/file-migrations/index.mjs +3 -3
  65. package/dist/forward-rpc-VRWFZXHN.mjs +44 -0
  66. package/dist/operation-registry-CGhNT-Sn.d.ts +96 -0
  67. package/dist/primary-wins-CHNOYuN7.d.ts +81 -0
  68. package/dist/protocol-DFpNNsIT.d.ts +153 -0
  69. package/dist/query-types-5B02ixH0.d.ts +129 -0
  70. package/dist/react/index.d.ts +22 -22
  71. package/dist/replication/coordinator/etcd.d.ts +37 -31
  72. package/dist/replication/coordinator/etcd.mjs +296 -131
  73. package/dist/replication/index.d.ts +201 -161
  74. package/dist/replication/index.mjs +630 -529
  75. package/dist/restore-options-CH8mE-Tb.d.ts +84 -0
  76. package/dist/server/index.d.ts +201 -191
  77. package/dist/server/index.mjs +736 -619
  78. package/dist/server-options-aYtCkefX.d.ts +317 -0
  79. package/dist/server-streams-24XL4T43.mjs +254 -0
  80. package/dist/sirannon-J3EgPVD7.d.ts +179 -0
  81. package/dist/transport/grpc.d.ts +37 -24
  82. package/dist/transport/grpc.mjs +83 -3199
  83. package/dist/transport/memory.d.ts +25 -26
  84. package/dist/transport/memory.mjs +18 -9
  85. package/dist/types-BrAiDGOb.d.ts +505 -0
  86. package/dist/types-CEJG0eFS.d.ts +88 -0
  87. package/dist/types-Dc8oWlxj.d.ts +1580 -0
  88. package/dist/types-DqODKeOq.d.ts +123 -0
  89. package/dist/types-guEKSzMr.d.ts +367 -0
  90. package/package.json +7 -7
  91. package/dist/baseline-D93hcIEE.d.ts +0 -17
  92. package/dist/change-tracker-CjRFuZdB.d.ts +0 -144
  93. package/dist/client-base-Cmd9EVme.d.ts +0 -397
  94. package/dist/database-BLQvqjsw.d.ts +0 -917
  95. package/dist/database-backup-B7oszQ2n.d.ts +0 -141
  96. package/dist/errors-Dei4GdBb.d.ts +0 -196
  97. package/dist/operation-registry-oiPuSYI6.d.ts +0 -94
  98. package/dist/primary-wins-B0np8JS3.d.ts +0 -85
  99. package/dist/protocol-HI-nY8DD.d.ts +0 -152
  100. package/dist/query-types-p1liOP-b.d.ts +0 -127
  101. package/dist/restore-options-CksYsH1l.d.ts +0 -83
  102. package/dist/server-options-Bj5iJnjx.d.ts +0 -292
  103. package/dist/sirannon-BPxC5tC2.d.ts +0 -178
  104. package/dist/types-B0EhgDbr.d.ts +0 -340
  105. package/dist/types-CbjfRwrK.d.ts +0 -499
  106. package/dist/types-CjhxcjhA.d.ts +0 -123
  107. package/dist/types-DPSmjU54.d.ts +0 -1518
  108. package/dist/types-DTO4CYhW.d.ts +0 -88
@@ -1,8 +1,8 @@
1
- import { tableColumnNames, tablePkColumns, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteChangesBeforeSql, selectTableExists, ensureChangesTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectChangesAfterSeqSql, selectTablesChangesInRangeSql } from './chunk-45MKELYA.mjs';
2
- import { CHANGES_TABLE, isReservedIdentifier, CDC_TRIGGER_PREFIX, encodeTaggedValues, changedAtToEventTimestamp, decodeTaggedValues, SAFE_INT_BOUND_TEXT } from './chunk-6CMCO7S4.mjs';
3
- import { synchronousPragmaValue } from './chunk-OUSWVNWT.mjs';
1
+ import { tableColumnNames, tablePkColumns, selectMinChangeSeqSql, selectMaxChangeSeq, deleteChangesBeforeUpToSeqSql, deleteChangesBeforeSql, selectTableExists, ensureChangesTable, ensureMetaTable, insertMetaValueIfAbsent, selectMetaValue, selectChangesAfterSeqSql, selectTablesChangesInRangeSql, deleteExpiredDeviceCursors, selectMinDeviceCursorBoundary } from './chunk-ESTZZPT5.mjs';
2
+ import { CHANGES_TABLE, isReservedIdentifier, CDC_TRIGGER_PREFIX, encodeTaggedValues, changedAtToEventTimestamp, decodeTaggedValues, SAFE_INT_BOUND_TEXT, DEVICE_CURSORS_TABLE } from './chunk-2O3PP4CF.mjs';
3
+ import { synchronousPragmaValue } from './chunk-YCMB6GR4.mjs';
4
4
  import { randomHex, invokeCallerCallback, reportCallerFailure, toError } from './chunk-QBBLSQHQ.mjs';
5
- import { SirannonError, ForbiddenSqlError, CDCError, MigrationError } from './chunk-PBRXXISQ.mjs';
5
+ import { SirannonError, ForbiddenSqlError, CDCError, MigrationError } from './chunk-KINDFBQP.mjs';
6
6
 
7
7
  // src/core/bulk-load.ts
8
8
  var DEFAULT_LOAD_DURABILITY = "off";
@@ -119,6 +119,38 @@ async function readSinceTables(conn, cache, changesTable, tables, afterSeq, upTo
119
119
  return rows.map(rowToEvent);
120
120
  }
121
121
 
122
+ // src/core/cdc/prune-boundaries.ts
123
+ var PruneBoundaries = class {
124
+ bySource = /* @__PURE__ */ new Map();
125
+ set(source, seq) {
126
+ this.bySource.set(source, seq);
127
+ }
128
+ clear(source) {
129
+ this.bySource.delete(source);
130
+ }
131
+ lowest() {
132
+ let lowest = null;
133
+ for (const boundary of this.bySource.values()) {
134
+ if (lowest === null || boundary < lowest) {
135
+ lowest = boundary;
136
+ }
137
+ }
138
+ return lowest;
139
+ }
140
+ };
141
+ function seqBoundFor(lowest, lastSeq) {
142
+ if (lastSeq > 0n && lowest !== null) {
143
+ return lastSeq < lowest ? lastSeq : lowest;
144
+ }
145
+ if (lowest !== null) {
146
+ return lowest;
147
+ }
148
+ if (lastSeq > 0n) {
149
+ return lastSeq;
150
+ }
151
+ return null;
152
+ }
153
+
122
154
  // src/core/cdc/statement-cache.ts
123
155
  var StatementCache = class {
124
156
  cache = /* @__PURE__ */ new WeakMap();
@@ -216,7 +248,7 @@ var ChangeTracker = class {
216
248
  changesTableEnsured = false;
217
249
  watchedTablesCache = null;
218
250
  stmtCache = new StatementCache();
219
- pruneBoundary = null;
251
+ pruneBoundaries = new PruneBoundaries();
220
252
  lastPollAtTxBoundary = true;
221
253
  constructor(options) {
222
254
  this.retentionMs = options?.retention ?? DEFAULT_RETENTION_MS;
@@ -225,10 +257,10 @@ var ChangeTracker = class {
225
257
  this.assertIdentifier(this.changesTable, "changes table name");
226
258
  }
227
259
  /**
228
- * Starts recording changes to a table by installing its change-capture triggers.
260
+ * Installs change-capture triggers on a table, so that every later write to it adds a row to the change log.
229
261
  *
230
- * @param conn - Writer connection the triggers are created on.
231
- * @param table - Name of the table to watch.
262
+ * @param conn - The writer connection on which the tracker creates the triggers.
263
+ * @param table - The name of the table to watch.
232
264
  */
233
265
  async watch(conn, table) {
234
266
  this.assertIdentifier(table, "table name");
@@ -261,10 +293,10 @@ var ChangeTracker = class {
261
293
  this.watchedTablesCache = null;
262
294
  }
263
295
  /**
264
- * Stops recording changes to a table and drops its triggers.
296
+ * Drops a table's change-capture triggers and removes the table from the watched set.
265
297
  *
266
- * @param conn - Writer connection the triggers are dropped from.
267
- * @param table - Name of the table to stop watching.
298
+ * @param conn - The writer connection on which the tracker drops the triggers.
299
+ * @param table - The name of the table to stop watching.
268
300
  */
269
301
  async unwatch(conn, table) {
270
302
  if (!this.watched.has(table)) {
@@ -275,29 +307,17 @@ var ChangeTracker = class {
275
307
  this.watchedTablesCache = null;
276
308
  }
277
309
  /**
278
- * Rebuilds CDC triggers for every watched table directly on the supplied
279
- * connection, without opening a nested transaction.
280
- *
281
- * Required when a caller has already issued `BEGIN` on `conn` and runs a
282
- * DDL statement that changes a watched table's column list: the next DML
283
- * inside the same transaction must see triggers compiled against the new
284
- * columns, otherwise CDC `new_data` silently omits them. Callers that are
285
- * not inside an active transaction may also use this method; CREATE
286
- * TRIGGER and DROP TRIGGER statements are committed individually by the
287
- * driver in that case.
310
+ * Rebuilds the change-capture triggers on `conn` for every watched table
311
+ * whose column list has changed, without opening a nested transaction.
288
312
  *
289
- * Failure semantics:
290
- * - If a watched table no longer exists (e.g. it was just dropped by the
291
- * DDL), it is skipped. The watched-map entry is left alone so a separate
292
- * cleanup path can handle it; throwing here would roll back the user's
293
- * transaction over a benign condition.
294
- * - If reading column metadata succeeds but the column list is unchanged,
295
- * no triggers are touched.
296
- * - If reading column metadata succeeds and the column list differs from
297
- * the cached one, the existing triggers are dropped and reinstalled on
298
- * `conn`. The cached column list is updated on success.
299
- * - Any other error (driver failure, identifier validation failure) is
300
- * rethrown so the caller's transaction can roll back deterministically.
313
+ * Call it after a DDL statement inside an open transaction, so that the next
314
+ * write in that transaction records the new columns in `new_data`. You can
315
+ * also call it outside a transaction, in which case SQLite commits each
316
+ * `CREATE TRIGGER` and `DROP TRIGGER` statement on its own. The method skips
317
+ * a watched table that no longer exists, and it leaves that table's entry for
318
+ * `pruneDroppedTables` to remove after the commit. A
319
+ * driver error or an invalid column name propagates to the caller, so that
320
+ * the caller can roll back its transaction.
301
321
  *
302
322
  * @internal
303
323
  */
@@ -329,24 +349,16 @@ var ChangeTracker = class {
329
349
  }
330
350
  }
331
351
  /**
332
- * Removes the supplied tables from the watched map and drops any leftover
333
- * CDC triggers carrying their identifier on the supplied connection.
352
+ * Drops the change-capture triggers of each named table that the tracker
353
+ * watches, and removes that table from the watched set.
334
354
  *
335
- * Intended to be called after a DDL transaction that dropped one or more
336
- * watched tables has committed. On rollback the caller must not invoke
337
- * this method; the rollback semantics rely on the caller discarding its
338
- * captured drop list before reaching this call.
339
- *
340
- * Idempotent: tables not currently in the watched map are silently
341
- * skipped. `dropCdcTriggers` issues `DROP TRIGGER IF EXISTS` so calling
342
- * twice in succession produces the same state.
343
- *
344
- * Defence in depth: even after a `DROP TABLE`, the in-transaction trigger
345
- * refresh path may have observed a freshly-created table of the same name
346
- * (the DROP and CREATE happened inside the same transaction) and
347
- * re-installed triggers compiled against the new schema. Those triggers
348
- * are dropped here so the recreated table starts with a clean slate and
349
- * the caller must explicitly `watch` it again.
355
+ * Call it once a transaction that drops watched tables commits, and skip the
356
+ * call when that transaction rolls back. The method skips each table outside
357
+ * the watched set and issues `DROP TRIGGER IF EXISTS`, so a second call leaves
358
+ * the same state. When one transaction drops a table and creates another with
359
+ * the same name, `refreshAllTriggersUsingConnection` can
360
+ * install triggers on the new table. This method drops those triggers too, so
361
+ * you have to call `watch` again for the new table.
350
362
  *
351
363
  * @internal
352
364
  */
@@ -386,7 +398,7 @@ var ChangeTracker = class {
386
398
  return this.lastPollAtTxBoundary;
387
399
  }
388
400
  /**
389
- * The highest seq already polled; live subscribers receive events beyond it.
401
+ * Returns the sequence number of the last change that the tracker polled or skipped with `advanceToLatest`, so live subscribers receive only later changes.
390
402
  *
391
403
  * @internal
392
404
  */
@@ -405,7 +417,7 @@ var ChangeTracker = class {
405
417
  return readSinceTables(conn, this.stmtCache, this.changesTable, tables, afterSeq, upToSeq, limit);
406
418
  }
407
419
  /**
408
- * The lowest retained seq, or `null` when the change log is empty.
420
+ * Returns the lowest sequence number in the change log, or `null` when the log is empty or missing.
409
421
  *
410
422
  * @internal
411
423
  */
@@ -461,12 +473,16 @@ var ChangeTracker = class {
461
473
  return result.changes;
462
474
  }
463
475
  /** @internal */
464
- setPruneBoundary(seq) {
465
- this.pruneBoundary = seq;
476
+ setPruneBoundary(source, seq) {
477
+ this.pruneBoundaries.set(source, seq);
478
+ }
479
+ /** @internal */
480
+ clearPruneBoundary(source) {
481
+ this.pruneBoundaries.clear(source);
466
482
  }
467
483
  /** @internal */
468
- clearPruneBoundary() {
469
- this.pruneBoundary = null;
484
+ get changeLogTable() {
485
+ return this.changesTable;
470
486
  }
471
487
  /** @internal */
472
488
  get watchedTables() {
@@ -476,17 +492,7 @@ var ChangeTracker = class {
476
492
  return this.watchedTablesCache;
477
493
  }
478
494
  computeSeqBound() {
479
- const boundary = this.pruneBoundary;
480
- if (this.lastSeq > 0n && boundary !== null) {
481
- return this.lastSeq < boundary ? this.lastSeq : boundary;
482
- }
483
- if (boundary !== null) {
484
- return boundary;
485
- }
486
- if (this.lastSeq > 0n) {
487
- return this.lastSeq;
488
- }
489
- return null;
495
+ return seqBoundFor(this.pruneBoundaries.lowest(), this.lastSeq);
490
496
  }
491
497
  assertIdentifier(name, label) {
492
498
  if (!IDENTIFIER_RE.test(name)) {
@@ -550,6 +556,42 @@ function reconcileMigrationChecksums(migrations, applied) {
550
556
  return backfills;
551
557
  }
552
558
 
559
+ // src/core/cdc/device-retention.ts
560
+ var DEFAULT_DEVICE_CURSOR_RETENTION_MS = 30 * 24 * 36e5;
561
+ function resolveDeviceRetentionPolicy(options, fallback) {
562
+ return {
563
+ cursorRetentionMs: options?.deviceCursorRetention ?? fallback?.deviceCursorRetention ?? DEFAULT_DEVICE_CURSOR_RETENTION_MS,
564
+ maxChangesHeldForDevice: options?.maxChangesHeldForDevice ?? fallback?.maxChangesHeldForDevice ?? 0
565
+ };
566
+ }
567
+ async function bothTablesExist(conn, changesTable) {
568
+ if (!await selectTableExists(conn, DEVICE_CURSORS_TABLE)) return false;
569
+ return selectTableExists(conn, changesTable);
570
+ }
571
+ async function dropExpiredDeviceCursors(conn, policy, changesTable = CHANGES_TABLE) {
572
+ if (!await bothTablesExist(conn, changesTable)) return 0;
573
+ const cutoff = Date.now() / 1e3 - policy.cursorRetentionMs / 1e3;
574
+ return deleteExpiredDeviceCursors(conn, changesTable, {
575
+ idleCutoff: cutoff,
576
+ changeAgeCutoff: cutoff,
577
+ maxChangesHeldForDevice: policy.maxChangesHeldForDevice
578
+ });
579
+ }
580
+ async function resolveMinDeviceCursorBoundary(conn, changesTable = CHANGES_TABLE) {
581
+ if (!await bothTablesExist(conn, changesTable)) return null;
582
+ return selectMinDeviceCursorBoundary(conn, changesTable);
583
+ }
584
+ async function applyDeviceCursorBoundary(conn, tracker, policy) {
585
+ const changesTable = tracker.changeLogTable;
586
+ await dropExpiredDeviceCursors(conn, policy, changesTable);
587
+ const boundary = await resolveMinDeviceCursorBoundary(conn, changesTable);
588
+ if (boundary === null) {
589
+ tracker.clearPruneBoundary("device-cursors");
590
+ return;
591
+ }
592
+ tracker.setPruneBoundary("device-cursors", boundary);
593
+ }
594
+
553
595
  // src/core/cdc/epoch.ts
554
596
  var EPOCH_KEY = "cdc_epoch";
555
597
  async function ensureCdcEpoch(conn) {
@@ -563,7 +605,11 @@ async function ensureCdcEpoch(conn) {
563
605
  }
564
606
 
565
607
  // src/core/cdc/subscription.ts
608
+ var NO_SUBSCRIBERS = /* @__PURE__ */ new Set();
566
609
  var SubscriptionManager = class {
610
+ constructor(onDispatched) {
611
+ this.onDispatched = onDispatched;
612
+ }
567
613
  nextId = 1;
568
614
  subscriptions = /* @__PURE__ */ new Map();
569
615
  byTable = /* @__PURE__ */ new Map();
@@ -592,15 +638,17 @@ var SubscriptionManager = class {
592
638
  }
593
639
  dispatch(events) {
594
640
  for (const event of events) {
595
- const ids = this.byTable.get(event.table);
596
- if (!ids) continue;
641
+ const ids = this.byTable.get(event.table) ?? NO_SUBSCRIBERS;
642
+ let deliveredTo = 0;
597
643
  for (const id of ids) {
598
644
  const sub = this.subscriptions.get(id);
599
645
  if (!sub) continue;
600
646
  const delivered = sub.filter === void 0 ? event : filteredChange(event, sub.filter);
601
647
  if (delivered === null) continue;
648
+ deliveredTo++;
602
649
  invokeCallerCallback(() => sub.callback(delivered), sub.onError);
603
650
  }
651
+ this.onDispatched?.(event, deliveredTo);
604
652
  }
605
653
  }
606
654
  reportError(error) {
@@ -640,7 +688,10 @@ var SubscriptionBuilderImpl = class {
640
688
  return this.manager.subscribe(this.table, this.conditions, callback, options);
641
689
  }
642
690
  };
643
- function startPolling(conn, tracker, manager, intervalMs, onError, runExclusive) {
691
+ function startPolling(conn, tracker, manager, intervalMs, onError, runExclusive, deviceRetention = {
692
+ cursorRetentionMs: DEFAULT_DEVICE_CURSOR_RETENTION_MS,
693
+ maxChangesHeldForDevice: 0
694
+ }) {
644
695
  let consecutiveErrors = 0;
645
696
  let tickCount = 0;
646
697
  let polling = false;
@@ -661,7 +712,10 @@ function startPolling(conn, tracker, manager, intervalMs, onError, runExclusive)
661
712
  tickCount++;
662
713
  if (tickCount >= CLEANUP_INTERVAL_TICKS) {
663
714
  tickCount = 0;
664
- await exclusive(() => tracker.cleanup(conn));
715
+ await exclusive(async () => {
716
+ await applyDeviceCursorBoundary(conn, tracker, deviceRetention);
717
+ await tracker.cleanup(conn);
718
+ });
665
719
  }
666
720
  } catch (err) {
667
721
  consecutiveErrors++;
@@ -850,4 +904,4 @@ var TransactionGrouper = class {
850
904
  }
851
905
  };
852
906
 
853
- export { ChangeTracker, PrimedSubscription, SubscriptionBuilderImpl, SubscriptionManager, TransactionGrouper, ensureCdcEpoch, filteredChange, isBulkLoadDurability, migrationChecksum, migrationContentChecksum, needsResync, reconcileMigrationChecksums, runBulkLoad, startPolling };
907
+ export { ChangeTracker, PrimedSubscription, SubscriptionBuilderImpl, SubscriptionManager, TransactionGrouper, applyDeviceCursorBoundary, ensureCdcEpoch, filteredChange, isBulkLoadDurability, migrationChecksum, migrationContentChecksum, needsResync, reconcileMigrationChecksums, resolveDeviceRetentionPolicy, runBulkLoad, startPolling };
@@ -1,4 +1,4 @@
1
- import { CDCError } from './chunk-PBRXXISQ.mjs';
1
+ import { CDCError } from './chunk-KINDFBQP.mjs';
2
2
 
3
3
  // src/core/live/sql-tokens.ts
4
4
  var WHITESPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", "\f", "\v"]);