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.
- package/dist/server/sync/alarm.d.ts +35 -0
- package/dist/server/sync/alarm.d.ts.map +1 -0
- package/dist/server/sync/alarm.js +92 -0
- package/dist/server/sync/alarm.js.map +1 -0
- package/dist/server/sync/commands.d.ts +88 -0
- package/dist/server/sync/commands.d.ts.map +1 -0
- package/dist/server/sync/commands.js +242 -0
- package/dist/server/sync/commands.js.map +1 -0
- package/dist/server/sync/engine.d.ts +63 -0
- package/dist/server/sync/engine.d.ts.map +1 -0
- package/dist/server/sync/engine.js +185 -0
- package/dist/server/sync/engine.js.map +1 -0
- package/dist/server/sync/http.d.ts +107 -0
- package/dist/server/sync/http.d.ts.map +1 -0
- package/dist/server/sync/http.js +244 -0
- package/dist/server/sync/http.js.map +1 -0
- package/dist/server/sync/index.d.ts +42 -0
- package/dist/server/sync/index.d.ts.map +1 -0
- package/dist/server/sync/index.js +42 -0
- package/dist/server/sync/index.js.map +1 -0
- package/dist/server/sync/opLog.d.ts +62 -0
- package/dist/server/sync/opLog.d.ts.map +1 -0
- package/dist/server/sync/opLog.js +97 -0
- package/dist/server/sync/opLog.js.map +1 -0
- package/dist/server/sync/planner.d.ts +32 -0
- package/dist/server/sync/planner.d.ts.map +1 -0
- package/dist/server/sync/planner.js +31 -0
- package/dist/server/sync/planner.js.map +1 -0
- package/dist/server/sync/status.d.ts +64 -0
- package/dist/server/sync/status.d.ts.map +1 -0
- package/dist/server/sync/status.js +50 -0
- package/dist/server/sync/status.js.map +1 -0
- package/dist/server/sync/timer.d.ts +53 -0
- package/dist/server/sync/timer.d.ts.map +1 -0
- package/dist/server/sync/timer.js +171 -0
- package/dist/server/sync/timer.js.map +1 -0
- package/dist/server/sync/tokens.d.ts +26 -0
- package/dist/server/sync/tokens.d.ts.map +1 -0
- package/dist/server/sync/tokens.js +52 -0
- package/dist/server/sync/tokens.js.map +1 -0
- package/dist/server/sync/types.d.ts +102 -0
- package/dist/server/sync/types.d.ts.map +1 -0
- package/dist/server/sync/types.js +30 -0
- package/dist/server/sync/types.js.map +1 -0
- package/package.json +19 -7
- package/src/leafSubpathsImportNothing.spec.ts +59 -3
- package/src/server/sync/alarm.spec.ts +149 -0
- package/src/server/sync/alarm.ts +137 -0
- package/src/server/sync/commands.spec.ts +145 -0
- package/src/server/sync/commands.ts +361 -0
- package/src/server/sync/engine.spec.ts +496 -0
- package/src/server/sync/engine.ts +255 -0
- package/src/server/sync/http.ts +316 -0
- package/src/server/sync/httpRemote.spec.ts +158 -0
- package/src/server/sync/index.ts +90 -0
- package/src/server/sync/opLog.spec.ts +110 -0
- package/src/server/sync/opLog.ts +189 -0
- package/src/server/sync/planner.spec.ts +53 -0
- package/src/server/sync/planner.ts +62 -0
- package/src/server/sync/status.spec.ts +92 -0
- package/src/server/sync/status.ts +108 -0
- package/src/server/sync/timer.spec.ts +150 -0
- package/src/server/sync/timer.ts +207 -0
- package/src/server/sync/tokens.spec.ts +38 -0
- package/src/server/sync/tokens.ts +94 -0
- 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
|
+
}
|