@opinionated-machine/sse-fallback 0.1.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.
@@ -0,0 +1,568 @@
1
+ export class Reconciler {
2
+ config;
3
+ snapshotToEvents;
4
+ terminalSet;
5
+ highWatermark = null;
6
+ hydrationBuffer = null;
7
+ hydrationBufferLimit;
8
+ stateValue;
9
+ stateInitialized = false;
10
+ stateSuspended = false;
11
+ /**
12
+ * Events delivered while the state layer is gap-suspended, in arrival
13
+ * order, so the repair snapshot can re-apply the ones it does not cover.
14
+ */
15
+ stateReplayBuffer = [];
16
+ /**
17
+ * The replay buffer overflowed and was dropped. A below-watermark snapshot
18
+ * can no longer repair state without losing the events it does not cover,
19
+ * so the suspension holds until a snapshot reaches the watermark.
20
+ */
21
+ stateReplayTruncated = false;
22
+ terminated = false;
23
+ onInvalidVersion;
24
+ constructor(config, options) {
25
+ this.config = config;
26
+ this.onInvalidVersion = options.onInvalidVersion;
27
+ const shorthandEvent = config.snapshotEvent;
28
+ this.snapshotToEvents =
29
+ config.snapshotToEvents ??
30
+ (shorthandEvent !== undefined
31
+ ? (snapshot) => [{ event: shorthandEvent, data: snapshot }]
32
+ : () => []);
33
+ this.terminalSet = new Set(config.terminalEvents ?? []);
34
+ this.hydrationBufferLimit = options.hydrationBufferLimit;
35
+ }
36
+ get isTerminated() {
37
+ return this.terminated;
38
+ }
39
+ get isHydrating() {
40
+ return this.hydrationBuffer !== null;
41
+ }
42
+ /**
43
+ * Whether the state layer is suspended after a detected gap. `apply` stays
44
+ * disabled until a snapshot repairs state, so `getState()` is known-stale
45
+ * while this is true.
46
+ */
47
+ get isStateSuspended() {
48
+ return this.stateSuspended;
49
+ }
50
+ getState() {
51
+ return this.stateValue;
52
+ }
53
+ /** Start buffering live events until the next snapshot arrives. */
54
+ beginHydration() {
55
+ if (this.terminated)
56
+ return;
57
+ this.hydrationBuffer = [];
58
+ }
59
+ /**
60
+ * Give up on subscribe-first hydration and resume direct delivery.
61
+ *
62
+ * The buffered events are FLUSHED, not discarded: hydration never completed,
63
+ * so no snapshot subsumes them and dropping them would lose exactly the
64
+ * events the buffer existed to protect. They pass through the version gate
65
+ * in arrival order, so a later snapshot still wins on version.
66
+ */
67
+ abandonHydration() {
68
+ const outcome = {
69
+ deliveries: [],
70
+ stale: false,
71
+ advanced: false,
72
+ hydrationCompleted: false,
73
+ terminated: false,
74
+ stateSuspended: this.stateSuspended,
75
+ stateRepaired: false,
76
+ };
77
+ if (this.hydrationBuffer === null)
78
+ return outcome;
79
+ this.finishHydration(null, outcome);
80
+ return outcome;
81
+ }
82
+ handleEvent(incoming) {
83
+ const outcome = {
84
+ deliveries: [],
85
+ duplicate: false,
86
+ buffered: false,
87
+ bufferOverflow: false,
88
+ terminated: false,
89
+ stateSuspended: this.stateSuspended,
90
+ };
91
+ if (this.terminated)
92
+ return outcome;
93
+ if (this.hydrationBuffer !== null) {
94
+ if (this.hydrationBuffer.length >= this.hydrationBufferLimit) {
95
+ // Drop the buffer; the caller refetches the snapshot, which subsumes
96
+ // everything dropped (or is older than still-flowing live events,
97
+ // which the version gate handles either way).
98
+ this.hydrationBuffer = [];
99
+ outcome.bufferOverflow = true;
100
+ return outcome;
101
+ }
102
+ this.hydrationBuffer.push(incoming);
103
+ outcome.buffered = true;
104
+ return outcome;
105
+ }
106
+ this.gateAndDeliver(incoming, 'sse', outcome);
107
+ outcome.stateSuspended = this.stateSuspended;
108
+ return outcome;
109
+ }
110
+ handleSnapshot(snapshot) {
111
+ const outcome = {
112
+ deliveries: [],
113
+ stale: false,
114
+ advanced: false,
115
+ hydrationCompleted: false,
116
+ terminated: false,
117
+ stateSuspended: this.stateSuspended,
118
+ stateRepaired: false,
119
+ };
120
+ if (this.terminated)
121
+ return outcome;
122
+ const versioned = this.config.version !== 'none';
123
+ // A version this reconciler cannot order (a body missing the field, NaN,
124
+ // an empty string) is treated as no version at all rather than stored:
125
+ // an unorderable watermark compares as "not less than" against every
126
+ // later item, which would drop the whole stream as stale duplicates.
127
+ const snapshotVersion = versioned
128
+ ? (this.normalizeVersion(this.config.version.ofSnapshot(snapshot), 'snapshot') ?? null)
129
+ : null;
130
+ // An epoch change re-scopes ordering, so counters on either side are not
131
+ // comparable and "below the watermark" means nothing: a snapshot from the
132
+ // new epoch is the resync, not a stale arrival. Without this a writer
133
+ // migrated to a lower epoch (the documented `createEventIdSequence` ->
134
+ // `createRedisEventIdSequence` move) would have every snapshot dropped.
135
+ const epochChanged = snapshotVersion !== null &&
136
+ this.highWatermark !== null &&
137
+ this.isEpochChange(this.highWatermark, snapshotVersion);
138
+ const watermarkComparison = !epochChanged && snapshotVersion !== null && this.highWatermark !== null
139
+ ? this.compare(snapshotVersion, this.highWatermark)
140
+ : undefined;
141
+ if (snapshotVersion !== null && watermarkComparison !== undefined && watermarkComparison <= 0) {
142
+ return this.handleStaleSnapshot(snapshot, snapshotVersion, watermarkComparison, outcome);
143
+ }
144
+ // Deliver the snapshot: state layer first (replacement semantics), then
145
+ // synthesized events. Synthetic events are NOT applied to state — the
146
+ // snapshot already subsumes them.
147
+ if (this.config.state) {
148
+ this.stateValue = this.config.state.init(snapshot);
149
+ this.stateInitialized = true;
150
+ // This snapshot is at or above the watermark, so it covers every event
151
+ // delivered during a suspension: there is nothing left to replay.
152
+ this.stateSuspended = false;
153
+ this.stateReplayBuffer = [];
154
+ this.stateReplayTruncated = false;
155
+ outcome.state = { value: this.stateValue };
156
+ }
157
+ const synthetic = this.snapshotToEvents(snapshot);
158
+ for (const event of synthetic) {
159
+ const delivered = {
160
+ ...event,
161
+ origin: 'poll',
162
+ };
163
+ outcome.deliveries.push(delivered);
164
+ if (this.terminalSet.has(event.event)) {
165
+ this.terminated = true;
166
+ outcome.terminated = true;
167
+ // Nothing is delivered after the terminal event — `finishHydration`
168
+ // and `handleEvent` apply the same rule.
169
+ break;
170
+ }
171
+ }
172
+ // An empty synthetic list still advances the watermark: the poll proved
173
+ // the state at snapshotVersion, so later stale arrivals must be droppable.
174
+ if (versioned && snapshotVersion !== null) {
175
+ this.highWatermark = snapshotVersion;
176
+ }
177
+ outcome.advanced = true;
178
+ this.finishHydration(snapshotVersion, outcome);
179
+ outcome.stateSuspended = this.stateSuspended;
180
+ return outcome;
181
+ }
182
+ /**
183
+ * The stale-poll race: everything this snapshot describes has already been
184
+ * delivered (or superseded) through the stream.
185
+ *
186
+ * One exception: a gap-suspended state layer is repaired by ANY snapshot,
187
+ * not only one whose version matches the watermark exactly. Live events keep
188
+ * advancing the watermark after a gap, so the repair snapshot this branch
189
+ * was polled for usually arrives strictly below it; requiring equality left
190
+ * `apply` disabled forever and froze `getState()` at its pre-gap value while
191
+ * events kept flowing. The events the snapshot does not cover are replayed
192
+ * onto it, so nothing delivered during the suspension is dropped from state.
193
+ * The watermark is NOT rewound, so nothing is re-delivered.
194
+ *
195
+ * With a dropped replay buffer that is impossible, so the suspension holds
196
+ * until a snapshot reaches the watermark, which covers everything delivered
197
+ * during it and needs no replay.
198
+ */
199
+ handleStaleSnapshot(snapshot, snapshotVersion, watermarkComparison, outcome) {
200
+ outcome.stale = true;
201
+ const canRepair = !this.stateReplayTruncated || watermarkComparison === 0;
202
+ if (this.stateSuspended && this.config.state && canRepair) {
203
+ this.repairState(snapshot, snapshotVersion, outcome);
204
+ }
205
+ this.finishHydration(snapshotVersion, outcome);
206
+ outcome.stateSuspended = this.stateSuspended;
207
+ return outcome;
208
+ }
209
+ /**
210
+ * Re-initialize a gap-suspended state layer from a snapshot below the
211
+ * watermark, then re-apply the buffered events the snapshot does not cover.
212
+ *
213
+ * The repair snapshot is usually older than the watermark, because live
214
+ * events keep arriving while the repair poll is in flight. Without the
215
+ * replay those events would be missing from state permanently: delivered to
216
+ * listeners, skipped by `apply` while suspended, then overwritten by an
217
+ * `init` that predates them. The next event would apply to a state that
218
+ * never saw them.
219
+ *
220
+ * The cut is by arrival order, anchored on the last buffered event the
221
+ * snapshot demonstrably covers. Events after it are replayed even when they
222
+ * carry no orderable version, because arrival order is the only ordering
223
+ * they have.
224
+ */
225
+ repairState(snapshot, snapshotVersion, outcome) {
226
+ const state = this.config.state;
227
+ if (!state)
228
+ return;
229
+ this.stateValue = state.init(snapshot);
230
+ this.stateInitialized = true;
231
+ this.stateSuspended = false;
232
+ const buffered = this.stateReplayBuffer;
233
+ this.stateReplayBuffer = [];
234
+ this.stateReplayTruncated = false;
235
+ let covered = -1;
236
+ for (const [index, entry] of buffered.entries()) {
237
+ if (entry.version !== undefined && this.compare(entry.version, snapshotVersion) <= 0) {
238
+ covered = index;
239
+ }
240
+ }
241
+ for (const entry of buffered.slice(covered + 1)) {
242
+ this.stateValue = state.apply(this.stateValue, entry.delivered);
243
+ }
244
+ outcome.state = { value: this.stateValue };
245
+ outcome.stateRepaired = true;
246
+ }
247
+ /**
248
+ * Keep a delivered event for the repair snapshot to replay. Overflow drops
249
+ * the whole buffer rather than half of it: replaying a partial buffer would
250
+ * apply deltas across a hole, which is exactly what the suspension exists
251
+ * to prevent.
252
+ */
253
+ recordSuspendedEvent(delivered, version) {
254
+ if (this.stateReplayTruncated)
255
+ return;
256
+ if (this.stateReplayBuffer.length >= this.hydrationBufferLimit) {
257
+ this.stateReplayBuffer = [];
258
+ this.stateReplayTruncated = true;
259
+ return;
260
+ }
261
+ this.stateReplayBuffer.push({ delivered, version });
262
+ }
263
+ finishHydration(snapshotVersion, outcome) {
264
+ if (this.hydrationBuffer === null)
265
+ return;
266
+ const buffered = this.hydrationBuffer;
267
+ this.hydrationBuffer = null;
268
+ outcome.hydrationCompleted = true;
269
+ // Flush buffered live events newer than the snapshot, in arrival order.
270
+ // Everything at or below the snapshot version is subsumed by it.
271
+ for (const incoming of buffered) {
272
+ if (this.terminated)
273
+ break;
274
+ const eventOutcome = {
275
+ deliveries: [],
276
+ duplicate: false,
277
+ buffered: false,
278
+ bufferOverflow: false,
279
+ terminated: false,
280
+ stateSuspended: this.stateSuspended,
281
+ };
282
+ this.gateAndDeliver(incoming, 'sse', eventOutcome);
283
+ outcome.deliveries.push(...eventOutcome.deliveries);
284
+ if (eventOutcome.state)
285
+ outcome.state = eventOutcome.state;
286
+ // The first hole is enough: one repair poll covers every later one, and
287
+ // reporting the earliest keeps `from` at the last version actually
288
+ // delivered before the loss.
289
+ if (eventOutcome.gap && !outcome.gap)
290
+ outcome.gap = eventOutcome.gap;
291
+ if (eventOutcome.terminated)
292
+ outcome.terminated = true;
293
+ }
294
+ outcome.stateSuspended = this.stateSuspended;
295
+ // snapshotVersion is already the watermark, so gateAndDeliver dropped
296
+ // anything the snapshot subsumed. (Parameter kept for readability.)
297
+ void snapshotVersion;
298
+ }
299
+ /**
300
+ * Record a detected gap on the outcome and suspend the state layer, so the
301
+ * caller polls for a repair snapshot instead of applying deltas across a
302
+ * hole.
303
+ */
304
+ registerGap(gap, outcome) {
305
+ outcome.gap = gap;
306
+ if (this.config.state && (this.config.state.reinitOnGap ?? true)) {
307
+ // Deltas must not be applied across a gap; the repair snapshot
308
+ // re-initializes state.
309
+ this.stateSuspended = true;
310
+ }
311
+ }
312
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the version gate is the correctness core — splitting it would scatter the invariants it guards
313
+ gateAndDeliver(incoming, origin, outcome) {
314
+ const versioned = this.config.version !== 'none';
315
+ let version;
316
+ if (versioned) {
317
+ version = this.extractEventVersion(incoming);
318
+ if (version !== undefined && this.highWatermark !== null) {
319
+ const gap = this.detectGap(this.highWatermark, version);
320
+ // An epoch change is resolved BEFORE the duplicate gate, because the
321
+ // two epochs are not comparable: a lower epoch is not an older event,
322
+ // it is a different ordering scope. Ranking it as a duplicate is how
323
+ // a writer that moved to a lower epoch (the documented
324
+ // `createEventIdSequence` -> `createRedisEventIdSequence` migration,
325
+ // whose DEFAULT_EPOCH is '0') wedged the subscription for good —
326
+ // every event sorted below a watermark it could never reach.
327
+ // Adopting the new epoch and reporting the gap turns that into what
328
+ // the docs promise: a resync poll. Two writers with different epochs
329
+ // broadcasting into one room (explicitly unsupported) now alternate
330
+ // gaps instead of silently dropping one writer's events; the repair
331
+ // polls are rate-limited by the deadman backoff and the poll gate.
332
+ if (gap?.reason === 'epoch-change' && this.ordersEpochs) {
333
+ this.registerGap(gap, outcome);
334
+ }
335
+ else {
336
+ const comparison = this.compare(version, this.highWatermark);
337
+ if (comparison <= 0) {
338
+ outcome.duplicate = true;
339
+ return;
340
+ }
341
+ if (gap)
342
+ this.registerGap(gap, outcome);
343
+ }
344
+ }
345
+ }
346
+ const delivered = {
347
+ event: incoming.event,
348
+ data: incoming.data,
349
+ ...(incoming.id !== undefined ? { id: incoming.id } : {}),
350
+ origin,
351
+ };
352
+ outcome.deliveries.push(delivered);
353
+ if (this.config.state && this.stateInitialized && !this.stateSuspended) {
354
+ this.stateValue = this.config.state.apply(this.stateValue, delivered);
355
+ outcome.state = { value: this.stateValue };
356
+ }
357
+ else if (this.config.state && this.stateSuspended) {
358
+ this.recordSuspendedEvent(delivered, version);
359
+ }
360
+ if (versioned && version !== undefined) {
361
+ this.highWatermark = version;
362
+ }
363
+ if (this.terminalSet.has(incoming.event)) {
364
+ this.terminated = true;
365
+ outcome.terminated = true;
366
+ }
367
+ }
368
+ extractEventVersion(incoming) {
369
+ const versionConfig = this.config.version;
370
+ if (versionConfig === 'none')
371
+ return undefined;
372
+ if (versionConfig.ofEvent) {
373
+ const extracted = versionConfig.ofEvent({
374
+ event: incoming.event,
375
+ data: incoming.data,
376
+ ...(incoming.id !== undefined ? { id: incoming.id } : {}),
377
+ origin: 'sse',
378
+ });
379
+ // `undefined` is a documented answer here ("this event carries no
380
+ // version"), so it is not reported; anything else unorderable is.
381
+ return extracted === undefined ? undefined : this.normalizeVersion(extracted, 'event');
382
+ }
383
+ // Default: the SSE id, when it is a shape this package can order —
384
+ // a bare integer, or a `createEventIdSequence()` id.
385
+ if (incoming.id === undefined || incoming.id === '')
386
+ return undefined;
387
+ return parseDefaultVersion(incoming.id);
388
+ }
389
+ /**
390
+ * A version the gate can actually order, or `undefined`.
391
+ *
392
+ * Nothing in the type system stops an extractor from handing back
393
+ * `undefined` (a snapshot body whose version field is absent), `NaN`, an
394
+ * empty string or an object, and storing one as the watermark is the worst
395
+ * outcome the gate has: `defaultCompareVersions` falls through to a
396
+ * lexicographic comparison against `'undefined'`/`'NaN'`, every later item
397
+ * ranks at or below it, and the subscription drops everything as a
398
+ * duplicate — silently, forever. Dropping the version instead costs
399
+ * deduplication (at-least-once delivery) and keeps the stream flowing.
400
+ */
401
+ normalizeVersion(value, source) {
402
+ if (typeof value === 'number' && Number.isFinite(value))
403
+ return value;
404
+ if (typeof value === 'string' && value !== '')
405
+ return value;
406
+ this.onInvalidVersion?.({ source, value });
407
+ return undefined;
408
+ }
409
+ /**
410
+ * Whether the epoch bypass below applies. A binding that declares
411
+ * `version.compare` owns ordering end to end, including across epochs, so
412
+ * its verdict is never overridden here — the bypass exists to correct the
413
+ * DEFAULT comparator, which ranks by epoch and therefore reads a lowered
414
+ * epoch as an older version.
415
+ */
416
+ get ordersEpochs() {
417
+ const versionConfig = this.config.version;
418
+ return versionConfig !== 'none' && versionConfig.compare === undefined;
419
+ }
420
+ /**
421
+ * Whether two versions belong to different ordering scopes under the
422
+ * default comparator — see {@link ordersEpochs}.
423
+ */
424
+ isEpochChange(a, b) {
425
+ return this.ordersEpochs && isEpochChange(a, b);
426
+ }
427
+ compare(a, b) {
428
+ const versionConfig = this.config.version;
429
+ if (versionConfig !== 'none' && versionConfig.compare) {
430
+ return versionConfig.compare(a, b);
431
+ }
432
+ return defaultCompareVersions(a, b);
433
+ }
434
+ detectGap(watermark, next) {
435
+ const versionConfig = this.config.version;
436
+ if (versionConfig === 'none')
437
+ return undefined;
438
+ const watermarkCounter = toCounter(watermark);
439
+ const nextCounter = toCounter(next);
440
+ if (watermarkCounter === undefined || nextCounter === undefined)
441
+ return undefined;
442
+ // Counters restart with a new epoch, so a cross-epoch step measures
443
+ // nothing — which makes it MORE of a resynchronization point, not less.
444
+ // Reporting it repairs delta state from a snapshot; staying silent would
445
+ // apply deltas across the restart and let a busy stream keep the deadman
446
+ // moving, so the repair never happens.
447
+ if (watermarkCounter.epoch !== nextCounter.epoch) {
448
+ return { from: watermark, to: next, reason: 'epoch-change' };
449
+ }
450
+ // A skipped counter is only measurable when the versions are dense.
451
+ if (!versionConfig.dense)
452
+ return undefined;
453
+ return nextCounter.counter > watermarkCounter.counter + 1n
454
+ ? { from: watermark, to: next, reason: 'sequence' }
455
+ : undefined;
456
+ }
457
+ }
458
+ const INTEGER_STRING_PATTERN = /^-?\d+$/;
459
+ /**
460
+ * The numeric value of a version, or `undefined` when comparing it as a
461
+ * number would lose information.
462
+ *
463
+ * Integer strings beyond `Number.MAX_SAFE_INTEGER` are rejected here on
464
+ * purpose: `Number('9007199254740992')` and `Number('9007199254740993')`
465
+ * are the same double, so a numeric comparison would report the second event
466
+ * as a duplicate of the first and drop it. Rejecting them hands the pair to
467
+ * {@link compareSequence}, which compares the digits as `bigint`.
468
+ */
469
+ function toNumeric(version) {
470
+ if (typeof version === 'number')
471
+ return version;
472
+ const numeric = Number(version);
473
+ if (Number.isNaN(numeric))
474
+ return undefined;
475
+ if (INTEGER_STRING_PATTERN.test(version) && !Number.isSafeInteger(numeric))
476
+ return undefined;
477
+ return numeric;
478
+ }
479
+ /**
480
+ * Ids produced by the server-side `createEventIdSequence()`:
481
+ * `"<numeric epoch>-<zero-padded counter>"`.
482
+ *
483
+ * The epoch is required to be numeric so that ids in unrelated shapes are NOT
484
+ * mistaken for versions. A UUID, for example, has several dashes and hex digits
485
+ * and cannot match — which matters, because treating one as a version would
486
+ * order events at random and silently drop them as duplicates. The server-side
487
+ * generators (`createEventIdSequence`, `formatEventId`,
488
+ * `createRedisEventIdSequence`) refuse a non-numeric epoch for exactly this
489
+ * reason, so every id they produce is one this extractor can order. Any other
490
+ * id scheme declares `version.ofEvent` explicitly.
491
+ */
492
+ const SEQUENCE_ID_PATTERN = /^(\d+)-(\d+)$/;
493
+ function toCounter(version) {
494
+ if (typeof version === 'number') {
495
+ return Number.isInteger(version) ? { epoch: 0n, counter: BigInt(version) } : undefined;
496
+ }
497
+ const parsed = SEQUENCE_ID_PATTERN.exec(version);
498
+ if (parsed) {
499
+ return { epoch: BigInt(parsed[1]), counter: BigInt(parsed[2]) };
500
+ }
501
+ return /^\d+$/.test(version) ? { epoch: 0n, counter: BigInt(version) } : undefined;
502
+ }
503
+ /**
504
+ * Whether two versions belong to different ordering scopes (a writer restart,
505
+ * or a move to a differently-seeded sequence). Counters on either side of an
506
+ * epoch change measure nothing against each other, so neither "newer" nor
507
+ * "older" is meaningful — only "resync".
508
+ */
509
+ function isEpochChange(a, b) {
510
+ const left = toCounter(a);
511
+ const right = toCounter(b);
512
+ if (left === undefined || right === undefined)
513
+ return false;
514
+ return left.epoch !== right.epoch;
515
+ }
516
+ /**
517
+ * The version a bare SSE `id:` carries under the default extractor: a bare
518
+ * integer becomes a number, a `createEventIdSequence()` id stays a string
519
+ * (ordered by {@link defaultCompareVersions}), anything else carries no
520
+ * version at all.
521
+ */
522
+ export function parseDefaultVersion(id) {
523
+ if (/^\d+$/.test(id)) {
524
+ const numeric = Number(id);
525
+ return Number.isSafeInteger(numeric) ? numeric : id;
526
+ }
527
+ return SEQUENCE_ID_PATTERN.test(id) ? id : undefined;
528
+ }
529
+ /**
530
+ * Numeric when both sides are numeric, epoch-then-counter when both are
531
+ * `createEventIdSequence()` ids, lexicographic otherwise.
532
+ *
533
+ * Comparing sequence ids by their parsed parts rather than lexicographically
534
+ * keeps ordering correct when the counter outgrows its zero padding, and
535
+ * makes a new epoch (a process restart) sort above the old one, so the
536
+ * restarted counter does not read as a flood of duplicates.
537
+ */
538
+ export function defaultCompareVersions(a, b) {
539
+ const numeric = compareNumeric(a, b);
540
+ if (numeric !== undefined)
541
+ return numeric;
542
+ const sequence = compareSequence(a, b);
543
+ if (sequence !== undefined)
544
+ return sequence;
545
+ return compareLexicographic(String(a), String(b));
546
+ }
547
+ function compareNumeric(a, b) {
548
+ const aNumber = toNumeric(a);
549
+ const bNumber = toNumeric(b);
550
+ if (aNumber === undefined || bNumber === undefined)
551
+ return undefined;
552
+ return aNumber === bNumber ? 0 : aNumber < bNumber ? -1 : 1;
553
+ }
554
+ function compareSequence(a, b) {
555
+ const aParts = toCounter(a);
556
+ const bParts = toCounter(b);
557
+ if (!aParts || !bParts)
558
+ return undefined;
559
+ if (aParts.epoch !== bParts.epoch)
560
+ return aParts.epoch < bParts.epoch ? -1 : 1;
561
+ if (aParts.counter === bParts.counter)
562
+ return 0;
563
+ return aParts.counter < bParts.counter ? -1 : 1;
564
+ }
565
+ function compareLexicographic(a, b) {
566
+ return a === b ? 0 : a < b ? -1 : 1;
567
+ }
568
+ //# sourceMappingURL=reconciler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciler.js","sourceRoot":"","sources":["../src/reconciler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { BackoffConfig } from './bindingTypes.ts';
2
+ /** A single named resettable timer. */
3
+ export declare class ResettableTimer {
4
+ private handle;
5
+ private readonly onFire;
6
+ constructor(onFire: () => void);
7
+ /** (Re)arm the timer; a previously armed timeout is cancelled. */
8
+ arm(delayMs: number): void;
9
+ clear(): void;
10
+ get isArmed(): boolean;
11
+ }
12
+ /**
13
+ * Full-jitter exponential backoff: `random() * min(cap, base * factor^attempt)`.
14
+ * Jitter prevents a thundering herd after a shared outage.
15
+ */
16
+ export declare function backoffDelay(config: BackoffConfig, attempt: number, random: () => number): number;
17
+ /** Sleep that resolves early (with `false`) when the signal aborts. */
18
+ export declare function sleep(delayMs: number, signal: AbortSignal): Promise<boolean>;
19
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAStD,uCAAuC;AACvC,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;gBAEvB,MAAM,EAAE,MAAM,IAAI;IAI9B,kEAAkE;IAClE,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO1B,KAAK,IAAI,IAAI;IAOb,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,GAAG,MAAM,CAGjG;AAED,uEAAuE;AACvE,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAiB5E"}
@@ -0,0 +1,51 @@
1
+ /** A single named resettable timer. */
2
+ export class ResettableTimer {
3
+ handle;
4
+ onFire;
5
+ constructor(onFire) {
6
+ this.onFire = onFire;
7
+ }
8
+ /** (Re)arm the timer; a previously armed timeout is cancelled. */
9
+ arm(delayMs) {
10
+ this.clear();
11
+ this.handle = setTimeout(this.onFire, delayMs);
12
+ this.handle.unref?.();
13
+ }
14
+ clear() {
15
+ if (this.handle !== undefined) {
16
+ clearTimeout(this.handle);
17
+ this.handle = undefined;
18
+ }
19
+ }
20
+ get isArmed() {
21
+ return this.handle !== undefined;
22
+ }
23
+ }
24
+ /**
25
+ * Full-jitter exponential backoff: `random() * min(cap, base * factor^attempt)`.
26
+ * Jitter prevents a thundering herd after a shared outage.
27
+ */
28
+ export function backoffDelay(config, attempt, random) {
29
+ const ceiling = Math.min(config.maxMs, config.baseMs * config.factor ** attempt);
30
+ return Math.max(0, Math.floor(random() * ceiling));
31
+ }
32
+ /** Sleep that resolves early (with `false`) when the signal aborts. */
33
+ export function sleep(delayMs, signal) {
34
+ return new Promise((resolve) => {
35
+ if (signal.aborted) {
36
+ resolve(false);
37
+ return;
38
+ }
39
+ const timer = setTimeout(() => {
40
+ signal.removeEventListener('abort', onAbort);
41
+ resolve(true);
42
+ }, delayMs);
43
+ timer.unref?.();
44
+ const onAbort = () => {
45
+ clearTimeout(timer);
46
+ resolve(false);
47
+ };
48
+ signal.addEventListener('abort', onAbort, { once: true });
49
+ });
50
+ }
51
+ //# sourceMappingURL=scheduler.js.map