@bilig/workbook 0.90.0 → 0.90.4
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 +23 -12
- package/dist/command-ranges.js +11 -4
- package/dist/command-ranges.js.map +1 -1
- package/dist/command-receipt-ranges.d.ts +7 -0
- package/dist/command-receipt-ranges.js +64 -0
- package/dist/command-receipt-ranges.js.map +1 -0
- package/dist/command-result-undo.d.ts +2 -0
- package/dist/command-result-undo.js +148 -0
- package/dist/command-result-undo.js.map +1 -0
- package/dist/command-result.js +76 -136
- package/dist/command-result.js.map +1 -1
- package/dist/features.js +22 -60
- package/dist/features.js.map +1 -1
- package/dist/model.js +7 -7
- package/dist/model.js.map +1 -1
- package/dist/plan-data.js +1 -1
- package/dist/plan-data.js.map +1 -1
- package/dist/readback.js +1 -1
- package/dist/readback.js.map +1 -1
- package/dist/requirements.js +1 -1
- package/dist/requirements.js.map +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/run.js +1 -1
- package/dist/run.js.map +1 -1
- package/dist/verify.js +1 -1
- package/dist/verify.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -169,8 +169,9 @@ an agent inspected cannot be mutated behind the same handoff object.
|
|
|
169
169
|
`verifyPlan` also treats plans as data: malformed, sparse, or accessor-backed
|
|
170
170
|
handoff objects return an `invalid_plan` issue instead of executing hidden
|
|
171
171
|
properties or throwing at the caller.
|
|
172
|
-
`verifyModel` keeps the same behavior at whole-model scope: invalid
|
|
173
|
-
return an invalid verdict with an
|
|
172
|
+
`verifyModel` keeps the same behavior at whole-model scope: invalid,
|
|
173
|
+
array-backed, or accessor-backed manifests return an invalid verdict with an
|
|
174
|
+
`invalid_model` error and no actions.
|
|
174
175
|
Its `{ inputs }` option is data-only too: accessor-backed option payloads or
|
|
175
176
|
per-action inputs produce structured `invalid_action_input` results without
|
|
176
177
|
running hidden consumer code.
|
|
@@ -220,7 +221,10 @@ proof keep nested JSON paths such as `input.rows[1]` and
|
|
|
220
221
|
hydration. Plan-data guards only trust own payload fields; inherited
|
|
221
222
|
prototype fields never satisfy the transport contract. Transported plan arrays
|
|
222
223
|
must contain own enumerable data entries too; holes, non-enumerable entries, or
|
|
223
|
-
accessor-backed entries are rejected without running getters. The
|
|
224
|
+
accessor-backed entries are rejected without running getters. The plan root and
|
|
225
|
+
nested plan entries such as commands, changes, checks, formula labels, and
|
|
226
|
+
expectations must be record-shaped payloads, not arrays with attached fields.
|
|
227
|
+
The hydrated plan
|
|
224
228
|
exposes `refs: { refsUsed }` instead of the consumer's private model-shaped
|
|
225
229
|
`refs` object, so transported execution stays generic. A valid
|
|
226
230
|
`checkPlanData(data)` result returns canonical plan data, stripping caller-owned
|
|
@@ -296,6 +300,8 @@ must be own enumerable data entries; holes, non-enumerable entries, or
|
|
|
296
300
|
accessor-backed entries are rejected without running getters. Use
|
|
297
301
|
`checkRuntimeAdapter(planOrRequirements, adapter)` when an agent wants to check
|
|
298
302
|
`apply`, `read`, and `verifyChecks` coverage before calling the runtime.
|
|
303
|
+
The requirements root object, every requirement entry, and runtime adapter
|
|
304
|
+
objects must be record-shaped payloads, not arrays with attached fields.
|
|
299
305
|
Runtime requirement descriptions are frozen normalized data too:
|
|
300
306
|
`describeRuntimeRequirements` and `checkRuntimeRequirements` strip
|
|
301
307
|
caller-owned extra fields and freeze the returned requirement tree, including
|
|
@@ -340,14 +346,18 @@ and the frozen run-result description, and roll back engine ops if post-apply
|
|
|
340
346
|
readback or check proof fails.
|
|
341
347
|
Runtime apply results, undo refs, apply errors, and check verifier output are
|
|
342
348
|
validated from own fields only; prototype-inherited fields are ignored before
|
|
343
|
-
they can become run proof.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
non-enumerable
|
|
347
|
-
|
|
349
|
+
they can become run proof. Apply-result objects and verifier check objects must
|
|
350
|
+
be plain record-shaped payloads, not arrays with attached fields.
|
|
351
|
+
Adapter-returned ops and verifier proof must be data properties, including
|
|
352
|
+
non-enumerable guard fields such as `kind`. Runtime evidence arrays must contain
|
|
353
|
+
own enumerable data entries; holes, non-enumerable entries, and accessors are
|
|
354
|
+
rejected before any getter can run during validation, cloning, or preview/apply
|
|
355
|
+
comparison.
|
|
348
356
|
Readback checks attach proof to passed checks, such as
|
|
349
357
|
`{ source: "readback", value: 12 }` or
|
|
350
358
|
`{ source: "readback", formula: "Table[Quantity]*Table[Rate]" }`.
|
|
359
|
+
Readback proof objects, check objects, expectations, and formula labels must be
|
|
360
|
+
record-shaped payloads, not arrays with attached fields.
|
|
351
361
|
Formula readback proof is parsed with `@bilig/formula` and stored in canonical
|
|
352
362
|
no-leading-`=` form, so harmless runtime differences such as a leading equals
|
|
353
363
|
sign, whitespace, or redundant parentheses do not make proof fail.
|
|
@@ -455,10 +465,11 @@ It returns the same boring `{ status, issues }` shape for receipt fields such as
|
|
|
455
465
|
`changedRanges`, `proof`, `metadata`, and `errors`. Feature manifests, command
|
|
456
466
|
requests, and command receipts are validated from own payload fields only;
|
|
457
467
|
prototype-inherited fields are ignored. Receipt verdicts are frozen. Receipt ops are frozen after
|
|
458
|
-
normalization, changed ranges
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
468
|
+
normalization, changed ranges are canonicalized through the same workbook range
|
|
469
|
+
normalizer used by command scopes, and manifest or receipt arrays must contain
|
|
470
|
+
own enumerable data entries. Holes, non-enumerable entries, invalid range
|
|
471
|
+
addresses, and accessor-backed ops, undo ops, ranges, or errors are rejected
|
|
472
|
+
before any getter can run.
|
|
462
473
|
Receipt statuses are semantic: `previewed` cannot include applied proof,
|
|
463
474
|
`applied` cannot include errors and must carry applied evidence, `rejected`
|
|
464
475
|
cannot claim changed workbook proof, and `noop` cannot claim changed ranges or
|
package/dist/command-ranges.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { formatAddress, parseCellAddress } from '@bilig/formula';
|
|
2
|
+
const commandRangeDataFields = Object.freeze(['sheetName', 'startAddress', 'endAddress']);
|
|
2
3
|
export function normalizeCommandRange(value, path, label = 'Workbook command bundle') {
|
|
3
4
|
if (!isRecord(value)) {
|
|
4
5
|
throw new Error(`${label} ${path} must be an object`);
|
|
5
6
|
}
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
for (const field of commandRangeDataFields) {
|
|
8
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, field);
|
|
9
|
+
if (descriptor !== undefined && !('value' in descriptor)) {
|
|
10
|
+
throw new Error(`${label} ${path}.${field} must be a data property`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const sheetName = ownDataValue(value, 'sheetName');
|
|
14
|
+
const startAddress = ownDataValue(value, 'startAddress');
|
|
15
|
+
const endAddress = ownDataValue(value, 'endAddress');
|
|
9
16
|
if (typeof sheetName !== 'string' || typeof startAddress !== 'string' || typeof endAddress !== 'string') {
|
|
10
17
|
throw new Error(`${label} ${path} must include sheetName, startAddress, and endAddress strings`);
|
|
11
18
|
}
|
|
@@ -76,7 +83,7 @@ function normalizeExactString(value, path, label) {
|
|
|
76
83
|
}
|
|
77
84
|
return normalized;
|
|
78
85
|
}
|
|
79
|
-
function
|
|
86
|
+
function ownDataValue(value, key) {
|
|
80
87
|
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
81
88
|
}
|
|
82
89
|
function isRecord(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-ranges.js","sourceRoot":"","sources":["../src/command-ranges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAkBhE,MAAM,UAAU,qBAAqB,CAAC,KAAc,EAAE,IAAY,EAAE,KAAK,GAAG,yBAAyB;IACnG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI,IAAI,oBAAoB,CAAC,CAAA;IACvD,CAAC;IACD,MAAM,SAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"command-ranges.js","sourceRoot":"","sources":["../src/command-ranges.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CellRangeRef } from '@bilig/protocol';
|
|
2
|
+
export interface WorkbookCommandReceiptRangeIssue {
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function commandReceiptChangedRangeIssues(value: unknown): readonly WorkbookCommandReceiptRangeIssue[];
|
|
7
|
+
export declare function normalizeCommandReceiptChangedRanges(value: unknown): readonly CellRangeRef[] | null;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { normalizeCommandRange } from './command-ranges.js';
|
|
2
|
+
const commandReceiptRangeDataFields = Object.freeze(['sheetName', 'startAddress', 'endAddress']);
|
|
3
|
+
export function commandReceiptChangedRangeIssues(value) {
|
|
4
|
+
if (!Array.isArray(value)) {
|
|
5
|
+
return Object.freeze([rangeIssue('changedRanges', 'Workbook command receipt changed ranges must be an array')]);
|
|
6
|
+
}
|
|
7
|
+
const issues = [];
|
|
8
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
9
|
+
const path = `changedRanges[${String(index)}]`;
|
|
10
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
11
|
+
if (descriptor === undefined || !descriptor.enumerable || !('value' in descriptor)) {
|
|
12
|
+
issues.push(rangeIssue(path, 'Workbook command receipt changed ranges must contain only data properties'));
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const dataFieldIssue = firstRangeDataFieldIssue(descriptor.value, path);
|
|
16
|
+
if (dataFieldIssue !== null) {
|
|
17
|
+
issues.push(dataFieldIssue);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
normalizeCommandRange(descriptor.value, path, 'Workbook command receipt');
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
issues.push(rangeIssue(path, errorMessage(error)));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return Object.freeze(issues);
|
|
28
|
+
}
|
|
29
|
+
function firstRangeDataFieldIssue(value, path) {
|
|
30
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
for (const field of commandReceiptRangeDataFields) {
|
|
34
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, field);
|
|
35
|
+
if (descriptor !== undefined && !('value' in descriptor)) {
|
|
36
|
+
return rangeIssue(`${path}.${field}`, `Workbook command receipt ${path}.${field} must be a data property`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
export function normalizeCommandReceiptChangedRanges(value) {
|
|
42
|
+
if (commandReceiptChangedRangeIssues(value).length > 0 || !Array.isArray(value)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const ranges = [];
|
|
46
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
47
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
48
|
+
if (descriptor === undefined || !descriptor.enumerable || !('value' in descriptor)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
ranges.push(normalizeCommandRange(descriptor.value, `changedRanges[${String(index)}]`, 'Workbook command receipt').range);
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze(ranges);
|
|
54
|
+
}
|
|
55
|
+
function rangeIssue(path, message) {
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
path,
|
|
58
|
+
message,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function errorMessage(error) {
|
|
62
|
+
return error instanceof Error ? error.message : String(error);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=command-receipt-ranges.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-receipt-ranges.js","sourceRoot":"","sources":["../src/command-receipt-ranges.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { isWorkbookOp } from './guards.js';
|
|
2
|
+
export function normalizeCommandResultUndoRef(value, path) {
|
|
3
|
+
if (!isRecord(value)) {
|
|
4
|
+
throw new Error(`Workbook command result ${path} must be an object`);
|
|
5
|
+
}
|
|
6
|
+
const accessorKeys = ownAccessorKeys(value, ['id', 'ops']);
|
|
7
|
+
if (accessorKeys.length > 0) {
|
|
8
|
+
throw new Error(`Workbook command result ${path}.${accessorKeys[0]} must be a data property`);
|
|
9
|
+
}
|
|
10
|
+
const id = ownValue(value, 'id');
|
|
11
|
+
if (typeof id !== 'string') {
|
|
12
|
+
throw new Error(`Workbook command result ${path}.id must be a string`);
|
|
13
|
+
}
|
|
14
|
+
const ops = ownValue(value, 'ops');
|
|
15
|
+
if (ops !== undefined && !Array.isArray(ops)) {
|
|
16
|
+
throw new Error(`Workbook command result ${path}.ops must be an array`);
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(ops)) {
|
|
19
|
+
const accessorPath = firstAccessorPath(ops, `${path}.ops`);
|
|
20
|
+
if (accessorPath !== null) {
|
|
21
|
+
throw new Error(`Workbook command result ${accessorPath} must contain only data properties`);
|
|
22
|
+
}
|
|
23
|
+
const normalizedOps = [];
|
|
24
|
+
for (let index = 0; index < ops.length; index += 1) {
|
|
25
|
+
const op = arrayDataValue(ops, index);
|
|
26
|
+
if (op === undefined) {
|
|
27
|
+
throw new Error(`Workbook command result ${path}.ops[${String(index)}] must contain only data properties`);
|
|
28
|
+
}
|
|
29
|
+
normalizedOps.push(normalizeOp(op));
|
|
30
|
+
}
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
id: normalizeExactString(id, `${path}.id`),
|
|
33
|
+
ops: Object.freeze(normalizedOps),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return Object.freeze({
|
|
37
|
+
id: normalizeExactString(id, `${path}.id`),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function normalizeOp(value) {
|
|
41
|
+
if (!isWorkbookOp(value)) {
|
|
42
|
+
throw new Error('Workbook command result op is invalid');
|
|
43
|
+
}
|
|
44
|
+
const cloned = cloneData(value);
|
|
45
|
+
if (!isWorkbookOp(cloned)) {
|
|
46
|
+
throw new Error('Workbook command result op clone is invalid');
|
|
47
|
+
}
|
|
48
|
+
return freezeData(cloned);
|
|
49
|
+
}
|
|
50
|
+
function normalizeExactString(value, path) {
|
|
51
|
+
const normalized = value.trim();
|
|
52
|
+
if (normalized === '') {
|
|
53
|
+
throw new Error(`Workbook command result ${path} cannot be empty`);
|
|
54
|
+
}
|
|
55
|
+
if (normalized !== value) {
|
|
56
|
+
throw new Error(`Workbook command result ${path} must not have leading or trailing whitespace`);
|
|
57
|
+
}
|
|
58
|
+
return normalized;
|
|
59
|
+
}
|
|
60
|
+
function arrayDataValue(value, index) {
|
|
61
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
62
|
+
return descriptor !== undefined && descriptor.enumerable && 'value' in descriptor ? descriptor.value : undefined;
|
|
63
|
+
}
|
|
64
|
+
function ownValue(value, key) {
|
|
65
|
+
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
66
|
+
}
|
|
67
|
+
function ownAccessorKeys(value, keys) {
|
|
68
|
+
const accessors = [];
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
71
|
+
if (descriptor !== undefined && !('value' in descriptor)) {
|
|
72
|
+
accessors.push(key);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return accessors;
|
|
76
|
+
}
|
|
77
|
+
function isRecord(value) {
|
|
78
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
function firstAccessorPath(value, path, seen = new WeakSet()) {
|
|
81
|
+
if (typeof value !== 'object' || value === null) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (seen.has(value)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
seen.add(value);
|
|
88
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
89
|
+
const childPath = Array.isArray(value) && /^\d+$/.test(key) ? `${path}[${key}]` : `${path}.${key}`;
|
|
90
|
+
if (!('value' in descriptor)) {
|
|
91
|
+
return childPath;
|
|
92
|
+
}
|
|
93
|
+
const nestedPath = firstAccessorPath(descriptor.value, childPath, seen);
|
|
94
|
+
if (nestedPath !== null) {
|
|
95
|
+
return nestedPath;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function freezeData(value, seen = new WeakSet()) {
|
|
101
|
+
if (typeof value !== 'object' || value === null) {
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
if (seen.has(value)) {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
seen.add(value);
|
|
108
|
+
Object.values(Object.getOwnPropertyDescriptors(value)).forEach((descriptor) => {
|
|
109
|
+
if ('value' in descriptor) {
|
|
110
|
+
freezeData(descriptor.value, seen);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return Object.freeze(value);
|
|
114
|
+
}
|
|
115
|
+
function cloneData(value, seen = new WeakMap()) {
|
|
116
|
+
if (typeof value !== 'object' || value === null) {
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
const existing = seen.get(value);
|
|
120
|
+
if (existing !== undefined) {
|
|
121
|
+
return existing;
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
const cloned = [];
|
|
125
|
+
seen.set(value, cloned);
|
|
126
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
127
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
128
|
+
if (descriptor !== undefined && descriptor.enumerable && 'value' in descriptor) {
|
|
129
|
+
cloned[index] = cloneData(descriptor.value, seen);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return cloned;
|
|
133
|
+
}
|
|
134
|
+
const cloned = Object.create(Object.getPrototypeOf(value));
|
|
135
|
+
seen.set(value, cloned);
|
|
136
|
+
Object.entries(Object.getOwnPropertyDescriptors(value)).forEach(([key, descriptor]) => {
|
|
137
|
+
if (descriptor.enumerable && 'value' in descriptor) {
|
|
138
|
+
Object.defineProperty(cloned, key, {
|
|
139
|
+
configurable: true,
|
|
140
|
+
enumerable: true,
|
|
141
|
+
value: cloneData(descriptor.value, seen),
|
|
142
|
+
writable: true,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return cloned;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=command-result-undo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-result-undo.js","sourceRoot":"","sources":["../src/command-result-undo.ts"],"names":[],"mappings":"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"}
|
package/dist/command-result.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { commandRangeBounds, commandRangeContains, commandRangeLabel, normalizeCommandRange } from './command-ranges.js';
|
|
2
|
+
import { normalizeCommandResultUndoRef } from './command-result-undo.js';
|
|
2
3
|
import { checkWorkbookCommandReceipt, normalizeWorkbookCommandReceipt, workbookCommandReceiptOpsMatch, workbookCommandReceiptStatuses, } from './features.js';
|
|
3
|
-
import { isWorkbookOp } from './guards.js';
|
|
4
4
|
export const workbookCommandResultStatuses = Object.freeze([
|
|
5
5
|
'accepted',
|
|
6
6
|
...workbookCommandReceiptStatuses,
|
|
7
7
|
]);
|
|
8
8
|
const WORKBOOK_COMMAND_RESULT_STATUS_SET = new Set(workbookCommandResultStatuses);
|
|
9
9
|
const acceptedResultSettledFields = Object.freeze(['receipts', 'matched', 'changedRanges', 'revision', 'undo', 'errors']);
|
|
10
|
+
const commandResultDataFields = Object.freeze([
|
|
11
|
+
'status',
|
|
12
|
+
'bundleId',
|
|
13
|
+
'targetRevision',
|
|
14
|
+
'idempotencyKey',
|
|
15
|
+
'commandCount',
|
|
16
|
+
'touchedRanges',
|
|
17
|
+
'touchedCellCount',
|
|
18
|
+
...acceptedResultSettledFields,
|
|
19
|
+
]);
|
|
10
20
|
export const workbookOpCommandFeatureId = 'workbook-op';
|
|
11
21
|
export function isWorkbookCommandResultStatus(value) {
|
|
12
22
|
return typeof value === 'string' && WORKBOOK_COMMAND_RESULT_STATUS_SET.has(value);
|
|
@@ -45,7 +55,7 @@ export function workbookCommandResultForReceipts(bundle, receipts, options = {})
|
|
|
45
55
|
if (revision !== undefined && !isSafeNonNegativeInteger(revision)) {
|
|
46
56
|
throw new Error('Workbook command result is invalid: revision must be a safe non-negative integer');
|
|
47
57
|
}
|
|
48
|
-
const undo = options.undo === undefined ? undefined :
|
|
58
|
+
const undo = options.undo === undefined ? undefined : normalizeCommandResultUndoRef(options.undo, 'undo');
|
|
49
59
|
const changedRanges = normalizedReceipts.flatMap((receipt) => [...(receipt.changedRanges ?? [])]);
|
|
50
60
|
const errors = commandResultErrorsForReceipts(normalizedReceipts);
|
|
51
61
|
return normalizeWorkbookCommandResult({
|
|
@@ -83,37 +93,69 @@ export function checkWorkbookCommandResult(value) {
|
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
95
|
const issues = [];
|
|
86
|
-
const
|
|
96
|
+
const accessorKeys = new Set(ownAccessorKeys(value, commandResultDataFields));
|
|
97
|
+
accessorKeys.forEach((key) => {
|
|
98
|
+
issues.push(commandResultIssue('invalid_command_result', key, `Workbook command result ${key} must be a data property`));
|
|
99
|
+
});
|
|
100
|
+
const status = accessorKeys.has('status') ? undefined : ownValue(value, 'status');
|
|
87
101
|
if (!isWorkbookCommandResultStatus(status)) {
|
|
88
102
|
issues.push(commandResultIssue('invalid_command_result', 'status', 'Workbook command result status is invalid'));
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
if (!accessorKeys.has('bundleId')) {
|
|
105
|
+
pushResultOptionalStringIssue(issues, ownValue(value, 'bundleId'), 'bundleId', 'bundle id');
|
|
106
|
+
}
|
|
107
|
+
if (!accessorKeys.has('targetRevision')) {
|
|
108
|
+
pushResultSafeIntegerIssue(issues, ownValue(value, 'targetRevision'), 'targetRevision', 'target revision', true);
|
|
109
|
+
}
|
|
110
|
+
if (!accessorKeys.has('idempotencyKey')) {
|
|
111
|
+
pushResultRequiredStringIssue(issues, ownValue(value, 'idempotencyKey'), 'idempotencyKey', 'idempotency key');
|
|
112
|
+
}
|
|
113
|
+
if (!accessorKeys.has('commandCount')) {
|
|
114
|
+
pushResultSafeIntegerIssue(issues, ownValue(value, 'commandCount'), 'commandCount', 'command count', true);
|
|
115
|
+
}
|
|
116
|
+
if (!accessorKeys.has('touchedRanges')) {
|
|
117
|
+
pushResultRangesIssues(issues, ownValue(value, 'touchedRanges'), 'touchedRanges', 'touched ranges');
|
|
118
|
+
}
|
|
119
|
+
if (!accessorKeys.has('touchedCellCount')) {
|
|
120
|
+
pushResultSafeIntegerIssue(issues, ownValue(value, 'touchedCellCount'), 'touchedCellCount', 'touched cell count', true);
|
|
121
|
+
}
|
|
96
122
|
if (status === 'accepted') {
|
|
97
123
|
pushAcceptedResultSettledFieldIssues(issues, value);
|
|
98
124
|
}
|
|
99
125
|
else if (isWorkbookCommandResultStatus(status)) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
issues
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
normalizeUndoRef(undo, 'undo');
|
|
126
|
+
if (!accessorKeys.has('revision')) {
|
|
127
|
+
pushResultSafeIntegerIssue(issues, ownValue(value, 'revision'), 'revision', 'revision', false);
|
|
128
|
+
}
|
|
129
|
+
if (!accessorKeys.has('receipts')) {
|
|
130
|
+
pushResultReceiptsIssues(issues, ownValue(value, 'receipts'));
|
|
131
|
+
}
|
|
132
|
+
if (!accessorKeys.has('matched')) {
|
|
133
|
+
const matched = ownValue(value, 'matched');
|
|
134
|
+
if (matched !== null && typeof matched !== 'boolean') {
|
|
135
|
+
issues.push(commandResultIssue('invalid_command_result', 'matched', 'Workbook command result matched must be boolean or null'));
|
|
111
136
|
}
|
|
112
|
-
|
|
113
|
-
|
|
137
|
+
}
|
|
138
|
+
if (!accessorKeys.has('changedRanges')) {
|
|
139
|
+
pushResultRangesIssues(issues, ownValue(value, 'changedRanges'), 'changedRanges', 'changed ranges');
|
|
140
|
+
}
|
|
141
|
+
const undo = accessorKeys.has('undo') ? undefined : ownValue(value, 'undo');
|
|
142
|
+
if (!accessorKeys.has('undo') && undo !== undefined) {
|
|
143
|
+
const undoAccessorKeys = isRecord(undo) ? ownAccessorKeys(undo, ['id', 'ops']) : [];
|
|
144
|
+
undoAccessorKeys.forEach((key) => {
|
|
145
|
+
issues.push(commandResultIssue('invalid_undo', `undo.${key}`, `Workbook command result undo.${key} must be a data property`));
|
|
146
|
+
});
|
|
147
|
+
if (undoAccessorKeys.length === 0) {
|
|
148
|
+
try {
|
|
149
|
+
normalizeCommandResultUndoRef(undo, 'undo');
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
issues.push(commandResultIssue('invalid_undo', 'undo', errorMessage(error)));
|
|
153
|
+
}
|
|
114
154
|
}
|
|
115
155
|
}
|
|
116
|
-
|
|
156
|
+
if (!accessorKeys.has('errors')) {
|
|
157
|
+
pushResultErrorsIssues(issues, ownValue(value, 'errors'));
|
|
158
|
+
}
|
|
117
159
|
}
|
|
118
160
|
if (issues.length > 0) {
|
|
119
161
|
return Object.freeze({
|
|
@@ -545,7 +587,7 @@ function normalizeWorkbookCommandResultData(value) {
|
|
|
545
587
|
receipts: Object.freeze(normalizeResultReceipts(ownValue(value, 'receipts'))),
|
|
546
588
|
matched: matched === true ? true : matched === false ? false : null,
|
|
547
589
|
changedRanges: Object.freeze(normalizeResultRanges(ownValue(value, 'changedRanges'), 'changedRanges')),
|
|
548
|
-
...(undo !== undefined ? { undo:
|
|
590
|
+
...(undo !== undefined ? { undo: normalizeCommandResultUndoRef(undo, 'undo') } : {}),
|
|
549
591
|
...(Array.isArray(errors) ? { errors: Object.freeze(normalizeResultErrors(errors)) } : {}),
|
|
550
592
|
});
|
|
551
593
|
}
|
|
@@ -569,50 +611,6 @@ function normalizeResultErrors(errors) {
|
|
|
569
611
|
return normalizeExactString(entry, `errors[${String(index)}]`);
|
|
570
612
|
});
|
|
571
613
|
}
|
|
572
|
-
function normalizeUndoRef(value, path) {
|
|
573
|
-
if (!isRecord(value)) {
|
|
574
|
-
throw new Error(`Workbook command result ${path} must be an object`);
|
|
575
|
-
}
|
|
576
|
-
const id = ownValue(value, 'id');
|
|
577
|
-
if (typeof id !== 'string') {
|
|
578
|
-
throw new Error(`Workbook command result ${path}.id must be a string`);
|
|
579
|
-
}
|
|
580
|
-
const ops = ownValue(value, 'ops');
|
|
581
|
-
if (ops !== undefined && !Array.isArray(ops)) {
|
|
582
|
-
throw new Error(`Workbook command result ${path}.ops must be an array`);
|
|
583
|
-
}
|
|
584
|
-
if (Array.isArray(ops)) {
|
|
585
|
-
const accessorPath = firstAccessorPath(ops, `${path}.ops`);
|
|
586
|
-
if (accessorPath !== null) {
|
|
587
|
-
throw new Error(`Workbook command result ${accessorPath} must contain only data properties`);
|
|
588
|
-
}
|
|
589
|
-
const normalizedOps = [];
|
|
590
|
-
for (let index = 0; index < ops.length; index += 1) {
|
|
591
|
-
const op = arrayDataValue(ops, index);
|
|
592
|
-
if (op === undefined) {
|
|
593
|
-
throw new Error(`Workbook command result ${path}.ops[${String(index)}] must contain only data properties`);
|
|
594
|
-
}
|
|
595
|
-
normalizedOps.push(normalizeOp(op));
|
|
596
|
-
}
|
|
597
|
-
return Object.freeze({
|
|
598
|
-
id: normalizeExactString(id, `${path}.id`),
|
|
599
|
-
ops: Object.freeze(normalizedOps),
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
return Object.freeze({
|
|
603
|
-
id: normalizeExactString(id, `${path}.id`),
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
function normalizeOp(value) {
|
|
607
|
-
if (!isWorkbookOp(value)) {
|
|
608
|
-
throw new Error('Workbook command result op is invalid');
|
|
609
|
-
}
|
|
610
|
-
const cloned = cloneData(value);
|
|
611
|
-
if (!isWorkbookOp(cloned)) {
|
|
612
|
-
throw new Error('Workbook command result op clone is invalid');
|
|
613
|
-
}
|
|
614
|
-
return freezeData(cloned);
|
|
615
|
-
}
|
|
616
614
|
function normalizeExactString(value, path) {
|
|
617
615
|
const normalized = value.trim();
|
|
618
616
|
if (normalized === '') {
|
|
@@ -630,6 +628,16 @@ function arrayDataValue(value, index) {
|
|
|
630
628
|
function ownValue(value, key) {
|
|
631
629
|
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
632
630
|
}
|
|
631
|
+
function ownAccessorKeys(value, keys) {
|
|
632
|
+
const accessors = [];
|
|
633
|
+
for (const key of keys) {
|
|
634
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
635
|
+
if (descriptor !== undefined && !('value' in descriptor)) {
|
|
636
|
+
accessors.push(key);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return accessors;
|
|
640
|
+
}
|
|
633
641
|
function isRecord(value) {
|
|
634
642
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
635
643
|
}
|
|
@@ -639,72 +647,4 @@ function isSafeNonNegativeInteger(value) {
|
|
|
639
647
|
function errorMessage(error) {
|
|
640
648
|
return error instanceof Error ? error.message : String(error);
|
|
641
649
|
}
|
|
642
|
-
function firstAccessorPath(value, path, seen = new WeakSet()) {
|
|
643
|
-
if (typeof value !== 'object' || value === null) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
if (seen.has(value)) {
|
|
647
|
-
return null;
|
|
648
|
-
}
|
|
649
|
-
seen.add(value);
|
|
650
|
-
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
651
|
-
const childPath = Array.isArray(value) && /^\d+$/.test(key) ? `${path}[${key}]` : `${path}.${key}`;
|
|
652
|
-
if (!('value' in descriptor)) {
|
|
653
|
-
return childPath;
|
|
654
|
-
}
|
|
655
|
-
const nestedPath = firstAccessorPath(descriptor.value, childPath, seen);
|
|
656
|
-
if (nestedPath !== null) {
|
|
657
|
-
return nestedPath;
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
function freezeData(value, seen = new WeakSet()) {
|
|
663
|
-
if (typeof value !== 'object' || value === null) {
|
|
664
|
-
return value;
|
|
665
|
-
}
|
|
666
|
-
if (seen.has(value)) {
|
|
667
|
-
return value;
|
|
668
|
-
}
|
|
669
|
-
seen.add(value);
|
|
670
|
-
Object.values(Object.getOwnPropertyDescriptors(value)).forEach((descriptor) => {
|
|
671
|
-
if ('value' in descriptor) {
|
|
672
|
-
freezeData(descriptor.value, seen);
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
return Object.freeze(value);
|
|
676
|
-
}
|
|
677
|
-
function cloneData(value, seen = new WeakMap()) {
|
|
678
|
-
if (typeof value !== 'object' || value === null) {
|
|
679
|
-
return value;
|
|
680
|
-
}
|
|
681
|
-
const existing = seen.get(value);
|
|
682
|
-
if (existing !== undefined) {
|
|
683
|
-
return existing;
|
|
684
|
-
}
|
|
685
|
-
if (Array.isArray(value)) {
|
|
686
|
-
const cloned = [];
|
|
687
|
-
seen.set(value, cloned);
|
|
688
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
689
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
690
|
-
if (descriptor !== undefined && descriptor.enumerable && 'value' in descriptor) {
|
|
691
|
-
cloned[index] = cloneData(descriptor.value, seen);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
return cloned;
|
|
695
|
-
}
|
|
696
|
-
const cloned = Object.create(Object.getPrototypeOf(value));
|
|
697
|
-
seen.set(value, cloned);
|
|
698
|
-
Object.entries(Object.getOwnPropertyDescriptors(value)).forEach(([key, descriptor]) => {
|
|
699
|
-
if (descriptor.enumerable && 'value' in descriptor) {
|
|
700
|
-
Object.defineProperty(cloned, key, {
|
|
701
|
-
configurable: true,
|
|
702
|
-
enumerable: true,
|
|
703
|
-
value: cloneData(descriptor.value, seen),
|
|
704
|
-
writable: true,
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
return cloned;
|
|
709
|
-
}
|
|
710
650
|
//# sourceMappingURL=command-result.js.map
|