@furystack/entity-sync 2.0.0 → 3.0.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/CHANGELOG.md +41 -0
- package/esm/client-sync-message.d.ts +2 -2
- package/esm/sync-state.d.ts +12 -1
- package/esm/sync-state.d.ts.map +1 -1
- package/esm/sync-version.d.ts +11 -3
- package/esm/sync-version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/client-sync-message.ts +2 -2
- package/src/sync-state.ts +12 -1
- package/src/sync-version.ts +11 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.0.0] - 2026-05-21
|
|
4
|
+
|
|
5
|
+
### 📚 Documentation
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
### 💥 Breaking Changes
|
|
10
|
+
|
|
11
|
+
### `SyncVersion.seq` and `ClientSyncMessage.lastSeq` are now opaque strings
|
|
12
|
+
|
|
13
|
+
`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`.
|
|
14
|
+
|
|
15
|
+
**Affected types:**
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
type SyncVersion = {
|
|
19
|
+
// ❌ Before
|
|
20
|
+
seq: number
|
|
21
|
+
// ✅ After
|
|
22
|
+
seq: string
|
|
23
|
+
timestamp: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type ClientSyncMessage =
|
|
27
|
+
| { type: 'subscribe-entity'; /* ... */ lastSeq?: string /* was: number */ }
|
|
28
|
+
| { type: 'subscribe-collection'; /* ... */ lastSeq?: string /* was: number */ }
|
|
29
|
+
// ...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**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.
|
|
33
|
+
|
|
34
|
+
**Migration:**
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
2. Treat `seq` as a black-box token: store it, send it back, never inspect it.
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
### ⬆️ Dependencies
|
|
41
|
+
|
|
42
|
+
- Re-released to pick up the bumped workspace dependency `@furystack/core`. No source changes — version bump only.
|
|
43
|
+
|
|
3
44
|
## [2.0.0] - 2026-04-25
|
|
4
45
|
|
|
5
46
|
### 💥 Breaking Changes
|
|
@@ -8,7 +8,7 @@ export type ClientSyncMessage = {
|
|
|
8
8
|
requestId: string;
|
|
9
9
|
model: string;
|
|
10
10
|
key: unknown;
|
|
11
|
-
lastSeq?:
|
|
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?:
|
|
20
|
+
lastSeq?: string;
|
|
21
21
|
} | {
|
|
22
22
|
type: 'unsubscribe';
|
|
23
23
|
subscriptionId: string;
|
package/esm/sync-state.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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';
|
package/esm/sync-state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-state.d.ts","sourceRoot":"","sources":["../src/sync-state.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
package/esm/sync-version.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version metadata included in all server sync messages.
|
|
3
|
-
*
|
|
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
|
-
/**
|
|
7
|
-
seq:
|
|
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
|
|
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": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Shared protocol types for FuryStack entity synchronization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/furystack/furystack",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@furystack/core": "^17.
|
|
40
|
+
"@furystack/core": "^17.1.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^6.0.3"
|
|
@@ -10,7 +10,7 @@ export type ClientSyncMessage =
|
|
|
10
10
|
requestId: string
|
|
11
11
|
model: string
|
|
12
12
|
key: unknown
|
|
13
|
-
lastSeq?:
|
|
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?:
|
|
23
|
+
lastSeq?: string
|
|
24
24
|
}
|
|
25
25
|
| {
|
|
26
26
|
type: 'unsubscribe'
|
package/src/sync-state.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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' }
|
package/src/sync-version.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version metadata included in all server sync messages.
|
|
3
|
-
*
|
|
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
|
-
/**
|
|
7
|
-
seq:
|
|
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
|
}
|