@semics-tech/mongolite 1.3.0 → 1.4.1

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 (67) hide show
  1. package/README.md +80 -21
  2. package/dist/cjs/collection.js +9 -6
  3. package/dist/cjs/cursors/findCursor.js +6 -4
  4. package/dist/cjs/index.js +108 -1
  5. package/dist/cjs/mongo-client.js +18 -1
  6. package/dist/cjs/server.js +164 -0
  7. package/dist/cjs/sync/commands.js +126 -0
  8. package/dist/cjs/sync/diff.js +174 -0
  9. package/dist/cjs/sync/httpSink.js +159 -0
  10. package/dist/cjs/sync/index.js +40 -0
  11. package/dist/cjs/sync/mongoExecutor.js +306 -0
  12. package/dist/cjs/sync/mongoSink.js +161 -0
  13. package/dist/cjs/sync/outbox.js +358 -0
  14. package/dist/cjs/sync/protocol.js +177 -0
  15. package/dist/cjs/sync/replicator.js +747 -0
  16. package/dist/cjs/sync/shadow.js +178 -0
  17. package/dist/cjs/sync/types.js +2 -0
  18. package/dist/cloudflare.d.ts +1 -1
  19. package/dist/cloudflare.js.map +1 -1
  20. package/dist/collection.js +9 -6
  21. package/dist/collection.js.map +1 -1
  22. package/dist/cursors/findCursor.d.ts +2 -1
  23. package/dist/cursors/findCursor.js +6 -4
  24. package/dist/cursors/findCursor.js.map +1 -1
  25. package/dist/index.d.ts +68 -2
  26. package/dist/index.js +96 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/mongo-client.d.ts +13 -0
  29. package/dist/mongo-client.js +18 -1
  30. package/dist/mongo-client.js.map +1 -1
  31. package/dist/server.d.ts +97 -0
  32. package/dist/server.js +158 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/sync/commands.d.ts +49 -0
  35. package/dist/sync/commands.js +118 -0
  36. package/dist/sync/commands.js.map +1 -0
  37. package/dist/sync/diff.d.ts +55 -0
  38. package/dist/sync/diff.js +169 -0
  39. package/dist/sync/diff.js.map +1 -0
  40. package/dist/sync/httpSink.d.ts +93 -0
  41. package/dist/sync/httpSink.js +155 -0
  42. package/dist/sync/httpSink.js.map +1 -0
  43. package/dist/sync/index.d.ts +17 -0
  44. package/dist/sync/index.js +10 -0
  45. package/dist/sync/index.js.map +1 -0
  46. package/dist/sync/mongoExecutor.d.ts +37 -0
  47. package/dist/sync/mongoExecutor.js +302 -0
  48. package/dist/sync/mongoExecutor.js.map +1 -0
  49. package/dist/sync/mongoSink.d.ts +109 -0
  50. package/dist/sync/mongoSink.js +123 -0
  51. package/dist/sync/mongoSink.js.map +1 -0
  52. package/dist/sync/outbox.d.ts +120 -0
  53. package/dist/sync/outbox.js +355 -0
  54. package/dist/sync/outbox.js.map +1 -0
  55. package/dist/sync/protocol.d.ts +93 -0
  56. package/dist/sync/protocol.js +168 -0
  57. package/dist/sync/protocol.js.map +1 -0
  58. package/dist/sync/replicator.d.ts +177 -0
  59. package/dist/sync/replicator.js +744 -0
  60. package/dist/sync/replicator.js.map +1 -0
  61. package/dist/sync/shadow.d.ts +54 -0
  62. package/dist/sync/shadow.js +173 -0
  63. package/dist/sync/shadow.js.map +1 -0
  64. package/dist/sync/types.d.ts +388 -0
  65. package/dist/sync/types.js +2 -0
  66. package/dist/sync/types.js.map +1 -0
  67. package/package.json +16 -2
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Drains the {@link SyncOutbox} into an upstream {@link SyncSink}.
3
+ *
4
+ * ## Delivery guarantees
5
+ *
6
+ * Delivery is **at-least-once**, and every operation the replicator emits is
7
+ * idempotent (`replaceOne` with `upsert`, or `deleteOne` by `_id`). Replaying a
8
+ * batch therefore converges on the same upstream state, which is what makes it
9
+ * safe to acknowledge *after* the upstream write rather than before: a crash in
10
+ * the window between the two costs a duplicate write, never a lost one.
11
+ *
12
+ * ## Ordering
13
+ *
14
+ * Changes are read in outbox order and coalesced per document, so the newest
15
+ * revision wins. Within a batch, operations touch distinct documents and are
16
+ * applied unordered for throughput; across batches, order is preserved.
17
+ */
18
+ import { EventEmitter } from 'events';
19
+ import type { IDatabaseAdapter } from '../db.js';
20
+ import { SyncOutbox } from './outbox.js';
21
+ import { SyncShadow } from './shadow.js';
22
+ import type { SyncDeadLetter, SyncOptions, SyncSink, SyncStatus } from './types.js';
23
+ export declare class SyncReplicator extends EventEmitter {
24
+ private readonly db;
25
+ private readonly sink;
26
+ private readonly outbox;
27
+ private readonly shadow;
28
+ private readonly opts;
29
+ private running;
30
+ private connected;
31
+ private loop;
32
+ private checkpoint;
33
+ private backfilled;
34
+ private watched;
35
+ private lastCollectionScan;
36
+ private appliedCount;
37
+ private retryCount;
38
+ private deadLetterCount;
39
+ private conflictCount;
40
+ private lastError;
41
+ /** Resolves the current sleep early when a local write arrives or `stop()` is called. */
42
+ private wake;
43
+ constructor(db: IDatabaseAdapter, sink: SyncSink, options?: SyncOptions);
44
+ /** The outbox backing this replicator — for dead-letter inspection and manual maintenance. */
45
+ get log(): SyncOutbox;
46
+ /** The shadow store holding last-known upstream state — for diagnostics and reseeding. */
47
+ get baseline(): SyncShadow;
48
+ /**
49
+ * Installs capture triggers, backfills existing documents if this replicator
50
+ * has never run, and starts streaming changes upstream.
51
+ *
52
+ * Resolves once replication is *running*, not once it has caught up — use
53
+ * {@link SyncReplicator.waitForDrain} for that.
54
+ */
55
+ start(): Promise<void>;
56
+ /**
57
+ * Stops replication. Capture triggers stay installed, so changes made while
58
+ * stopped are still recorded and picked up on the next `start()`.
59
+ *
60
+ * @param options.flush Drain everything pending before stopping. Defaults to `false`.
61
+ */
62
+ stop(options?: {
63
+ flush?: boolean;
64
+ }): Promise<void>;
65
+ /**
66
+ * Nudges the replicator to check the outbox now instead of waiting for the
67
+ * next poll. Called by the client after local writes so replication latency
68
+ * is bounded by the upstream round trip, not the poll interval.
69
+ */
70
+ notify(): void;
71
+ /**
72
+ * Resolves once every change written *before this call* has reached the
73
+ * upstream. Rejects if replication stops with an error.
74
+ *
75
+ * The target is pinned to the outbox position at call time rather than
76
+ * "outbox is empty" — otherwise a concurrent writer could keep the outbox
77
+ * non-empty forever, and, worse, a `drained` event from a scan that began
78
+ * before the caller's own write would resolve it too early.
79
+ */
80
+ waitForDrain(): Promise<void>;
81
+ /** Current replication progress. */
82
+ status(): Promise<SyncStatus>;
83
+ /** Changes permanently rejected upstream. */
84
+ deadLetters(limit?: number): Promise<SyncDeadLetter[]>;
85
+ private pending;
86
+ /** `null` means "every collection"; an array restricts the outbox query. */
87
+ private collectionFilter;
88
+ /**
89
+ * Discovers collections to watch and installs their triggers.
90
+ *
91
+ * Re-run periodically so collections created after `start()` join replication
92
+ * without a restart.
93
+ */
94
+ private refreshCollections;
95
+ /**
96
+ * Seeds the upstream with documents that existed before replication started.
97
+ *
98
+ * The checkpoint is pinned *before* the scan begins, so a write racing the
99
+ * scan is either seen by the scan, or captured in the outbox above the
100
+ * checkpoint, or both — and "both" is harmless because upserts are idempotent.
101
+ */
102
+ private backfill;
103
+ /** The replication loop: read, coalesce, apply, checkpoint, repeat. */
104
+ private run;
105
+ /**
106
+ * Applies one batch.
107
+ * @returns `true` if there was work to do, `false` if the outbox was empty.
108
+ */
109
+ private drainOnce;
110
+ /** Acknowledges up to `id` and prunes rows no replicator needs any more. */
111
+ private advanceTo;
112
+ /**
113
+ * Collapses a run of changes into one operation per document.
114
+ *
115
+ * Records are in ascending outbox order, so the last one seen for a document
116
+ * is its current state — an upsert of the newest revision, or a delete.
117
+ */
118
+ private coalesce;
119
+ /**
120
+ * Turns coalesced records into upstream operations, attaching the base version and
121
+ * minimal diff from the shadow.
122
+ *
123
+ * Shadows load per collection in one query rather than per document, so a batch of
124
+ * 500 changes costs one extra read per collection, not 500.
125
+ */
126
+ private buildOperations;
127
+ /**
128
+ * Records the new upstream state for operations that landed.
129
+ *
130
+ * The post-write state is computed by applying the diff to the previous *server*
131
+ * document rather than by re-reading it — one fewer round trip, and it keeps the BSON
132
+ * values of untouched fields exactly as they were.
133
+ */
134
+ private commitShadows;
135
+ /**
136
+ * Reconciles a push that lost a race.
137
+ *
138
+ * The upstream state goes into the shadow first, so whatever happens next — a retry,
139
+ * or the application's own decision — is made against current reality rather than
140
+ * against the version that already lost.
141
+ */
142
+ private resolveConflict;
143
+ /**
144
+ * Writes the upstream version of a document into the local collection.
145
+ *
146
+ * The write trips the capture triggers and lands back in the outbox, which would
147
+ * normally mean pushing it straight back up. It does not, because the local row now
148
+ * matches the shadow exactly: the diff comes out empty and the echo is dropped in
149
+ * {@link SyncReplicator.buildOperations}. That is why the local copy is written from
150
+ * the same projection the diff compares against.
151
+ */
152
+ private adoptUpstream;
153
+ private fetchUpstream;
154
+ /** Maps an outbox record to an upstream operation, applying rename and transform. */
155
+ private toOperation;
156
+ /**
157
+ * Applies a batch, retrying transient failures with exponential backoff and
158
+ * jitter. Returns the operations the upstream rejected permanently, or `null`
159
+ * if the replicator was stopped before the batch landed.
160
+ *
161
+ * The checkpoint is never advanced while this is retrying, so an upstream
162
+ * outage simply parks the batch — the outbox absorbs the backlog.
163
+ */
164
+ private applyWithRetry;
165
+ private ensureConnected;
166
+ private recordDeadLetter;
167
+ /**
168
+ * Keeps the outbox from growing without bound during a long outage.
169
+ * Compaction is lossless here because replication is full-document
170
+ * last-write-wins: only superseded revisions are dropped.
171
+ */
172
+ private enforceOutboxLimit;
173
+ /** Sleeps, but returns early when `notify()` or `stop()` is called. */
174
+ private sleep;
175
+ private fail;
176
+ private log_;
177
+ }