@furystack/entity-sync 2.0.0 → 3.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.0.1] - 2026-06-05
4
+
5
+ ### 👷 CI
6
+
7
+ - Raised the minimum supported Node.js to `>=24.0.0` (Node 24 LTS) in `engines`, dropping Node 22.
8
+
9
+ ### 🔧 Chores
10
+
11
+ - Patch release to track the workspace `@furystack/core` bump. No source or direct dependency changes in this package.
12
+
13
+ ## [3.0.0] - 2026-05-21
14
+
15
+ ### 📚 Documentation
16
+
17
+ - Rewrote JSDoc on `SyncState` and the shared transport types to follow the new value-test guidance: dropped restate-the-type narration, kept intent / trade-offs / constraints.
18
+
19
+ ### 💥 Breaking Changes
20
+
21
+ ### `SyncVersion.seq` and `ClientSyncMessage.lastSeq` are now opaque strings
22
+
23
+ `seq` is no longer a numeric counter assigned by the server — it is now an **opaque, ordered token** assigned by the underlying `@furystack/cross-node-bus` adapter (integer counters in-process, `<ms>-<n>` Redis Stream ids, etc.). Clients **must not** compare seqs lexicographically or numerically; the only safe operations are equality and round-tripping the value back to the server via `lastSeq`.
24
+
25
+ **Affected types:**
26
+
27
+ ```typescript
28
+ type SyncVersion = {
29
+ // ❌ Before
30
+ seq: number
31
+ // ✅ After
32
+ seq: string
33
+ timestamp: string
34
+ }
35
+
36
+ type ClientSyncMessage =
37
+ | { type: 'subscribe-entity'; /* ... */ lastSeq?: string /* was: number */ }
38
+ | { type: 'subscribe-collection'; /* ... */ lastSeq?: string /* was: number */ }
39
+ // ...
40
+ ```
41
+
42
+ **Impact:** Any code that does arithmetic on `seq` (e.g. `seq + 1`, `seq > previous`) breaks. Any persisted client-side state holding `lastSeq: number` becomes incompatible.
43
+
44
+ **Migration:**
45
+
46
+ 1. Replace numeric comparisons with equality checks. Adapter-specific ordering, when needed, lives behind the bus's `compareSeq(a, b)` — do not re-implement it.
47
+ 2. Treat `seq` as a black-box token: store it, send it back, never inspect it.
48
+ 3. Persisted client caches (e.g. `SyncCacheEntry` in `@furystack/entity-sync-client`) carry `lastSeq` typed as `number` from previous versions and will not load. Either drop the cache on upgrade or write a one-shot migration that converts numeric seqs to their string representation.
49
+
50
+ ### ⬆️ Dependencies
51
+
52
+ - Re-released to pick up the bumped workspace dependency `@furystack/core`. No source changes — version bump only.
53
+
3
54
  ## [2.0.0] - 2026-04-25
4
55
 
5
56
  ### 💥 Breaking Changes
@@ -8,7 +8,7 @@ export type ClientSyncMessage = {
8
8
  requestId: string;
9
9
  model: string;
10
10
  key: unknown;
11
- lastSeq?: number;
11
+ lastSeq?: string;
12
12
  } | {
13
13
  type: 'subscribe-collection';
14
14
  requestId: string;
@@ -17,7 +17,7 @@ export type ClientSyncMessage = {
17
17
  top?: number;
18
18
  skip?: number;
19
19
  order?: Record<string, 'ASC' | 'DESC'>;
20
- lastSeq?: number;
20
+ lastSeq?: string;
21
21
  } | {
22
22
  type: 'unsubscribe';
23
23
  subscriptionId: string;
@@ -1,5 +1,16 @@
1
1
  /**
2
- * Client-side sync state for a subscribed entity or collection
2
+ * Discriminated union over the lifecycle of a subscribed entity or
3
+ * collection on the client:
4
+ *
5
+ * - `connecting` — handshake in progress, no `data` yet (or first load).
6
+ * - `cached` — local cache hit; rendering immediately while a `delta` sync
7
+ * confirms freshness.
8
+ * - `suspended` — connection dropped; the last-known `data` is still
9
+ * served while reconnect attempts run in the background.
10
+ * - `synced` — server confirmed our seq matches; `data` is authoritative.
11
+ * - `error` — terminal: subscription rejected; `error` carries the reason.
12
+ *
13
+ * Discriminate on `status` to narrow.
3
14
  */
4
15
  export type SyncState<T> = {
5
16
  status: 'connecting';
@@ -1 +1 @@
1
- {"version":3,"file":"sync-state.d.ts","sourceRoot":"","sources":["../src/sync-state.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IACnB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"sync-state.d.ts","sourceRoot":"","sources":["../src/sync-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IACnB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA"}
@@ -1,10 +1,18 @@
1
1
  /**
2
2
  * Version metadata included in all server sync messages.
3
- * Every change to a model increments the seq and records a timestamp.
3
+ *
4
+ * `seq` is an **opaque, ordered token** assigned by the server's underlying
5
+ * cross-node bus adapter — the broadcaster never invents the value, it only
6
+ * forwards what the bus stamps on each message. Clients **must not** compare
7
+ * seqs lexicographically or numerically: the only safe operations are
8
+ * round-tripping the value back to the server (via `lastSeq` on
9
+ * `ClientSyncMessage`) and equality. The string encoding lets adapters use
10
+ * native id formats — e.g. integer counters in-process, `<ms>-<n>` Redis
11
+ * Stream ids — without a translation layer.
4
12
  */
5
13
  export type SyncVersion = {
6
- /** Monotonically increasing sequence number per model */
7
- seq: number;
14
+ /** Adapter-assigned, opaque, ordered per-topic identifier. */
15
+ seq: string;
8
16
  /** ISO 8601 timestamp, used for time-based changelog pruning */
9
17
  timestamp: string;
10
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"sync-version.d.ts","sourceRoot":"","sources":["../src/sync-version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAA;IACX,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA"}
1
+ {"version":3,"file":"sync-version.d.ts","sourceRoot":"","sources":["../src/sync-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAA;IACX,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furystack/entity-sync",
3
- "version": "2.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Shared protocol types for FuryStack entity synchronization",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -37,13 +37,13 @@
37
37
  },
38
38
  "homepage": "https://github.com/furystack/furystack",
39
39
  "dependencies": {
40
- "@furystack/core": "^17.0.0"
40
+ "@furystack/core": "^17.1.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "typescript": "^6.0.3"
44
44
  },
45
45
  "engines": {
46
- "node": ">=22.0.0"
46
+ "node": ">=24.0.0"
47
47
  },
48
48
  "sideEffects": false
49
49
  }
@@ -10,7 +10,7 @@ export type ClientSyncMessage =
10
10
  requestId: string
11
11
  model: string
12
12
  key: unknown
13
- lastSeq?: number
13
+ lastSeq?: string
14
14
  }
15
15
  | {
16
16
  type: 'subscribe-collection'
@@ -20,7 +20,7 @@ export type ClientSyncMessage =
20
20
  top?: number
21
21
  skip?: number
22
22
  order?: Record<string, 'ASC' | 'DESC'>
23
- lastSeq?: number
23
+ lastSeq?: string
24
24
  }
25
25
  | {
26
26
  type: 'unsubscribe'
package/src/sync-state.ts CHANGED
@@ -1,5 +1,16 @@
1
1
  /**
2
- * Client-side sync state for a subscribed entity or collection
2
+ * Discriminated union over the lifecycle of a subscribed entity or
3
+ * collection on the client:
4
+ *
5
+ * - `connecting` — handshake in progress, no `data` yet (or first load).
6
+ * - `cached` — local cache hit; rendering immediately while a `delta` sync
7
+ * confirms freshness.
8
+ * - `suspended` — connection dropped; the last-known `data` is still
9
+ * served while reconnect attempts run in the background.
10
+ * - `synced` — server confirmed our seq matches; `data` is authoritative.
11
+ * - `error` — terminal: subscription rejected; `error` carries the reason.
12
+ *
13
+ * Discriminate on `status` to narrow.
3
14
  */
4
15
  export type SyncState<T> =
5
16
  | { status: 'connecting' }
@@ -1,10 +1,18 @@
1
1
  /**
2
2
  * Version metadata included in all server sync messages.
3
- * Every change to a model increments the seq and records a timestamp.
3
+ *
4
+ * `seq` is an **opaque, ordered token** assigned by the server's underlying
5
+ * cross-node bus adapter — the broadcaster never invents the value, it only
6
+ * forwards what the bus stamps on each message. Clients **must not** compare
7
+ * seqs lexicographically or numerically: the only safe operations are
8
+ * round-tripping the value back to the server (via `lastSeq` on
9
+ * `ClientSyncMessage`) and equality. The string encoding lets adapters use
10
+ * native id formats — e.g. integer counters in-process, `<ms>-<n>` Redis
11
+ * Stream ids — without a translation layer.
4
12
  */
5
13
  export type SyncVersion = {
6
- /** Monotonically increasing sequence number per model */
7
- seq: number
14
+ /** Adapter-assigned, opaque, ordered per-topic identifier. */
15
+ seq: string
8
16
  /** ISO 8601 timestamp, used for time-based changelog pruning */
9
17
  timestamp: string
10
18
  }