cursedbelt 2.6.0 → 2.7.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 (66) hide show
  1. package/dist/server/sync/alarm.d.ts +35 -0
  2. package/dist/server/sync/alarm.d.ts.map +1 -0
  3. package/dist/server/sync/alarm.js +92 -0
  4. package/dist/server/sync/alarm.js.map +1 -0
  5. package/dist/server/sync/commands.d.ts +88 -0
  6. package/dist/server/sync/commands.d.ts.map +1 -0
  7. package/dist/server/sync/commands.js +242 -0
  8. package/dist/server/sync/commands.js.map +1 -0
  9. package/dist/server/sync/engine.d.ts +63 -0
  10. package/dist/server/sync/engine.d.ts.map +1 -0
  11. package/dist/server/sync/engine.js +185 -0
  12. package/dist/server/sync/engine.js.map +1 -0
  13. package/dist/server/sync/http.d.ts +107 -0
  14. package/dist/server/sync/http.d.ts.map +1 -0
  15. package/dist/server/sync/http.js +244 -0
  16. package/dist/server/sync/http.js.map +1 -0
  17. package/dist/server/sync/index.d.ts +42 -0
  18. package/dist/server/sync/index.d.ts.map +1 -0
  19. package/dist/server/sync/index.js +42 -0
  20. package/dist/server/sync/index.js.map +1 -0
  21. package/dist/server/sync/opLog.d.ts +62 -0
  22. package/dist/server/sync/opLog.d.ts.map +1 -0
  23. package/dist/server/sync/opLog.js +97 -0
  24. package/dist/server/sync/opLog.js.map +1 -0
  25. package/dist/server/sync/planner.d.ts +32 -0
  26. package/dist/server/sync/planner.d.ts.map +1 -0
  27. package/dist/server/sync/planner.js +31 -0
  28. package/dist/server/sync/planner.js.map +1 -0
  29. package/dist/server/sync/status.d.ts +64 -0
  30. package/dist/server/sync/status.d.ts.map +1 -0
  31. package/dist/server/sync/status.js +50 -0
  32. package/dist/server/sync/status.js.map +1 -0
  33. package/dist/server/sync/timer.d.ts +53 -0
  34. package/dist/server/sync/timer.d.ts.map +1 -0
  35. package/dist/server/sync/timer.js +171 -0
  36. package/dist/server/sync/timer.js.map +1 -0
  37. package/dist/server/sync/tokens.d.ts +26 -0
  38. package/dist/server/sync/tokens.d.ts.map +1 -0
  39. package/dist/server/sync/tokens.js +52 -0
  40. package/dist/server/sync/tokens.js.map +1 -0
  41. package/dist/server/sync/types.d.ts +102 -0
  42. package/dist/server/sync/types.d.ts.map +1 -0
  43. package/dist/server/sync/types.js +30 -0
  44. package/dist/server/sync/types.js.map +1 -0
  45. package/package.json +19 -7
  46. package/src/leafSubpathsImportNothing.spec.ts +59 -3
  47. package/src/server/sync/alarm.spec.ts +149 -0
  48. package/src/server/sync/alarm.ts +137 -0
  49. package/src/server/sync/commands.spec.ts +145 -0
  50. package/src/server/sync/commands.ts +361 -0
  51. package/src/server/sync/engine.spec.ts +496 -0
  52. package/src/server/sync/engine.ts +255 -0
  53. package/src/server/sync/http.ts +316 -0
  54. package/src/server/sync/httpRemote.spec.ts +158 -0
  55. package/src/server/sync/index.ts +90 -0
  56. package/src/server/sync/opLog.spec.ts +110 -0
  57. package/src/server/sync/opLog.ts +189 -0
  58. package/src/server/sync/planner.spec.ts +53 -0
  59. package/src/server/sync/planner.ts +62 -0
  60. package/src/server/sync/status.spec.ts +92 -0
  61. package/src/server/sync/status.ts +108 -0
  62. package/src/server/sync/timer.spec.ts +150 -0
  63. package/src/server/sync/timer.ts +207 -0
  64. package/src/server/sync/tokens.spec.ts +38 -0
  65. package/src/server/sync/tokens.ts +94 -0
  66. package/src/server/sync/types.ts +108 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The wire and store types every consumer of this sync engine shares. The op
3
+ * envelope is the notes shape — `(origin, originSeq)` gives exactly-once application
4
+ * with no coordination — and the report is vault's cursor discipline (the reader may
5
+ * advance exactly to `cursor`, never further) merged with notes' `seen` (a duplicate
6
+ * is not an error, it is proof the dedupe ledger works).
7
+ *
8
+ * ── 🔴 "sync-kit" is a PAST NAME. The engine is `cursedbelt/sync` ────────────
9
+ * The retired generation published this as `@satellites/sync-kit`. It came across
10
+ * whole into `apps/vault/src/kit/sync/` when that app graduated, and came here —
11
+ * `cursedbelt/sync` — on 2026-09-15, before `apps/station` could graduate and fork
12
+ * it a second time. That is the move this header used to call an open question in
13
+ * `tasks/`; it is done, and the app-local copies are what is temporary now.
14
+ *
15
+ * `@satellites/sync-kit` is NOT installable and never was — it was never published
16
+ * to npm — and its design plan (`plans/01-sync-kit-adoption-design.md`) belonged to
17
+ * the retired monorepo and did not come across. Comments in consuming apps still say
18
+ * "the sync-kit adoption": that is the NAME OF AN EVENT, the 2026-08 change that
19
+ * moved vault from its own `oplog` table onto this engine's `sync_ops`, and it is
20
+ * worth keeping because the dual-compat code only makes sense in its light.
21
+ */
22
+
23
+ /** One durable mutation in the shared op log. `payload` is app-defined per `kind`. */
24
+ export interface SyncOp {
25
+ /** This instance's monotonic sequence for the op (assigned by the local log). */
26
+ seq: number;
27
+ /** The instance id that BORN the op (stable per instance, never per device). */
28
+ origin: string;
29
+ /** The origin's own monotonic counter — `(origin, originSeq)` is the identity. */
30
+ originSeq: number;
31
+ /** Wall-clock ms when the op was born. LWW inputs, display, nothing structural. */
32
+ ts: number;
33
+ /** App-defined kind, e.g. `doc.set`. The version-negotiation unit. */
34
+ kind: string;
35
+ payload: unknown;
36
+ }
37
+
38
+ /** What applying one batch did. `cursor` is the last seq FULLY applied — a stopped
39
+ * batch leaves it just before the failure so the next run resumes there. */
40
+ export interface ApplyReport {
41
+ /** Ops that changed local state. */
42
+ applied: number;
43
+ /** Ops already held (dedupe hit) — counted, never re-applied. */
44
+ seen: number;
45
+ /** Concurrent edits reconciled (however the app's applier chose to). */
46
+ conflicts: number;
47
+ cursor: number;
48
+ /** Non-null when the batch stopped early (version skew, deferred ordering…). */
49
+ error: string | null;
50
+ }
51
+
52
+ /** The peer info handshake. `opKinds` is the compatibility advertisement — a dialer
53
+ * stops BEFORE the first op a peer cannot take (notes' version-skew lesson: sending
54
+ * it wedges the push with an error that reads like corruption). */
55
+ export interface PeerInfo {
56
+ instanceId: string;
57
+ head: number;
58
+ opKinds: string[];
59
+ }
60
+
61
+ /** The transport seam. Pure interface so tests drive the engine against a REAL second
62
+ * store mounted in a Hono app — no HTTP mocks (the notes/vault discipline). */
63
+ export interface RemoteApi {
64
+ info(): Promise<PeerInfo>;
65
+ pull(
66
+ after: number,
67
+ excludeOrigin: string,
68
+ ): Promise<{ ops: SyncOp[]; cursor: number; head: number }>;
69
+ push(payload: { ops: SyncOp[]; peerHas: number }): Promise<ApplyReport>;
70
+ /** The receiver's "someone pressed Sync now here" stamp; null when unsupported. */
71
+ requestedAt?(): Promise<number | null>;
72
+ }
73
+
74
+ /** Context the applier gets per batch — vault's concurrency bound. */
75
+ export interface ApplyContext {
76
+ /** The peer's stable instance id. */
77
+ peerId: string;
78
+ /** Max LOCAL-born seq the peer already holds of OURS — an entity whose newest
79
+ * local op is beyond this AND diverges is a true concurrent edit. */
80
+ peerReceivedUpTo: number;
81
+ }
82
+
83
+ /** The app's merge logic for ONE op that passed the dedupe ledger. Return value:
84
+ * - `"applied"` — state changed
85
+ * - `"ignored"` — stale/no-op (still recorded in the ledger, cursor advances)
86
+ * - `"conflict"` — applied with a materialized conflict artifact
87
+ * Throw {@link StopBatch} to stop BEFORE this op (cursor stays short of it). */
88
+ export type ApplyOne = (op: SyncOp, ctx: ApplyContext) => "applied" | "ignored" | "conflict";
89
+
90
+ /** Stop the current batch before the offending op — deferred ordering, a payload this
91
+ * build cannot hold yet, anything where "retry after the world changes" is right. */
92
+ export class StopBatch extends Error {
93
+ constructor(reason: string) {
94
+ super(reason);
95
+ this.name = "StopBatch";
96
+ }
97
+ }
98
+
99
+ export interface SyncSummary {
100
+ remoteId: string;
101
+ pushed: number;
102
+ pulled: number;
103
+ conflicts: number;
104
+ /** Ops the export policy held back (counted, never sent). */
105
+ held: number;
106
+ /** Set when the remote runs an older schema and cannot take what comes next. */
107
+ blockedBy?: string;
108
+ }