json-patch-to-crdt 0.3.0 → 0.4.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/README.md +79 -1
- package/dist/{compact-BS7F604m.mjs → compact-BcwxBNx_.mjs} +810 -409
- package/dist/{compact-BToZE6Q6.js → compact-CXfvMNCT.js} +839 -408
- package/dist/{depth-BTHjgY18.d.mts → depth-CpJSyZE5.d.mts} +72 -14
- package/dist/{depth-DSl2ghKu.d.ts → depth-D88VeWb-.d.ts} +72 -14
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -2
- package/dist/index.mjs +2 -2
- package/dist/internals.d.mts +2 -2
- package/dist/internals.d.ts +2 -2
- package/dist/internals.js +6 -1
- package/dist/internals.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,7 +102,9 @@ For array-heavy snapshots, `diffJsonPatch` and `crdtToJsonPatch` support:
|
|
|
102
102
|
- `arrayStrategy: "lcs-linear"`: deterministic index-level array edits using a lower-memory linear-space LCS traversal.
|
|
103
103
|
- `arrayStrategy: "atomic"`: replace the whole array with a single patch operation.
|
|
104
104
|
|
|
105
|
-
`lcsMaxCells` only applies to `arrayStrategy: "lcs"`. If the classic LCS matrix would exceed the configured cap, the diff falls back to an atomic array `replace`.
|
|
105
|
+
`lcsMaxCells` only applies to `arrayStrategy: "lcs"`. If the classic LCS matrix for the trimmed unmatched window would exceed the configured cap, the diff falls back to an atomic array `replace`.
|
|
106
|
+
|
|
107
|
+
`lcsLinearMaxCells` is the matching opt-in guardrail for `arrayStrategy: "lcs-linear"`. It uses the same trimmed unmatched-window estimate, but caps worst-case runtime instead of matrix allocation. When the cap is exceeded, `lcs-linear` also falls back to an atomic array `replace`. If you do not set `lcsLinearMaxCells`, `lcs-linear` keeps its previous behavior and will continue to run without an automatic fallback.
|
|
106
108
|
|
|
107
109
|
`diffJsonPatch` keeps the existing `add`/`remove`/`replace` output by default. Set
|
|
108
110
|
`emitMoves` and/or `emitCopies` to opt into deterministic RFC 6902 `move`/`copy`
|
|
@@ -129,6 +131,15 @@ console.log(toJson(next));
|
|
|
129
131
|
// { counter: 2 }
|
|
130
132
|
```
|
|
131
133
|
|
|
134
|
+
Persisted snapshot compatibility:
|
|
135
|
+
|
|
136
|
+
- `serializeState(...)` emits a versioned envelope.
|
|
137
|
+
- `deserializeState(...)` accepts both the current versioned format and legacy unversioned snapshots.
|
|
138
|
+
- Future envelope versions are rejected until an explicit migration path is added.
|
|
139
|
+
|
|
140
|
+
The same compatibility contract applies to the lower-level `serializeDoc(...)` and
|
|
141
|
+
`deserializeDoc(...)` helpers exported from `json-patch-to-crdt/internals`.
|
|
142
|
+
|
|
132
143
|
## Error Handling
|
|
133
144
|
|
|
134
145
|
`applyPatch` throws `PatchError` when a patch cannot be applied.
|
|
@@ -147,6 +158,67 @@ try {
|
|
|
147
158
|
|
|
148
159
|
If you prefer non-throwing results, use `tryApplyPatch(...)` / `tryMergeState(...)`.
|
|
149
160
|
|
|
161
|
+
## Version Vector Helpers
|
|
162
|
+
|
|
163
|
+
`observedVersionVector(...)` lets you inspect the highest observed counter per
|
|
164
|
+
actor from either a `Doc` or a `CrdtState`. Use `mergeVersionVectors(...)` to
|
|
165
|
+
union peer observations, and `intersectVersionVectors(...)` when you need a
|
|
166
|
+
causally-stable checkpoint that every replica has already seen.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import {
|
|
170
|
+
compactStateTombstones,
|
|
171
|
+
intersectVersionVectors,
|
|
172
|
+
mergeVersionVectors,
|
|
173
|
+
observedVersionVector,
|
|
174
|
+
versionVectorCovers,
|
|
175
|
+
} from "json-patch-to-crdt";
|
|
176
|
+
|
|
177
|
+
const seenByReplicaA = observedVersionVector(replicaA);
|
|
178
|
+
const seenByReplicaB = observedVersionVector(replicaB);
|
|
179
|
+
|
|
180
|
+
const mergedCheckpoint = mergeVersionVectors(seenByReplicaA, seenByReplicaB);
|
|
181
|
+
const stableCheckpoint = intersectVersionVectors(seenByReplicaA, seenByReplicaB);
|
|
182
|
+
|
|
183
|
+
if (versionVectorCovers(mergedCheckpoint, stableCheckpoint)) {
|
|
184
|
+
const compacted = compactStateTombstones(state, { stable: stableCheckpoint });
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Use `mergeVersionVectors(...)` for sync-style "what has this cluster observed?"
|
|
189
|
+
bookkeeping. Use `intersectVersionVectors(...)` for tombstone compaction
|
|
190
|
+
checkpoints, because compaction is only safe once every live peer covers the
|
|
191
|
+
same delete dots. If you call `intersectVersionVectors(...)` with a single
|
|
192
|
+
vector, it returns that vector unchanged.
|
|
193
|
+
|
|
194
|
+
## Runtime JSON Validation
|
|
195
|
+
|
|
196
|
+
`createState`, `applyPatch`, `diffJsonPatch`, and `crdtToJsonPatch` accept a
|
|
197
|
+
`jsonValidation` option for callers that may pass runtime values through `any`.
|
|
198
|
+
|
|
199
|
+
- `"none"` keeps the current behavior with no extra runtime validation.
|
|
200
|
+
- `"strict"` rejects values that are not valid JSON, including non-finite
|
|
201
|
+
numbers, `undefined`, and non-plain objects such as `Date`, `Map`, `Set`,
|
|
202
|
+
`RegExp`, typed arrays, and class instances.
|
|
203
|
+
- `"normalize"` coerces invalid values into JSON-safe output. Non-finite
|
|
204
|
+
numbers become `null`. Non-plain objects also become `null` at the root or
|
|
205
|
+
inside arrays, and are omitted from object properties.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { createState, toJson } from "json-patch-to-crdt";
|
|
209
|
+
|
|
210
|
+
const unsafeInput = {
|
|
211
|
+
keep: true,
|
|
212
|
+
nested: { when: new Date("2020-01-01") },
|
|
213
|
+
arr: [new Uint8Array([1, 2, 3])],
|
|
214
|
+
} as any;
|
|
215
|
+
|
|
216
|
+
const state = createState(unsafeInput, { actor: "A", jsonValidation: "normalize" });
|
|
217
|
+
|
|
218
|
+
console.log(toJson(state));
|
|
219
|
+
// { keep: true, nested: {}, arr: [null] }
|
|
220
|
+
```
|
|
221
|
+
|
|
150
222
|
## API Overview
|
|
151
223
|
|
|
152
224
|
Main exports most apps need:
|
|
@@ -157,6 +229,11 @@ Main exports most apps need:
|
|
|
157
229
|
- `tryApplyPatch(state, patch, options?)`
|
|
158
230
|
- `mergeState(local, remote, { actor })`
|
|
159
231
|
- `tryMergeState(local, remote, options?)`
|
|
232
|
+
- `observedVersionVector(stateOrDoc)`
|
|
233
|
+
- `mergeVersionVectors(...vectors)`
|
|
234
|
+
- `intersectVersionVectors(...vectors)`
|
|
235
|
+
- `versionVectorCovers(observed, required)`
|
|
236
|
+
- `compactStateTombstones(state, { stable })`
|
|
160
237
|
- `toJson(stateOrDoc)`
|
|
161
238
|
- `diffJsonPatch(baseJson, nextJson, options?)`
|
|
162
239
|
- `serializeState(state)` / `deserializeState(payload)`
|
|
@@ -173,6 +250,7 @@ import { crdtToJsonPatch, applyPatchAsActor } from "json-patch-to-crdt/internals
|
|
|
173
250
|
- Arrays use a CRDT sequence internally; concurrent inserts are preserved.
|
|
174
251
|
- Patches are interpreted relative to a snapshot (RFC-style sequential execution by default).
|
|
175
252
|
- Merge assumes replicas come from the same origin state (use `forkState`).
|
|
253
|
+
- Persisted CRDT snapshots currently use envelope version `1`; legacy unversioned snapshots remain readable.
|
|
176
254
|
|
|
177
255
|
## License
|
|
178
256
|
|