@dabble/patches 0.4.4 → 0.4.6
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/algorithms/client/applyCommittedChanges.d.ts +8 -2
- package/dist/algorithms/client/applyCommittedChanges.js +30 -38
- package/dist/algorithms/client/batching.d.ts +8 -2
- package/dist/algorithms/client/batching.js +38 -37
- package/dist/algorithms/client/breakChange.d.ts +8 -2
- package/dist/algorithms/client/breakChange.js +191 -240
- package/dist/algorithms/client/createStateFromSnapshot.d.ts +8 -2
- package/dist/algorithms/client/createStateFromSnapshot.js +7 -8
- package/dist/algorithms/client/getJSONByteSize.d.ts +3 -1
- package/dist/algorithms/client/getJSONByteSize.js +12 -11
- package/dist/algorithms/client/makeChange.d.ts +8 -2
- package/dist/algorithms/client/makeChange.js +28 -36
- package/dist/algorithms/server/commitChanges.d.ts +9 -3
- package/dist/algorithms/server/commitChanges.js +69 -78
- package/dist/algorithms/server/createVersion.d.ts +9 -3
- package/dist/algorithms/server/createVersion.js +21 -27
- package/dist/algorithms/server/getSnapshotAtRevision.d.ts +9 -3
- package/dist/algorithms/server/getSnapshotAtRevision.js +27 -28
- package/dist/algorithms/server/getStateAtRevision.d.ts +9 -3
- package/dist/algorithms/server/getStateAtRevision.js +13 -17
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.d.ts +9 -3
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.js +60 -77
- package/dist/algorithms/server/transformIncomingChanges.d.ts +8 -2
- package/dist/algorithms/server/transformIncomingChanges.js +27 -39
- package/dist/algorithms/shared/applyChanges.d.ts +8 -2
- package/dist/algorithms/shared/applyChanges.js +11 -16
- package/dist/algorithms/shared/rebaseChanges.d.ts +8 -2
- package/dist/algorithms/shared/rebaseChanges.js +30 -49
- package/dist/chunk-IZ2YBCUP.js +56 -0
- package/dist/client/InMemoryStore.d.ts +9 -3
- package/dist/client/InMemoryStore.js +92 -101
- package/dist/client/IndexedDBStore.d.ts +9 -3
- package/dist/client/IndexedDBStore.js +378 -491
- package/dist/client/Patches.d.ts +18 -13
- package/dist/client/Patches.js +152 -207
- package/dist/client/PatchesDoc.d.ts +14 -8
- package/dist/client/PatchesDoc.js +147 -154
- package/dist/client/PatchesHistoryClient.d.ts +12 -5
- package/dist/client/PatchesHistoryClient.js +110 -117
- package/dist/client/PatchesStore.d.ts +9 -3
- package/dist/client/PatchesStore.js +0 -1
- package/dist/client/index.d.ts +12 -6
- package/dist/client/index.js +5 -5
- package/dist/data/change.d.ts +9 -3
- package/dist/data/change.js +23 -15
- package/dist/data/version.d.ts +9 -3
- package/dist/data/version.js +11 -15
- package/dist/event-signal.d.ts +7 -6
- package/dist/event-signal.js +24 -39
- package/dist/index-CvQws3AB.d.ts +36 -0
- package/dist/index.d.ts +27 -5
- package/dist/index.js +10 -4
- package/dist/json-patch/JSONPatch.d.ts +9 -5
- package/dist/json-patch/JSONPatch.js +175 -183
- package/dist/json-patch/applyPatch.d.ts +5 -2
- package/dist/json-patch/applyPatch.js +27 -35
- package/dist/json-patch/composePatch.d.ts +5 -2
- package/dist/json-patch/composePatch.js +34 -34
- package/dist/json-patch/createJSONPatch.d.ts +7 -2
- package/dist/json-patch/createJSONPatch.js +11 -38
- package/dist/json-patch/index.d.ts +14 -6
- package/dist/json-patch/index.js +20 -9
- package/dist/json-patch/invertPatch.d.ts +5 -2
- package/dist/json-patch/invertPatch.js +31 -30
- package/dist/json-patch/ops/add.d.ts +5 -2
- package/dist/json-patch/ops/add.js +53 -51
- package/dist/json-patch/ops/bitmask.d.ts +8 -5
- package/dist/json-patch/ops/bitmask.js +41 -44
- package/dist/json-patch/ops/copy.d.ts +5 -2
- package/dist/json-patch/ops/copy.js +32 -33
- package/dist/json-patch/ops/increment.d.ts +5 -2
- package/dist/json-patch/ops/increment.js +21 -20
- package/dist/json-patch/ops/index.d.ts +10 -21
- package/dist/json-patch/ops/index.js +34 -24
- package/dist/json-patch/ops/move.d.ts +5 -2
- package/dist/json-patch/ops/move.js +132 -198
- package/dist/json-patch/ops/remove.d.ts +5 -2
- package/dist/json-patch/ops/remove.js +33 -30
- package/dist/json-patch/ops/replace.d.ts +5 -2
- package/dist/json-patch/ops/replace.js +45 -43
- package/dist/json-patch/ops/test.d.ts +5 -2
- package/dist/json-patch/ops/test.js +25 -21
- package/dist/json-patch/ops/text.d.ts +5 -2
- package/dist/json-patch/ops/text.js +54 -54
- package/dist/json-patch/pathProxy.d.ts +9 -3
- package/dist/json-patch/pathProxy.js +27 -48
- package/dist/json-patch/state.d.ts +5 -2
- package/dist/json-patch/state.js +11 -7
- package/dist/json-patch/transformPatch.d.ts +6 -2
- package/dist/json-patch/transformPatch.js +21 -24
- package/dist/json-patch/types.d.ts +9 -7
- package/dist/json-patch/types.js +0 -1
- package/dist/json-patch/utils/deepEqual.d.ts +3 -1
- package/dist/json-patch/utils/deepEqual.js +32 -28
- package/dist/json-patch/utils/exit.d.ts +5 -2
- package/dist/json-patch/utils/exit.js +7 -3
- package/dist/json-patch/utils/get.d.ts +5 -2
- package/dist/json-patch/utils/get.js +8 -4
- package/dist/json-patch/utils/getOpData.d.ts +5 -2
- package/dist/json-patch/utils/getOpData.js +12 -9
- package/dist/json-patch/utils/getType.d.ts +6 -3
- package/dist/json-patch/utils/getType.js +9 -4
- package/dist/json-patch/utils/index.d.ts +15 -14
- package/dist/json-patch/utils/index.js +14 -14
- package/dist/json-patch/utils/log.d.ts +4 -2
- package/dist/json-patch/utils/log.js +8 -3
- package/dist/json-patch/utils/ops.d.ts +8 -5
- package/dist/json-patch/utils/ops.js +83 -100
- package/dist/json-patch/utils/paths.d.ts +12 -9
- package/dist/json-patch/utils/paths.js +54 -51
- package/dist/json-patch/utils/pluck.d.ts +8 -5
- package/dist/json-patch/utils/pluck.js +32 -26
- package/dist/json-patch/utils/shallowCopy.d.ts +3 -1
- package/dist/json-patch/utils/shallowCopy.js +22 -18
- package/dist/json-patch/utils/softWrites.d.ts +6 -3
- package/dist/json-patch/utils/softWrites.js +17 -16
- package/dist/json-patch/utils/toArrayIndex.d.ts +3 -1
- package/dist/json-patch/utils/toArrayIndex.js +14 -10
- package/dist/json-patch/utils/toKeys.d.ts +3 -1
- package/dist/json-patch/utils/toKeys.js +15 -11
- package/dist/json-patch/utils/updateArrayIndexes.d.ts +5 -2
- package/dist/json-patch/utils/updateArrayIndexes.js +33 -37
- package/dist/json-patch/utils/updateArrayPath.d.ts +5 -2
- package/dist/json-patch/utils/updateArrayPath.js +29 -42
- package/dist/net/PatchesClient.d.ts +128 -0
- package/dist/net/PatchesClient.js +161 -0
- package/dist/net/PatchesSync.d.ts +19 -9
- package/dist/net/PatchesSync.js +291 -386
- package/dist/net/error.d.ts +3 -1
- package/dist/net/error.js +9 -6
- package/dist/net/http/FetchTransport.d.ts +21 -0
- package/dist/net/http/FetchTransport.js +34 -0
- package/dist/net/index.d.ts +26 -12
- package/dist/net/index.js +12 -10
- package/dist/net/protocol/JSONRPCClient.d.ts +11 -4
- package/dist/net/protocol/JSONRPCClient.js +95 -103
- package/dist/net/protocol/JSONRPCServer.d.ts +15 -8
- package/dist/net/protocol/JSONRPCServer.js +101 -123
- package/dist/net/protocol/types.d.ts +21 -15
- package/dist/net/protocol/types.js +0 -1
- package/dist/net/protocol/utils.d.ts +12 -0
- package/dist/net/protocol/utils.js +15 -0
- package/dist/net/types.d.ts +4 -2
- package/dist/net/types.js +0 -1
- package/dist/net/webrtc/WebRTCAwareness.d.ts +14 -4
- package/dist/net/webrtc/WebRTCAwareness.js +111 -120
- package/dist/net/webrtc/WebRTCTransport.d.ts +16 -8
- package/dist/net/webrtc/WebRTCTransport.js +149 -157
- package/dist/net/webrtc/index.d.ts +10 -2
- package/dist/net/webrtc/index.js +2 -2
- package/dist/net/websocket/AuthorizationProvider.d.ts +7 -5
- package/dist/net/websocket/AuthorizationProvider.js +12 -17
- package/dist/net/websocket/PatchesWebSocket.d.ts +14 -109
- package/dist/net/websocket/PatchesWebSocket.js +37 -184
- package/dist/net/websocket/RPCServer.d.ts +19 -10
- package/dist/net/websocket/RPCServer.js +190 -192
- package/dist/net/websocket/SignalingService.d.ts +12 -32
- package/dist/net/websocket/SignalingService.js +126 -133
- package/dist/net/websocket/WebSocketServer.d.ts +17 -4
- package/dist/net/websocket/WebSocketServer.js +64 -72
- package/dist/net/websocket/WebSocketTransport.d.ts +13 -5
- package/dist/net/websocket/WebSocketTransport.js +178 -207
- package/dist/net/websocket/onlineState.d.ts +6 -3
- package/dist/net/websocket/onlineState.js +25 -21
- package/dist/server/PatchesBranchManager.d.ts +12 -5
- package/dist/server/PatchesBranchManager.js +132 -142
- package/dist/server/PatchesHistoryManager.d.ts +11 -3
- package/dist/server/PatchesHistoryManager.js +81 -84
- package/dist/server/PatchesServer.d.ts +16 -10
- package/dist/server/PatchesServer.js +131 -137
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.js +9 -3
- package/dist/server/types.d.ts +9 -3
- package/dist/server/types.js +0 -1
- package/dist/types.d.ts +49 -19
- package/dist/types.js +1 -1
- package/dist/utils/concurrency.d.ts +7 -5
- package/dist/utils/concurrency.js +43 -53
- package/dist/utils/deferred.d.ts +4 -2
- package/dist/utils/deferred.js +25 -21
- package/package.json +5 -7
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { PatchesSnapshot } from '../../types.js';
|
|
2
|
+
import '../../json-patch/JSONPatch.js';
|
|
3
|
+
import '@dabble/delta';
|
|
4
|
+
import '../../json-patch/types.js';
|
|
5
|
+
|
|
2
6
|
/**
|
|
3
7
|
* Creates the in-memory state from a snapshot.
|
|
4
8
|
* @param snapshot The snapshot to create a state from.
|
|
5
9
|
* @returns The new state.
|
|
6
10
|
*/
|
|
7
|
-
|
|
11
|
+
declare function createStateFromSnapshot<T = any>(snapshot: PatchesSnapshot<T>): T;
|
|
12
|
+
|
|
13
|
+
export { createStateFromSnapshot };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* @returns The new state.
|
|
6
|
-
*/
|
|
7
|
-
export function createStateFromSnapshot(snapshot) {
|
|
8
|
-
return applyChanges(snapshot.state, snapshot.changes);
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { applyChanges } from "../shared/applyChanges.js";
|
|
3
|
+
function createStateFromSnapshot(snapshot) {
|
|
4
|
+
return applyChanges(snapshot.state, snapshot.changes);
|
|
9
5
|
}
|
|
6
|
+
export {
|
|
7
|
+
createStateFromSnapshot
|
|
8
|
+
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
throw new Error('Error calculating JSON size: ' + e);
|
|
11
|
-
}
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
function getJSONByteSize(data) {
|
|
3
|
+
try {
|
|
4
|
+
const stringified = JSON.stringify(data);
|
|
5
|
+
return stringified ? new TextEncoder().encode(stringified).length : 0;
|
|
6
|
+
} catch (e) {
|
|
7
|
+
console.error("Error calculating JSON size:", e);
|
|
8
|
+
throw new Error("Error calculating JSON size: " + e);
|
|
9
|
+
}
|
|
12
10
|
}
|
|
11
|
+
export {
|
|
12
|
+
getJSONByteSize
|
|
13
|
+
};
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { PatchesSnapshot, ChangeMutator, Change } from '../../types.js';
|
|
2
|
+
import '../../json-patch/JSONPatch.js';
|
|
3
|
+
import '@dabble/delta';
|
|
4
|
+
import '../../json-patch/types.js';
|
|
5
|
+
|
|
6
|
+
declare function makeChange<T = any>(snapshot: PatchesSnapshot<T>, mutator: ChangeMutator<T>, changeMetadata?: Record<string, any>, maxPayloadBytes?: number): Change[];
|
|
7
|
+
|
|
8
|
+
export { makeChange };
|
|
@@ -1,37 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.error('Failed to apply change to state during makeChange:', error);
|
|
27
|
-
throw new Error(`Failed to apply change to state during makeChange: ${error}`);
|
|
28
|
-
}
|
|
29
|
-
if (maxPayloadBytes) {
|
|
30
|
-
// If the single change (or its parts) exceed maxPayloadBytes, break it down.
|
|
31
|
-
// breakChange will handle creating multiple Change objects if necessary,
|
|
32
|
-
// maintaining the original baseRev but incrementing revs for the pieces.
|
|
33
|
-
newChangesArray = breakChange(newChangesArray[0], maxPayloadBytes);
|
|
34
|
-
}
|
|
35
|
-
// PatchesDoc.change will take this returned array and push its contents onto its internal snapshot.
|
|
36
|
-
return newChangesArray;
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { createChange } from "../../data/change.js";
|
|
3
|
+
import { createJSONPatch } from "../../json-patch/createJSONPatch.js";
|
|
4
|
+
import { breakChange } from "./breakChange.js";
|
|
5
|
+
import { createStateFromSnapshot } from "./createStateFromSnapshot.js";
|
|
6
|
+
function makeChange(snapshot, mutator, changeMetadata, maxPayloadBytes) {
|
|
7
|
+
const pendingChanges = snapshot.changes;
|
|
8
|
+
const pendingRev = pendingChanges[pendingChanges.length - 1]?.rev ?? snapshot.rev;
|
|
9
|
+
const state = createStateFromSnapshot(snapshot);
|
|
10
|
+
const patch = createJSONPatch(mutator);
|
|
11
|
+
if (patch.ops.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const rev = pendingRev + 1;
|
|
15
|
+
let newChangesArray = [createChange(snapshot.rev, rev, patch.ops, changeMetadata)];
|
|
16
|
+
try {
|
|
17
|
+
patch.apply(state);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("Failed to apply change to state during makeChange:", error);
|
|
20
|
+
throw new Error(`Failed to apply change to state during makeChange: ${error}`);
|
|
21
|
+
}
|
|
22
|
+
if (maxPayloadBytes) {
|
|
23
|
+
newChangesArray = breakChange(newChangesArray[0], maxPayloadBytes);
|
|
24
|
+
}
|
|
25
|
+
return newChangesArray;
|
|
37
26
|
}
|
|
27
|
+
export {
|
|
28
|
+
makeChange
|
|
29
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { PatchesStoreBackend } from '../../server/types.js';
|
|
2
|
+
import { ChangeInput, Change } from '../../types.js';
|
|
3
|
+
import '../../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Commits a set of changes to a document, applying operational transformation as needed.
|
|
5
9
|
* @param docId - The ID of the document.
|
|
@@ -9,4 +13,6 @@ import type { Change } from '../../types.js';
|
|
|
9
13
|
* - committedChanges: Changes that were already committed to the server after the client's base revision
|
|
10
14
|
* - transformedChanges: The client's changes after being transformed against concurrent changes
|
|
11
15
|
*/
|
|
12
|
-
|
|
16
|
+
declare function commitChanges(store: PatchesStoreBackend, docId: string, changes: ChangeInput[], sessionTimeoutMillis: number): Promise<[Change[], Change[]]>;
|
|
17
|
+
|
|
18
|
+
export { commitChanges };
|
|
@@ -1,80 +1,71 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { applyChanges } from "../shared/applyChanges.js";
|
|
3
|
+
import { createVersion } from "./createVersion.js";
|
|
4
|
+
import { getSnapshotAtRevision } from "./getSnapshotAtRevision.js";
|
|
5
|
+
import { getStateAtRevision } from "./getStateAtRevision.js";
|
|
6
|
+
import { handleOfflineSessionsAndBatches } from "./handleOfflineSessionsAndBatches.js";
|
|
7
|
+
import { transformIncomingChanges } from "./transformIncomingChanges.js";
|
|
8
|
+
async function commitChanges(store, docId, changes, sessionTimeoutMillis) {
|
|
9
|
+
if (changes.length === 0) {
|
|
10
|
+
return [[], []];
|
|
11
|
+
}
|
|
12
|
+
const batchId = changes[0].batchId;
|
|
13
|
+
const { state: initialState, rev: initialRev, changes: currentChanges } = await getSnapshotAtRevision(store, docId);
|
|
14
|
+
const currentState = applyChanges(initialState, currentChanges);
|
|
15
|
+
const currentRev = currentChanges.at(-1)?.rev ?? initialRev;
|
|
16
|
+
const baseRev = changes[0].baseRev ?? currentRev;
|
|
17
|
+
let rev = baseRev + 1;
|
|
18
|
+
changes.forEach((c) => {
|
|
19
|
+
if (c.baseRev == null) c.baseRev = baseRev;
|
|
20
|
+
else if (c.baseRev !== baseRev) {
|
|
21
|
+
throw new Error(`Client changes must have consistent baseRev in all changes for doc ${docId}.`);
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// - or the first change is older than the session timeout (single-batch offline)
|
|
66
|
-
const isOfflineTimestamp = changes[0].created < Date.now() - sessionTimeoutMillis;
|
|
67
|
-
if (isOfflineTimestamp || batchId) {
|
|
68
|
-
changes = await handleOfflineSessionsAndBatches(store, sessionTimeoutMillis, docId, changes, baseRev, batchId);
|
|
69
|
-
}
|
|
70
|
-
// 5. Transform the *entire batch* of incoming (and potentially collapsed offline) changes
|
|
71
|
-
// against committed changes that happened *after* the client's baseRev.
|
|
72
|
-
// The state used for transformation should be the server state *at the client's baseRev*.
|
|
73
|
-
const stateAtBaseRev = (await getStateAtRevision(store, docId, baseRev)).state;
|
|
74
|
-
const transformedChanges = transformIncomingChanges(changes, stateAtBaseRev, committedChanges, currentRev);
|
|
75
|
-
if (transformedChanges.length > 0) {
|
|
76
|
-
await store.saveChanges(docId, transformedChanges);
|
|
77
|
-
}
|
|
78
|
-
// Return committed changes and newly transformed changes separately
|
|
79
|
-
return [committedChanges, transformedChanges];
|
|
23
|
+
if (c.rev == null) c.rev = rev++;
|
|
24
|
+
else rev = c.rev + 1;
|
|
25
|
+
c.created = Math.min(c.created, Date.now());
|
|
26
|
+
});
|
|
27
|
+
if (baseRev > currentRev) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Client baseRev (${baseRev}) is ahead of server revision (${currentRev}) for doc ${docId}. Client needs to reload the document.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const laterPartOfAnInitialBatch = batchId && changes[0].rev > 1;
|
|
33
|
+
if (baseRev === 0 && currentRev > 0 && !laterPartOfAnInitialBatch && changes[0].ops[0]?.path === "") {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Document ${docId} already exists at rev ${currentRev}, but client is attempting to create it. Client needs to load the existing document.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const lastChange = currentChanges[currentChanges.length - 1];
|
|
39
|
+
if (lastChange && lastChange.created < Date.now() - sessionTimeoutMillis) {
|
|
40
|
+
await createVersion(store, docId, currentState, currentChanges);
|
|
41
|
+
}
|
|
42
|
+
const committedChanges = await store.listChanges(docId, {
|
|
43
|
+
startAfter: baseRev,
|
|
44
|
+
withoutBatchId: batchId
|
|
45
|
+
});
|
|
46
|
+
const committedIds = new Set(committedChanges.map((c) => c.id));
|
|
47
|
+
let incomingChanges = changes.filter((c) => !committedIds.has(c.id));
|
|
48
|
+
if (incomingChanges.length === 0) {
|
|
49
|
+
return [committedChanges, []];
|
|
50
|
+
}
|
|
51
|
+
const isOfflineTimestamp = incomingChanges[0].created < Date.now() - sessionTimeoutMillis;
|
|
52
|
+
if (isOfflineTimestamp || batchId) {
|
|
53
|
+
incomingChanges = await handleOfflineSessionsAndBatches(
|
|
54
|
+
store,
|
|
55
|
+
sessionTimeoutMillis,
|
|
56
|
+
docId,
|
|
57
|
+
incomingChanges,
|
|
58
|
+
baseRev,
|
|
59
|
+
batchId
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const stateAtBaseRev = (await getStateAtRevision(store, docId, baseRev)).state;
|
|
63
|
+
const transformedChanges = transformIncomingChanges(incomingChanges, stateAtBaseRev, committedChanges, currentRev);
|
|
64
|
+
if (transformedChanges.length > 0) {
|
|
65
|
+
await store.saveChanges(docId, transformedChanges);
|
|
66
|
+
}
|
|
67
|
+
return [committedChanges, transformedChanges];
|
|
80
68
|
}
|
|
69
|
+
export {
|
|
70
|
+
commitChanges
|
|
71
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { PatchesStoreBackend } from '../../server/types.js';
|
|
2
|
+
import { Change, EditableVersionMetadata, VersionMetadata } from '../../types.js';
|
|
3
|
+
import '../../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Creates a new version snapshot of a document's state from changes.
|
|
5
9
|
* @param store The storage backend to save the version to.
|
|
@@ -9,4 +13,6 @@ import type { Change, EditableVersionMetadata, VersionMetadata } from '../../typ
|
|
|
9
13
|
* @param metadata Optional additional metadata for the version.
|
|
10
14
|
* @returns The created version metadata, or undefined if no changes provided.
|
|
11
15
|
*/
|
|
12
|
-
|
|
16
|
+
declare function createVersion(store: PatchesStoreBackend, docId: string, state: any, changes: Change[], metadata?: EditableVersionMetadata): Promise<VersionMetadata | undefined>;
|
|
17
|
+
|
|
18
|
+
export { createVersion };
|
|
@@ -1,28 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
origin: 'main',
|
|
20
|
-
startDate: changes[0].created,
|
|
21
|
-
endDate: changes[changes.length - 1].created,
|
|
22
|
-
rev: changes[changes.length - 1].rev,
|
|
23
|
-
baseRev,
|
|
24
|
-
...metadata,
|
|
25
|
-
});
|
|
26
|
-
await store.createVersion(docId, sessionMetadata, state, changes);
|
|
27
|
-
return sessionMetadata;
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { createVersionMetadata } from "../../data/version.js";
|
|
3
|
+
async function createVersion(store, docId, state, changes, metadata) {
|
|
4
|
+
if (changes.length === 0) return;
|
|
5
|
+
const baseRev = changes[0].baseRev;
|
|
6
|
+
if (baseRev === void 0) {
|
|
7
|
+
throw new Error(`Client changes must include baseRev for doc ${docId}.`);
|
|
8
|
+
}
|
|
9
|
+
const sessionMetadata = createVersionMetadata({
|
|
10
|
+
origin: "main",
|
|
11
|
+
startDate: changes[0].created,
|
|
12
|
+
endDate: changes[changes.length - 1].created,
|
|
13
|
+
rev: changes[changes.length - 1].rev,
|
|
14
|
+
baseRev,
|
|
15
|
+
...metadata
|
|
16
|
+
});
|
|
17
|
+
await store.createVersion(docId, sessionMetadata, state, changes);
|
|
18
|
+
return sessionMetadata;
|
|
28
19
|
}
|
|
20
|
+
export {
|
|
21
|
+
createVersion
|
|
22
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { PatchesStoreBackend } from '../../server/types.js';
|
|
2
|
+
import { PatchesSnapshot } from '../../types.js';
|
|
3
|
+
import '../../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Retrieves the document state of the version before the given revision and changes after up to that revision or all
|
|
5
9
|
* changes since that version.
|
|
@@ -7,4 +11,6 @@ import type { PatchesSnapshot } from '../../types.js';
|
|
|
7
11
|
* @param rev The revision number. If not provided, the latest state, its revision, and all changes since are returned.
|
|
8
12
|
* @returns The document state at the last version before the revision, its revision number, and all changes up to the specified revision (or all changes if no revision is provided).
|
|
9
13
|
*/
|
|
10
|
-
|
|
14
|
+
declare function getSnapshotAtRevision(store: PatchesStoreBackend, docId: string, rev?: number): Promise<PatchesSnapshot>;
|
|
15
|
+
|
|
16
|
+
export { getSnapshotAtRevision };
|
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
state: versionState, // State from the base version
|
|
26
|
-
rev: versionRev, // Revision of the base version's state
|
|
27
|
-
changes: changesSinceVersion, // Changes that occurred *after* the base version state
|
|
28
|
-
};
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
async function getSnapshotAtRevision(store, docId, rev) {
|
|
3
|
+
const versions = await store.listVersions(docId, {
|
|
4
|
+
limit: 1,
|
|
5
|
+
reverse: true,
|
|
6
|
+
startAfter: rev ? rev + 1 : void 0,
|
|
7
|
+
origin: "main",
|
|
8
|
+
orderBy: "rev"
|
|
9
|
+
});
|
|
10
|
+
const latestMainVersion = versions[0];
|
|
11
|
+
const versionState = latestMainVersion && await store.loadVersionState(docId, latestMainVersion.id) || null;
|
|
12
|
+
const versionRev = latestMainVersion?.rev ?? 0;
|
|
13
|
+
const changesSinceVersion = await store.listChanges(docId, {
|
|
14
|
+
startAfter: versionRev,
|
|
15
|
+
endBefore: rev ? rev + 1 : void 0
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
state: versionState,
|
|
19
|
+
// State from the base version
|
|
20
|
+
rev: versionRev,
|
|
21
|
+
// Revision of the base version's state
|
|
22
|
+
changes: changesSinceVersion
|
|
23
|
+
// Changes that occurred *after* the base version state
|
|
24
|
+
};
|
|
29
25
|
}
|
|
26
|
+
export {
|
|
27
|
+
getSnapshotAtRevision
|
|
28
|
+
};
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { PatchesStoreBackend } from '../../server/types.js';
|
|
2
|
+
import { PatchesState } from '../../types.js';
|
|
3
|
+
import '../../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Gets the state at a specific revision.
|
|
5
9
|
* @param docId The document ID.
|
|
6
10
|
* @param rev The revision number. If not provided, the latest state and its revision is returned.
|
|
7
11
|
* @returns The state at the specified revision *and* its revision number.
|
|
8
12
|
*/
|
|
9
|
-
|
|
13
|
+
declare function getStateAtRevision(store: PatchesStoreBackend, docId: string, rev?: number): Promise<PatchesState>;
|
|
14
|
+
|
|
15
|
+
export { getStateAtRevision };
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// We need to apply the changes to get the state *at* the target revision.
|
|
12
|
-
const { state: versionState, rev: snapshotRev, changes } = await getSnapshotAtRevision(store, docId, rev);
|
|
13
|
-
return {
|
|
14
|
-
// Ensure null is passed if versionState or versionState.state is null/undefined
|
|
15
|
-
state: applyChanges(versionState?.state ?? null, changes),
|
|
16
|
-
rev: changes.at(-1)?.rev ?? snapshotRev,
|
|
17
|
-
};
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { applyChanges } from "../shared/applyChanges.js";
|
|
3
|
+
import { getSnapshotAtRevision } from "./getSnapshotAtRevision.js";
|
|
4
|
+
async function getStateAtRevision(store, docId, rev) {
|
|
5
|
+
const { state: versionState, rev: snapshotRev, changes } = await getSnapshotAtRevision(store, docId, rev);
|
|
6
|
+
return {
|
|
7
|
+
// Ensure null is passed if versionState or versionState.state is null/undefined
|
|
8
|
+
state: applyChanges(versionState?.state ?? null, changes),
|
|
9
|
+
rev: changes.at(-1)?.rev ?? snapshotRev
|
|
10
|
+
};
|
|
18
11
|
}
|
|
12
|
+
export {
|
|
13
|
+
getStateAtRevision
|
|
14
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { PatchesStoreBackend } from '../../server/types.js';
|
|
2
|
+
import { Change } from '../../types.js';
|
|
3
|
+
import '../../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Handles offline/large batch versioning logic for multi-batch uploads.
|
|
5
9
|
* Groups changes into sessions, merges with previous batch if needed, and creates/extends versions.
|
|
@@ -9,4 +13,6 @@ import type { Change } from '../../types.js';
|
|
|
9
13
|
* @param batchId The batch identifier
|
|
10
14
|
* @returns The collapsed changes for transformation
|
|
11
15
|
*/
|
|
12
|
-
|
|
16
|
+
declare function handleOfflineSessionsAndBatches(store: PatchesStoreBackend, sessionTimeoutMillis: number, docId: string, changes: Change[], baseRev: number, batchId?: string): Promise<Change[]>;
|
|
17
|
+
|
|
18
|
+
export { handleOfflineSessionsAndBatches };
|