document-model 5.2.0-staging.9 → 6.0.0-dev.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/dist/src/core/actions.d.ts +10 -2
- package/dist/src/core/actions.d.ts.map +1 -1
- package/dist/src/core/actions.js +8 -5
- package/dist/src/core/actions.js.map +1 -1
- package/dist/src/core/documents.d.ts +21 -3
- package/dist/src/core/documents.d.ts.map +1 -1
- package/dist/src/core/documents.js +73 -9
- package/dist/src/core/documents.js.map +1 -1
- package/dist/src/core/errors.d.ts.map +1 -1
- package/dist/src/core/errors.js +1 -2
- package/dist/src/core/errors.js.map +1 -1
- package/dist/src/core/files.js +4 -13
- package/dist/src/core/files.js.map +1 -1
- package/dist/src/core/header.d.ts +7 -11
- package/dist/src/core/header.d.ts.map +1 -1
- package/dist/src/core/header.js +45 -54
- package/dist/src/core/header.js.map +1 -1
- package/dist/src/core/operations.d.ts +16 -0
- package/dist/src/core/operations.d.ts.map +1 -1
- package/dist/src/core/operations.js +38 -0
- package/dist/src/core/operations.js.map +1 -1
- package/dist/src/core/ph-types.d.ts +43 -7
- package/dist/src/core/ph-types.d.ts.map +1 -1
- package/dist/src/core/ph-types.js.map +1 -1
- package/dist/src/core/reducer.d.ts +5 -3
- package/dist/src/core/reducer.d.ts.map +1 -1
- package/dist/src/core/reducer.js +61 -22
- package/dist/src/core/reducer.js.map +1 -1
- package/dist/src/core/schemas.d.ts +1 -1
- package/dist/src/core/schemas.d.ts.map +1 -1
- package/dist/src/core/types.d.ts +39 -7
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/utils.d.ts +4 -0
- package/dist/src/core/utils.d.ts.map +1 -1
- package/dist/src/core/utils.js +8 -0
- package/dist/src/core/utils.js.map +1 -1
- package/dist/src/document-model/actions.d.ts.map +1 -1
- package/dist/src/document-model/actions.js +3 -1
- package/dist/src/document-model/actions.js.map +1 -1
- package/dist/src/document-model/reducers.d.ts.map +1 -1
- package/dist/src/document-model/reducers.js +5 -0
- package/dist/src/document-model/reducers.js.map +1 -1
- package/dist/src/document-model/validation.d.ts +25 -0
- package/dist/src/document-model/validation.d.ts.map +1 -1
- package/dist/src/document-model/validation.js +64 -3
- package/dist/src/document-model/validation.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/test/document/crypto.test.js +28 -27
- package/dist/test/document/crypto.test.js.map +1 -1
- package/dist/test/document/event-vs-command.test.js +4 -1
- package/dist/test/document/event-vs-command.test.js.map +1 -1
- package/dist/test/document/operation-id.test.js +8 -45
- package/dist/test/document/operation-id.test.js.map +1 -1
- package/dist/test/document/reducer.test.js +4 -1
- package/dist/test/document/reducer.test.js.map +1 -1
- package/dist/test/document/skip-operations.test.js +4 -4
- package/dist/test/document/skip-operations.test.js.map +1 -1
- package/dist/test/document/undo-redo-v2.test.d.ts +2 -0
- package/dist/test/document/undo-redo-v2.test.d.ts.map +1 -0
- package/dist/test/document/undo-redo-v2.test.js +207 -0
- package/dist/test/document/undo-redo-v2.test.js.map +1 -0
- package/dist/test/document/undo-redo.test.js +23 -8
- package/dist/test/document/undo-redo.test.js.map +1 -1
- package/dist/test/document/utils.test.js +37 -22
- package/dist/test/document/utils.test.js.map +1 -1
- package/dist/test/document-helpers/groupOperationsByScope.test.js +9 -5
- package/dist/test/document-helpers/groupOperationsByScope.test.js.map +1 -1
- package/dist/test/document-helpers/removeExistingOperations.test.js +46 -31
- package/dist/test/document-helpers/removeExistingOperations.test.js.map +1 -1
- package/dist/test/document-helpers/skipHeaderOperations.test.js +7 -2
- package/dist/test/document-helpers/skipHeaderOperations.test.js.map +1 -1
- package/dist/test/document-helpers/utils.d.ts +6 -0
- package/dist/test/document-helpers/utils.d.ts.map +1 -1
- package/dist/test/document-helpers/utils.js +34 -0
- package/dist/test/document-helpers/utils.js.map +1 -1
- package/dist/test/document-model/replay.test.js +5 -5
- package/dist/test/document-model/replay.test.js.map +1 -1
- package/dist/test/document-model/skip-operations.test.js +1 -1
- package/dist/test/document-model/skip-operations.test.js.map +1 -1
- package/dist/test/document-model/validation.test.js +197 -1
- package/dist/test/document-model/validation.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
package/dist/src/core/header.js
CHANGED
|
@@ -4,56 +4,49 @@ import { generateId } from "./utils.js";
|
|
|
4
4
|
*/
|
|
5
5
|
const generateStablePayload = (parameters) => `${parameters.documentType}:${parameters.createdAtUtcIso}:${parameters.nonce}`;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Creates a verification-only signer from a public key.
|
|
8
|
+
* This signer can only verify signatures, not sign data.
|
|
9
|
+
*
|
|
10
|
+
* @param pubKey - The public key to use for verification.
|
|
11
|
+
* @returns An ISigner that can only verify signatures.
|
|
8
12
|
*/
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
isValid = await subtleCrypto.verify("Ed25519", this.publicCryptoKey, new Uint8Array(signature), new Uint8Array(data));
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
throw new Error("invalid signature");
|
|
42
|
-
}
|
|
43
|
-
if (!isValid) {
|
|
44
|
-
throw new Error("invalid signature");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
13
|
+
export function createVerificationSigner(pubKey) {
|
|
14
|
+
let cachedCryptoKey;
|
|
15
|
+
return {
|
|
16
|
+
async publicKey() {
|
|
17
|
+
return pubKey;
|
|
18
|
+
},
|
|
19
|
+
async sign(_data) {
|
|
20
|
+
throw new Error("verification-only signer cannot sign data");
|
|
21
|
+
},
|
|
22
|
+
async signAction(_action, _abortSignal) {
|
|
23
|
+
throw new Error("verification-only signer cannot sign actions");
|
|
24
|
+
},
|
|
25
|
+
async verify(data, signature) {
|
|
26
|
+
if (!cachedCryptoKey) {
|
|
27
|
+
cachedCryptoKey = await crypto.subtle.importKey("jwk", pubKey, { name: "ECDSA", namedCurve: "P-256" }, true, ["verify"]);
|
|
28
|
+
}
|
|
29
|
+
let isValid;
|
|
30
|
+
try {
|
|
31
|
+
isValid = await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, cachedCryptoKey, new Uint8Array(signature), new Uint8Array(data));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error("invalid signature");
|
|
35
|
+
}
|
|
36
|
+
if (!isValid) {
|
|
37
|
+
throw new Error("invalid signature");
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
47
41
|
}
|
|
48
42
|
/**
|
|
49
|
-
* Creates a signer from a header.
|
|
43
|
+
* Creates a verification-only signer from a header.
|
|
50
44
|
*
|
|
51
45
|
* @param header - The header to create a signer from.
|
|
52
|
-
*
|
|
53
|
-
* @returns A signer for the header.
|
|
46
|
+
* @returns A signer that can verify the header's signature.
|
|
54
47
|
*/
|
|
55
|
-
const createSignerFromHeader =
|
|
56
|
-
return
|
|
48
|
+
const createSignerFromHeader = (header) => {
|
|
49
|
+
return createVerificationSigner(header.sig.publicKey);
|
|
57
50
|
};
|
|
58
51
|
/**
|
|
59
52
|
* Signs a header. Generally, this is not called directly, but rather through
|
|
@@ -99,8 +92,8 @@ export const verify = async (parameters, signature, signer) => {
|
|
|
99
92
|
* Validates a header signature.
|
|
100
93
|
*/
|
|
101
94
|
export const validateHeader = async (header) => {
|
|
102
|
-
const signer =
|
|
103
|
-
return
|
|
95
|
+
const signer = createSignerFromHeader(header);
|
|
96
|
+
return verify({
|
|
104
97
|
documentType: header.documentType,
|
|
105
98
|
createdAtUtcIso: header.createdAtUtcIso,
|
|
106
99
|
nonce: header.sig.nonce,
|
|
@@ -159,19 +152,17 @@ export const createSignedHeader = async (unsignedHeader, documentType, signer) =
|
|
|
159
152
|
documentType,
|
|
160
153
|
createdAtUtcIso: unsignedHeader.createdAtUtcIso,
|
|
161
154
|
// mutable fields
|
|
162
|
-
slug:
|
|
163
|
-
name:
|
|
164
|
-
branch:
|
|
165
|
-
revision:
|
|
166
|
-
document: 0,
|
|
167
|
-
},
|
|
155
|
+
slug: unsignedHeader.slug,
|
|
156
|
+
name: unsignedHeader.name,
|
|
157
|
+
branch: unsignedHeader.branch,
|
|
158
|
+
revision: unsignedHeader.revision,
|
|
168
159
|
lastModifiedAtUtcIso: unsignedHeader.lastModifiedAtUtcIso,
|
|
169
|
-
meta:
|
|
160
|
+
meta: unsignedHeader.meta,
|
|
170
161
|
};
|
|
171
162
|
};
|
|
172
163
|
/**
|
|
173
164
|
* Creates a signed header for a document. The document header requires a signer
|
|
174
|
-
* as the document id is a
|
|
165
|
+
* as the document id is a cryptographic signature.
|
|
175
166
|
*
|
|
176
167
|
* @param documentType - The type of the document.
|
|
177
168
|
* @param signer - The signer of the document.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.js","sourceRoot":"","sources":["../../../src/core/header.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAAC,UAA6B,EAAU,EAAE,CACtE,GAAG,UAAU,CAAC,YAAY,IAAI,UAAU,CAAC,eAAe,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;AAEjF
|
|
1
|
+
{"version":3,"file":"header.js","sourceRoot":"","sources":["../../../src/core/header.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,9 @@ export declare function setNameOperation<TDocument extends PHDocument>(document:
|
|
|
13
13
|
[scope: string]: number;
|
|
14
14
|
};
|
|
15
15
|
lastModifiedAtUtcIso: string;
|
|
16
|
+
protocolVersions?: {
|
|
17
|
+
[key: string]: number;
|
|
18
|
+
};
|
|
16
19
|
meta?: import("./ph-types.js").PHDocumentMeta;
|
|
17
20
|
};
|
|
18
21
|
};
|
|
@@ -22,6 +25,19 @@ export declare function undoOperation<TDocument extends PHDocument>(document: TD
|
|
|
22
25
|
skip: number;
|
|
23
26
|
reuseLastOperationIndex: boolean;
|
|
24
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* V2 of undoOperation for protocol version 2+.
|
|
30
|
+
* Key differences from undoOperation:
|
|
31
|
+
* - Never reuses operation index (always increments)
|
|
32
|
+
* - Always sets skip=1 (consecutive NOOPs are handled during rebuild/GC)
|
|
33
|
+
* - No complex skip calculation - simpler model where each UNDO is independent
|
|
34
|
+
*/
|
|
35
|
+
export declare function undoOperationV2<TDocument extends PHDocument>(document: TDocument, action: Action, skip: number): {
|
|
36
|
+
document: TDocument;
|
|
37
|
+
action: Action;
|
|
38
|
+
skip: number;
|
|
39
|
+
reuseLastOperationIndex: false;
|
|
40
|
+
};
|
|
25
41
|
export declare function redoOperation<TDocument extends PHDocument>(document: TDocument, action: Action, skip: number): {
|
|
26
42
|
document: TDocument;
|
|
27
43
|
action: Action;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../src/core/operations.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,wBAAgB,gBAAgB,CAAC,SAAS,SAAS,UAAU,EAC3D,QAAQ,EAAE,SAAS,EACnB,IAAI,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../src/core/operations.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,wBAAgB,gBAAgB,CAAC,SAAS,SAAS,UAAU,EAC3D,QAAQ,EAAE,SAAS,EACnB,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;EAGb;AAED,wBAAgB,aAAa,CAAC,SAAS,SAAS,UAAU,EACxD,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB,EAAE,OAAO,CAAC;CAClC,CA8CA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,SAAS,UAAU,EAC1D,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB,EAAE,KAAK,CAAC;CAChC,CAuCA;AAED,wBAAgB,aAAa,CAAC,SAAS,SAAS,UAAU,EACxD,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB,EAAE,OAAO,CAAC;CAClC,CA8CA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,WAAW,EAC3D,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,EAC5B,MAAM,EAAE,oBAAoB,GAC3B,UAAU,CAAC,MAAM,CAAC,CAMpB"}
|
|
@@ -41,6 +41,44 @@ export function undoOperation(document, action, skip) {
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* V2 of undoOperation for protocol version 2+.
|
|
46
|
+
* Key differences from undoOperation:
|
|
47
|
+
* - Never reuses operation index (always increments)
|
|
48
|
+
* - Always sets skip=1 (consecutive NOOPs are handled during rebuild/GC)
|
|
49
|
+
* - No complex skip calculation - simpler model where each UNDO is independent
|
|
50
|
+
*/
|
|
51
|
+
export function undoOperationV2(document, action, skip) {
|
|
52
|
+
const { scope } = action;
|
|
53
|
+
const defaultResult = {
|
|
54
|
+
document,
|
|
55
|
+
action,
|
|
56
|
+
skip,
|
|
57
|
+
reuseLastOperationIndex: false,
|
|
58
|
+
};
|
|
59
|
+
return create(defaultResult, (draft) => {
|
|
60
|
+
const operations = document.operations[scope] || [];
|
|
61
|
+
const sortedOperations = sortOperations([...operations]);
|
|
62
|
+
// Count non-NOOP operations to determine if there's anything to undo
|
|
63
|
+
const nonNoopOps = sortedOperations.filter((op) => op.action.type !== "NOOP");
|
|
64
|
+
// Count consecutive NOOPs at the end (these represent pending undos)
|
|
65
|
+
let noopChainLength = 0;
|
|
66
|
+
for (let i = sortedOperations.length - 1; i >= 0; i--) {
|
|
67
|
+
if (sortedOperations[i].action.type === "NOOP") {
|
|
68
|
+
noopChainLength++;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Check if we can undo: need more non-NOOP ops than the current NOOP chain
|
|
75
|
+
if (nonNoopOps.length <= noopChainLength) {
|
|
76
|
+
throw new Error(`Cannot undo: no more operations to undo in scope history`);
|
|
77
|
+
}
|
|
78
|
+
draft.action = noop(scope);
|
|
79
|
+
draft.skip = 1;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
44
82
|
export function redoOperation(document, action, skip) {
|
|
45
83
|
const { scope, input } = action;
|
|
46
84
|
const defaultResult = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/core/operations.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/core/operations.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,6 +62,17 @@ export type PHDocumentHeader = {
|
|
|
62
62
|
* The timestamp of the last change in the document, in UTC ISO format.
|
|
63
63
|
**/
|
|
64
64
|
lastModifiedAtUtcIso: string;
|
|
65
|
+
/**
|
|
66
|
+
* This is a map from protocol name to version. A protocol can be any set of
|
|
67
|
+
* rules that are applied to the document.
|
|
68
|
+
*
|
|
69
|
+
* Examples of protocols include:
|
|
70
|
+
*
|
|
71
|
+
* - "base-reducer"
|
|
72
|
+
*/
|
|
73
|
+
protocolVersions?: {
|
|
74
|
+
[key: string]: number;
|
|
75
|
+
};
|
|
65
76
|
/** Meta information about the document. */
|
|
66
77
|
meta?: PHDocumentMeta;
|
|
67
78
|
};
|
|
@@ -91,7 +102,11 @@ export type HashConfig = {
|
|
|
91
102
|
* The document state of the document.
|
|
92
103
|
*/
|
|
93
104
|
export type PHDocumentState = {
|
|
94
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* The current document model schema version of the document. This is used
|
|
107
|
+
* with the UPGRADE_DOCUMENT operation to specify the DocumentModelModule
|
|
108
|
+
* version to use for reducer execution.
|
|
109
|
+
*/
|
|
95
110
|
version: number;
|
|
96
111
|
/** Hash configuration for operation state verification */
|
|
97
112
|
hash: HashConfig;
|
|
@@ -191,25 +206,46 @@ export type Action = {
|
|
|
191
206
|
* @remarks
|
|
192
207
|
* Wraps an action with an index, to be added to the operations history of a Document.
|
|
193
208
|
* The `index` field is used to keep all operations in order and enable replaying the
|
|
194
|
-
* document's history from the beginning.
|
|
209
|
+
* document's history from the beginning. Note that indices and skips are relative to
|
|
210
|
+
* a specific reactor. Example below:
|
|
211
|
+
*
|
|
212
|
+
* For (index, skip, ts, action)
|
|
213
|
+
* A - [(0, 0, 1, "A0"), (1, 0, 2, "A1")]
|
|
214
|
+
* B - [(0, 0, 0, "B0"), (1, 0, 3, "B1")]
|
|
215
|
+
* ...
|
|
216
|
+
* B gets A's Operations Scenario:
|
|
217
|
+
* B' - [(0, 0, 0, "B0"), (1, 0, 3, "B1"), (2, 1, 1, "A0"), (3, 0, 2, "A1"), (4, 0, 3, "B1")]
|
|
218
|
+
* Then A needs to end up with:
|
|
219
|
+
* A' - [(0, 0, 1, "A0"), (1, 0, 2, "A1"), (2, 2, 0, "B0"), (3, 0, 1, "A0"), (4, 0, 2, "A1"), (5, 0, 3, "B1")]
|
|
220
|
+
* So that both A and B end up with the stream of actions (action):
|
|
221
|
+
* [("B0"), ("A0"), ("A1"), ("B1")]
|
|
195
222
|
*
|
|
196
223
|
* @typeParam A - The type of the action.
|
|
197
224
|
*/
|
|
198
225
|
export type Operation = {
|
|
199
|
-
/**
|
|
226
|
+
/**
|
|
227
|
+
* This is a stable id, derived from various document and action properties
|
|
228
|
+
* in deriveOperationId().
|
|
229
|
+
*
|
|
230
|
+
* It _cannot_ be an arbitrary string.
|
|
231
|
+
*
|
|
232
|
+
* It it also not unique per operation, as reshuffled operations will keep'
|
|
233
|
+
* the same id they had before they were reshuffled. This means that the
|
|
234
|
+
* IOperationStore may have multiple operations with the same operation id.
|
|
235
|
+
**/
|
|
236
|
+
id: string;
|
|
237
|
+
/** Position of the operation in the history. This is relative to a specific reactor -- they may not all agree on this value. */
|
|
200
238
|
index: number;
|
|
239
|
+
/** The number of operations skipped with this Operation. This is relative to a specific reactor -- they may not all agree on this value. */
|
|
240
|
+
skip: number;
|
|
201
241
|
/** Timestamp of when the operation was added */
|
|
202
242
|
timestampUtcMs: string;
|
|
203
243
|
/** Hash of the resulting document data after the operation */
|
|
204
244
|
hash: string;
|
|
205
|
-
/** The number of operations skipped with this Operation */
|
|
206
|
-
skip: number;
|
|
207
245
|
/** Error message for a failed action */
|
|
208
246
|
error?: string;
|
|
209
247
|
/** The resulting state after the operation */
|
|
210
248
|
resultingState?: string;
|
|
211
|
-
/** Unique operation id. This is distinct from the action id and can be undefined and assigned later. */
|
|
212
|
-
id?: string;
|
|
213
249
|
/**
|
|
214
250
|
* The action that was applied to the document to produce this operation.
|
|
215
251
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ph-types.d.ts","sourceRoot":"","sources":["../../../src/core/ph-types.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;QAEI;IACJ,SAAS,EAAE,UAAU,CAAC;IAEtB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,cAAc,GAAG;IAC3B,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;;;QAII;IACJ,EAAE,EAAE,MAAM,CAAC;IAEX;;;;QAII;IACJ,GAAG,EAAE,uBAAuB,CAAC;IAE7B;;;;;QAKI;IACJ,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;QAKI;IACJ,eAAe,EAAE,MAAM,CAAC;IAExB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IAEb,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IAEb,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,EAAE;QACR,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;KACzB,CAAC;IAEF;;QAEI;IACJ,oBAAoB,EAAE,MAAM,CAAC;IAE7B,2CAA2C;IAC3C,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,EAAE,CAAC;AAG7B,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAG9C,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAElB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IAEjB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B
|
|
1
|
+
{"version":3,"file":"ph-types.d.ts","sourceRoot":"","sources":["../../../src/core/ph-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ph-types.js","sourceRoot":"","sources":["../../../src/core/ph-types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,YAAY;AACZ,EAAE;AACF,4EAA4E;AAC5E,gDAAgD;AAChD,+EAA+E;
|
|
1
|
+
{"version":3,"file":"ph-types.js","sourceRoot":"","sources":["../../../src/core/ph-types.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,YAAY;AACZ,EAAE;AACF,4EAA4E;AAC5E,gDAAgD;AAChD,+EAA+E;AAoG/E,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAC1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE9C,6DAA6D;AAC7D,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAC7C,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type OperationContext } from "./actions.js";
|
|
1
2
|
import type { Action, DocumentOperations, Operation, PHBaseState, PHDocument, PHDocumentHeader } from "./ph-types.js";
|
|
2
3
|
import type { PruneActionInput, Reducer, ReducerOptions, ReplayDocumentOptions, SignalDispatch, SkipHeaderOperations, StateReducer } from "./types.js";
|
|
3
|
-
export declare function replayOperations<TState extends PHBaseState = PHBaseState>(initialState: TState, clearedOperations: DocumentOperations, stateReducer: StateReducer<TState>,
|
|
4
|
+
export declare function replayOperations<TState extends PHBaseState = PHBaseState>(initialState: TState, clearedOperations: DocumentOperations, stateReducer: StateReducer<TState>, header: PHDocumentHeader, dispatch?: SignalDispatch, documentReducer?: typeof baseReducer, skipHeaderOperations?: SkipHeaderOperations, options?: ReplayDocumentOptions): PHDocument<TState>;
|
|
4
5
|
/**
|
|
5
6
|
* Updates the document state based on the provided action.
|
|
6
7
|
*
|
|
@@ -8,9 +9,10 @@ export declare function replayOperations<TState extends PHBaseState = PHBaseStat
|
|
|
8
9
|
* @param action The action being applied to the document.
|
|
9
10
|
* @param skip The number of operations to skip before applying the action.
|
|
10
11
|
* @param reuseLastOperationIndex Whether to reuse the last operation index (used when a an UNDO operation is performed after an existing one).
|
|
12
|
+
* @param context The operation context for deterministic ID generation.
|
|
11
13
|
* @returns The updated document state.
|
|
12
14
|
*/
|
|
13
|
-
export declare function updateDocument<TDocument extends PHDocument>(document: TDocument, action: Action, reuseLastOperationIndex
|
|
15
|
+
export declare function updateDocument<TDocument extends PHDocument>(document: TDocument, action: Action, reuseLastOperationIndex: boolean, skip: number, context: OperationContext, operation?: Operation, skipIndexValidation?: boolean): TDocument;
|
|
14
16
|
/**
|
|
15
17
|
* Processes an UNDO or REDO action.
|
|
16
18
|
*
|
|
@@ -19,7 +21,7 @@ export declare function updateDocument<TDocument extends PHDocument>(document: T
|
|
|
19
21
|
* @param skip The number of operations to skip before applying the action.
|
|
20
22
|
* @returns The updated document, calculated skip value and transformed action (if applied).
|
|
21
23
|
*/
|
|
22
|
-
export declare function processUndoRedo<TState extends PHBaseState = PHBaseState>(document: PHDocument<TState>, action: Action, skip: number): {
|
|
24
|
+
export declare function processUndoRedo<TState extends PHBaseState = PHBaseState>(document: PHDocument<TState>, action: Action, skip: number, protocolVersion?: number): {
|
|
23
25
|
document: PHDocument<TState>;
|
|
24
26
|
action: Action;
|
|
25
27
|
skip: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../../src/core/reducer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../../src/core/reducer.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAuBtB,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,UAAU,EACV,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,gBAAgB,EAChB,OAAO,EACP,cAAc,EACd,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,WAAW,GAAG,WAAW,EACvE,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,kBAAkB,EACrC,YAAY,EAAE,YAAY,CAAC,MAAM,CAAC,EAClC,MAAM,EAAE,gBAAgB,EACxB,QAAQ,CAAC,EAAE,cAAc,EACzB,eAAe,qBAAc,EAC7B,oBAAoB,GAAE,oBAAyB,EAC/C,OAAO,CAAC,EAAE,qBAAqB,GAC9B,UAAU,CAAC,MAAM,CAAC,CAcpB;AA8FD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,SAAS,SAAS,UAAU,EACzD,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,MAAM,EACd,uBAAuB,EAAE,OAAO,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,EACzB,SAAS,CAAC,EAAE,SAAS,EACrB,mBAAmB,CAAC,EAAE,OAAO,GAC5B,SAAS,CAyBX;AA+BD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,SAAS,WAAW,GAAG,WAAW,EACtE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,eAAe,SAAI,GAClB;IACD,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAYA;AAoHD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,SAAS,WAAW,GAAG,WAAW,EAClE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,CAAC,MAAM,CAAC,EACnC,QAAQ,CAAC,EAAE,cAAc,EACzB,OAAO,GAAE,cAAmB,GAC3B,UAAU,CAAC,MAAM,CAAC,CA+OpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,MAAM,SAAS,WAAW,GAAG,WAAW,EACpE,YAAY,EAAE,YAAY,CAAC,MAAM,CAAC,EAClC,eAAe,qBAAc,GAC5B,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAgB,cAAc,CAAC,MAAM,SAAS,WAAW,GAAG,WAAW,EACrE,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,EAC5B,KAAK,EAAE,gBAAgB,EACvB,cAAc,EAAE,YAAY,CAAC,MAAM,CAAC,GACnC,UAAU,CAAC,MAAM,CAAC,CAoEpB"}
|
package/dist/src/core/reducer.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { castDraft, create, unsafe } from "mutative";
|
|
2
2
|
import { actionFromAction, loadState, operationFromAction, operationFromOperation, } from "./actions.js";
|
|
3
|
-
import { diffOperations, garbageCollect, garbageCollectDocumentOperations, hashDocumentStateForScope, isDocumentAction, isUndo, isUndoRedo, parseResultingState, replayDocument, skipHeaderOperations, sortOperations, updateHeaderRevision, } from "./documents.js";
|
|
4
|
-
import { loadStateOperation, redoOperation, setNameOperation, undoOperation, } from "./operations.js";
|
|
3
|
+
import { diffOperations, garbageCollect, garbageCollectDocumentOperations, garbageCollectV2, hashDocumentStateForScope, isDocumentAction, isUndo, isUndoRedo, parseResultingState, replayDocument, skipHeaderOperations, sortOperations, updateHeaderRevision, } from "./documents.js";
|
|
4
|
+
import { loadStateOperation, redoOperation, setNameOperation, undoOperation, undoOperationV2, } from "./operations.js";
|
|
5
5
|
import { DocumentActionSchema } from "./schemas.js";
|
|
6
6
|
// This rebuilds the document according to the provided actions.
|
|
7
|
-
export function replayOperations(initialState, clearedOperations, stateReducer,
|
|
7
|
+
export function replayOperations(initialState, clearedOperations, stateReducer, header, dispatch, documentReducer = baseReducer, skipHeaderOperations = {}, options) {
|
|
8
8
|
// wraps the provided custom reducer with the
|
|
9
9
|
// base document reducer
|
|
10
10
|
const wrappedReducer = createReducer(stateReducer, documentReducer);
|
|
11
|
-
return replayDocument(initialState, clearedOperations, wrappedReducer,
|
|
11
|
+
return replayDocument(initialState, clearedOperations, wrappedReducer, header, dispatch, skipHeaderOperations, options);
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Updates the operations history of the document based on the provided action.
|
|
@@ -18,9 +18,10 @@ export function replayOperations(initialState, clearedOperations, stateReducer,
|
|
|
18
18
|
* @param index The index of the operation to update.
|
|
19
19
|
* @param skip The number of operations to skip before applying the action.
|
|
20
20
|
* @param reuseLastOperationIndex Whether to reuse the last operation index (used when a an UNDO operation is performed after an existing one).
|
|
21
|
+
* @param context The operation context for deterministic ID generation.
|
|
21
22
|
* @returns The updated document state.
|
|
22
23
|
*/
|
|
23
|
-
function updateOperationsForAction(document, action, reuseLastOperationIndex
|
|
24
|
+
function updateOperationsForAction(document, action, reuseLastOperationIndex, skip, context) {
|
|
24
25
|
// UNDO, REDO and PRUNE are meta operations
|
|
25
26
|
// that alter the operations history themselves
|
|
26
27
|
if (["UNDO", "REDO", "PRUNE"].includes(action.type)) {
|
|
@@ -36,7 +37,7 @@ function updateOperationsForAction(document, action, reuseLastOperationIndex = f
|
|
|
36
37
|
const index = reuseLastOperationIndex
|
|
37
38
|
? lastOperationIndex
|
|
38
39
|
: lastOperationIndex + 1;
|
|
39
|
-
const newOperation = operationFromAction(action, index, skip);
|
|
40
|
+
const newOperation = operationFromAction(action, index, skip, context);
|
|
40
41
|
operations.push(newOperation);
|
|
41
42
|
// adds the action to the operations history with
|
|
42
43
|
// the latest index and current timestamp
|
|
@@ -45,7 +46,7 @@ function updateOperationsForAction(document, action, reuseLastOperationIndex = f
|
|
|
45
46
|
operations: { ...document.operations, [scope]: operations },
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
|
-
function updateOperationsForOperation(document, operation, reuseLastOperationIndex
|
|
49
|
+
function updateOperationsForOperation(document, operation, reuseLastOperationIndex, skip, context, skipIndexValidation) {
|
|
49
50
|
const scope = operation.action.scope;
|
|
50
51
|
const scopeOperations = document.operations[scope];
|
|
51
52
|
const operations = scopeOperations
|
|
@@ -56,10 +57,10 @@ function updateOperationsForOperation(document, operation, reuseLastOperationInd
|
|
|
56
57
|
const nextIndex = reuseLastOperationIndex
|
|
57
58
|
? lastOperationIndex
|
|
58
59
|
: lastOperationIndex + 1;
|
|
59
|
-
if (operation.index - skip > nextIndex) {
|
|
60
|
+
if (!skipIndexValidation && operation.index - skip > nextIndex) {
|
|
60
61
|
throw new Error(`Missing operations: expected ${nextIndex} with skip 0 or equivalent, got index ${operation.index} with skip ${skip}`);
|
|
61
62
|
}
|
|
62
|
-
const newOperation = operationFromOperation(operation, skip);
|
|
63
|
+
const newOperation = operationFromOperation(operation, operation.index, skip, context);
|
|
63
64
|
operations.push(newOperation);
|
|
64
65
|
// adds the action to the operations history with
|
|
65
66
|
// the latest index and current timestamp
|
|
@@ -75,17 +76,18 @@ function updateOperationsForOperation(document, operation, reuseLastOperationInd
|
|
|
75
76
|
* @param action The action being applied to the document.
|
|
76
77
|
* @param skip The number of operations to skip before applying the action.
|
|
77
78
|
* @param reuseLastOperationIndex Whether to reuse the last operation index (used when a an UNDO operation is performed after an existing one).
|
|
79
|
+
* @param context The operation context for deterministic ID generation.
|
|
78
80
|
* @returns The updated document state.
|
|
79
81
|
*/
|
|
80
|
-
export function updateDocument(document, action, reuseLastOperationIndex
|
|
82
|
+
export function updateDocument(document, action, reuseLastOperationIndex, skip, context, operation, skipIndexValidation) {
|
|
81
83
|
let newDocument;
|
|
82
84
|
if (operation) {
|
|
83
85
|
// operation
|
|
84
|
-
newDocument = updateOperationsForOperation(document, operation, reuseLastOperationIndex, skip);
|
|
86
|
+
newDocument = updateOperationsForOperation(document, operation, reuseLastOperationIndex, skip, context, skipIndexValidation);
|
|
85
87
|
}
|
|
86
88
|
else {
|
|
87
89
|
// action
|
|
88
|
-
newDocument = updateOperationsForAction(document, action, reuseLastOperationIndex, skip);
|
|
90
|
+
newDocument = updateOperationsForAction(document, action, reuseLastOperationIndex, skip, context);
|
|
89
91
|
}
|
|
90
92
|
newDocument = updateHeaderRevision(newDocument, action.scope);
|
|
91
93
|
return newDocument;
|
|
@@ -102,6 +104,7 @@ function _baseReducer(document, action, wrappedReducer) {
|
|
|
102
104
|
// throws if action is not valid base action
|
|
103
105
|
const parsedAction = DocumentActionSchema().parse(action);
|
|
104
106
|
switch (parsedAction.type) {
|
|
107
|
+
// TODO: This needs to be changed to a HEADER scope action if it's changing the header.
|
|
105
108
|
case "SET_NAME":
|
|
106
109
|
return setNameOperation(document, parsedAction.input);
|
|
107
110
|
case "PRUNE":
|
|
@@ -120,9 +123,12 @@ function _baseReducer(document, action, wrappedReducer) {
|
|
|
120
123
|
* @param skip The number of operations to skip before applying the action.
|
|
121
124
|
* @returns The updated document, calculated skip value and transformed action (if applied).
|
|
122
125
|
*/
|
|
123
|
-
export function processUndoRedo(document, action, skip) {
|
|
126
|
+
export function processUndoRedo(document, action, skip, protocolVersion = 1) {
|
|
124
127
|
switch (action.type) {
|
|
125
128
|
case "UNDO":
|
|
129
|
+
if (protocolVersion >= 2) {
|
|
130
|
+
return undoOperationV2(document, action, skip);
|
|
131
|
+
}
|
|
126
132
|
return undoOperation(document, action, skip);
|
|
127
133
|
case "REDO":
|
|
128
134
|
return redoOperation(document, action, skip);
|
|
@@ -153,7 +159,7 @@ function processSkipOperation(document, action, customReducer, skipValue, reuseO
|
|
|
153
159
|
scopeState = resultingStateParser(lastRemainingOperation.resultingState);
|
|
154
160
|
}
|
|
155
161
|
else {
|
|
156
|
-
const { state } = replayOperations(document.initialState, documentOperations, customReducer,
|
|
162
|
+
const { state } = replayOperations(document.initialState, documentOperations, customReducer, document.header, undefined, undefined, undefined, {
|
|
157
163
|
reuseOperationResultingState,
|
|
158
164
|
operationResultingStateParser: resultingStateParser,
|
|
159
165
|
});
|
|
@@ -187,7 +193,7 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
|
|
|
187
193
|
}
|
|
188
194
|
const clearedOperations = [...documentScopeOps];
|
|
189
195
|
const diff = diffOperations(garbageCollect(sortedOperations), clearedOperations);
|
|
190
|
-
const doc = replayOperations(document.initialState, documentOperations, customReducer,
|
|
196
|
+
const doc = replayOperations(document.initialState, documentOperations, customReducer, document.header, undefined, undefined, undefined, {
|
|
191
197
|
reuseOperationResultingState,
|
|
192
198
|
operationResultingStateParser: resultingStateParser,
|
|
193
199
|
});
|
|
@@ -207,7 +213,7 @@ function processUndoOperation(document, scope, customReducer, reuseOperationResu
|
|
|
207
213
|
* @returns The new state of the document.
|
|
208
214
|
*/
|
|
209
215
|
export function baseReducer(document, action, customReducer, dispatch, options = {}) {
|
|
210
|
-
const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, } = options;
|
|
216
|
+
const { skip, ignoreSkipOperations = false, reuseOperationResultingState = false, operationResultingStateParser, pruneOnSkip = true, branch = "main", } = options;
|
|
211
217
|
let _action = actionFromAction(action);
|
|
212
218
|
let skipValue = skip ?? options.replayOptions?.operation.skip ?? 0;
|
|
213
219
|
let newDocument = {
|
|
@@ -216,7 +222,7 @@ export function baseReducer(document, action, customReducer, dispatch, options =
|
|
|
216
222
|
let reuseLastOperationIndex = false;
|
|
217
223
|
const shouldProcessSkipOperation = !ignoreSkipOperations && skipValue > 0;
|
|
218
224
|
if (isUndoRedo(_action)) {
|
|
219
|
-
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex, } = processUndoRedo(document, _action, skipValue);
|
|
225
|
+
const { skip: calculatedSkip, action: transformedAction, document: processedDocument, reuseLastOperationIndex: reuseIndex, } = processUndoRedo(document, _action, skipValue, options.protocolVersion ?? 1);
|
|
220
226
|
_action = transformedAction;
|
|
221
227
|
skipValue = calculatedSkip;
|
|
222
228
|
newDocument = processedDocument;
|
|
@@ -235,13 +241,46 @@ export function baseReducer(document, action, customReducer, dispatch, options =
|
|
|
235
241
|
}
|
|
236
242
|
// updates the document revision number, last modified date
|
|
237
243
|
// and operation history
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
const operationContext = {
|
|
245
|
+
documentId: document.header.id,
|
|
246
|
+
scope: _action.scope,
|
|
247
|
+
branch,
|
|
248
|
+
};
|
|
249
|
+
newDocument = updateDocument(newDocument, _action, reuseLastOperationIndex, skipValue, operationContext, options.replayOptions?.operation, options.skipIndexValidation);
|
|
250
|
+
// Only process undo for actual UNDO actions in protocol v1
|
|
251
|
+
// For v2, NOOPs always have skip=1 and indices increment
|
|
240
252
|
// NOOP operations with skip > 0 will have their clipboard populated server-side
|
|
241
|
-
|
|
253
|
+
const protocolVersion = options.protocolVersion ?? 1;
|
|
254
|
+
if (isUndo(action) && protocolVersion < 2) {
|
|
242
255
|
const result = processUndoOperation(newDocument, action.scope, customReducer);
|
|
243
256
|
return result;
|
|
244
257
|
}
|
|
258
|
+
// V2 UNDO: Rebuild state using garbageCollectV2 which handles consecutive NOOPs as chains
|
|
259
|
+
// Also trigger for NOOP operations loaded from sync (they have skip > 0)
|
|
260
|
+
const isNoopWithSkip = _action.type === "NOOP" && skipValue > 0;
|
|
261
|
+
if ((isUndo(action) || isNoopWithSkip) && protocolVersion >= 2) {
|
|
262
|
+
const scope = _action.scope;
|
|
263
|
+
const scopeOperations = newDocument.operations[scope] || [];
|
|
264
|
+
const sortedOps = sortOperations([...scopeOperations]);
|
|
265
|
+
// Get operations that should be applied (excludes undone operations)
|
|
266
|
+
const effectiveOps = garbageCollectV2(sortedOps);
|
|
267
|
+
// Build operations for replay - only include non-NOOP operations
|
|
268
|
+
const opsToReplay = effectiveOps.filter((op) => op.action.type !== "NOOP");
|
|
269
|
+
// Create document operations with only the effective ops for this scope
|
|
270
|
+
const replayOps = {
|
|
271
|
+
...newDocument.operations,
|
|
272
|
+
[scope]: opsToReplay,
|
|
273
|
+
};
|
|
274
|
+
// Replay to rebuild state using replayOperations which wraps the reducer
|
|
275
|
+
// Pass skipIndexValidation since garbageCollectV2 creates gapped indices
|
|
276
|
+
const rebuiltDoc = replayOperations(newDocument.initialState, replayOps, customReducer, newDocument.header, dispatch, baseReducer, {}, { skipIndexValidation: true });
|
|
277
|
+
// Return document with rebuilt state but original operations (including all NOOPs)
|
|
278
|
+
return {
|
|
279
|
+
...rebuiltDoc,
|
|
280
|
+
operations: newDocument.operations,
|
|
281
|
+
clipboard: [],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
245
284
|
if (shouldProcessSkipOperation) {
|
|
246
285
|
const processed = processSkipOperation(newDocument, _action, customReducer, skipValue, reuseOperationResultingState, operationResultingStateParser);
|
|
247
286
|
// Preserve operations when pruneOnSkip is false
|
|
@@ -378,7 +417,7 @@ export function pruneOperation(document, input, wrappedReducer) {
|
|
|
378
417
|
const newDocument = replayOperations(document.initialState, {
|
|
379
418
|
...document.operations,
|
|
380
419
|
global: actionsToKeepStart.concat(actionsToPrune),
|
|
381
|
-
}, wrappedReducer);
|
|
420
|
+
}, wrappedReducer, document.header);
|
|
382
421
|
const newState = newDocument.state;
|
|
383
422
|
const name = newDocument.header.name;
|
|
384
423
|
// the new operation has the index of the first pruned operation
|
|
@@ -411,6 +450,6 @@ export function pruneOperation(document, input, wrappedReducer) {
|
|
|
411
450
|
index: loadStateIndex + index + 1,
|
|
412
451
|
})),
|
|
413
452
|
],
|
|
414
|
-
}, wrappedReducer);
|
|
453
|
+
}, wrappedReducer, document.header);
|
|
415
454
|
}
|
|
416
455
|
//# sourceMappingURL=reducer.js.map
|