@openpresentation/opf 0.26.10 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +6 -0
  2. package/dist/browser.js +20 -17
  3. package/dist/{chunk-H7XMO36O.js → chunk-353VBJ6K.js} +5 -5
  4. package/dist/chunk-43JWUIGF.js +8 -0
  5. package/dist/chunk-4ACS57MG.js +236 -0
  6. package/dist/{chunk-F4HFDP6I.js → chunk-4ZM5G3XX.js} +1 -1
  7. package/dist/{chunk-NVT2YDVC.js → chunk-7XUEKIXH.js} +2 -2
  8. package/dist/{chunk-Y25DYRME.js → chunk-ABFDFZIW.js} +1 -1
  9. package/dist/{chunk-A54NGT2K.js → chunk-APLNGCEZ.js} +2 -2
  10. package/dist/{chunk-G5WJMJV3.js → chunk-CXPBU5W4.js} +1 -1
  11. package/dist/{chunk-PHP45MO7.js → chunk-EO3JWEO5.js} +1 -1
  12. package/dist/chunk-GJO5NUVT.js +1 -0
  13. package/dist/chunk-GM7WFPGG.js +10 -0
  14. package/dist/{chunk-3S2XVDPN.js → chunk-GNKESMZT.js} +6 -6
  15. package/dist/chunk-JBARNYFR.js +23 -0
  16. package/dist/{chunk-TKQ2VFV5.js → chunk-JSHMFTSY.js} +1 -1
  17. package/dist/{chunk-VZBFQLCA.js → chunk-KZPZRHL6.js} +6 -6
  18. package/dist/{chunk-XRMV2GMB.js → chunk-LRTQXGJO.js} +236 -144
  19. package/dist/{chunk-EQ6S5SEZ.js → chunk-O3SA5SJ4.js} +1 -1
  20. package/dist/{chunk-GIWNHRTX.js → chunk-OSM3LZ2Y.js} +4 -4
  21. package/dist/{chunk-IVPA6NG6.js → chunk-PAGVSTVF.js} +348 -97
  22. package/dist/{chunk-Y4TZCE7R.js → chunk-SYW4BCGZ.js} +2 -2
  23. package/dist/{chunk-D25BNPTW.js → chunk-TMREBZBQ.js} +4 -4
  24. package/dist/{chunk-77KL2PMM.js → chunk-WJ4XTCOD.js} +2 -2
  25. package/dist/{chunk-L52N5FZZ.js → chunk-ZGTOZA2S.js} +6 -6
  26. package/dist/{composition-DIftf6iP.d.ts → composition-BGmpe5e4.d.ts} +51 -5
  27. package/dist/composition.d.ts +1 -1
  28. package/dist/composition.js +2 -1
  29. package/dist/{conversion-ChmGA8Ul.d.ts → conversion-8SQD4dqu.d.ts} +6 -6
  30. package/dist/conversion-LN6PFRZL.js +24 -0
  31. package/dist/convert.d.ts +2 -2
  32. package/dist/convert.js +9 -7
  33. package/dist/data.d.ts +1 -1
  34. package/dist/data.js +3 -2
  35. package/dist/{deck-report-5vwMGcQz.d.ts → deck-report-BaKgPh_N.d.ts} +2 -2
  36. package/dist/deck.d.ts +6 -6
  37. package/dist/deck.js +13 -11
  38. package/dist/diff.d.ts +1 -1
  39. package/dist/diff.js +6 -4
  40. package/dist/docs.js +6 -4
  41. package/dist/examples.d.ts +1 -1
  42. package/dist/examples.js +2 -0
  43. package/dist/{finding-l9dlQ2NF.d.ts → finding-Bh56sGtF.d.ts} +1 -1
  44. package/dist/font-policy.js +1 -0
  45. package/dist/format.js +3 -2
  46. package/dist/ids.d.ts +113 -0
  47. package/dist/ids.js +6 -0
  48. package/dist/index.d.ts +15 -12
  49. package/dist/index.js +24 -21
  50. package/dist/markdown.d.ts +4 -4
  51. package/dist/markdown.js +10 -8
  52. package/dist/node-engine.d.ts +8 -8
  53. package/dist/node-engine.js +24 -21
  54. package/dist/{pagination-D_kJe4jH.d.ts → pagination-BsUTsQvk.d.ts} +1 -1
  55. package/dist/pagination.d.ts +2 -2
  56. package/dist/pagination.js +6 -4
  57. package/dist/patch.d.ts +87 -4
  58. package/dist/patch.js +5 -3
  59. package/dist/precompiled.d.ts +2 -0
  60. package/dist/precompiled.js +7 -0
  61. package/dist/{presentation-Bwo8Ko6D.d.ts → presentation-C-nHZyMw.d.ts} +1 -1
  62. package/dist/repo-readme.js +2 -0
  63. package/dist/schema-engine-ajv.js +37 -0
  64. package/dist/schema-engine-precompiled.js +3 -0
  65. package/dist/schema.js +5 -3
  66. package/dist/schemas.js +2 -1
  67. package/dist/source.d.ts +1 -1
  68. package/dist/source.js +12 -10
  69. package/dist/spec/schemas/opf.schema.json +1 -1
  70. package/dist/spec-files.js +1 -0
  71. package/dist/symbol-font-encodings.js +2 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{validation-types-3ARvTsB0.d.ts → validation-types-6_jEaHeE.d.ts} +2 -2
  74. package/dist/validator.d.ts +5 -5
  75. package/dist/validator.js +5 -3
  76. package/dist/yaml.d.ts +4 -4
  77. package/dist/yaml.js +9 -7
  78. package/package.json +29 -8
  79. package/dist/conversion-IVKCKMCN.js +0 -21
@@ -1,7 +1,7 @@
1
- import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, chartSeriesPalette, normalizeHexColor, resolveChartColors, colorContrast, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, normalizeColorVariant, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, defaultSlideBackground, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-XRMV2GMB.js';
2
- import { schemas, schemaEntries } from './chunk-F4HFDP6I.js';
3
- import Ajv2020 from 'ajv/dist/2020.js';
4
- import addFormats from 'ajv-formats';
1
+ import { original } from './chunk-JBARNYFR.js';
2
+ import { schemas, schemaEntries } from './chunk-4ZM5G3XX.js';
3
+ import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, chartSeriesPalette, chartLeadFromScheme, normalizeHexColor, resolveChartColors, colorContrast, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, normalizeColorVariant, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, defaultSlideBackground, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-LRTQXGJO.js';
4
+ import { schemaEngine } from '#schema-engine';
5
5
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
6
6
 
7
7
  // src/catalog-schemas.ts
@@ -88,38 +88,9 @@ function unusedReferenceWarnings(value) {
88
88
  }
89
89
 
90
90
  // src/union-errors.ts
91
- var compiled = /* @__PURE__ */ new WeakMap();
92
- var origin = /* @__PURE__ */ new WeakMap();
93
91
  var isUnion = (error) => (error.keyword === "oneOf" || error.keyword === "anyOf") && Array.isArray(error.schema);
94
92
  var key = (error) => `${error.instancePath}\0${error.keyword}\0${error.message}\0${JSON.stringify(error.params)}`;
95
93
  var depth = (pointer4) => pointer4 ? pointer4.split("/").length - 1 : 0;
96
- var original = (node) => {
97
- let current = node;
98
- while (current && typeof current === "object" && origin.has(current)) current = origin.get(current);
99
- return current;
100
- };
101
- function qualify(node, rootId) {
102
- if (Array.isArray(node)) {
103
- const copy = node.map((item) => qualify(item, rootId));
104
- origin.set(copy, node);
105
- return copy;
106
- }
107
- if (node && typeof node === "object") {
108
- const copy = {};
109
- for (const [name, value] of Object.entries(node)) copy[name] = name === "$ref" && typeof value === "string" && value.startsWith("#") ? rootId + value : qualify(value, rootId);
110
- origin.set(copy, node);
111
- return copy;
112
- }
113
- return node;
114
- }
115
- function branchValidator(ajv2, rootId, branch) {
116
- let validate2 = compiled.get(branch);
117
- if (!validate2) {
118
- validate2 = ajv2.compile(qualify(branch, rootId));
119
- compiled.set(branch, validate2);
120
- }
121
- return validate2;
122
- }
123
94
  function branchLabel(branch, index) {
124
95
  if (typeof branch.$ref === "string") return branch.$ref.slice(branch.$ref.lastIndexOf("/") + 1);
125
96
  if (typeof branch.type === "string") return branch.type;
@@ -135,12 +106,12 @@ function describe(errors, base) {
135
106
  const rest = shallow.instancePath.split("/").slice(base + 1).join("/");
136
107
  return depth(shallow.instancePath) === base || !rest ? shallow.message ?? "does not match" : `${shallow.message} at /${rest}`;
137
108
  }
138
- function bestMatchErrors(ajv2, rootId, errors) {
109
+ function bestMatchErrors(engine, rootId, errors) {
139
110
  const unions = errors.filter(isUnion);
140
111
  if (!unions.length) return errors;
141
112
  const analyses = [];
142
113
  for (const union of unions) {
143
- const analysis = analyze(ajv2, rootId, union);
114
+ const analysis = analyze(engine, rootId, union);
144
115
  if (analysis) analyses.push(analysis);
145
116
  }
146
117
  if (!analyses.length) return errors;
@@ -156,19 +127,20 @@ function bestMatchErrors(ajv2, rootId, errors) {
156
127
  }
157
128
  return result;
158
129
  }
159
- function analyze(ajv2, rootId, union) {
130
+ function analyze(engine, rootId, union) {
160
131
  const branches = union.schema;
161
132
  const base = depth(union.instancePath);
162
133
  const results = [];
163
134
  for (const [index, branch] of branches.entries()) {
164
135
  if (!branch || typeof branch !== "object") return void 0;
165
- const validate2 = branchValidator(ajv2, rootId, branch);
136
+ const validate2 = engine.branch(branch, rootId);
137
+ if (!validate2) return void 0;
166
138
  if (validate2(union.data) === true) return void 0;
167
139
  const raw = (validate2.errors ?? []).map((error) => ({ ...error, instancePath: union.instancePath + error.instancePath }));
168
140
  results.push({ label: branchLabel(branch, index), errors: raw });
169
141
  }
170
142
  const span = /* @__PURE__ */ new Set([key(union), ...results.flatMap((result) => result.errors.map(key))]);
171
- const reduced = results.map((result) => ({ label: result.label, errors: bestMatchErrors(ajv2, rootId, result.errors) }));
143
+ const reduced = results.map((result) => ({ label: result.label, errors: bestMatchErrors(engine, rootId, result.errors) }));
172
144
  const mismatch = (error) => {
173
145
  const level = depth(error.instancePath) - base;
174
146
  return level === 0 && (error.keyword === "type" || isUnion(error)) || level === 1 && error.keyword === "const";
@@ -248,7 +220,6 @@ function validationDefinition(issue2) {
248
220
 
249
221
  // src/schema-check.ts
250
222
  var schemaNameByCatalogKind = catalogSchemaNames;
251
- var ajv;
252
223
  var promotedRegionKeySet = new Set(promotedRegionKeys);
253
224
  var rootPayloadFields = [
254
225
  "type",
@@ -326,23 +297,6 @@ var rowSpans = {
326
297
  "middle+bottom": [1, 2],
327
298
  "top+middle+bottom": [0, 1, 2]
328
299
  };
329
- function getAjv() {
330
- if (ajv) {
331
- return ajv;
332
- }
333
- const instance = new Ajv2020({
334
- allErrors: true,
335
- verbose: true,
336
- strict: false,
337
- allowUnionTypes: true
338
- });
339
- addFormats(instance);
340
- for (const schema of Object.values(schemas)) {
341
- instance.addSchema(schema);
342
- }
343
- ajv = instance;
344
- return instance;
345
- }
346
300
  function isSchemaName(value) {
347
301
  return typeof value === "string" && hasOwn(schemas, value);
348
302
  }
@@ -350,17 +304,12 @@ function isCatalogKind(value) {
350
304
  return typeof value === "string" && hasOwn(schemaNameByCatalogKind, value);
351
305
  }
352
306
  function resolveValidator(schemaOrKind) {
353
- const instance = getAjv();
354
307
  if (isSchemaName(schemaOrKind)) {
355
- const schema = schemas[schemaOrKind];
356
- const validator = instance.getSchema(schema.$id) ?? instance.compile(schema);
357
- return { validate: validator, schemaName: schemaOrKind };
308
+ return { validate: schemaEngine().root(schemaOrKind), schemaName: schemaOrKind };
358
309
  }
359
310
  if (isCatalogKind(schemaOrKind)) {
360
311
  const schemaName = schemaNameByCatalogKind[schemaOrKind];
361
- const schema = schemas[schemaName];
362
- const validator = instance.getSchema(schema.$id) ?? instance.compile(schema);
363
- return { validate: validator, schemaName, catalogKind: schemaOrKind };
312
+ return { validate: schemaEngine().root(schemaName), schemaName, catalogKind: schemaOrKind };
364
313
  }
365
314
  throw new TypeError(`Unknown schema or catalog kind ${JSON.stringify(schemaOrKind)}.`);
366
315
  }
@@ -983,7 +932,6 @@ function typedVariableErrors(errors, value) {
983
932
  if (!errors.some((error) => error.instancePath.startsWith("/variables/"))) return errors;
984
933
  const variables = value.variables;
985
934
  const kept = errors.filter((error) => !error.instancePath.startsWith("/variables/"));
986
- const schemaId = schemas.presentation.$id;
987
935
  for (const [id, entry2] of Object.entries(variables)) {
988
936
  if (typeof entry2 === "string") {
989
937
  if (!hexColorPattern.test(entry2)) kept.push(variableIssue(id, "", "type", "must be a hex color string or a variable object", {}));
@@ -999,7 +947,7 @@ function typedVariableErrors(errors, value) {
999
947
  continue;
1000
948
  }
1001
949
  const definition = `${kind.charAt(0).toUpperCase()}${kind.slice(1)}Variable`;
1002
- const check = getAjv().getSchema(`${schemaId}#/$defs/${definition}`);
950
+ const check = schemaEngine().definition("presentation", definition);
1003
951
  if (!check || check(entry2) === true) continue;
1004
952
  for (const error of check.errors ?? []) {
1005
953
  kept.push({ ...error, instancePath: `/variables/${id.replaceAll("~", "~0").replaceAll("/", "~1")}${error.instancePath}`, schemaPath: `#/$defs/${definition}${error.schemaPath.replace(/^#/, "")}` });
@@ -1020,7 +968,6 @@ function pointerValue(root, pointer4) {
1020
968
  var datasetTableMessage = (field) => `'${field}' is not allowed on a dataset-backed table: it takes its headers, rows and column formats from the dataset`;
1021
969
  function dataUnionErrors(errors, value) {
1022
970
  let kept = errors;
1023
- const schemaId = schemas.presentation.$id;
1024
971
  for (const union of errors) {
1025
972
  if (union.keyword !== "oneOf") continue;
1026
973
  const chartData = /\/chart\/data$/.test(union.instancePath) && /\/properties\/data\/oneOf$/.test(union.schemaPath);
@@ -1029,7 +976,7 @@ function dataUnionErrors(errors, value) {
1029
976
  const target = pointerValue(value, union.instancePath);
1030
977
  if (!isRecord(target)) continue;
1031
978
  const branch = chartData ? hasOwn(target, "dataset") ? "DatasetRef" : hasOwn(target, "rows") ? "ChartData" : void 0 : hasOwn(target, "value") ? "StyledTableCell" : hasOwn(target, "name") ? "DataColumn" : void 0;
1032
- const check = branch ? getAjv().getSchema(`${schemaId}#/$defs/${branch}`) : void 0;
979
+ const check = branch ? schemaEngine().definition("presentation", branch) : void 0;
1033
980
  if (!check || check(target) === true) continue;
1034
981
  const branchErrors = (check.errors ?? []).map((error) => ({ ...error, instancePath: `${union.instancePath}${error.instancePath}`, schemaPath: `#/$defs/${branch}${error.schemaPath.replace(/^#/, "")}` }));
1035
982
  const prefix = `${union.instancePath}/`;
@@ -1076,7 +1023,7 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options2 =
1076
1023
  const valid = resolved.validate(subject) === true;
1077
1024
  const ajvErrors = valid ? [] : typedVariableErrors(resolved.validate.errors ?? [], subject);
1078
1025
  const specific = resolved.schemaName === "presentation" ? dataUnionErrors(ajvErrors, subject) : ajvErrors;
1079
- const mapped = bestMatchErrors(getAjv(), schemas[resolved.schemaName].$id, specific).map(toIssue);
1026
+ const mapped = bestMatchErrors(schemaEngine(), schemas[resolved.schemaName].$id, specific).map(toIssue);
1080
1027
  const errors = mapped;
1081
1028
  if (resolved.schemaName === "layout" && valid) for (const issue2 of layoutTemplateIssues(subject)) errors.push(semanticIssue(issue2.path, issue2.message, { code: issue2.code }));
1082
1029
  const warnings = [];
@@ -2457,7 +2404,8 @@ var chartRules = [
2457
2404
  const count = slices ? data.rows.length : data.columns.length - 1;
2458
2405
  if (count < 2) continue;
2459
2406
  const surface = slide.design.colors.surface;
2460
- const palette = chartPaletteForFill(surface, context.chartPalette ?? chartSeriesPalette(slide.design.colorScheme, chart.colors, chartColorContext(slide.design)));
2407
+ const paletteContext = chartColorContext(slide.design);
2408
+ const palette = chartPaletteForFill(surface, context.chartPalette ?? chartSeriesPalette(slide.design.colorScheme, chart.colors, paletteContext), { legibleLead: context.chartPalette === void 0 && chartLeadFromScheme(chart.colors, slide.design.colorScheme, paletteContext) });
2461
2409
  if (!palette.length) continue;
2462
2410
  const used = Array.from({ length: Math.min(count, 24) }, (_, i) => palette[i % palette.length]);
2463
2411
  const pairs = [];
@@ -3526,6 +3474,157 @@ function contractFindings(document, contracts) {
3526
3474
  return findings;
3527
3475
  }
3528
3476
 
3477
+ // src/document-hash.ts
3478
+ function canonicalJson(value) {
3479
+ const stack = [];
3480
+ const write = (item) => {
3481
+ if (item === null || typeof item === "boolean" || typeof item === "string") return JSON.stringify(item);
3482
+ if (typeof item === "number") return JSON.stringify(item);
3483
+ if (typeof item === "bigint") throw new TypeError("A BigInt is not JSON.");
3484
+ if (typeof item !== "object") return void 0;
3485
+ if (stack.includes(item)) throw new TypeError("A cyclic value has no canonical JSON.");
3486
+ stack.push(item);
3487
+ let text2;
3488
+ if (Array.isArray(item)) {
3489
+ text2 = `[${item.map((entry2) => write(entry2) ?? "null").join(",")}]`;
3490
+ } else {
3491
+ const record = item;
3492
+ const members = [];
3493
+ for (const key2 of Object.keys(record).sort()) {
3494
+ const member = write(record[key2]);
3495
+ if (member !== void 0) members.push(`${JSON.stringify(key2)}:${member}`);
3496
+ }
3497
+ text2 = `{${members.join(",")}}`;
3498
+ }
3499
+ stack.pop();
3500
+ return text2;
3501
+ };
3502
+ return write(value) ?? "null";
3503
+ }
3504
+ var K = new Uint32Array([
3505
+ 1116352408,
3506
+ 1899447441,
3507
+ 3049323471,
3508
+ 3921009573,
3509
+ 961987163,
3510
+ 1508970993,
3511
+ 2453635748,
3512
+ 2870763221,
3513
+ 3624381080,
3514
+ 310598401,
3515
+ 607225278,
3516
+ 1426881987,
3517
+ 1925078388,
3518
+ 2162078206,
3519
+ 2614888103,
3520
+ 3248222580,
3521
+ 3835390401,
3522
+ 4022224774,
3523
+ 264347078,
3524
+ 604807628,
3525
+ 770255983,
3526
+ 1249150122,
3527
+ 1555081692,
3528
+ 1996064986,
3529
+ 2554220882,
3530
+ 2821834349,
3531
+ 2952996808,
3532
+ 3210313671,
3533
+ 3336571891,
3534
+ 3584528711,
3535
+ 113926993,
3536
+ 338241895,
3537
+ 666307205,
3538
+ 773529912,
3539
+ 1294757372,
3540
+ 1396182291,
3541
+ 1695183700,
3542
+ 1986661051,
3543
+ 2177026350,
3544
+ 2456956037,
3545
+ 2730485921,
3546
+ 2820302411,
3547
+ 3259730800,
3548
+ 3345764771,
3549
+ 3516065817,
3550
+ 3600352804,
3551
+ 4094571909,
3552
+ 275423344,
3553
+ 430227734,
3554
+ 506948616,
3555
+ 659060556,
3556
+ 883997877,
3557
+ 958139571,
3558
+ 1322822218,
3559
+ 1537002063,
3560
+ 1747873779,
3561
+ 1955562222,
3562
+ 2024104815,
3563
+ 2227730452,
3564
+ 2361852424,
3565
+ 2428436474,
3566
+ 2756734187,
3567
+ 3204031479,
3568
+ 3329325298
3569
+ ]);
3570
+ function sha256Hex(bytes) {
3571
+ const length = bytes.length;
3572
+ const padded = new Uint8Array(length + 9 + 63 >> 6 << 6);
3573
+ padded.set(bytes);
3574
+ padded[length] = 128;
3575
+ const view = new DataView(padded.buffer);
3576
+ view.setUint32(padded.length - 8, Math.floor(length / 536870912), false);
3577
+ view.setUint32(padded.length - 4, length << 3 >>> 0, false);
3578
+ const h = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
3579
+ const w = new Uint32Array(64);
3580
+ for (let offset = 0; offset < padded.length; offset += 64) {
3581
+ for (let i = 0; i < 16; i += 1) w[i] = view.getUint32(offset + i * 4, false);
3582
+ for (let i = 16; i < 64; i += 1) {
3583
+ const a2 = w[i - 15], b2 = w[i - 2];
3584
+ const s0 = (a2 >>> 7 | a2 << 25) ^ (a2 >>> 18 | a2 << 14) ^ a2 >>> 3;
3585
+ const s1 = (b2 >>> 17 | b2 << 15) ^ (b2 >>> 19 | b2 << 13) ^ b2 >>> 10;
3586
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1 | 0;
3587
+ }
3588
+ let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];
3589
+ for (let i = 0; i < 64; i += 1) {
3590
+ const s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
3591
+ const choose = e & f ^ ~e & g;
3592
+ const t1 = hh + s1 + choose + K[i] + w[i] | 0;
3593
+ const s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
3594
+ const majority = a & b ^ a & c ^ b & c;
3595
+ const t2 = s0 + majority | 0;
3596
+ hh = g;
3597
+ g = f;
3598
+ f = e;
3599
+ e = d + t1 | 0;
3600
+ d = c;
3601
+ c = b;
3602
+ b = a;
3603
+ a = t1 + t2 | 0;
3604
+ }
3605
+ h[0] = h[0] + a | 0;
3606
+ h[1] = h[1] + b | 0;
3607
+ h[2] = h[2] + c | 0;
3608
+ h[3] = h[3] + d | 0;
3609
+ h[4] = h[4] + e | 0;
3610
+ h[5] = h[5] + f | 0;
3611
+ h[6] = h[6] + g | 0;
3612
+ h[7] = h[7] + hh | 0;
3613
+ }
3614
+ let hex = "";
3615
+ for (const word of h) hex += word.toString(16).padStart(8, "0");
3616
+ return hex;
3617
+ }
3618
+ function documentHash(document) {
3619
+ return `sha256:${sha256Hex(new TextEncoder().encode(canonicalJson(document)))}`;
3620
+ }
3621
+ var HASH_PATTERN = /^(?:sha256:)?([0-9a-fA-F]{64})$/;
3622
+ function normalizeDocumentHash(value) {
3623
+ if (typeof value !== "string") return void 0;
3624
+ const match2 = HASH_PATTERN.exec(value);
3625
+ return match2 ? `sha256:${match2[1].toLowerCase()}` : void 0;
3626
+ }
3627
+
3529
3628
  // src/patch.ts
3530
3629
  var OPFPatchError = class extends Error {
3531
3630
  code;
@@ -3552,6 +3651,18 @@ var OPFPatchValidationError = class extends OPFPatchError {
3552
3651
  this.validation = validation;
3553
3652
  }
3554
3653
  };
3654
+ var OPFPatchHashError = class extends OPFPatchError {
3655
+ /** The hash the caller expected, in the form `documentHash` returns. */
3656
+ expected;
3657
+ /** The hash of the document the patch was given. */
3658
+ actual;
3659
+ constructor(expected, actual) {
3660
+ super("patch-hash-mismatch", "The document changed since it was read: its hash is not the expected hash. Nothing was applied.");
3661
+ this.name = "OPFPatchHashError";
3662
+ this.expected = expected;
3663
+ this.actual = actual;
3664
+ }
3665
+ };
3555
3666
  var own2 = (value, key2) => Object.prototype.hasOwnProperty.call(value, key2);
3556
3667
  var isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
3557
3668
  var clone = (value) => structuredClone(value);
@@ -3597,13 +3708,83 @@ function splitPointer2(pointer4) {
3597
3708
  if (!tokens.length) throw new OPFPatchError("invalid-json-pointer", "The root pointer has no parent.", { path: pointer4 });
3598
3709
  return { parent: formatPointer(tokens.slice(0, -1)), token: tokens[tokens.length - 1] };
3599
3710
  }
3711
+ var isIdToken = (token) => token.length > 1 && token.startsWith("@");
3712
+ var idToken = (id) => `@${id}`;
3713
+ var INDEX_TOKEN = /^(0|[1-9][0-9]*)$/;
3714
+ function indexOfId(array, id) {
3715
+ let found = -1;
3716
+ for (let i = 0; i < array.length; i += 1) {
3717
+ const item = array[i];
3718
+ if (isObject(item) && own2(item, "id") && item.id === id) {
3719
+ if (found !== -1) return -2;
3720
+ found = i;
3721
+ }
3722
+ }
3723
+ return found;
3724
+ }
3725
+ function uniqueIdAt(array, at2) {
3726
+ const item = array[at2];
3727
+ if (!isObject(item) || !own2(item, "id") || typeof item.id !== "string" || item.id === "") return void 0;
3728
+ return indexOfId(array, item.id) === at2 ? item.id : void 0;
3729
+ }
3730
+ function resolveTokens(document, tokens, pointer4) {
3731
+ const out = [];
3732
+ let cursor = document, walking = true;
3733
+ for (const token of tokens) {
3734
+ let resolved = token;
3735
+ if (walking && Array.isArray(cursor) && isIdToken(token)) {
3736
+ const at2 = indexOfId(cursor, token.slice(1));
3737
+ if (at2 === -1) throw new OPFPatchError("patch-id-not-found", `No element of ${formatPointer(out) || "/"} has the id '${token.slice(1)}'.`, { path: pointer4 });
3738
+ if (at2 === -2) throw new OPFPatchError("patch-id-ambiguous", `More than one element of ${formatPointer(out) || "/"} has the id '${token.slice(1)}'.`, { path: pointer4 });
3739
+ resolved = String(at2);
3740
+ }
3741
+ out.push(resolved);
3742
+ if (walking) {
3743
+ const next = readPointer(cursor, [resolved]);
3744
+ if (next.found) cursor = next.value;
3745
+ else walking = false;
3746
+ }
3747
+ }
3748
+ return out;
3749
+ }
3750
+ function resolveIdPointer(document, pointer4) {
3751
+ const tokens = parsePointer(pointer4);
3752
+ return tokens.some(isIdToken) ? formatPointer(resolveTokens(document, tokens, pointer4)) : pointer4;
3753
+ }
3754
+ function idTokens(document, tokens, convertLast) {
3755
+ const out = [];
3756
+ let cursor = document, walking = true;
3757
+ tokens.forEach((token, i) => {
3758
+ let named = token;
3759
+ if (walking && Array.isArray(cursor) && INDEX_TOKEN.test(token) && (convertLast || i < tokens.length - 1)) {
3760
+ const id = uniqueIdAt(cursor, Number(token));
3761
+ if (id !== void 0) named = idToken(id);
3762
+ }
3763
+ out.push(named);
3764
+ if (walking) {
3765
+ const next = readPointer(cursor, [token]);
3766
+ if (next.found) cursor = next.value;
3767
+ else walking = false;
3768
+ }
3769
+ });
3770
+ return out;
3771
+ }
3772
+ function toIdPointer(document, pointer4, options2 = {}) {
3773
+ return formatPointer(idTokens(document, resolveTokens(document, parsePointer(pointer4), pointer4), !options2.keepLast));
3774
+ }
3600
3775
  function readPointer(document, pointer4) {
3601
3776
  const tokens = typeof pointer4 === "string" ? parsePointer(pointer4) : pointer4;
3602
3777
  let value = document;
3603
3778
  for (const token of tokens) {
3604
3779
  if (Array.isArray(value)) {
3605
- if (!/^(0|[1-9][0-9]*)$/.test(token)) return { found: false };
3606
- const at2 = Number(token);
3780
+ let at2;
3781
+ if (isIdToken(token)) {
3782
+ at2 = indexOfId(value, token.slice(1));
3783
+ if (at2 < 0) return { found: false };
3784
+ } else {
3785
+ if (!INDEX_TOKEN.test(token)) return { found: false };
3786
+ at2 = Number(token);
3787
+ }
3607
3788
  if (!Number.isSafeInteger(at2) || at2 >= value.length) return { found: false };
3608
3789
  value = value[at2];
3609
3790
  } else if (value && typeof value === "object" && own2(value, token)) value = value[token];
@@ -3622,7 +3803,7 @@ function getAtPointer(document, pointer4) {
3622
3803
  var hasPointer = (document, pointer4) => readPointer(document, pointer4).found;
3623
3804
  function arrayIndex(token, length, allowEnd, path) {
3624
3805
  if (allowEnd && token === "-") return length;
3625
- if (!/^(0|[1-9][0-9]*)$/.test(token)) throw new OPFPatchError("invalid-array-index", `Invalid array index: ${token}`, { path });
3806
+ if (!INDEX_TOKEN.test(token)) throw new OPFPatchError("invalid-array-index", `Invalid array index: ${token}`, { path });
3626
3807
  const at2 = Number(token);
3627
3808
  if (!Number.isSafeInteger(at2) || at2 > length || !allowEnd && at2 >= length) throw new OPFPatchError("invalid-array-index", `Array index out of bounds: ${token}`, { path });
3628
3809
  return at2;
@@ -3667,38 +3848,42 @@ function parentOf(document, tokens, path) {
3667
3848
  }
3668
3849
  var NONE = /* @__PURE__ */ Symbol("none");
3669
3850
  function addAt(document, path, value) {
3670
- const tokens = parsePointer(path);
3671
- if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) } };
3851
+ const tokens = resolveTokens(document, parsePointer(path), path);
3852
+ if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) }, pointer: "" };
3672
3853
  const { parent, key: key2 } = parentOf(document, tokens, path);
3673
3854
  if (Array.isArray(parent)) {
3674
3855
  const at2 = arrayIndex(key2, parent.length, true, path);
3675
3856
  parent.splice(at2, 0, clone(value));
3676
- return { document, inverse: { op: "remove", path: formatPointer([...tokens.slice(0, -1), at2]) } };
3857
+ const pointer5 = formatPointer([...tokens.slice(0, -1), at2]);
3858
+ return { document, inverse: { op: "remove", path: pointer5 }, pointer: pointer5 };
3677
3859
  }
3678
3860
  const record = parent;
3679
3861
  const previous = own2(record, key2) ? record[key2] : NONE;
3680
3862
  Object.defineProperty(record, key2, { value: clone(value), enumerable: true, configurable: true, writable: true });
3681
- return { document, inverse: previous === NONE ? { op: "remove", path } : { op: "replace", path, value: clone(previous) } };
3863
+ const pointer4 = formatPointer(tokens);
3864
+ return { document, inverse: previous === NONE ? { op: "remove", path: pointer4 } : { op: "replace", path: pointer4, value: clone(previous) }, pointer: pointer4 };
3682
3865
  }
3683
3866
  function replaceAt(document, path, value) {
3684
- const tokens = parsePointer(path);
3685
- if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) } };
3867
+ const tokens = resolveTokens(document, parsePointer(path), path);
3868
+ if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) }, pointer: "" };
3686
3869
  const { parent, key: key2 } = parentOf(document, tokens, path);
3687
3870
  let previous;
3871
+ let pointer4 = formatPointer(tokens);
3688
3872
  if (Array.isArray(parent)) {
3689
3873
  const at2 = arrayIndex(key2, parent.length, false, path);
3690
3874
  previous = parent[at2];
3691
3875
  parent[at2] = clone(value);
3876
+ pointer4 = formatPointer([...tokens.slice(0, -1), at2]);
3692
3877
  } else {
3693
3878
  const record = parent;
3694
3879
  if (!own2(record, key2)) throw new OPFPatchError("patch-path-missing", `Patch path does not exist: ${path}.`, { path });
3695
3880
  previous = record[key2];
3696
3881
  Object.defineProperty(record, key2, { value: clone(value), enumerable: true, configurable: true, writable: true });
3697
3882
  }
3698
- return { document, inverse: { op: "replace", path, value: clone(previous) } };
3883
+ return { document, inverse: { op: "replace", path: pointer4, value: clone(previous) }, pointer: pointer4 };
3699
3884
  }
3700
3885
  function removeAt(document, path) {
3701
- const tokens = parsePointer(path);
3886
+ const tokens = resolveTokens(document, parsePointer(path), path);
3702
3887
  if (!tokens.length) throw new OPFPatchError("patch-root-remove", "The document root cannot be removed.", { path });
3703
3888
  const { parent, key: key2 } = parentOf(document, tokens, path);
3704
3889
  let previous;
@@ -3706,60 +3891,71 @@ function removeAt(document, path) {
3706
3891
  const at2 = arrayIndex(key2, parent.length, false, path);
3707
3892
  previous = parent[at2];
3708
3893
  parent.splice(at2, 1);
3709
- return { document, inverse: { op: "add", path: formatPointer([...tokens.slice(0, -1), at2]), value: clone(previous) } };
3894
+ const pointer5 = formatPointer([...tokens.slice(0, -1), at2]);
3895
+ return { document, inverse: { op: "add", path: pointer5, value: clone(previous) }, pointer: pointer5 };
3710
3896
  }
3711
3897
  const record = parent;
3712
3898
  if (!own2(record, key2)) throw new OPFPatchError("patch-path-missing", `Patch path does not exist: ${path}.`, { path });
3713
3899
  previous = record[key2];
3714
3900
  delete record[key2];
3715
- return { document, inverse: { op: "add", path, value: clone(previous) } };
3901
+ const pointer4 = formatPointer(tokens);
3902
+ return { document, inverse: { op: "add", path: pointer4, value: clone(previous) }, pointer: pointer4 };
3716
3903
  }
3717
3904
  function applyOne(document, operation) {
3718
3905
  switch (operation.op) {
3719
3906
  case "add": {
3720
3907
  const r = addAt(document, operation.path, operation.value);
3721
- return { document: r.document, inverse: [r.inverse] };
3908
+ return { document: r.document, inverse: [r.inverse], resolved: { op: "add", path: r.pointer, value: operation.value } };
3722
3909
  }
3723
3910
  case "replace": {
3724
3911
  const r = replaceAt(document, operation.path, operation.value);
3725
- return { document: r.document, inverse: [r.inverse] };
3912
+ return { document: r.document, inverse: [r.inverse], resolved: { op: "replace", path: r.pointer, value: operation.value } };
3726
3913
  }
3727
3914
  case "remove": {
3728
3915
  const r = removeAt(document, operation.path);
3729
- return { document: r.document, inverse: [r.inverse] };
3916
+ return { document: r.document, inverse: [r.inverse], resolved: { op: "remove", path: r.pointer } };
3730
3917
  }
3731
3918
  case "test": {
3732
3919
  const found = readPointer(document, operation.path);
3733
3920
  if (!found.found || !jsonEqual(found.value, operation.value)) throw new OPFPatchError("patch-test-failed", `Test failed at ${operation.path || "/"}: the value changed.`, { path: operation.path });
3734
- return { document, inverse: [] };
3921
+ return { document, inverse: [], resolved: { op: "test", path: resolveIdPointer(document, operation.path), value: operation.value } };
3735
3922
  }
3736
3923
  case "copy": {
3737
- const value = clone(getAtPointer(document, operation.from));
3924
+ const from = resolveIdPointer(document, operation.from);
3925
+ const value = clone(getAtPointer(document, from));
3738
3926
  const r = addAt(document, operation.path, value);
3739
- return { document: r.document, inverse: [r.inverse] };
3927
+ return { document: r.document, inverse: [r.inverse], resolved: { op: "copy", from, path: r.pointer } };
3740
3928
  }
3741
3929
  case "move": {
3742
- const from = parsePointer(operation.from), to = parsePointer(operation.path);
3930
+ const from = resolveTokens(document, parsePointer(operation.from), operation.from);
3931
+ let to;
3932
+ try {
3933
+ to = resolveTokens(document, parsePointer(operation.path), operation.path);
3934
+ } catch {
3935
+ to = parsePointer(operation.path);
3936
+ }
3743
3937
  const value = clone(getAtPointer(document, from));
3744
- if (from.length === to.length && from.every((token, i) => token === to[i])) return { document, inverse: [] };
3938
+ if (from.length === to.length && from.every((token, i) => token === to[i])) return { document, inverse: [], resolved: { op: "move", from: formatPointer(from), path: formatPointer(to) } };
3745
3939
  if (to.length > from.length && from.every((token, i) => to[i] === token)) {
3746
3940
  throw new OPFPatchError("patch-invalid-move", "Cannot move a value into its descendant.", { path: operation.path });
3747
3941
  }
3748
- const removed = removeAt(document, operation.from);
3942
+ const removed = removeAt(document, formatPointer(from));
3749
3943
  const added = addAt(removed.document, operation.path, value);
3750
- return { document: added.document, inverse: [added.inverse, removed.inverse] };
3944
+ return { document: added.document, inverse: [added.inverse, removed.inverse], resolved: { op: "move", from: removed.pointer, path: added.pointer } };
3751
3945
  }
3752
3946
  }
3753
3947
  }
3754
- function applyPatchWithInverse(document, patch2, options2 = {}) {
3948
+ function walkPatch(document, patch2, hooks = {}) {
3755
3949
  const operations = normalizePatch(patch2);
3756
3950
  let working = clone(document);
3757
3951
  const inverse = [];
3758
3952
  for (const [index, operation] of operations.entries()) {
3759
3953
  try {
3760
- const step = applyOne(working, operation);
3761
- working = step.document;
3762
- inverse.unshift(...step.inverse);
3954
+ hooks.before?.(index, operation, working);
3955
+ const applied = applyOne(working, operation);
3956
+ working = applied.document;
3957
+ inverse.unshift(...applied.inverse);
3958
+ hooks.after?.({ index, operation, resolved: applied.resolved, inverse: applied.inverse }, working);
3763
3959
  } catch (error) {
3764
3960
  if (error instanceof OPFPatchError && error.index === void 0) {
3765
3961
  const wrapped = new OPFPatchError(error.code, `Operation ${index}: ${error.message}`, { path: error.path, index, operation });
@@ -3768,6 +3964,17 @@ function applyPatchWithInverse(document, patch2, options2 = {}) {
3768
3964
  throw error;
3769
3965
  }
3770
3966
  }
3967
+ return { document: working, operations, inverse };
3968
+ }
3969
+ function assertExpectedHash(document, expectHash) {
3970
+ const expected = normalizeDocumentHash(expectHash);
3971
+ if (expected === void 0) throw new OPFPatchError("invalid-expect-hash", "expectHash must be a document hash: the value documentHash(document) returns.");
3972
+ const actual = documentHash(document);
3973
+ if (actual !== expected) throw new OPFPatchHashError(expected, actual);
3974
+ }
3975
+ function applyPatchWithInverse(document, patch2, options2 = {}) {
3976
+ if (options2.expectHash !== void 0) assertExpectedHash(document, options2.expectHash);
3977
+ const { document: working, operations, inverse } = walkPatch(document, patch2);
3771
3978
  const result = { presentation: working, inverse, patch: operations };
3772
3979
  if (options2.validate) {
3773
3980
  const validator = typeof options2.validate === "function" ? options2.validate : (value) => validate(value, { only: options2.strict ? ["format", "references"] : ["format"] });
@@ -3783,6 +3990,50 @@ function applyPatch(document, patch2, options2 = {}) {
3783
3990
  function invertPatch(document, patch2) {
3784
3991
  return applyPatchWithInverse(document, patch2).inverse;
3785
3992
  }
3993
+ function resolveIdPatch(document, patch2) {
3994
+ const resolved = [];
3995
+ walkPatch(document, patch2, { after: (step) => {
3996
+ resolved.push(step.resolved);
3997
+ } });
3998
+ return resolved;
3999
+ }
4000
+ function toIdPatch(document, patch2) {
4001
+ const out = [];
4002
+ const pending = /* @__PURE__ */ new Map();
4003
+ walkPatch(document, patch2, {
4004
+ before: (index, operation, working) => {
4005
+ const named = (pointer4) => toIdPointer(working, pointer4);
4006
+ switch (operation.op) {
4007
+ case "replace":
4008
+ case "remove":
4009
+ case "test":
4010
+ pending.set(index, { ...operation, path: named(operation.path) });
4011
+ break;
4012
+ case "copy":
4013
+ pending.set(index, { ...operation, from: named(operation.from), path: toIdPointer(working, operation.path, { keepLast: true }) });
4014
+ break;
4015
+ case "add":
4016
+ pending.set(index, { ...operation, path: toIdPointer(working, operation.path, { keepLast: true }) });
4017
+ break;
4018
+ case "move":
4019
+ pending.set(index, { ...operation, from: named(operation.from) });
4020
+ break;
4021
+ }
4022
+ },
4023
+ after: (step, working) => {
4024
+ const early = pending.get(step.index);
4025
+ pending.delete(step.index);
4026
+ if (step.operation.op !== "move") {
4027
+ out.push(early);
4028
+ return;
4029
+ }
4030
+ const appended = parsePointer(step.operation.path).at(-1) === "-";
4031
+ const destination = toIdPointer(working, step.resolved.path, { keepLast: true });
4032
+ out.push({ ...early, path: appended ? formatPointer([...parsePointer(destination).slice(0, -1), "-"]) : destination });
4033
+ }
4034
+ });
4035
+ return out;
4036
+ }
3786
4037
 
3787
4038
  // src/schema-catalog-kinds.ts
3788
4039
  var schemaCatalogKinds = {
@@ -7457,4 +7708,4 @@ function assertValidCatalogRecord(kind, value) {
7457
7708
  if (!report2.valid) throw new OPFValidationError(report2);
7458
7709
  }
7459
7710
 
7460
- export { BLOCK_AS_VALUES, BLOCK_OPTION_KEYS, CATALOG_SCHEMA, COMMON_KEYS, DEFAULT_VALIDATION_THRESHOLDS, FENCE_ATTRIBUTES, FENCE_KINDS, LINE_KEYWORDS, METRIC_KEYS, OPFPatchError, OPFPatchValidationError, OPFValidationError, PATTERN_PHRASES, SLIDE_OPTION_KEYS, SOURCE_EXPLAIN, SPAN_FLAGS, SPAN_VALUES, UNRANKED_PRIORITY, activeSchema, analyzeJsonSyntax, applyPatch, applyPatchWithInverse, applyTextEdits, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogKindAt, catalogSchemaNames, createLocator, createSchemaValue, describeField, describeFinding, distance2 as distance, escapePointerToken, findValidationRule, formatPointer, getAtPointer, hasPointer, invertPatch, isPromotedRegionKey, jsonEqual, listSchemaFields, nearest, normalizePatch, parsePointer, parseSource, pointerFromPath, readPointer, rememberByText, resolveSchema, resolveSlideContext, resolveSlideScriptMeasurement, ruleDocumentation, schemaAtPath, schemaCatalogKinds, schemaLabel, schemaType, schemaVariants, segmentsOf, splitOpfPath, splitPointer2 as splitPointer, starterValue, unescapePointerToken, us, validate, validateCatalogRecord, validationCategories, validationRules };
7711
+ export { BLOCK_AS_VALUES, BLOCK_OPTION_KEYS, CATALOG_SCHEMA, COMMON_KEYS, DEFAULT_VALIDATION_THRESHOLDS, FENCE_ATTRIBUTES, FENCE_KINDS, LINE_KEYWORDS, METRIC_KEYS, OPFPatchError, OPFPatchHashError, OPFPatchValidationError, OPFValidationError, PATTERN_PHRASES, SLIDE_OPTION_KEYS, SOURCE_EXPLAIN, SPAN_FLAGS, SPAN_VALUES, UNRANKED_PRIORITY, activeSchema, analyzeJsonSyntax, applyPatch, applyPatchWithInverse, applyTextEdits, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogKindAt, catalogSchemaNames, createLocator, createSchemaValue, describeField, describeFinding, distance2 as distance, documentHash, escapePointerToken, findValidationRule, formatPointer, getAtPointer, hasPointer, idToken, invertPatch, isIdToken, isPromotedRegionKey, jsonEqual, listSchemaFields, nearest, normalizePatch, parsePointer, parseSource, pointerFromPath, readPointer, rememberByText, resolveIdPatch, resolveIdPointer, resolveSchema, resolveSlideContext, resolveSlideScriptMeasurement, ruleDocumentation, schemaAtPath, schemaCatalogKinds, schemaLabel, schemaType, schemaVariants, segmentsOf, splitOpfPath, splitPointer2 as splitPointer, starterValue, toIdPatch, toIdPointer, unescapePointerToken, us, validate, validateCatalogRecord, validationCategories, validationRules, walkPatch };