@openpresentation/opf 0.26.4 → 0.26.5

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 (38) hide show
  1. package/README.md +2 -0
  2. package/dist/browser.js +13 -12
  3. package/dist/{chunk-XFOOKVRI.js → chunk-CIWC7DRZ.js} +1977 -373
  4. package/dist/{chunk-O6BY3AU2.js → chunk-DXTPLVAK.js} +1 -1
  5. package/dist/{chunk-QH2W6HIM.js → chunk-EFMR32A5.js} +1 -1
  6. package/dist/{chunk-6NBTZYYD.js → chunk-HPEZCAYC.js} +1 -1
  7. package/dist/{chunk-ZSZDVIDV.js → chunk-K5Q3LLYK.js} +2 -2
  8. package/dist/chunk-KPALXWX3.js +712 -0
  9. package/dist/{chunk-6IXSHIAL.js → chunk-L7YEVQHP.js} +6 -6
  10. package/dist/chunk-MD3CJ5YJ.js +452 -0
  11. package/dist/{chunk-MN7DRK57.js → chunk-MLH5LIGF.js} +11 -21
  12. package/dist/{chunk-EDCRBTJF.js → chunk-STIYINEL.js} +3 -3
  13. package/dist/{chunk-H5QC3X3K.js → chunk-TAV5S2JG.js} +1 -1
  14. package/dist/{chunk-NZLW6FBQ.js → chunk-UFEWQDQI.js} +1 -1
  15. package/dist/{chunk-GWJBZ4LC.js → chunk-UY6SQVR2.js} +5 -5
  16. package/dist/{chunk-PIO73BQ6.js → chunk-WAZDP2WS.js} +11 -20
  17. package/dist/{conversion-DSafOGzh.d.ts → conversion-C7o2EpDb.d.ts} +2 -2
  18. package/dist/conversion-PMJWLHDV.js +21 -0
  19. package/dist/convert.js +5 -5
  20. package/dist/deck.js +8 -8
  21. package/dist/diff.js +2 -2
  22. package/dist/docs.js +3 -3
  23. package/dist/explain-JSuESV-Y.d.ts +71 -0
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.js +17 -16
  26. package/dist/markdown.js +6 -6
  27. package/dist/node-engine.d.ts +2 -2
  28. package/dist/node-engine.js +17 -16
  29. package/dist/pagination.js +2 -2
  30. package/dist/patch.js +1 -1
  31. package/dist/schema.js +1 -1
  32. package/dist/source.d.ts +83 -2
  33. package/dist/source.js +10 -9
  34. package/dist/validator.js +1 -1
  35. package/dist/yaml.js +4 -4
  36. package/package.json +5 -5
  37. package/dist/chunk-QAEBYDKY.js +0 -192
  38. package/dist/conversion-QIAJM6DQ.js +0 -20
@@ -22,8 +22,8 @@ var catalogSchemaNames = {
22
22
  };
23
23
 
24
24
  // src/annotation-validation.ts
25
- var issue = (path, message, params) => ({ path, message, keyword: "opf", schemaPath: "#/x-opf-semantics", params });
26
- var pointer = (dotted) => `/${dotted.split(".").map((part) => part.replaceAll("~", "~0").replaceAll("/", "~1")).join("/")}`;
25
+ var issue = (path, message, params2) => ({ path, message, keyword: "opf", schemaPath: "#/x-opf-semantics", params: params2 });
26
+ var pointer = (dotted2) => `/${dotted2.split(".").map((part) => part.replaceAll("~", "~0").replaceAll("/", "~1")).join("/")}`;
27
27
  function markedRuns(value) {
28
28
  if (!Array.isArray(value)) return [];
29
29
  return value.flatMap((run, index) => isRecord(run) && (run.cite !== void 0 || run.footnote !== void 0) ? [index] : []);
@@ -41,9 +41,9 @@ function annotationIssues(value) {
41
41
  const unsupported = (runs, path, where) => {
42
42
  for (const runIndex of markedRuns(runs)) issues.push(issue(`${path}/${runIndex}`, `cite and footnote are not supported in ${where}; markers are drawn only in the slide title, subtitle and tag, text, bullets, list items and quote text`, { code: "cite-unsupported-location" }));
43
43
  };
44
- const cell = (entry, path) => {
45
- if (Array.isArray(entry)) unsupported(entry, path, "table cells");
46
- else if (isRecord(entry) && Array.isArray(entry.value)) unsupported(entry.value, pathFor(path, "value"), "table cells");
44
+ const cell = (entry2, path) => {
45
+ if (Array.isArray(entry2)) unsupported(entry2, path, "table cells");
46
+ else if (isRecord(entry2) && Array.isArray(entry2.value)) unsupported(entry2.value, pathFor(path, "value"), "table cells");
47
47
  };
48
48
  const payload = (node, path) => {
49
49
  if (isRecord(node.table)) {
@@ -52,8 +52,8 @@ function annotationIssues(value) {
52
52
  cell(column, `${pathFor(tablePath, "columns")}/${index}`);
53
53
  });
54
54
  if (Array.isArray(table.rows)) table.rows.forEach((row, rowIndex) => {
55
- if (Array.isArray(row)) row.forEach((entry, cellIndex) => {
56
- cell(entry, `${pathFor(tablePath, "rows")}/${rowIndex}/${cellIndex}`);
55
+ if (Array.isArray(row)) row.forEach((entry2, cellIndex) => {
56
+ cell(entry2, `${pathFor(tablePath, "rows")}/${rowIndex}/${cellIndex}`);
57
57
  });
58
58
  });
59
59
  }
@@ -68,15 +68,15 @@ function annotationIssues(value) {
68
68
  const slidePath = `/slides/${index}`;
69
69
  payload(slide, slidePath);
70
70
  visitContentPayloads(slide, slidePath, payload);
71
- walkCitationRuns(slide, `slides.${index}`, (entry) => {
72
- const runPath = pointer(entry.path);
73
- const cite = entry.run.cite;
71
+ walkCitationRuns(slide, `slides.${index}`, (entry2) => {
72
+ const runPath = pointer(entry2.path);
73
+ const cite = entry2.run.cite;
74
74
  if (typeof cite === "string") {
75
75
  if (!ids.has(cite)) issues.push(issue(`${runPath}/cite`, `unknown reference '${cite}'; add it to the top-level references list`, { code: "cite-unknown-reference", id: cite }));
76
76
  } else if (Array.isArray(cite)) cite.forEach((id, position) => {
77
77
  if (typeof id === "string" && !ids.has(id)) issues.push(issue(`${runPath}/cite/${position}`, `unknown reference '${id}'; add it to the top-level references list`, { code: "cite-unknown-reference", id }));
78
78
  });
79
- if (Array.isArray(entry.footnote)) unsupported(entry.footnote, `${runPath}/footnote`, "footnote text");
79
+ if (Array.isArray(entry2.footnote)) unsupported(entry2.footnote, `${runPath}/footnote`, "footnote text");
80
80
  });
81
81
  });
82
82
  return issues;
@@ -144,7 +144,7 @@ function bestMatchErrors(ajv2, rootId, errors) {
144
144
  if (analysis) analyses.push(analysis);
145
145
  }
146
146
  if (!analyses.length) return errors;
147
- const within = (inner, other) => other !== inner && [...inner.span].every((entry) => other.span.has(entry)) && (other.span.size > inner.span.size || errors.indexOf(other.union) > errors.indexOf(inner.union));
147
+ const within = (inner, other) => other !== inner && [...inner.span].every((entry2) => other.span.has(entry2)) && (other.span.size > inner.span.size || errors.indexOf(other.union) > errors.indexOf(inner.union));
148
148
  const outer = analyses.filter((analysis) => !analyses.some((other) => within(analysis, other)));
149
149
  const replaced = new Map(outer.map((analysis) => [analysis.union, analysis]));
150
150
  const drop = new Set(outer.flatMap((analysis) => [...analysis.span]));
@@ -213,18 +213,18 @@ function rememberValidationDefinition(issue2, parentSchema, keyword) {
213
213
  if (!parentSchema || typeof parentSchema !== "object") return;
214
214
  if (!schemaLocations) {
215
215
  schemaLocations = /* @__PURE__ */ new WeakMap();
216
- for (const entry of schemaEntries) {
216
+ for (const entry2 of schemaEntries) {
217
217
  const stack = [
218
- { value: entry.schema, pointer: "" }
218
+ { value: entry2.schema, pointer: "" }
219
219
  ];
220
220
  while (stack.length) {
221
221
  const current = stack.pop();
222
222
  if (!current?.value || typeof current.value !== "object")
223
223
  continue;
224
224
  schemaLocations.set(current.value, {
225
- schemaName: entry.name,
225
+ schemaName: entry2.name,
226
226
  pointer: current.pointer,
227
- uri: `${entry.schema.$id}#${current.pointer}`
227
+ uri: `${entry2.schema.$id}#${current.pointer}`
228
228
  });
229
229
  for (const [key2, value] of Object.entries(current.value)) {
230
230
  const segment = key2.replaceAll("~", "~0").replaceAll("/", "~1");
@@ -379,13 +379,13 @@ function toIssue(error) {
379
379
  rememberValidationDefinition(issue2, error.parentSchema, error.keyword);
380
380
  return issue2;
381
381
  }
382
- function semanticIssue(path, message, params = {}) {
382
+ function semanticIssue(path, message, params2 = {}) {
383
383
  return {
384
384
  path,
385
385
  message,
386
386
  keyword: "opf",
387
387
  schemaPath: "#/x-opf-semantics",
388
- params
388
+ params: params2
389
389
  };
390
390
  }
391
391
  function hasOwn(value, key2) {
@@ -414,22 +414,22 @@ function inferredKinds(value) {
414
414
  if (hasOwn(value, "timeline")) kinds.push("timeline");
415
415
  return kinds;
416
416
  }
417
- function isImplicitBlocksComposition(value, inferred, options) {
418
- if (options.slideRoot !== true || hasOwn(value, "blocks")) {
417
+ function isImplicitBlocksComposition(value, inferred, options2) {
418
+ if (options2.slideRoot !== true || hasOwn(value, "blocks")) {
419
419
  return false;
420
420
  }
421
421
  return inferred.length > 1;
422
422
  }
423
- function validateContentPayload(value, path, options = {}) {
423
+ function validateContentPayload(value, path, options2 = {}) {
424
424
  const issues = [];
425
425
  const explicitType = value.type;
426
426
  const payloadFields = presentFields(value, rootPayloadFields);
427
427
  const hasBlocks = hasOwn(value, "blocks");
428
- const captionIssue = (reason, params = {}) => semanticIssue(pathFor(path, "caption"), `caption is only valid on an image, chart, table or video payload; ${reason}`, { code: "caption-unsupported-payload", ...params });
429
- const imageOptionIssues = (reason, params = {}) => {
430
- for (const field of imageOptionFields) if (hasOwn(value, field)) issues.push(semanticIssue(pathFor(path, field), `${field} is only valid on an image payload; ${reason}`, { code: "image-option-unsupported-payload", field, ...params }));
428
+ const captionIssue = (reason, params2 = {}) => semanticIssue(pathFor(path, "caption"), `caption is only valid on an image, chart, table or video payload; ${reason}`, { code: "caption-unsupported-payload", ...params2 });
429
+ const imageOptionIssues = (reason, params2 = {}) => {
430
+ for (const field of imageOptionFields) if (hasOwn(value, field)) issues.push(semanticIssue(pathFor(path, field), `${field} is only valid on an image payload; ${reason}`, { code: "image-option-unsupported-payload", field, ...params2 }));
431
431
  };
432
- if (hasOwn(value, "placement") && !options.topLevel && !hasBlocks && explicitType !== "group" && inferredKinds(value).includes("image")) {
432
+ if (hasOwn(value, "placement") && !options2.topLevel && !hasBlocks && explicitType !== "group" && inferredKinds(value).includes("image")) {
433
433
  issues.push(semanticIssue(pathFor(path, "placement"), "placement is only valid on a top-level block (slides.N.blocks.I); a block inside a group or a promoted region cannot bleed to a slide edge", { code: "image-placement-invalid" }));
434
434
  }
435
435
  if (hasBlocks || explicitType === "group") {
@@ -438,11 +438,11 @@ function validateContentPayload(value, path, options = {}) {
438
438
  if (explicitType !== void 0 && explicitType !== "group") issues.push(semanticIssue(path, "a group must use type 'group' or omit type"));
439
439
  const incompatible2 = payloadFields.filter((field) => field !== "blocks" && field !== "type");
440
440
  if (incompatible2.length) issues.push(semanticIssue(path, "blocks cannot be mixed with leaf payload fields", { fields: incompatible2 }));
441
- if (!Array.isArray(value.blocks) || value.blocks.length === 0 && !options.slideRoot) issues.push(semanticIssue(pathFor(path, "blocks"), "a group requires at least one block"));
441
+ if (!Array.isArray(value.blocks) || value.blocks.length === 0 && !options2.slideRoot) issues.push(semanticIssue(pathFor(path, "blocks"), "a group requires at least one block"));
442
442
  if (Array.isArray(value.blocks)) value.blocks.forEach((block, index) => {
443
- if (isRecord(block)) issues.push(...validateContentPayload(block, `${pathFor(path, "blocks")}/${index}`, { topLevel: options.slideRoot === true }));
443
+ if (isRecord(block)) issues.push(...validateContentPayload(block, `${pathFor(path, "blocks")}/${index}`, { topLevel: options2.slideRoot === true }));
444
444
  });
445
- if (options.slideRoot && Array.isArray(value.blocks)) {
445
+ if (options2.slideRoot && Array.isArray(value.blocks)) {
446
446
  const edges = /* @__PURE__ */ new Map();
447
447
  value.blocks.forEach((block, index) => {
448
448
  const edge = isRecord(block) && isRecord(block.placement) ? block.placement.edge : void 0;
@@ -453,7 +453,7 @@ function validateContentPayload(value, path, options = {}) {
453
453
  }
454
454
  return issues;
455
455
  }
456
- if (hasOwn(value, "composition") && !options.slideRoot) issues.push(semanticIssue(pathFor(path, "composition"), "composition is only valid on a group containing blocks"));
456
+ if (hasOwn(value, "composition") && !options2.slideRoot) issues.push(semanticIssue(pathFor(path, "composition"), "composition is only valid on a group containing blocks"));
457
457
  if (explicitType !== void 0 && !isContentKind(explicitType)) {
458
458
  return issues;
459
459
  }
@@ -468,7 +468,7 @@ function validateContentPayload(value, path, options = {}) {
468
468
  return issues;
469
469
  }
470
470
  if (!kind && inferred.length > 1) {
471
- if (isImplicitBlocksComposition(value, inferred, options)) {
471
+ if (isImplicitBlocksComposition(value, inferred, options2)) {
472
472
  if (hasOwn(value, "caption")) issues.push(captionIssue("a slide root with several payloads cannot carry one; put the caption on a block", { inferredTypes: inferred }));
473
473
  return issues;
474
474
  }
@@ -577,22 +577,22 @@ function deckMetadataIssues(value) {
577
577
  const issues = [];
578
578
  const entries = (key2) => {
579
579
  const field = value[key2];
580
- if (Array.isArray(field)) return field.flatMap((entry, index) => isRecord(entry) ? [{ entry, path: `/${key2}/${index}` }] : []);
580
+ if (Array.isArray(field)) return field.flatMap((entry2, index) => isRecord(entry2) ? [{ entry: entry2, path: `/${key2}/${index}` }] : []);
581
581
  return isRecord(field) ? [{ entry: field, path: `/${key2}` }] : [];
582
582
  };
583
583
  const organizationIds = /* @__PURE__ */ new Set();
584
584
  for (const key2 of ["organization", "speaker"]) {
585
585
  const seen = /* @__PURE__ */ new Set();
586
- for (const { entry, path } of entries(key2)) {
587
- if (typeof entry.id !== "string") continue;
588
- if (seen.has(entry.id)) issues.push(semanticIssue(`${path}/id`, `${key2} ids must be unique within a presentation`, { id: entry.id }));
589
- seen.add(entry.id);
590
- if (key2 === "organization") organizationIds.add(entry.id);
586
+ for (const { entry: entry2, path } of entries(key2)) {
587
+ if (typeof entry2.id !== "string") continue;
588
+ if (seen.has(entry2.id)) issues.push(semanticIssue(`${path}/id`, `${key2} ids must be unique within a presentation`, { id: entry2.id }));
589
+ seen.add(entry2.id);
590
+ if (key2 === "organization") organizationIds.add(entry2.id);
591
591
  }
592
592
  }
593
- for (const { entry, path } of entries("speaker")) {
594
- if (typeof entry.organizationId === "string" && !organizationIds.has(entry.organizationId)) {
595
- issues.push(semanticIssue(`${path}/organizationId`, `organizationId '${entry.organizationId}' names no organization in the presentation; use the id of an entry in organization`, { id: entry.organizationId }));
593
+ for (const { entry: entry2, path } of entries("speaker")) {
594
+ if (typeof entry2.organizationId === "string" && !organizationIds.has(entry2.organizationId)) {
595
+ issues.push(semanticIssue(`${path}/organizationId`, `organizationId '${entry2.organizationId}' names no organization in the presentation; use the id of an entry in organization`, { id: entry2.organizationId }));
596
596
  }
597
597
  }
598
598
  return issues;
@@ -659,11 +659,11 @@ function slideThemeDimensionsWarnings(document, slide, index, catalogs) {
659
659
  const slidePath = `/slides/${index}`;
660
660
  const slideDesign = isRecord(slide.design) ? slide.design : void 0;
661
661
  if (!slideDesign || typeof slideDesign.theme !== "string") return [];
662
- const options = { catalogs: catalogs ?? [] };
663
- const slideTheme = resolveDesignRecords(document, index, options);
662
+ const options2 = { catalogs: catalogs ?? [] };
663
+ const slideTheme = resolveDesignRecords(document, index, options2);
664
664
  if (slideTheme.diagnostics.some((diagnostic) => diagnostic.kind === "themes") || slideTheme.theme.dimensions === void 0) return [];
665
665
  const deckDesign = isRecord(document.design) ? document.design : {};
666
- const deckDimensions = deckDesign.dimensions ?? resolveDesignRecords(document, void 0, options).theme.dimensions;
666
+ const deckDimensions = deckDesign.dimensions ?? resolveDesignRecords(document, void 0, options2).theme.dimensions;
667
667
  const slideSize = canvasSize(slideTheme.theme.dimensions);
668
668
  const deckSize = canvasSize(deckDimensions);
669
669
  if (slideSize === deckSize) return [];
@@ -790,28 +790,28 @@ function variableReferenceWarnings(value) {
790
790
  }
791
791
  const variables = isRecord(value.variables) ? value.variables : {};
792
792
  const issues = [];
793
- const colorRef = (entry, path) => {
794
- if (typeof entry !== "string" || !entry.startsWith("var:")) return;
795
- const id = entry.slice("var:".length);
793
+ const colorRef = (entry2, path) => {
794
+ if (typeof entry2 !== "string" || !entry2.startsWith("var:")) return;
795
+ const id = entry2.slice("var:".length);
796
796
  if (!variableIdPattern.test(id) || hasOwn(variables, id)) return;
797
797
  issues.push(semanticIssue(path, `unknown variable '${id}'; declare it in the top-level variables map`, { id, code: "variable-reference-unknown" }));
798
798
  };
799
- const runColor = (entry, path) => {
800
- if (typeof entry !== "string") return;
801
- if (hexColorPattern.test(entry) || schemeColorNames.has(entry)) return;
802
- if (entry.startsWith("var:") && variableIdPattern.test(entry.slice("var:".length))) {
803
- colorRef(entry, path);
799
+ const runColor = (entry2, path) => {
800
+ if (typeof entry2 !== "string") return;
801
+ if (hexColorPattern.test(entry2) || schemeColorNames.has(entry2)) return;
802
+ if (entry2.startsWith("var:") && variableIdPattern.test(entry2.slice("var:".length))) {
803
+ colorRef(entry2, path);
804
804
  return;
805
805
  }
806
806
  issues.push(semanticIssue(
807
807
  path,
808
- `run color '${entry}' is not a hex color, a color-scheme name, or a 'var:' reference; renderers fall back to the theme color`,
809
- { color: entry, code: "run-color-unrecognized" }
808
+ `run color '${entry2}' is not a hex color, a color-scheme name, or a 'var:' reference; renderers fall back to the theme color`,
809
+ { color: entry2, code: "run-color-unrecognized" }
810
810
  ));
811
811
  };
812
- const richText = (entry, path) => {
813
- if (!Array.isArray(entry)) return;
814
- entry.forEach((run, index) => {
812
+ const richText = (entry2, path) => {
813
+ if (!Array.isArray(entry2)) return;
814
+ entry2.forEach((run, index) => {
815
815
  if (isRecord(run)) runColor(run.color, `${path}/${index}/color`);
816
816
  });
817
817
  };
@@ -855,15 +855,15 @@ function variableReferenceWarnings(value) {
855
855
  };
856
856
  const textEntries = (entries, path) => {
857
857
  if (!Array.isArray(entries)) return;
858
- entries.forEach((entry, index) => {
858
+ entries.forEach((entry2, index) => {
859
859
  const entryPath = `${path}/${index}`;
860
- if (Array.isArray(entry)) {
861
- richText(entry, entryPath);
860
+ if (Array.isArray(entry2)) {
861
+ richText(entry2, entryPath);
862
862
  return;
863
863
  }
864
- if (!isRecord(entry)) return;
865
- richText(entry.text, pathFor(entryPath, "text"));
866
- richText(entry.description, pathFor(entryPath, "description"));
864
+ if (!isRecord(entry2)) return;
865
+ richText(entry2.text, pathFor(entryPath, "text"));
866
+ richText(entry2.description, pathFor(entryPath, "description"));
867
867
  });
868
868
  };
869
869
  const backgroundColors = (background, path) => {
@@ -919,9 +919,9 @@ function presentationReferenceWarnings(value, catalogs) {
919
919
  numberingWarnings(slide, slidePath);
920
920
  visitContentPayloads(slide, slidePath, numberingWarnings);
921
921
  const highlightWarnings = (payload, path) => {
922
- const code = payload.code;
923
- if (!isRecord(code) || !Array.isArray(code.highlight) || typeof code.source !== "string") return;
924
- for (const finding2 of codeHighlightLines(code.highlight, code.source).issues) {
922
+ const code2 = payload.code;
923
+ if (!isRecord(code2) || !Array.isArray(code2.highlight) || typeof code2.source !== "string") return;
924
+ for (const finding2 of codeHighlightLines(code2.highlight, code2.source).issues) {
925
925
  if (finding2.code === "code-highlight-invalid") continue;
926
926
  issues.push(semanticIssue(pathFor(pathFor(pathFor(path, "code"), "highlight"), String(finding2.index)), finding2.message, { code: finding2.code }));
927
927
  }
@@ -958,23 +958,23 @@ function contentDepthIssues(value) {
958
958
  for (const key2 of promotedRegionKeys) if (hasOwn(slide, key2)) stack.push({ value: slide[key2], path: `/slides/${index}/${key2}`, depth: 0, ancestors: [] });
959
959
  });
960
960
  while (stack.length) {
961
- const entry = stack.pop();
962
- if (!isRecord(entry.value) || !Array.isArray(entry.value.blocks)) continue;
963
- if (entry.ancestors.includes(entry.value) || entry.depth >= MAX_COMPOSITION_DEPTH) return [semanticIssue(entry.path, `content groups must be acyclic and nest at most ${MAX_COMPOSITION_DEPTH} levels`)];
964
- const ancestors = [...entry.ancestors, entry.value];
965
- entry.value.blocks.forEach((block, i) => {
966
- stack.push({ value: block, path: `${entry.path}/blocks/${i}`, depth: entry.depth + 1, ancestors });
961
+ const entry2 = stack.pop();
962
+ if (!isRecord(entry2.value) || !Array.isArray(entry2.value.blocks)) continue;
963
+ if (entry2.ancestors.includes(entry2.value) || entry2.depth >= MAX_COMPOSITION_DEPTH) return [semanticIssue(entry2.path, `content groups must be acyclic and nest at most ${MAX_COMPOSITION_DEPTH} levels`)];
964
+ const ancestors = [...entry2.ancestors, entry2.value];
965
+ entry2.value.blocks.forEach((block, i) => {
966
+ stack.push({ value: block, path: `${entry2.path}/blocks/${i}`, depth: entry2.depth + 1, ancestors });
967
967
  });
968
968
  }
969
969
  return [];
970
970
  }
971
971
  var variableTypeList = VARIABLE_KINDS.join(", ");
972
- function variableIssue(id, suffix, keyword, message, params) {
972
+ function variableIssue(id, suffix, keyword, message, params2) {
973
973
  return {
974
974
  instancePath: `/variables/${id.replaceAll("~", "~0").replaceAll("/", "~1")}${suffix}`,
975
975
  schemaPath: "#/$defs/Variable/oneOf",
976
976
  keyword,
977
- params,
977
+ params: params2,
978
978
  message
979
979
  };
980
980
  }
@@ -984,23 +984,23 @@ function typedVariableErrors(errors, value) {
984
984
  const variables = value.variables;
985
985
  const kept = errors.filter((error) => !error.instancePath.startsWith("/variables/"));
986
986
  const schemaId = schemas.presentation.$id;
987
- for (const [id, entry] of Object.entries(variables)) {
988
- if (typeof entry === "string") {
989
- if (!hexColorPattern.test(entry)) kept.push(variableIssue(id, "", "type", "must be a hex color string or a variable object", {}));
987
+ for (const [id, entry2] of Object.entries(variables)) {
988
+ if (typeof entry2 === "string") {
989
+ if (!hexColorPattern.test(entry2)) kept.push(variableIssue(id, "", "type", "must be a hex color string or a variable object", {}));
990
990
  continue;
991
991
  }
992
- if (!isRecord(entry)) {
992
+ if (!isRecord(entry2)) {
993
993
  kept.push(variableIssue(id, "", "type", "must be a hex color string or a variable object", {}));
994
994
  continue;
995
995
  }
996
- const kind = entry.type;
996
+ const kind = entry2.type;
997
997
  if (typeof kind !== "string" || !VARIABLE_KINDS.includes(kind)) {
998
998
  kept.push(variableIssue(id, "/type", "enum", `must be one of: ${variableTypeList} (a hex string is shorthand for a color variable)`, { allowedValues: [...VARIABLE_KINDS] }));
999
999
  continue;
1000
1000
  }
1001
1001
  const definition = `${kind.charAt(0).toUpperCase()}${kind.slice(1)}Variable`;
1002
1002
  const check = getAjv().getSchema(`${schemaId}#/$defs/${definition}`);
1003
- if (!check || check(entry) === true) continue;
1003
+ if (!check || check(entry2) === true) continue;
1004
1004
  for (const error of check.errors ?? []) {
1005
1005
  kept.push({ ...error, instancePath: `/variables/${id.replaceAll("~", "~0").replaceAll("/", "~1")}${error.instancePath}`, schemaPath: `#/$defs/${definition}${error.schemaPath.replace(/^#/, "")}` });
1006
1006
  }
@@ -1044,7 +1044,7 @@ function dataUnionErrors(errors, value) {
1044
1044
  return { ...error, message: field === "fields" ? "'fields' applies only to a dataset-backed table; add 'dataset' or remove 'fields'" : datasetTableMessage(field) };
1045
1045
  });
1046
1046
  }
1047
- function validateAgainstSchema(value, schemaOrKind = "presentation", options = {}) {
1047
+ function validateAgainstSchema(value, schemaOrKind = "presentation", options2 = {}) {
1048
1048
  const resolved = resolveValidator(schemaOrKind);
1049
1049
  if (resolved.schemaName === "presentation") {
1050
1050
  const errors2 = contentDepthIssues(value);
@@ -1055,15 +1055,15 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options = {
1055
1055
  let subject = value;
1056
1056
  let template;
1057
1057
  let unfilledVariables;
1058
- if (resolved.schemaName === "presentation" && isRecord(value) && (hasContentVariables(value) || options.values !== void 0 || options.template !== void 0)) {
1059
- template = options.template ?? isTemplate(value);
1060
- const view = instantiateForValidation(value, options.values ?? {}, template);
1058
+ if (resolved.schemaName === "presentation" && isRecord(value) && (hasContentVariables(value) || options2.values !== void 0 || options2.template !== void 0)) {
1059
+ template = options2.template ?? isTemplate(value);
1060
+ const view = instantiateForValidation(value, options2.values ?? {}, template);
1061
1061
  subject = view.presentation;
1062
1062
  unfilledVariables = view.unfilled;
1063
- for (const entry of view.diagnostics) {
1064
- if (entry.code === "variable-example-used" || entry.code === "variable-rich-flattened" || entry.code === "variable-unfilled") continue;
1065
- const issue2 = semanticIssue(entry.path, entry.message, { id: entry.id, code: entry.code });
1066
- (entry.severity === "error" ? variableIssues.errors : variableIssues.warnings).push(issue2);
1063
+ for (const entry2 of view.diagnostics) {
1064
+ if (entry2.code === "variable-example-used" || entry2.code === "variable-rich-flattened" || entry2.code === "variable-unfilled") continue;
1065
+ const issue2 = semanticIssue(entry2.path, entry2.message, { id: entry2.id, code: entry2.code });
1066
+ (entry2.severity === "error" ? variableIssues.errors : variableIssues.warnings).push(issue2);
1067
1067
  }
1068
1068
  if (!template) {
1069
1069
  const declared = isRecord(value.variables) ? value.variables : {};
@@ -1085,7 +1085,7 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options = {
1085
1085
  errors.push(...variableIssues.errors);
1086
1086
  const data = dataIssues(subject);
1087
1087
  errors.push(...data.errors);
1088
- warnings.push(...presentationReferenceWarnings(subject, options.catalogs));
1088
+ warnings.push(...presentationReferenceWarnings(subject, options2.catalogs));
1089
1089
  warnings.push(...data.warnings);
1090
1090
  warnings.push(...variableIssues.warnings);
1091
1091
  } else if (resolved.schemaName === "language") {
@@ -1104,9 +1104,9 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options = {
1104
1104
 
1105
1105
  // src/narrative-plan.ts
1106
1106
  var isRec = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1107
- function resolveNarrative(document, options = {}) {
1107
+ function resolveNarrative(document, options2 = {}) {
1108
1108
  if (!isRec(document) || typeof document.narrative !== "string") return void 0;
1109
- const found = resolveReference(document, "narratives", document.narrative, { catalogs: options.catalogs ?? [] });
1109
+ const found = resolveReference(document, "narratives", document.narrative, { catalogs: options2.catalogs ?? [] });
1110
1110
  if (!found) return void 0;
1111
1111
  const record = found.record;
1112
1112
  const beats = Array.isArray(record.beats) ? record.beats.flatMap((beat) => isRec(beat) && typeof beat.id === "string" ? [beat.id] : []) : [];
@@ -1230,7 +1230,7 @@ function simulateVision(hex, model) {
1230
1230
  }
1231
1231
  var PAGE = "#FFFFFF";
1232
1232
  var colorFrom = (scheme, slot, fallback) => typeof slot === "string" ? slot.startsWith("#") ? normalizeHexColor(slot) ?? fallback : normalizeHexColor(scheme[slot]) ?? fallback : fallback;
1233
- function backdropOf(definition, scheme, text, variables, canvasColor) {
1233
+ function backdropOf(definition, scheme, text2, variables, canvasColor) {
1234
1234
  const resolve2 = (value, fallback) => backgroundColorRef(value, scheme, variables, fallback);
1235
1235
  const canvas = canvasColor ?? defaultSlideBackground(scheme);
1236
1236
  if (!definition) return { kind: "solid", color: canvas };
@@ -1259,7 +1259,7 @@ function backdropOf(definition, scheme, text, variables, canvasColor) {
1259
1259
  kind: "pattern",
1260
1260
  colors: [
1261
1261
  blend(resolve2(pattern.backgroundColor, PAGE), PAGE, opacity),
1262
- blend(resolve2(pattern.foregroundColor, text), PAGE, opacity)
1262
+ blend(resolve2(pattern.foregroundColor, text2), PAGE, opacity)
1263
1263
  ]
1264
1264
  };
1265
1265
  }
@@ -1329,24 +1329,24 @@ function colorChoice(levels, scheme, variables = {}) {
1329
1329
  }
1330
1330
  function resolveDesign(document, index, context) {
1331
1331
  const deck = rec(document.design), own4 = rec(rec(rec(document.slides)[index]).design);
1332
- const { options, diagnostics, resolved } = context;
1332
+ const { options: options2, diagnostics, resolved } = context;
1333
1333
  const { theme, colorScheme, fontScheme, fontSchemePath } = resolved;
1334
1334
  const fontPath = `/${fontSchemePath.split(".").join("/")}`;
1335
1335
  const fontDiagnostic = diagnostics.find((diagnostic) => diagnostic.kind === "fontSchemes");
1336
- const dimensions = { width: options.width, height: options.height };
1336
+ const dimensions = { width: options2.width, height: options2.height };
1337
1337
  const variables = rec(document.variables);
1338
1338
  const choice = colorChoice([own4, deck, theme], colorScheme, variables);
1339
1339
  const roles = resolveColorRoles(colorScheme, { background: choice.background, variant: choice.variant });
1340
1340
  const dark = roles.dark;
1341
- const text = roles.text;
1342
- const backdrop = backdropOf(choice.drawn ? choice.drawn.definition : own4.background ?? deck.background ?? theme.background, colorScheme, text, variables, roles.map.bg1 === "light1" ? void 0 : roles.background);
1341
+ const text2 = roles.text;
1342
+ const backdrop = backdropOf(choice.drawn ? choice.drawn.definition : own4.background ?? deck.background ?? theme.background, colorScheme, text2, variables, roles.map.bg1 === "light1" ? void 0 : roles.background);
1343
1343
  const picture = imageBackground(choice.drawn ? choice.drawn.definition : own4.background ?? deck.background ?? theme.background);
1344
1344
  const backgroundImage = picture && own4.background !== void 0 ? { source: picture.src, fit: picture.fit, path: `/slides/${index}/design/background` } : picture && deck.background !== void 0 ? { source: picture.src, fit: picture.fit, path: "/design/background" } : void 0;
1345
1345
  return {
1346
1346
  theme,
1347
1347
  colorScheme,
1348
1348
  fontScheme,
1349
- fonts: options.fontFamilies,
1349
+ fonts: options2.fontFamilies,
1350
1350
  dimensions,
1351
1351
  backdrop,
1352
1352
  dark,
@@ -1357,7 +1357,7 @@ function resolveDesign(document, index, context) {
1357
1357
  colors: {
1358
1358
  background: roles.background,
1359
1359
  surface: roles.surface,
1360
- text,
1360
+ text: text2,
1361
1361
  mutedText: roles.textSecondary,
1362
1362
  primary: roles.primary,
1363
1363
  secondary: roles.secondary,
@@ -1424,10 +1424,10 @@ function backdropColors(backdrop, box, size, layer) {
1424
1424
  }
1425
1425
  }
1426
1426
  }
1427
- function worstContrast(text, sample) {
1427
+ function worstContrast(text2, sample) {
1428
1428
  let worst = { ratio: Infinity, against: sample.colors[0] ?? PAGE };
1429
1429
  for (const color of sample.colors) {
1430
- const ratio = contrastRatio(text, color);
1430
+ const ratio = contrastRatio(text2, color);
1431
1431
  if (ratio < worst.ratio) worst = { ratio, against: color };
1432
1432
  }
1433
1433
  return worst;
@@ -1459,11 +1459,11 @@ function textValues(slide, slidePath) {
1459
1459
  push(at2("text"), node.text, "body", "text");
1460
1460
  for (const field of ["items", "bullets"])
1461
1461
  if (Array.isArray(node[field]))
1462
- node[field].forEach((entry, index) => {
1463
- if (isText(entry)) push(at2(field, index), entry, "list", field);
1464
- else if (entry && typeof entry === "object") {
1465
- push(at2(field, index, "text"), entry.text, "list", field);
1466
- push(at2(field, index, "description"), entry.description, "list-description", field);
1462
+ node[field].forEach((entry2, index) => {
1463
+ if (isText(entry2)) push(at2(field, index), entry2, "list", field);
1464
+ else if (entry2 && typeof entry2 === "object") {
1465
+ push(at2(field, index, "text"), entry2.text, "list", field);
1466
+ push(at2(field, index, "description"), entry2.description, "list-description", field);
1467
1467
  }
1468
1468
  });
1469
1469
  const table = rec(node.table);
@@ -1529,9 +1529,9 @@ function altOf(value, assets) {
1529
1529
  if (typeof own4 === "string") return own4;
1530
1530
  let source = typeof value === "string" ? value : rec(value).src;
1531
1531
  for (let hop = 0; typeof source === "string" && source.startsWith("asset:") && hop < 8; hop++) {
1532
- const entry = rec(assets)[source.slice(6)];
1533
- if (typeof rec(entry).alt === "string") return rec(entry).alt;
1534
- source = typeof entry === "string" ? entry : rec(entry).src;
1532
+ const entry2 = rec(assets)[source.slice(6)];
1533
+ if (typeof rec(entry2).alt === "string") return rec(entry2).alt;
1534
+ source = typeof entry2 === "string" ? entry2 : rec(entry2).src;
1535
1535
  }
1536
1536
  return void 0;
1537
1537
  }
@@ -1568,10 +1568,10 @@ function assetRefs(document, slide, slidePath, includeDeck) {
1568
1568
  return;
1569
1569
  }
1570
1570
  for (const shape of LOGO_SHAPES) {
1571
- const entry = rec(logo)[shape];
1572
- if (entry === void 0) continue;
1573
- if (typeof entry === "string" || entry && typeof entry === "object" && "src" in entry) out.push({ path: `${base}/${shape}`, value: entry, kind: "organization" });
1574
- else for (const tone of ["onLight", "onDark"]) if (rec(entry)[tone] !== void 0) out.push({ path: `${base}/${shape}/${tone}`, value: rec(entry)[tone], kind: "organization" });
1571
+ const entry2 = rec(logo)[shape];
1572
+ if (entry2 === void 0) continue;
1573
+ if (typeof entry2 === "string" || entry2 && typeof entry2 === "object" && "src" in entry2) out.push({ path: `${base}/${shape}`, value: entry2, kind: "organization" });
1574
+ else for (const tone of ["onLight", "onDark"]) if (rec(entry2)[tone] !== void 0) out.push({ path: `${base}/${shape}/${tone}`, value: rec(entry2)[tone], kind: "organization" });
1575
1575
  }
1576
1576
  });
1577
1577
  const speakers = Array.isArray(document.speaker) ? document.speaker : document.speaker ? [document.speaker] : [];
@@ -1603,7 +1603,7 @@ function inkBox(item, measurement) {
1603
1603
  if (!fit || !Array.isArray(fit.lines) || !fit.lines.length || !item.textStyle || item.quoteLayout || item.metricLayout || item.timelineLayout) return item.box;
1604
1604
  try {
1605
1605
  const measure = textWidthMeasurer(item.textStyle, measurement);
1606
- const width = Math.min(item.box.width, Math.max(...fit.lines.map((line) => measure(line, fit.fontSize))));
1606
+ const width = Math.min(item.box.width, Math.max(...fit.lines.map((line2) => measure(line2, fit.fontSize))));
1607
1607
  const height = Math.min(item.box.height, fit.lines.length * fit.lineHeight);
1608
1608
  const x = item.alignment === "center" ? item.box.x + (item.box.width - width) / 2 : item.alignment === "right" ? item.box.x + item.box.width - width : item.box.x;
1609
1609
  return { x, y: item.box.y, width, height };
@@ -1971,12 +1971,12 @@ var readingOrderRules = [
1971
1971
  function bindingOrder(items, regions) {
1972
1972
  const rank = new Map(regions.map((region, index) => [region.name, index]));
1973
1973
  const blockRank = (item) => {
1974
- const region = regions.find((entry) => entry.name === item.region);
1974
+ const region = regions.find((entry2) => entry2.name === item.region);
1975
1975
  const blocks = region ? [...region.content, ...region.overflow ?? []] : [];
1976
1976
  const found = blocks.findIndex((block) => item.path === block || item.path.startsWith(`${block}.`));
1977
1977
  return found < 0 ? blocks.length : found;
1978
1978
  };
1979
- return items.map((item, index) => ({ item, index, region: rank.get(item.region ?? "") ?? regions.length, block: blockRank(item) })).sort((a, b) => a.region - b.region || a.block - b.block || a.index - b.index).map((entry) => entry.item.path);
1979
+ return items.map((item, index) => ({ item, index, region: rank.get(item.region ?? "") ?? regions.length, block: blockRank(item) })).sort((a, b) => a.region - b.region || a.block - b.block || a.index - b.index).map((entry2) => entry2.item.path);
1980
1980
  }
1981
1981
  function visualOrder(items, rtl) {
1982
1982
  if (rtl) return readingRows(items).flatMap((row) => row.map((item) => item.path));
@@ -2005,17 +2005,17 @@ var linkRules = [
2005
2005
  const run = runs[i];
2006
2006
  const link = run.style.link;
2007
2007
  if (typeof link !== "string") continue;
2008
- let text = run.text;
2008
+ let text2 = run.text;
2009
2009
  let last = i;
2010
- while (runs[last + 1] && runs[last + 1].style.link === link) text += runs[++last].text;
2011
- const normalized = text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, "").replace(/\s+/g, " ").trim();
2012
- const problem = !text.trim() ? "is blank" : GENERIC_LINKS.has(normalized) ? "is generic" : /^(https?:\/\/|www\.)\S{30,}$/i.test(text.trim()) ? "is a long raw URL" : void 0;
2010
+ while (runs[last + 1] && runs[last + 1].style.link === link) text2 += runs[++last].text;
2011
+ const normalized = text2.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, "").replace(/\s+/g, " ").trim();
2012
+ const problem = !text2.trim() ? "is blank" : GENERIC_LINKS.has(normalized) ? "is generic" : /^(https?:\/\/|www\.)\S{30,}$/i.test(text2.trim()) ? "is a long raw URL" : void 0;
2013
2013
  i = last;
2014
2014
  if (!problem) continue;
2015
2015
  context.report(linkRule, {
2016
2016
  path: run.path,
2017
2017
  slide,
2018
- message: `Link text ${JSON.stringify(text.trim().slice(0, 50))} ${problem}; the destination is ${link.length > 60 ? `${link.slice(0, 57)}...` : link}.`,
2018
+ message: `Link text ${JSON.stringify(text2.trim().slice(0, 50))} ${problem}; the destination is ${link.length > 60 ? `${link.slice(0, 57)}...` : link}.`,
2019
2019
  help: 'Use text that names the destination or action, for example "Q3 financial results (PDF)", instead of "click here".',
2020
2020
  fixes: [{ id: "focus-link", title: "Edit the text", kind: "focus", safe: true, focus: { path: run.valuePath, field: "link", value: link } }]
2021
2021
  });
@@ -2116,12 +2116,12 @@ var chartRules = [
2116
2116
  const resolved = resolveChartColors(chart.colors, slide.design.colorScheme, chartColorContext(slide.design));
2117
2117
  resolved.forEach((color, index) => {
2118
2118
  const path = `${payload.path === slide.path ? slide.path : payload.path}/chart/colors/${index}`;
2119
- const entry = chart.colors[index];
2119
+ const entry2 = chart.colors[index];
2120
2120
  if (color === void 0) {
2121
2121
  context.report(chartColorsRule, {
2122
2122
  path,
2123
2123
  slide,
2124
- message: `Chart colour ${index + 1} (${JSON.stringify(entry)}) cannot be resolved, so the palette colour of that position is used.`,
2124
+ message: `Chart colour ${index + 1} (${JSON.stringify(entry2)}) cannot be resolved, so the palette colour of that position is used.`,
2125
2125
  help: 'Use a hex colour, a colour-scheme name such as "accent2", or the id of a colour variable the deck defines (var:<id>).'
2126
2126
  });
2127
2127
  return;
@@ -2602,13 +2602,13 @@ var contentRules = [
2602
2602
  run(context) {
2603
2603
  for (const slide of context.slides)
2604
2604
  for (const tv of slide.texts) {
2605
- const text = plainText(tv.value);
2606
- const hit = PLACEHOLDERS.find(({ pattern }) => pattern.test(text));
2605
+ const text2 = plainText(tv.value);
2606
+ const hit = PLACEHOLDERS.find(({ pattern }) => pattern.test(text2));
2607
2607
  if (!hit) continue;
2608
2608
  context.report(placeholderRule, {
2609
2609
  path: tv.path,
2610
2610
  slide,
2611
- message: `Text looks like ${hit.label}: ${JSON.stringify(text.length > 60 ? `${text.slice(0, 57)}...` : text)}.`,
2611
+ message: `Text looks like ${hit.label}: ${JSON.stringify(text2.length > 60 ? `${text2.slice(0, 57)}...` : text2)}.`,
2612
2612
  help: "Replace it with the real content, or delete the element.",
2613
2613
  fixes: [{ id: "focus-text", title: "Edit the text", kind: "focus", safe: true, focus: { path: tv.path, field: "text" } }]
2614
2614
  });
@@ -2848,7 +2848,7 @@ var infos = [
2848
2848
  ...referenceRules,
2849
2849
  ...policyRules,
2850
2850
  ...dataRules,
2851
- ...implementations.flatMap((entry) => [entry.info, ...entry.also ?? []])
2851
+ ...implementations.flatMap((entry2) => [entry2.info, ...entry2.also ?? []])
2852
2852
  ];
2853
2853
  var validationRules = Object.freeze(
2854
2854
  infos.map((info, index) => ({ info, index })).sort((a, b) => validationCategories.indexOf(a.info.category) - validationCategories.indexOf(b.info.category) || a.index - b.index).map(({ info }) => Object.freeze({ ...info }))
@@ -2867,6 +2867,10 @@ function ruleInfo(id) {
2867
2867
  if (!found) throw new Error(`Validation rule ${JSON.stringify(id)} is not registered.`);
2868
2868
  return found;
2869
2869
  }
2870
+ var VALIDATE_DOCS = "https://github.com/OpenPresentation/opf/blob/main/docs/validate.md";
2871
+ function ruleDocumentation(id) {
2872
+ return `${VALIDATE_DOCS}#${id.replace("/", "")}`;
2873
+ }
2870
2874
 
2871
2875
  // src/check-document.ts
2872
2876
  var own = (value, key2) => Object.hasOwn(value, key2);
@@ -2885,12 +2889,12 @@ function at(value, path) {
2885
2889
  value
2886
2890
  );
2887
2891
  }
2888
- function checkDocumentOptions(options) {
2889
- checkCatalogsOption(options.catalogs, "validate");
2890
- if (options.contracts !== void 0 && !Array.isArray(options.contracts))
2892
+ function checkDocumentOptions(options2) {
2893
+ checkCatalogsOption(options2.catalogs, "validate");
2894
+ if (options2.contracts !== void 0 && !Array.isArray(options2.contracts))
2891
2895
  throw new TypeError("Validate contracts must be an array.");
2892
- for (const contract of options.contracts ?? []) {
2893
- if (!object(contract) || Object.keys(contract).some(
2896
+ for (const contract2 of options2.contracts ?? []) {
2897
+ if (!object(contract2) || Object.keys(contract2).some(
2894
2898
  (key2) => ![
2895
2899
  "path",
2896
2900
  "allowedValues",
@@ -2898,11 +2902,11 @@ function checkDocumentOptions(options) {
2898
2902
  "message",
2899
2903
  "documentation"
2900
2904
  ].includes(key2)
2901
- ) || typeof contract.path !== "string" || !Array.isArray(contract.allowedValues) || !contract.allowedValues.every(primitive) || contract.severity !== void 0 && (typeof contract.severity !== "string" || !["error", "warning", "info"].includes(contract.severity)) || contract.message !== void 0 && typeof contract.message !== "string" || contract.documentation !== void 0 && typeof contract.documentation !== "string")
2905
+ ) || typeof contract2.path !== "string" || !Array.isArray(contract2.allowedValues) || !contract2.allowedValues.every(primitive) || contract2.severity !== void 0 && (typeof contract2.severity !== "string" || !["error", "warning", "info"].includes(contract2.severity)) || contract2.message !== void 0 && typeof contract2.message !== "string" || contract2.documentation !== void 0 && typeof contract2.documentation !== "string")
2902
2906
  throw new TypeError(
2903
2907
  "Invalid contract: provide a path and primitive allowedValues, with optional severity, message and documentation."
2904
2908
  );
2905
- parts(contract.path);
2909
+ parts(contract2.path);
2906
2910
  }
2907
2911
  }
2908
2912
  function finding(ruleId, data) {
@@ -2912,10 +2916,10 @@ function finding(ruleId, data) {
2912
2916
  }
2913
2917
  var issueRuleId = (issue2) => typeof issue2.params.code === "string" && issue2.keyword === "opf" ? `opf/${issue2.params.code}` : "opf/schema";
2914
2918
  function issueFinding(issue2, ruleId = issueRuleId(issue2), schemaName = "presentation", prefix = "", scope = "document", severity) {
2915
- const path = prefix + (issue2.path === "/" ? "" : issue2.path) + (issue2.keyword === "additionalProperties" && typeof issue2.params.additionalProperty === "string" ? pointer3([issue2.params.additionalProperty]) : ""), entry = schemaEntries.find((entry2) => entry2.name === schemaName);
2916
- if (!entry) throw new TypeError(`Unknown schema ${schemaName}`);
2919
+ const path = prefix + (issue2.path === "/" ? "" : issue2.path) + (issue2.keyword === "additionalProperties" && typeof issue2.params.additionalProperty === "string" ? pointer3([issue2.params.additionalProperty]) : ""), entry2 = schemaEntries.find((entry3) => entry3.name === schemaName);
2920
+ if (!entry2) throw new TypeError(`Unknown schema ${schemaName}`);
2917
2921
  const precise = validationDefinition(issue2);
2918
- const definition = precise?.uri ?? (issue2.keyword === "opf" ? "packages/javascript/src/schema-check.ts" : issue2.schemaPath.startsWith("#") ? entry.schema.$id + issue2.schemaPath : issue2.schemaPath);
2922
+ const definition = precise?.uri ?? (issue2.keyword === "opf" ? "packages/javascript/src/schema-check.ts" : issue2.schemaPath.startsWith("#") ? entry2.schema.$id + issue2.schemaPath : issue2.schemaPath);
2919
2923
  const lookup = [
2920
2924
  "opf",
2921
2925
  "schema",
@@ -2932,7 +2936,7 @@ function issueFinding(issue2, ruleId = issueRuleId(issue2), schemaName = "presen
2932
2936
  else if (issue2.keyword === "opf")
2933
2937
  help = "Resolve the reported structural conflict while preserving every content field. Preview the result after editing.";
2934
2938
  const allowed = Array.isArray(issue2.params.allowedValues) ? issue2.params.allowedValues.filter(primitive) : [];
2935
- const nearest = issue2.keyword === "additionalProperties" ? nearestProperties(issue2, entry.schema, precise?.pointer, precise?.schemaName) : [];
2939
+ const nearest2 = issue2.keyword === "additionalProperties" ? nearestProperties(issue2, entry2.schema, precise?.pointer, precise?.schemaName) : [];
2936
2940
  return finding(ruleId, {
2937
2941
  ...severity ? { severity } : {},
2938
2942
  path,
@@ -2949,14 +2953,14 @@ function issueFinding(issue2, ruleId = issueRuleId(issue2), schemaName = "presen
2949
2953
  origin: "schema",
2950
2954
  definition
2951
2955
  }))
2952
- } : nearest.length ? { suggestions: nearest } : {}
2956
+ } : nearest2.length ? { suggestions: nearest2 } : {}
2953
2957
  });
2954
2958
  }
2955
2959
  var NEAREST_PROPERTIES = 3;
2956
2960
  function nearestProperties(issue2, fallbackRoot, pointerToKeyword, schemaName) {
2957
2961
  const typed = issue2.params.additionalProperty;
2958
2962
  if (typeof typed !== "string" || pointerToKeyword === void 0) return [];
2959
- const rootEntry = schemaEntries.find((entry) => entry.name === schemaName);
2963
+ const rootEntry = schemaEntries.find((entry2) => entry2.name === schemaName);
2960
2964
  let node = rootEntry?.schema ?? fallbackRoot;
2961
2965
  const base = pointerToKeyword.split("/").slice(0, -1);
2962
2966
  for (const token of base.slice(1)) {
@@ -2978,7 +2982,7 @@ function resolve(cursor, seen = /* @__PURE__ */ new Set()) {
2978
2982
  const id = String(cursor.root.$id ?? "") + ref;
2979
2983
  if (seen.has(id)) return { ...cursor, schema: {} };
2980
2984
  seen.add(id);
2981
- const [base, fragment = ""] = ref.split("#"), root = base ? schemaEntries.find((entry) => entry.schema.$id === base)?.schema : cursor.root;
2985
+ const [base, fragment = ""] = ref.split("#"), root = base ? schemaEntries.find((entry2) => entry2.schema.$id === base)?.schema : cursor.root;
2982
2986
  if (!root) return { ...cursor, schema: {} };
2983
2987
  const target = at(root, fragment);
2984
2988
  if (!object(target)) return { ...cursor, schema: {} };
@@ -3061,29 +3065,29 @@ function fields(document) {
3061
3065
  }
3062
3066
  ];
3063
3067
  while (stack.length) {
3064
- const entry = stack.pop();
3065
- if (!entry) break;
3066
- if (entry.ancestors.includes(entry.value)) continue;
3067
- const resolved = resolve(entry.cursor), selected = active(entry.cursor, entry.value);
3068
- const assetReference = [resolved, selected].some(
3068
+ const entry2 = stack.pop();
3069
+ if (!entry2) break;
3070
+ if (entry2.ancestors.includes(entry2.value)) continue;
3071
+ const resolved = resolve(entry2.cursor), selected = active(entry2.cursor, entry2.value);
3072
+ const assetReference2 = [resolved, selected].some(
3069
3073
  (cursor) => cursor.root.$id === schemas.presentation.$id && (cursor.path === "/$defs/Asset" || cursor.path === "/$defs/Asset/oneOf/0" || cursor.path === "/$defs/Watermark/properties/src") || String(cursor.schema.description ?? "").includes("asset:") || Array.isArray(cursor.schema.examples) && cursor.schema.examples.some(
3070
3074
  (example) => typeof example === "string" && example.startsWith("asset:")
3071
3075
  )
3072
3076
  );
3073
3077
  result.push({
3074
- path: pointer3(entry.path),
3075
- value: entry.value,
3078
+ path: pointer3(entry2.path),
3079
+ value: entry2.value,
3076
3080
  cursor: resolved,
3077
- assetReference
3081
+ assetReference: assetReference2
3078
3082
  });
3079
- const ancestors = [...entry.ancestors, entry.value];
3083
+ const ancestors = [...entry2.ancestors, entry2.value];
3080
3084
  const cursors = [selected, ...selected.baseSchemas ?? [], resolved];
3081
3085
  const arrayCursor = cursors.find((cursor) => object(cursor.schema.items));
3082
- if (Array.isArray(entry.value) && arrayCursor)
3083
- entry.value.forEach((value, index) => {
3086
+ if (Array.isArray(entry2.value) && arrayCursor)
3087
+ entry2.value.forEach((value, index) => {
3084
3088
  stack.push({
3085
3089
  value,
3086
- path: [...entry.path, index],
3090
+ path: [...entry2.path, index],
3087
3091
  cursor: {
3088
3092
  schema: arrayCursor.schema.items,
3089
3093
  root: arrayCursor.root,
@@ -3092,14 +3096,14 @@ function fields(document) {
3092
3096
  ancestors
3093
3097
  });
3094
3098
  });
3095
- else if (object(entry.value))
3096
- for (const [key2, value] of Object.entries(entry.value)) {
3097
- if (entry.path.length === 0 && key2 === "catalogs") continue;
3099
+ else if (object(entry2.value))
3100
+ for (const [key2, value] of Object.entries(entry2.value)) {
3101
+ if (entry2.path.length === 0 && key2 === "catalogs") continue;
3098
3102
  const cursor = childCursor(cursors, key2);
3099
3103
  if (!cursor) continue;
3100
3104
  stack.push({
3101
3105
  value,
3102
- path: [...entry.path, key2],
3106
+ path: [...entry2.path, key2],
3103
3107
  cursor,
3104
3108
  ancestors
3105
3109
  });
@@ -3113,7 +3117,7 @@ function recordIssues(kind, id, value) {
3113
3117
  const schemaName = catalogSchemaNames[kind];
3114
3118
  return validateAgainstSchema(object(value) ? { $schema: schemas[schemaName].$id, id, ...value } : value, kind);
3115
3119
  }
3116
- function embeddedRecordFindings(document, options, findings) {
3120
+ function embeddedRecordFindings(document, options2, findings) {
3117
3121
  const invalid = /* @__PURE__ */ new Set();
3118
3122
  if (!object(document) || !object(document.catalogs)) return invalid;
3119
3123
  for (const [group, value] of Object.entries(document.catalogs)) {
@@ -3126,8 +3130,8 @@ function embeddedRecordFindings(document, options, findings) {
3126
3130
  for (const issue2 of validation.errors) findings.push(issueFinding(issue2, recordRuleId(issue2), catalogSchemaNames[kind], path, "document"));
3127
3131
  if (!validation.valid) invalid.add(recordKey(group, kind, id));
3128
3132
  if (group === "custom") continue;
3129
- const source = catalogGroupSource(document, group, options);
3130
- const registered = registeredCatalog(source, options);
3133
+ const source = catalogGroupSource(document, group, options2);
3134
+ const registered = registeredCatalog(source, options2);
3131
3135
  if (!registered || Object.hasOwn(registered[kind] ?? {}, id)) continue;
3132
3136
  findings.push(
3133
3137
  finding("opf/catalog-record-not-in-source", {
@@ -3198,11 +3202,11 @@ function engineFindings(document, engine, enabled) {
3198
3202
  }
3199
3203
  let numberFixes;
3200
3204
  for (const issue2 of engine.warnings) {
3201
- const code = issue2.params.code;
3202
- if (typeof code !== "string") continue;
3203
- const ruleId = `opf/${code}`;
3205
+ const code2 = issue2.params.code;
3206
+ if (typeof code2 !== "string") continue;
3207
+ const ruleId = `opf/${code2}`;
3204
3208
  if (!enabled(ruleId)) continue;
3205
- if (code === "chart-value-not-numeric") {
3209
+ if (code2 === "chart-value-not-numeric") {
3206
3210
  numberFixes ??= chartNumberFixPaths(document);
3207
3211
  const fix = numberFixes.get(issue2.path);
3208
3212
  if (fix) {
@@ -3215,7 +3219,7 @@ function engineFindings(document, engine, enabled) {
3215
3219
  }
3216
3220
  }
3217
3221
  const found = issueFinding(issue2, ruleId, "presentation", "", "document", "warning");
3218
- const help = HELP[code];
3222
+ const help = HELP[code2];
3219
3223
  if (help) found.help = help;
3220
3224
  findings.push(found);
3221
3225
  }
@@ -3249,12 +3253,12 @@ function variableFindings(document, engine, schemaValid) {
3249
3253
  if (depth2 > 64) return;
3250
3254
  if (typeof value === "string") {
3251
3255
  if (value.startsWith("data:") || !value.includes("{{")) return;
3252
- for (const match of value.matchAll(VARIABLE_TOKEN))
3253
- if (match[1])
3256
+ for (const match2 of value.matchAll(VARIABLE_TOKEN))
3257
+ if (match2[1])
3254
3258
  findings.push(
3255
3259
  finding("opf/variable-unfilled", {
3256
3260
  path: pointer3(path),
3257
- message: `Template variable ${JSON.stringify(match[0])} is still in the text.`,
3261
+ message: `Template variable ${JSON.stringify(match2[0])} is still in the text.`,
3258
3262
  help: "Declare the variable and fill it, or replace the token with the real value.",
3259
3263
  severity: "warning"
3260
3264
  })
@@ -3262,15 +3266,15 @@ function variableFindings(document, engine, schemaValid) {
3262
3266
  return;
3263
3267
  }
3264
3268
  if (Array.isArray(value)) {
3265
- value.forEach((entry, index) => {
3266
- walk2(entry, [...path, String(index)], depth2 + 1);
3269
+ value.forEach((entry2, index) => {
3270
+ walk2(entry2, [...path, String(index)], depth2 + 1);
3267
3271
  });
3268
3272
  return;
3269
3273
  }
3270
3274
  if (value && typeof value === "object")
3271
- for (const [key2, entry] of Object.entries(value)) {
3275
+ for (const [key2, entry2] of Object.entries(value)) {
3272
3276
  if (path.length === 0 && ["variables", "extensions", "catalogs", "assets"].includes(key2)) continue;
3273
- walk2(entry, [...path, key2], depth2 + 1);
3277
+ walk2(entry2, [...path, key2], depth2 + 1);
3274
3278
  }
3275
3279
  };
3276
3280
  walk2(document, [], 0);
@@ -3297,22 +3301,22 @@ function undeclaredCatalogFindings(document) {
3297
3301
  }
3298
3302
  return findings;
3299
3303
  }
3300
- function contentReferenceFindings(document, options, findings) {
3301
- const invalid = embeddedRecordFindings(document, options, findings);
3304
+ function contentReferenceFindings(document, options2, findings) {
3305
+ const invalid = embeddedRecordFindings(document, options2, findings);
3302
3306
  const registeredChecked = /* @__PURE__ */ new Set();
3303
3307
  for (const site of catalogReferenceSites(document)) {
3304
3308
  const path = pointerPath(site.path);
3305
3309
  const parsed = parseReference(site.reference);
3306
3310
  if (parsed?.group !== void 0 && !catalogGroupDeclared(document, parsed.group)) continue;
3307
- const found = resolveReference(document, site.kind, site.reference, { ...options, ...site.group !== void 0 ? { group: site.group } : {} });
3311
+ const found = resolveReference(document, site.kind, site.reference, { ...options2, ...site.group !== void 0 ? { group: site.group } : {} });
3308
3312
  if (found?.origin === "host" && !registeredChecked.has(recordKey(found.group, site.kind, found.id))) {
3309
3313
  registeredChecked.add(recordKey(found.group, site.kind, found.id));
3310
3314
  for (const issue2 of recordIssues(site.kind, found.id, found.record).errors)
3311
3315
  findings.push(issueFinding(issue2, recordRuleId(issue2), catalogSchemaNames[site.kind], pointer3(["catalogs", found.group, site.kind, found.id]), "context"));
3312
3316
  }
3313
3317
  if (found && !(found.origin === "document" && invalid.has(recordKey(found.group, site.kind, found.id)))) continue;
3314
- const diagnostic = unresolvedReference(document, site.kind, site.reference, path, options);
3315
- const suggestions = catalogRecords(document, site.kind, options).map((entry) => ({ value: entry.reference, label: typeof entry.record.name === "string" ? entry.record.name : entry.reference, origin: entry.origin === "host" ? "registered" : "document", definition: entry.origin === "document" ? `document#${pointer3(["catalogs", entry.group, site.kind, entry.id])}` : `${entry.source ?? "context"}#/${site.kind}/${entry.id}` })).sort((a, b) => distance(site.reference, a.value) - distance(site.reference, b.value) || (a.value < b.value ? -1 : a.value > b.value ? 1 : 0)).slice(0, 5);
3318
+ const diagnostic = unresolvedReference(document, site.kind, site.reference, path, options2);
3319
+ const suggestions = catalogRecords(document, site.kind, options2).map((entry2) => ({ value: entry2.reference, label: typeof entry2.record.name === "string" ? entry2.record.name : entry2.reference, origin: entry2.origin === "host" ? "registered" : "document", definition: entry2.origin === "document" ? `document#${pointer3(["catalogs", entry2.group, site.kind, entry2.id])}` : `${entry2.source ?? "context"}#/${site.kind}/${entry2.id}` })).sort((a, b) => distance(site.reference, a.value) - distance(site.reference, b.value) || (a.value < b.value ? -1 : a.value > b.value ? 1 : 0)).slice(0, 5);
3316
3320
  findings.push(
3317
3321
  finding("opf/unresolved-reference", {
3318
3322
  path,
@@ -3325,9 +3329,9 @@ function contentReferenceFindings(document, options, findings) {
3325
3329
  );
3326
3330
  }
3327
3331
  }
3328
- function referenceFindings(document, engine, options) {
3332
+ function referenceFindings(document, engine, options2) {
3329
3333
  const findings = [];
3330
- const catalogs = { catalogs: options.catalogs ?? [] };
3334
+ const catalogs = { catalogs: options2.catalogs ?? [] };
3331
3335
  contentReferenceFindings(document, catalogs, findings);
3332
3336
  const assetValues = object(document) && object(document.assets) ? document.assets : {};
3333
3337
  for (const field of fields(document)) {
@@ -3402,10 +3406,10 @@ function referenceFindings(document, engine, options) {
3402
3406
  );
3403
3407
  return findings;
3404
3408
  }
3405
- function narrativeFindings(document, options) {
3409
+ function narrativeFindings(document, options2) {
3406
3410
  const findings = [];
3407
3411
  if (!object(document)) return findings;
3408
- const narrative = resolveNarrative(document, options);
3412
+ const narrative = resolveNarrative(document, options2);
3409
3413
  if (narrative) {
3410
3414
  const id = String(document.narrative);
3411
3415
  const beatSuggestions = (beat) => narrative.beats.slice().sort((a, b) => distance(beat, a) - distance(beat, b) || (a < b ? -1 : a > b ? 1 : 0)).slice(0, 5).map((value) => ({ value, label: value, origin: narrative.origin, definition: `narratives/${id}#/beats` }));
@@ -3449,32 +3453,32 @@ function narrativeFindings(document, options) {
3449
3453
  }
3450
3454
  function contractFindings(document, contracts) {
3451
3455
  const findings = [];
3452
- for (const [index, contract] of contracts.entries()) {
3453
- const pattern = parts(contract.path), stack = [{ value: document, path: [], depth: 0 }];
3456
+ for (const [index, contract2] of contracts.entries()) {
3457
+ const pattern = parts(contract2.path), stack = [{ value: document, path: [], depth: 0 }];
3454
3458
  while (stack.length) {
3455
- const entry = stack.pop();
3456
- if (!entry) break;
3457
- if (entry.depth === pattern.length) {
3458
- if (contract.allowedValues.some((value) => Object.is(value, entry.value)))
3459
+ const entry2 = stack.pop();
3460
+ if (!entry2) break;
3461
+ if (entry2.depth === pattern.length) {
3462
+ if (contract2.allowedValues.some((value) => Object.is(value, entry2.value)))
3459
3463
  continue;
3460
- const path = pointer3(entry.path), definition = contract.documentation ?? `context#/contracts/${index}`, values = {
3464
+ const path = pointer3(entry2.path), definition = contract2.documentation ?? `context#/contracts/${index}`, values = {
3461
3465
  path,
3462
- value: JSON.stringify(entry.value),
3463
- allowed: contract.allowedValues.map((value) => JSON.stringify(value)).join(", "),
3466
+ value: JSON.stringify(entry2.value),
3467
+ allowed: contract2.allowedValues.map((value) => JSON.stringify(value)).join(", "),
3464
3468
  file: definition
3465
3469
  };
3466
- const message = (contract.message ?? "This value is outside the allowed values for {{path}}.").replace(
3470
+ const message = (contract2.message ?? "This value is outside the allowed values for {{path}}.").replace(
3467
3471
  /\{\{(path|value|allowed|file)\}\}/g,
3468
3472
  (_, key2) => values[key2] ?? ""
3469
3473
  );
3470
3474
  findings.push(
3471
3475
  finding("opf/contract", {
3472
- severity: contract.severity ?? "error",
3476
+ severity: contract2.severity ?? "error",
3473
3477
  path,
3474
3478
  message,
3475
3479
  help: `Use one of the configured values: ${values.allowed}. This is an explicit host policy; no source was changed.`,
3476
3480
  definition,
3477
- suggestions: contract.allowedValues.map((value) => ({
3481
+ suggestions: contract2.allowedValues.map((value) => ({
3478
3482
  value,
3479
3483
  label: String(value),
3480
3484
  origin: "contract",
@@ -3482,14 +3486,14 @@ function contractFindings(document, contracts) {
3482
3486
  }))
3483
3487
  })
3484
3488
  );
3485
- } else if (object(entry.value) || Array.isArray(entry.value)) {
3486
- const key2 = pattern[entry.depth];
3487
- for (const [name, value] of Object.entries(entry.value))
3489
+ } else if (object(entry2.value) || Array.isArray(entry2.value)) {
3490
+ const key2 = pattern[entry2.depth];
3491
+ for (const [name, value] of Object.entries(entry2.value))
3488
3492
  if (key2 === "*" || key2 === name)
3489
3493
  stack.push({
3490
3494
  value,
3491
- path: [...entry.path, name],
3492
- depth: entry.depth + 1
3495
+ path: [...entry2.path, name],
3496
+ depth: entry2.depth + 1
3493
3497
  });
3494
3498
  }
3495
3499
  }
@@ -3506,10 +3510,10 @@ var OPFPatchError = class extends Error {
3506
3510
  index;
3507
3511
  /** The failing operation, when known. */
3508
3512
  operation;
3509
- constructor(code, message, details = {}) {
3513
+ constructor(code2, message, details = {}) {
3510
3514
  super(message);
3511
3515
  this.name = "OPFPatchError";
3512
- this.code = code;
3516
+ this.code = code2;
3513
3517
  if (details.path !== void 0) this.path = details.path;
3514
3518
  if (details.index !== void 0) this.index = details.index;
3515
3519
  if (details.operation !== void 0) this.operation = details.operation;
@@ -3598,12 +3602,12 @@ function arrayIndex(token, length, allowEnd, path) {
3598
3602
  if (!Number.isSafeInteger(at2) || at2 > length || !allowEnd && at2 >= length) throw new OPFPatchError("invalid-array-index", `Array index out of bounds: ${token}`, { path });
3599
3603
  return at2;
3600
3604
  }
3601
- function normalizePatch(patch) {
3602
- if (!Array.isArray(patch)) throw new OPFPatchError("invalid-patch", "A JSON Patch must be an array of operations.");
3603
- return patch.map((operation, index) => normalizeOperation(operation, index));
3605
+ function normalizePatch(patch2) {
3606
+ if (!Array.isArray(patch2)) throw new OPFPatchError("invalid-patch", "A JSON Patch must be an array of operations.");
3607
+ return patch2.map((operation, index) => normalizeOperation(operation, index));
3604
3608
  }
3605
3609
  function normalizeOperation(operation, index) {
3606
- const fail = (code, message, path) => new OPFPatchError(code, index === void 0 ? message : `Operation ${index}: ${message}`, { index, operation, path });
3610
+ const fail = (code2, message, path) => new OPFPatchError(code2, index === void 0 ? message : `Operation ${index}: ${message}`, { index, operation, path });
3607
3611
  if (!isObject(operation)) throw fail("invalid-patch-operation", "Expected an operation object.");
3608
3612
  const op = operation.op;
3609
3613
  if (typeof op !== "string" || !["add", "remove", "replace", "move", "copy", "test"].includes(op)) {
@@ -3722,8 +3726,8 @@ function applyOne(document, operation) {
3722
3726
  }
3723
3727
  }
3724
3728
  }
3725
- function applyPatchWithInverse(document, patch, options = {}) {
3726
- const operations = normalizePatch(patch);
3729
+ function applyPatchWithInverse(document, patch2, options2 = {}) {
3730
+ const operations = normalizePatch(patch2);
3727
3731
  let working = clone(document);
3728
3732
  const inverse = [];
3729
3733
  for (const [index, operation] of operations.entries()) {
@@ -3740,19 +3744,19 @@ function applyPatchWithInverse(document, patch, options = {}) {
3740
3744
  }
3741
3745
  }
3742
3746
  const result = { presentation: working, inverse, patch: operations };
3743
- if (options.validate) {
3744
- const validator = typeof options.validate === "function" ? options.validate : (value) => validate(value, { only: options.strict ? ["format", "references"] : ["format"] });
3747
+ if (options2.validate) {
3748
+ const validator = typeof options2.validate === "function" ? options2.validate : (value) => validate(value, { only: options2.strict ? ["format", "references"] : ["format"] });
3745
3749
  const validation = validator(working);
3746
- if (!validation.valid || options.strict && validation.findings?.some((entry) => entry.severity === "warning")) throw new OPFPatchValidationError(validation);
3750
+ if (!validation.valid || options2.strict && validation.findings?.some((entry2) => entry2.severity === "warning")) throw new OPFPatchValidationError(validation);
3747
3751
  result.validation = validation;
3748
3752
  }
3749
3753
  return result;
3750
3754
  }
3751
- function applyPatch(document, patch, options = {}) {
3752
- return applyPatchWithInverse(document, patch, options).presentation;
3755
+ function applyPatch(document, patch2, options2 = {}) {
3756
+ return applyPatchWithInverse(document, patch2, options2).presentation;
3753
3757
  }
3754
- function invertPatch(document, patch) {
3755
- return applyPatchWithInverse(document, patch).inverse;
3758
+ function invertPatch(document, patch2) {
3759
+ return applyPatchWithInverse(document, patch2).inverse;
3756
3760
  }
3757
3761
 
3758
3762
  // src/schema-catalog-kinds.ts
@@ -4018,7 +4022,7 @@ function variantType(variant) {
4018
4022
  if (Array.isArray(variant.enum) && variant.enum.length) return variant.enum[0] === null ? "null" : typeof variant.enum[0];
4019
4023
  return "any";
4020
4024
  }
4021
- var firstParagraph = (text) => typeof text === "string" && text.trim() ? (text.trim().split(/\n[ \t]*\n/)[0] ?? "").trim() : void 0;
4025
+ var firstParagraph = (text2) => typeof text2 === "string" && text2.trim() ? (text2.trim().split(/\n[ \t]*\n/)[0] ?? "").trim() : void 0;
4022
4026
  var definitionOf = (raw) => typeof raw.$ref === "string" ? /#\/\$defs\/([^/]+)$/.exec(raw.$ref)?.[1] : void 0;
4023
4027
  function enumValues(variants, inherited) {
4024
4028
  const seen = /* @__PURE__ */ new Map();
@@ -4204,14 +4208,14 @@ function catalogKindAt(path, document) {
4204
4208
  }
4205
4209
 
4206
4210
  // src/source-text.ts
4207
- function createLocator(text) {
4211
+ function createLocator(text2) {
4208
4212
  const lineStarts = [0];
4209
- for (let index = 0; index < text.length; index++) {
4210
- const code = text.charCodeAt(index);
4211
- if (code === 13) {
4212
- if (text.charCodeAt(index + 1) === 10) index++;
4213
+ for (let index = 0; index < text2.length; index++) {
4214
+ const code2 = text2.charCodeAt(index);
4215
+ if (code2 === 13) {
4216
+ if (text2.charCodeAt(index + 1) === 10) index++;
4213
4217
  lineStarts.push(index + 1);
4214
- } else if (code === 10) lineStarts.push(index + 1);
4218
+ } else if (code2 === 10) lineStarts.push(index + 1);
4215
4219
  }
4216
4220
  return (offset, length) => {
4217
4221
  let low = 0;
@@ -4224,28 +4228,28 @@ function createLocator(text) {
4224
4228
  return { offset, length, line: low + 1, column: offset - (lineStarts[low] ?? 0) + 1 };
4225
4229
  };
4226
4230
  }
4227
- function applyTextEdits(text, edits) {
4231
+ function applyTextEdits(text2, edits) {
4228
4232
  const ordered = edits.map((edit, index) => ({ edit, index })).sort((a, b) => a.edit.offset - b.edit.offset || a.index - b.index);
4229
4233
  let result = "";
4230
4234
  let cursor = 0;
4231
4235
  for (const { edit } of ordered) {
4232
4236
  if (edit.offset < cursor) throw new RangeError("Text edits overlap.");
4233
- result += text.slice(cursor, edit.offset) + edit.insert;
4237
+ result += text2.slice(cursor, edit.offset) + edit.insert;
4234
4238
  cursor = edit.offset + edit.length;
4235
4239
  }
4236
- return result + text.slice(cursor);
4240
+ return result + text2.slice(cursor);
4237
4241
  }
4238
4242
  function rememberByText(read, size = 2) {
4239
4243
  const kept = [];
4240
- return (text) => {
4241
- const at2 = kept.findIndex((entry) => entry.text === text);
4244
+ return (text2) => {
4245
+ const at2 = kept.findIndex((entry2) => entry2.text === text2);
4242
4246
  if (at2 >= 0) {
4243
- const [entry] = kept.splice(at2, 1);
4244
- kept.unshift(entry);
4245
- return entry.value;
4247
+ const [entry2] = kept.splice(at2, 1);
4248
+ kept.unshift(entry2);
4249
+ return entry2.value;
4246
4250
  }
4247
- const value = read(text);
4248
- kept.unshift({ text, value });
4251
+ const value = read(text2);
4252
+ kept.unshift({ text: text2, value });
4249
4253
  if (kept.length > size) kept.pop();
4250
4254
  return value;
4251
4255
  };
@@ -4257,18 +4261,18 @@ var NUMBER = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/;
4257
4261
  var TYPOGRAPHIC = /[\u2018-\u201F\u00AB\u00BB]/;
4258
4262
  var HEX = /^[0-9a-fA-F]{4}$/;
4259
4263
  var isBreak = (char) => char === "\n" || char === "\r";
4260
- function tokenize(text) {
4264
+ function tokenize(text2) {
4261
4265
  const tokens = [];
4262
4266
  const lexical = { escapes: /* @__PURE__ */ new Map(), unicode: /* @__PURE__ */ new Map(), controls: /* @__PURE__ */ new Map() };
4263
- const length = text.length;
4264
- let index = text.charCodeAt(0) === 65279 ? 1 : 0;
4267
+ const length = text2.length;
4268
+ let index = text2.charCodeAt(0) === 65279 ? 1 : 0;
4265
4269
  const push = (token) => {
4266
- const full = { ...token, text: text.slice(token.start, token.end) };
4270
+ const full = { ...token, text: text2.slice(token.start, token.end) };
4267
4271
  tokens.push(full);
4268
4272
  return full;
4269
4273
  };
4270
4274
  while (index < length) {
4271
- const char = text[index];
4275
+ const char = text2[index];
4272
4276
  if (char === " " || char === " " || char === "\n" || char === "\r") {
4273
4277
  index++;
4274
4278
  continue;
@@ -4286,7 +4290,7 @@ function tokenize(text) {
4286
4290
  const controls = [];
4287
4291
  let terminated = false;
4288
4292
  while (index < length) {
4289
- const current = text[index];
4293
+ const current = text2[index];
4290
4294
  if (current === '"') {
4291
4295
  terminated = true;
4292
4296
  index++;
@@ -4294,12 +4298,12 @@ function tokenize(text) {
4294
4298
  }
4295
4299
  if (isBreak(current)) break;
4296
4300
  if (current === "\\") {
4297
- const following = text[index + 1];
4301
+ const following = text2[index + 1];
4298
4302
  if (following === void 0 || isBreak(following)) {
4299
4303
  escapes.push(index);
4300
4304
  index++;
4301
4305
  } else if (following === "u") {
4302
- if (HEX.test(text.slice(index + 2, index + 6))) index += 6;
4306
+ if (HEX.test(text2.slice(index + 2, index + 6))) index += 6;
4303
4307
  else {
4304
4308
  unicode.push(index);
4305
4309
  index += 2;
@@ -4324,9 +4328,9 @@ function tokenize(text) {
4324
4328
  index++;
4325
4329
  let terminated = false;
4326
4330
  while (index < length) {
4327
- const current = text[index];
4331
+ const current = text2[index];
4328
4332
  if (isBreak(current)) break;
4329
- if (current === "\\" && index + 1 < length && !isBreak(text[index + 1])) {
4333
+ if (current === "\\" && index + 1 < length && !isBreak(text2[index + 1])) {
4330
4334
  index += 2;
4331
4335
  continue;
4332
4336
  }
@@ -4343,7 +4347,7 @@ function tokenize(text) {
4343
4347
  index++;
4344
4348
  let terminated = false;
4345
4349
  while (index < length) {
4346
- const current = text[index];
4350
+ const current = text2[index];
4347
4351
  if (isBreak(current)) break;
4348
4352
  index++;
4349
4353
  if (current === '"' || TYPOGRAPHIC.test(current)) {
@@ -4354,35 +4358,35 @@ function tokenize(text) {
4354
4358
  push({ kind: "string", start, end: index, quote: "typographic", terminated });
4355
4359
  continue;
4356
4360
  }
4357
- if (char === "/" && text[index + 1] === "/") {
4358
- while (index < length && !isBreak(text[index])) index++;
4361
+ if (char === "/" && text2[index + 1] === "/") {
4362
+ while (index < length && !isBreak(text2[index])) index++;
4359
4363
  push({ kind: "comment", start, end: index });
4360
4364
  continue;
4361
4365
  }
4362
- if (char === "/" && text[index + 1] === "*") {
4363
- const close = text.indexOf("*/", index + 2);
4366
+ if (char === "/" && text2[index + 1] === "*") {
4367
+ const close = text2.indexOf("*/", index + 2);
4364
4368
  index = close < 0 ? length : close + 2;
4365
4369
  push({ kind: "comment", start, end: index, block: true, terminated: close >= 0 });
4366
4370
  continue;
4367
4371
  }
4368
- if (WORD.test(String.fromCodePoint(text.codePointAt(index) ?? 0))) {
4372
+ if (WORD.test(String.fromCodePoint(text2.codePointAt(index) ?? 0))) {
4369
4373
  while (index < length) {
4370
- const code = text.codePointAt(index) ?? 0;
4371
- if (!WORD.test(String.fromCodePoint(code))) break;
4372
- index += code > 65535 ? 2 : 1;
4374
+ const code2 = text2.codePointAt(index) ?? 0;
4375
+ if (!WORD.test(String.fromCodePoint(code2))) break;
4376
+ index += code2 > 65535 ? 2 : 1;
4373
4377
  }
4374
- const lexeme = text.slice(start, index);
4378
+ const lexeme = text2.slice(start, index);
4375
4379
  const kind = NUMBER.test(lexeme) ? "number" : lexeme === "true" || lexeme === "false" || lexeme === "null" ? "literal" : /^[+\-.]?[0-9]|^[+-]$/.test(lexeme) ? "badnumber" : "word";
4376
4380
  push({ kind, start, end: index });
4377
4381
  continue;
4378
4382
  }
4379
4383
  while (index < length) {
4380
- const code = text.codePointAt(index) ?? 0;
4381
- const next = String.fromCodePoint(code);
4384
+ const code2 = text2.codePointAt(index) ?? 0;
4385
+ const next = String.fromCodePoint(code2);
4382
4386
  if (/[\s{}[\],:"'/]/.test(next) || TYPOGRAPHIC.test(next) || WORD.test(next)) break;
4383
- index += code > 65535 ? 2 : 1;
4387
+ index += code2 > 65535 ? 2 : 1;
4384
4388
  }
4385
- if (index === start) index += (text.codePointAt(index) ?? 0) > 65535 ? 2 : 1;
4389
+ if (index === start) index += (text2.codePointAt(index) ?? 0) > 65535 ? 2 : 1;
4386
4390
  push({ kind: "unknown", start, end: index });
4387
4391
  }
4388
4392
  push({ kind: "eof", start: length, end: length });
@@ -4393,14 +4397,14 @@ function clip(value, max = 40) {
4393
4397
  const collapsed = value.replace(/\s+/g, " ").trim();
4394
4398
  return collapsed.length > max ? `${collapsed.slice(0, max - 1)}\u2026` : collapsed;
4395
4399
  }
4396
- function lineBounds(text, offset) {
4400
+ function lineBounds(text2, offset) {
4397
4401
  let start = offset;
4398
- while (start > 0 && !isBreak(text[start - 1])) start--;
4402
+ while (start > 0 && !isBreak(text2[start - 1])) start--;
4399
4403
  let end = offset;
4400
- while (end < text.length && !isBreak(text[end])) end++;
4404
+ while (end < text2.length && !isBreak(text2[end])) end++;
4401
4405
  return { start, end };
4402
4406
  }
4403
- var indentOf = (text, offset) => /^[ \t]*/.exec(text.slice(lineBounds(text, offset).start))?.[0] ?? "";
4407
+ var indentOf = (text2, offset) => /^[ \t]*/.exec(text2.slice(lineBounds(text2, offset).start))?.[0] ?? "";
4404
4408
  function unquoteSingle(raw) {
4405
4409
  const inner = raw.endsWith("'") && raw.length > 1 ? raw.slice(1, -1) : raw.slice(1);
4406
4410
  let result = "";
@@ -4421,13 +4425,13 @@ var YAML_START = /^[A-Za-z_][\w.-]*\s*:(?:\s|$)/;
4421
4425
  var MINIMAL_DECK = '{\n "slides": [\n { "title": "" }\n ]\n}\n';
4422
4426
  var HINT_FORMS_LIMIT = 2;
4423
4427
  var Analyzer = class {
4424
- constructor(text) {
4425
- this.text = text;
4426
- const lexed = tokenize(text);
4428
+ constructor(text2) {
4429
+ this.text = text2;
4430
+ const lexed = tokenize(text2);
4427
4431
  this.comments = lexed.tokens.filter((token) => token.kind === "comment");
4428
4432
  this.tokens = lexed.tokens.filter((token) => token.kind !== "comment");
4429
4433
  this.lexical = lexed.lexical;
4430
- this.locate = createLocator(text);
4434
+ this.locate = createLocator(text2);
4431
4435
  }
4432
4436
  text;
4433
4437
  problems = [];
@@ -4622,7 +4626,7 @@ var Analyzer = class {
4622
4626
  return { offset: start, length: end - start, insert: "" };
4623
4627
  }
4624
4628
  stringProblems(token) {
4625
- const { text } = this;
4629
+ const { text: text2 } = this;
4626
4630
  if (token.quote === "single") {
4627
4631
  this.add({
4628
4632
  code: "single-quotes",
@@ -4649,7 +4653,7 @@ var Analyzer = class {
4649
4653
  const unicode = this.lexical.unicode.get(token);
4650
4654
  const controls = this.lexical.controls.get(token);
4651
4655
  if (token.terminated === false) {
4652
- const lineEnd = token.start + clipTrailing(text.slice(token.start, token.end)).length;
4656
+ const lineEnd = token.start + clipTrailing(text2.slice(token.start, token.end)).length;
4653
4657
  this.add({
4654
4658
  code: "unclosed-string",
4655
4659
  range: this.range(token.start, lineEnd),
@@ -4660,7 +4664,7 @@ var Analyzer = class {
4660
4664
  }
4661
4665
  if (escapes?.length) {
4662
4666
  const first = escapes[0];
4663
- const shown = text.slice(first, first + 2);
4667
+ const shown = text2.slice(first, first + 2);
4664
4668
  this.add({
4665
4669
  code: "invalid-escape",
4666
4670
  range: this.range(first, Math.min(token.end, first + 2)),
@@ -4692,9 +4696,9 @@ var Analyzer = class {
4692
4696
  title: "Replace with escape",
4693
4697
  safe: true,
4694
4698
  edits: controls.map((offset) => {
4695
- const code = text.charCodeAt(offset);
4699
+ const code2 = text2.charCodeAt(offset);
4696
4700
  const named = { 8: "\\b", 9: "\\t", 12: "\\f" };
4697
- return { offset, length: 1, insert: named[code] ?? `\\u${code.toString(16).padStart(4, "0")}` };
4701
+ return { offset, length: 1, insert: named[code2] ?? `\\u${code2.toString(16).padStart(4, "0")}` };
4698
4702
  })
4699
4703
  }
4700
4704
  ]
@@ -4963,10 +4967,10 @@ var Analyzer = class {
4963
4967
  }
4964
4968
  // -- structural problems --
4965
4969
  missingComma(valueEnd, previous) {
4966
- const line = this.line(Math.max(valueEnd - 1, 0));
4970
+ const line2 = this.line(Math.max(valueEnd - 1, 0));
4967
4971
  return {
4968
- message: `Missing comma after ${previous ? `\`${previous.text}\`` : "the previous value"} (line ${line}).`,
4969
- next: `add \`,\` right after it, on line ${line}.`,
4972
+ message: `Missing comma after ${previous ? `\`${previous.text}\`` : "the previous value"} (line ${line2}).`,
4973
+ next: `add \`,\` right after it, on line ${line2}.`,
4970
4974
  fixes: [{ id: "insert-comma", title: "Insert comma", safe: true, edits: [{ offset: valueEnd, length: 0, insert: "," }], caret: valueEnd + 1 }]
4971
4975
  };
4972
4976
  }
@@ -5049,7 +5053,7 @@ var Analyzer = class {
5049
5053
  }
5050
5054
  /** The edits that remove an entry whose value is missing (key through colon), and the comma that belonged to it. */
5051
5055
  removeEntry(start, end) {
5052
- const { text } = this;
5056
+ const { text: text2 } = this;
5053
5057
  const firstFrom = (offset) => {
5054
5058
  let low = 0;
5055
5059
  let high = this.tokens.length;
@@ -5070,18 +5074,18 @@ var Analyzer = class {
5070
5074
  let to = end;
5071
5075
  if (first?.kind === "comma") to = first.end;
5072
5076
  else if (!entryFollows(second) && before?.kind === "comma") from = before.start;
5073
- const alone = from === start && /^[ \t]*$/.test(text.slice(lineBounds(text, start).start, start)) && /^[ \t]*$/.test(text.slice(to, lineBounds(text, Math.max(to - 1, 0)).end));
5077
+ const alone = from === start && /^[ \t]*$/.test(text2.slice(lineBounds(text2, start).start, start)) && /^[ \t]*$/.test(text2.slice(to, lineBounds(text2, Math.max(to - 1, 0)).end));
5074
5078
  if (alone) {
5075
- const bounds = lineBounds(text, start);
5076
- let lineEnd = lineBounds(text, Math.max(to - 1, 0)).end;
5077
- if (text[lineEnd] === "\r" && text[lineEnd + 1] === "\n") lineEnd += 2;
5078
- else if (lineEnd < text.length) lineEnd++;
5079
+ const bounds = lineBounds(text2, start);
5080
+ let lineEnd = lineBounds(text2, Math.max(to - 1, 0)).end;
5081
+ if (text2[lineEnd] === "\r" && text2[lineEnd + 1] === "\n") lineEnd += 2;
5082
+ else if (lineEnd < text2.length) lineEnd++;
5079
5083
  return [{ offset: bounds.start, length: lineEnd - bounds.start, insert: "" }];
5080
5084
  }
5081
5085
  return [{ offset: from, length: to - from, insert: "" }];
5082
5086
  }
5083
5087
  unclosed(container) {
5084
- const { text } = this;
5088
+ const { text: text2 } = this;
5085
5089
  const closer = container.type === "{" ? "}" : "]";
5086
5090
  const open = container.open;
5087
5091
  const last = this.last && this.last.start >= open.start ? this.last : open;
@@ -5094,20 +5098,20 @@ var Analyzer = class {
5094
5098
  const removed = dangling ? last.end - last.start : 0;
5095
5099
  const sameLine = this.line(open.start) === this.line(Math.max(last.end - 1, 0));
5096
5100
  const own4 = sameLine ? closer : `
5097
- ${indentOf(text, open.start)}${closer}`;
5101
+ ${indentOf(text2, open.start)}${closer}`;
5098
5102
  const inner = this.cascade.last === this.last && removed === 0 ? this.cascade.inserts : [];
5099
5103
  const insert = inner.join("") + own4;
5100
5104
  this.cascade = { last: this.last, inserts: [...inner, own4] };
5101
5105
  const closers = insert.replace(/\s/g, "");
5102
5106
  const key2 = container.path.at(-1);
5103
- const entry = typeof key2 === "string" ? `"${key2}": ${open.text}` : open.text;
5104
- const context = container.type === "{" ? firstEntryOf(text, open) : void 0;
5107
+ const entry2 = typeof key2 === "string" ? `"${key2}": ${open.text}` : open.text;
5108
+ const context = container.type === "{" ? firstEntryOf(text2, open) : void 0;
5105
5109
  const what = container.type === "{" ? "property" : "item";
5106
5110
  this.add({
5107
5111
  code: container.type === "{" ? "unclosed-object" : "unclosed-array",
5108
5112
  range: this.range(open.start, open.end),
5109
5113
  path: [...container.path],
5110
- message: container.type === "{" ? `The \`${entry}\` on line ${this.line(open.start)}${context ? ` (starting \`${context}\`)` : ""} is never closed.` : `The list \`${entry}\` on line ${this.line(open.start)} is never closed.`,
5114
+ message: container.type === "{" ? `The \`${entry2}\` on line ${this.line(open.start)}${context ? ` (starting \`${context}\`)` : ""} is never closed.` : `The list \`${entry2}\` on line ${this.line(open.start)} is never closed.`,
5111
5115
  next: `add \`${closer}\` after its last ${what}${sameLine ? "" : ", on its own line"}.`,
5112
5116
  fixes: [{ id: "insert-closer", title: `Insert ${closers}`, safe: false, edits: [{ offset: insertAt, length: removed, insert }], caret: insertAt + insert.length }]
5113
5117
  });
@@ -5145,22 +5149,22 @@ function keyName(token) {
5145
5149
  function clipTrailing(value) {
5146
5150
  return value.replace(/[ \t]+$/, "");
5147
5151
  }
5148
- function entryText(text, start, end, kind) {
5149
- const raw = text.slice(start, end);
5152
+ function entryText(text2, start, end, kind) {
5153
+ const raw = text2.slice(start, end);
5150
5154
  if (kind === "object" || kind === "array") {
5151
5155
  const head = raw.slice(0, raw.indexOf(kind === "object" ? "{" : "["));
5152
5156
  return clip(`${head}${kind === "object" ? "{\u2026}" : "[\u2026]"}`, 40);
5153
5157
  }
5154
5158
  return clip(raw, 40);
5155
5159
  }
5156
- function firstEntryOf(text, open) {
5157
- const rest = text.slice(open.end, open.end + 200);
5158
- const match = /^\s*("(?:[^"\\\n]|\\.)*"\s*:\s*(?:"(?:[^"\\\n]|\\.)*"|[^\s,{}[\]]+))/.exec(rest);
5159
- return match?.[1] ? clip(match[1], 36) : void 0;
5160
+ function firstEntryOf(text2, open) {
5161
+ const rest = text2.slice(open.end, open.end + 200);
5162
+ const match2 = /^\s*("(?:[^"\\\n]|\\.)*"\s*:\s*(?:"(?:[^"\\\n]|\\.)*"|[^\s,{}[\]]+))/.exec(rest);
5163
+ return match2?.[1] ? clip(match2[1], 36) : void 0;
5160
5164
  }
5161
5165
  function jsonShort(value) {
5162
- const text = JSON.stringify(value);
5163
- return text !== void 0 && text.length <= 32 ? text : void 0;
5166
+ const text2 = JSON.stringify(value);
5167
+ return text2 !== void 0 && text2.length <= 32 ? text2 : void 0;
5164
5168
  }
5165
5169
  function exampleOf(info) {
5166
5170
  const example = info.examples.map(jsonShort).find((item) => item !== void 0) ?? (info.enum?.[0] ? jsonShort(info.enum[0].value) : void 0);
@@ -5169,7 +5173,7 @@ function exampleOf(info) {
5169
5173
  return type === "boolean" ? "true" : type === "number" || type === "integer" ? "1" : type === "object" ? "{ \u2026 }" : type === "array" ? "[ \u2026 ]" : '"\u2026"';
5170
5174
  }
5171
5175
  function listValues(values) {
5172
- const shown = values.slice(0, 5).map((entry) => String(entry.value));
5176
+ const shown = values.slice(0, 5).map((entry2) => String(entry2.value));
5173
5177
  return values.length > 5 ? `one of ${values.length} values (${shown.slice(0, 3).join(", ")}, \u2026)` : `one of ${shown.join(", ")}`;
5174
5178
  }
5175
5179
  function joinAlternatives(items) {
@@ -5253,16 +5257,16 @@ function valueHint(path) {
5253
5257
  }
5254
5258
  return { next, title, insert, caret };
5255
5259
  }
5256
- function analyzeJsonSyntax(text, options = {}) {
5257
- const limit = Math.max(0, options.limit ?? 5);
5258
- const analyzer = new Analyzer(String(text ?? ""));
5260
+ function analyzeJsonSyntax(text2, options2 = {}) {
5261
+ const limit = Math.max(0, options2.limit ?? 5);
5262
+ const analyzer = new Analyzer(String(text2 ?? ""));
5259
5263
  try {
5260
5264
  analyzer.run();
5261
5265
  } catch {
5262
5266
  return [];
5263
5267
  }
5264
5268
  const seen = /* @__PURE__ */ new Set();
5265
- return analyzer.problems.sort((a, b) => a.problem.range.offset - b.problem.range.offset || a.order - b.order).map((entry) => entry.problem).filter((problem) => {
5269
+ return analyzer.problems.sort((a, b) => a.problem.range.offset - b.problem.range.offset || a.order - b.order).map((entry2) => entry2.problem).filter((problem) => {
5266
5270
  const id = `${problem.code}:${problem.range.offset}`;
5267
5271
  if (seen.has(id)) return false;
5268
5272
  seen.add(id);
@@ -5353,16 +5357,1613 @@ function parseSource(source) {
5353
5357
  };
5354
5358
  }
5355
5359
 
5360
+ // src/finding-text-util.ts
5361
+ var WORDS = { object: "an object", array: "a list", string: "text", number: "a number", integer: "a whole number", boolean: "true or false", null: "null" };
5362
+ var KIND_WORDS = { layouts: "layout", themes: "theme", colorSchemes: "color scheme", fontSchemes: "font scheme", narratives: "narrative", audiences: "audience", purposes: "purpose", tones: "tone", languages: "language" };
5363
+ var KIND_EXAMPLES = { layouts: "`pillars` or `acme:hero`", themes: "`minimal` or `acme:bold`", colorSchemes: "`boost` or `acme:night`", fontSchemes: "`aptos` or `acme:brand`", narratives: "`classic-story`", audiences: "`board`", purposes: "`inform`", tones: "`formal`" };
5364
+ var ALIASES = { middle: "center", centre: "center", centered: "center", centred: "center", justify: "left", col: "column", cols: "column", pic: "image", picture: "image", photo: "image", paragraph: "text", bullets: "list", bullet: "list" };
5365
+ function segmentsOf(path) {
5366
+ if (Array.isArray(path)) return path.map(String);
5367
+ if (typeof path !== "string" || path === "" || path === "/") return [];
5368
+ return path.replace(/^\//, "").split("/").map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"));
5369
+ }
5370
+ function pointerOf(segments) {
5371
+ return segments.map((part) => `/${String(part).replaceAll("~", "~0").replaceAll("/", "~1")}`).join("");
5372
+ }
5373
+ var code = (value) => `\`${String(value)}\``;
5374
+ var isIndex = (segment) => /^\d+$/.test(String(segment));
5375
+ function cap(text2) {
5376
+ return text2.startsWith("`") ? text2 : text2.charAt(0).toUpperCase() + text2.slice(1);
5377
+ }
5378
+ function fieldLabel(segments) {
5379
+ if (!segments.length) return "the deck";
5380
+ const last = segments[segments.length - 1];
5381
+ if (isIndex(last)) {
5382
+ const owner = [...segments].reverse().find((segment) => !isIndex(segment));
5383
+ return owner === "slides" ? `slide ${Number(last) + 1}` : owner ? `${owner} entry ${Number(last) + 1}` : `entry ${Number(last) + 1}`;
5384
+ }
5385
+ return code(last);
5386
+ }
5387
+ function containerName(segments) {
5388
+ if (!segments.length) return "the deck";
5389
+ if (segments.length === 2 && segments[0] === "slides" && isIndex(segments[1])) return "a slide";
5390
+ const last = segments[segments.length - 1];
5391
+ return isIndex(last) ? fieldLabel(segments) : code(last);
5392
+ }
5393
+ function describeValue(value) {
5394
+ if (value === void 0) return "missing";
5395
+ if (value === null) return "null";
5396
+ if (Array.isArray(value)) return value.length ? `a list of ${value.length}` : "an empty list";
5397
+ if (typeof value === "object") return "an object";
5398
+ if (typeof value === "string") return value === "" ? "empty" : code(value.length > 30 ? `${value.slice(0, 29)}\u2026` : value);
5399
+ return `${typeof value === "number" ? "a number" : "true or false"} (${code(String(value))})`;
5400
+ }
5401
+ function describeKind(value) {
5402
+ if (typeof value === "string") return value === "" ? "empty text" : `text (${describeValue(value)})`;
5403
+ return describeValue(value);
5404
+ }
5405
+ function typeWords(type) {
5406
+ const list = (Array.isArray(type) ? type : [type]).filter(Boolean).map((entry2) => WORDS[String(entry2)] ?? String(entry2));
5407
+ return [...new Set(list)].join(" or ") || "a different kind of value";
5408
+ }
5409
+ function distance2(a, b) {
5410
+ const x = a.toLowerCase();
5411
+ const y = b.toLowerCase();
5412
+ if (x === y) return 0;
5413
+ const row = Array.from({ length: y.length + 1 }, (_, index) => index);
5414
+ for (let i = 1; i <= x.length; i++) {
5415
+ let previous = row[0];
5416
+ row[0] = i;
5417
+ for (let j = 1; j <= y.length; j++) {
5418
+ const keep3 = row[j];
5419
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (x[i - 1] === y[j - 1] ? 0 : 1));
5420
+ previous = keep3;
5421
+ }
5422
+ }
5423
+ return row[y.length];
5424
+ }
5425
+ function nearest(value, candidates, limit = 5) {
5426
+ const text2 = String(value ?? "");
5427
+ const alias = ALIASES[text2.toLowerCase()];
5428
+ const ranked = candidates.map((candidate) => ({ candidate, score: String(candidate).toLowerCase() === alias ? 0 : distance2(text2, String(candidate)) }));
5429
+ ranked.sort((a, b) => a.score - b.score);
5430
+ const close = ranked.filter((entry2) => entry2.score <= Math.max(2, Math.floor(text2.length / 4))).map((entry2) => entry2.candidate);
5431
+ return { close: close.slice(0, limit), all: ranked.map((entry2) => entry2.candidate) };
5432
+ }
5433
+ function valueList(values, max = 5) {
5434
+ const list = values.map((value) => typeof value === "string" ? value : JSON.stringify(value));
5435
+ if (list.length <= max) return list.length > 1 ? `${list.slice(0, -1).join(", ")} or ${list.at(-1)}` : list.join("");
5436
+ return `one of ${list.length} values (${list.slice(0, 3).join(", ")}, \u2026)`;
5437
+ }
5438
+ var firstSentence = (text2) => /^.*?[.!?](?=\s|$)/.exec(String(text2 ?? "").replace(/\s+/g, " ").trim())?.[0] ?? "";
5439
+ function asNext(text2) {
5440
+ const trimmed = text2.trim();
5441
+ return /^[A-Z][a-z]/.test(trimmed) ? trimmed.charAt(0).toLowerCase() + trimmed.slice(1) : trimmed;
5442
+ }
5443
+ var isRecord2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
5444
+ function readAt(document, segments) {
5445
+ let value = document;
5446
+ for (const segment of segments) {
5447
+ if (Array.isArray(value)) value = value[Number(segment)];
5448
+ else if (isRecord2(value)) value = value[segment];
5449
+ else return void 0;
5450
+ }
5451
+ return value;
5452
+ }
5453
+
5454
+ // src/finding-text-helpers.ts
5455
+ var ENDS = /[.?!]$/;
5456
+ function sentence(text2) {
5457
+ const trimmed = text2.trim();
5458
+ return ENDS.test(trimmed) ? trimmed : `${trimmed}.`;
5459
+ }
5460
+ function us(text2) {
5461
+ return text2.replace(/colour/g, "color").replace(/Colour/g, "Color");
5462
+ }
5463
+ var keep = (text2) => sentence(us(text2));
5464
+ function match(text2, regex) {
5465
+ return regex.exec(text2) ?? void 0;
5466
+ }
5467
+ function unquote(text2) {
5468
+ if (text2 === void 0) return "";
5469
+ try {
5470
+ const value = JSON.parse(text2);
5471
+ if (typeof value === "string") return value;
5472
+ } catch {
5473
+ }
5474
+ return text2.replace(/^['"]|['"]$/g, "");
5475
+ }
5476
+ function unquoteList(text2) {
5477
+ if (text2 === void 0) return [];
5478
+ try {
5479
+ const value = JSON.parse(`[${text2}]`);
5480
+ if (Array.isArray(value) && value.every((entry2) => typeof entry2 === "string")) return value;
5481
+ } catch {
5482
+ }
5483
+ return text2.split(/,\s*/).map((entry2) => entry2.replace(/^['"]|['"]$/g, "").trim()).filter((entry2) => entry2 && entry2 !== "...");
5484
+ }
5485
+ function slideNumber(finding2) {
5486
+ if (finding2.slide !== void 0) return finding2.slide + 1;
5487
+ const found = /^\/slides\/(\d+)(?:\/|$)/.exec(finding2.path);
5488
+ return found ? Number(found[1]) + 1 : void 0;
5489
+ }
5490
+ var slideWord = (finding2) => {
5491
+ const number = slideNumber(finding2);
5492
+ return number === void 0 ? void 0 : `slide ${number}`;
5493
+ };
5494
+ var onSlide = (finding2) => {
5495
+ const word = slideWord(finding2);
5496
+ return word ? ` on ${word}` : "";
5497
+ };
5498
+ function slideRef(finding2, helpers) {
5499
+ const number = slideNumber(finding2);
5500
+ if (number === void 0) return "The deck";
5501
+ const id = finding2.slideId ?? helpers.valueAt(["slides", String(number - 1), "id"]);
5502
+ if (typeof id === "string" && id) return `Slide ${number} (${code(id)})`;
5503
+ const layout = helpers.valueAt(["slides", String(number - 1), "layout"]);
5504
+ if (typeof layout === "string" && layout) return `Slide ${number} (layout ${code(layout)})`;
5505
+ return `Slide ${number}`;
5506
+ }
5507
+ var lastName = (segments) => [...segments].reverse().find((segment) => !isIndex(segment));
5508
+ function dotted(segments) {
5509
+ return segments.reduce((out, segment) => isIndex(segment) ? `${out}[${segment}]` : out ? `${out}.${segment}` : segment, "");
5510
+ }
5511
+ function keysAt(helpers, path) {
5512
+ const value = helpers.valueAt(path);
5513
+ return isRecord2(value) ? Object.keys(value) : [];
5514
+ }
5515
+ function idsAt(helpers, path) {
5516
+ const value = helpers.valueAt(path);
5517
+ return Array.isArray(value) ? value.flatMap((entry2) => isRecord2(entry2) && typeof entry2.id === "string" ? [entry2.id] : []) : [];
5518
+ }
5519
+ function codeList(names2, max = 5, joiner = "and") {
5520
+ const shown = names2.slice(0, max).map(code);
5521
+ const rest = names2.length - shown.length;
5522
+ if (rest > 0) return `${shown.join(", ")} and ${rest} more`;
5523
+ return shown.length > 1 ? `${shown.slice(0, -1).join(", ")} ${joiner} ${shown.at(-1)}` : shown.join("");
5524
+ }
5525
+ function plainList(names2, max = 6) {
5526
+ const shown = names2.slice(0, max);
5527
+ const rest = names2.length - shown.length;
5528
+ return rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
5529
+ }
5530
+ function andList(names2, max = 6) {
5531
+ const shown = names2.slice(0, max);
5532
+ const rest = names2.length - shown.length;
5533
+ if (rest > 0) return `${shown.join(", ")} and ${rest} more`;
5534
+ return shown.length > 1 ? `${shown.slice(0, -1).join(", ")} and ${shown.at(-1)}` : shown.join("");
5535
+ }
5536
+ function chooseNext(value, candidates, alternative = "", max = 3) {
5537
+ const { close } = nearest(value, candidates);
5538
+ const or = alternative ? ` Or ${alternative}.` : "";
5539
+ if (close.length) return `did you mean ${code(close[0])}?${or}`;
5540
+ if (candidates.length === 1) return `use ${code(candidates[0])}${alternative ? `, or ${alternative}` : ""}.`;
5541
+ if (candidates.length) return `pick one of ${codeList(candidates, max, "or")}${alternative ? `, or ${alternative}` : ""}.`;
5542
+ return alternative ? sentence(alternative) : "choose a different value.";
5543
+ }
5544
+ function kindPhrase(kind) {
5545
+ const phrases = { text: "text", code: "code", list: "a list", chart: "a chart", table: "a table", image: "an image", video: "a video", metric: "a metric", quote: "a quote", timeline: "a timeline", group: "a group" };
5546
+ return phrases[kind] ?? `${/^[aeiou]/i.test(kind) ? "an" : "a"} ${kind}`;
5547
+ }
5548
+ function freshName(base, taken = []) {
5549
+ const space = /\s/.test(base);
5550
+ for (let n = 2; n < 100; n++) {
5551
+ const candidate = space ? `${base} ${n}` : `${base}-${n}`;
5552
+ if (!taken.includes(candidate)) return candidate;
5553
+ }
5554
+ return `${base}-2`;
5555
+ }
5556
+ var BLOCK_KINDS = ["chart", "image", "video", "table", "code", "metric", "quote", "timeline", "items", "bullets", "text"];
5557
+ function blockKind(node) {
5558
+ if (!isRecord2(node)) return void 0;
5559
+ if (typeof node.type === "string" && node.type !== "group") return node.type;
5560
+ const found = BLOCK_KINDS.find((key2) => node[key2] !== void 0);
5561
+ if (found === "items" || found === "bullets") return "list";
5562
+ return found;
5563
+ }
5564
+ function helpAsNext(finding2, fallback) {
5565
+ const help = finding2.help?.trim();
5566
+ return help ? sentence(asNext(us(help))) : fallback;
5567
+ }
5568
+ var isText2 = (value) => typeof value === "string" && value !== "";
5569
+ function brief(text2, max = 40) {
5570
+ const flat = text2.replace(/\s+/g, " ").trim();
5571
+ return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
5572
+ }
5573
+
5574
+ // src/finding-text-audit.ts
5575
+ var measuredNumber = (finding2, key2) => {
5576
+ const value = finding2.measured?.[key2];
5577
+ return typeof value === "number" ? value : void 0;
5578
+ };
5579
+ var measuredText = (finding2, key2) => {
5580
+ const value = finding2.measured?.[key2];
5581
+ return typeof value === "string" ? value : void 0;
5582
+ };
5583
+ var num = (value, digits = 2) => String(Math.round(value * 10 ** digits) / 10 ** digits);
5584
+ var textContrast = (finding2) => {
5585
+ const ratio = measuredNumber(finding2, "ratio");
5586
+ const needed = measuredNumber(finding2, "needed");
5587
+ const foreground = measuredText(finding2, "textColor");
5588
+ const background = measuredText(finding2, "backgroundColor");
5589
+ if (ratio === void 0 || needed === void 0) return void 0;
5590
+ const label = match(finding2.message, /^(.+?) colou?r /)?.[1];
5591
+ const subject = label && !/^(Text|Colou?red text)$/.test(label) ? us(label) : "Text";
5592
+ const colors = foreground && background ? ` ${code(foreground)} on ${code(background)}` : "";
5593
+ const large = finding2.measured?.large === true ? " for large text" : finding2.measured?.large === false ? " for normal-size text" : "";
5594
+ const also = measuredNumber(finding2, "alsoAffected");
5595
+ const fix = finding2.fixes?.find((entry2) => entry2.id === "use-readable-color");
5596
+ const replacement = fix?.kind === "patch" && fix.patch?.[0] && "value" in fix.patch[0] ? fix.patch[0].value : void 0;
5597
+ const overrides = finding2.path.endsWith("/color");
5598
+ return {
5599
+ message: `${subject}${colors} has contrast ${num(ratio)}:1; WCAG needs ${num(needed)}:1${large}.${also ? ` ${also} more text element${also === 1 ? "" : "s"} on this slide ${also === 1 ? "has" : "have"} the same problem.` : ""}`,
5600
+ next: typeof replacement === "string" ? replacement === "text" ? `use ${code("text")}, the slide's text color, which is readable here.` : `use ${code(replacement)}, which is readable here.` : overrides ? "pick a color with more contrast against the background, or remove the override so the theme chooses a readable one." : "change the slide background or the color scheme, or give the text an explicit readable color."
5601
+ };
5602
+ };
5603
+ var textOnImage = (finding2) => {
5604
+ const ratio = measuredNumber(finding2, "ratio");
5605
+ const needed = measuredNumber(finding2, "needed");
5606
+ return {
5607
+ message: `Contrast over this picture can't be measured${ratio !== void 0 && needed !== void 0 ? `: against the lightest or darkest possible pixels it is as low as ${num(ratio, 1)}:1, and ${num(needed)}:1 is needed` : ""}.`,
5608
+ next: `add a full-frame ${code("overlay")} to the background image, so the text stays readable on any picture.`
5609
+ };
5610
+ };
5611
+ var ALT_SUBJECT = {
5612
+ Image: "This image",
5613
+ Video: "This video",
5614
+ "Header/footer image": "This header or footer image",
5615
+ "Speaker photo": "This speaker photo",
5616
+ "Quote photo": "This quote photo",
5617
+ "Organization logo": "This organization logo",
5618
+ Picture: "This picture"
5619
+ };
5620
+ var missingAltText = (finding2) => {
5621
+ const found = match(finding2.message, /^(.+?) has (no|blank) alt text\.$/);
5622
+ if (!found) return void 0;
5623
+ const subject = ALT_SUBJECT[found[1]] ?? `This ${found[1].toLowerCase()}`;
5624
+ return {
5625
+ message: `${subject} has ${found[2] === "blank" ? "blank" : "no"} alt text.`,
5626
+ next: `describe it in ${code("alt")}, or write ${code('alt: ""')} if it is only decoration.`
5627
+ };
5628
+ };
5629
+ var poorAltText = (finding2) => {
5630
+ const found = match(finding2.message, /^(?:(Chart) |(.+?) )?alt text ("(?:[^"\\]|\\.)*") (.+)\.$/);
5631
+ if (!found) return void 0;
5632
+ const alt = unquote(found[3]);
5633
+ const chart = found[1] === "Chart";
5634
+ const problem = found[4].replace(/^is a generic word, not what the data shows$/, "is a generic word");
5635
+ return {
5636
+ message: `Alt text ${code(brief(alt, 50))} ${us(problem)}, so it doesn't describe the ${chart ? "chart" : "picture"}.`,
5637
+ next: chart ? "state what the chart shows: its point and the key numbers, in a sentence or two." : `describe what the picture shows, in plain words and without a leading "image of".`
5638
+ };
5639
+ };
5640
+ var missingSlideTitle = (finding2, _context, helpers) => ({
5641
+ message: `${slideRef(finding2, helpers)} has no title, and screen reader users find slides by title.`,
5642
+ next: `write a ${code("title")} for it.`
5643
+ });
5644
+ var duplicateSlideTitle = (finding2) => {
5645
+ const found = match(finding2.message, /^Slide (\d+) has the same title as slide (\d+): ("(?:[^"\\]|\\.)*")\.$/);
5646
+ if (!found) return void 0;
5647
+ return {
5648
+ message: `Slides ${found[2]} and ${found[1]} share the title ${code(unquote(found[3]))}.`,
5649
+ next: "give one of them a title that tells it apart, so people can find each slide by title."
5650
+ };
5651
+ };
5652
+ var readingOrder = (finding2) => {
5653
+ const above = match(finding2.message, /^Content ("(?:[^"\\]|\\.)*") is drawn above the title but is read after it\.$/);
5654
+ if (above) {
5655
+ return {
5656
+ message: `${code(unquote(above[1]))} is drawn above the title but is read after it${onSlide(finding2)}.`,
5657
+ next: "move the title above the content, or choose a layout that puts the title first."
5658
+ };
5659
+ }
5660
+ const found = match(finding2.message, /^Reading order differs from visual order: ("(?:[^"\\]|\\.)*") is read at (position \d+) but appears at (position \d+)\./);
5661
+ if (!found) return void 0;
5662
+ return {
5663
+ message: `${code(unquote(found[1]))} is read at ${found[2]} but appears at ${found[3]}${onSlide(finding2)}, so a screen reader reads the slide out of order.`,
5664
+ next: "reorder the blocks so the reading order follows what people see, or pin them to layout regions."
5665
+ };
5666
+ };
5667
+ var linkText = (finding2) => {
5668
+ const found = match(finding2.message, /^Link text ("(?:[^"\\]|\\.)*") (is blank|is generic|is a long raw URL); the destination is (.+)\.$/);
5669
+ if (!found) return void 0;
5670
+ const text2 = unquote(found[1]);
5671
+ const destination = found[3];
5672
+ const message = found[2] === "is blank" ? "The link text is blank, so a screen reader announces only that there is a link." : found[2] === "is generic" ? `The link text ${code(brief(text2, 40))} doesn't say where it goes.` : "The link text is a long raw web address, which a screen reader reads out character by character.";
5673
+ return { message, next: `say what is at ${destination}, for example the page or file name.` };
5674
+ };
5675
+ var chartColorOnly = (finding2) => {
5676
+ const parts2 = match(finding2.message, /^(Series|Slices) may be hard to tell apart without colou?r/);
5677
+ const noun = parts2?.[1] === "Slices" ? "slices" : "series";
5678
+ const pairs = measuredNumber(finding2, "confusablePairs");
5679
+ const repeat = match(finding2.message, /(\d+) (?:series|slices) exceed the (\d+)-colou?r palette/);
5680
+ const count = measuredNumber(finding2, "series");
5681
+ const message = pairs !== void 0 ? `Some ${noun} differ by color only: ${pairs === 1 ? "one pair looks" : `${pairs} pairs look`} alike to color-blind viewers or in grayscale.${repeat ? ` ${repeat[1]} ${noun} exceed the ${repeat[2]}-color palette, so colors repeat.` : ""}` : `${cap(noun)} differ by color only${count !== void 0 ? ` (${count} ${noun})` : ""}.`;
5682
+ return { message, next: `add data labels or a direct legend, choose more distinct ${code("colors")}, or show fewer ${noun}.` };
5683
+ };
5684
+ var chartColorsAdjusted = (finding2, _context, helpers) => {
5685
+ const index = match(finding2.message, /^Chart colou?r (\d+) /)?.[1] ?? (isText2(helpers.segments.at(-1)) ? String(Number(helpers.segments.at(-1)) + 1) : void 0);
5686
+ const value = helpers.valueAt(helpers.segments);
5687
+ const shown = typeof value === "string" ? value : unquote(match(finding2.message, /^Chart colou?r \d+ \((.+?)\) /)?.[1]);
5688
+ const contrast = measuredNumber(finding2, "contrast");
5689
+ const panel = measuredText(finding2, "panel");
5690
+ const label = index ? `Chart color ${index}` : "A chart color";
5691
+ if (/cannot be resolved/.test(finding2.message)) {
5692
+ return {
5693
+ message: `${label} (${code(shown)}) can't be resolved, so the palette color of that position is used.`,
5694
+ next: `write a hex color like ${code("#1F4E79")}, a scheme color such as ${code("accent1")}, or a ${code("var:")} reference to a declared variable.`
5695
+ };
5696
+ }
5697
+ if (contrast === void 0) return void 0;
5698
+ return {
5699
+ message: `${label} (${code(shown)}) has ${num(contrast, 1)}:1 contrast against the chart panel${panel ? ` (${code(panel)})` : ""}, so it is drawn lighter or darker to reach 3:1.`,
5700
+ next: `pick a color with at least 3:1 contrast against ${panel ? code(panel) : "the panel"}, or remove ${code("colors")} to use the palette.`
5701
+ };
5702
+ };
5703
+ var chartTextAlternative = (finding2) => {
5704
+ const where = match(finding2.message, /^The (.+?) (?:has no alt text|is marked decorative)/)?.[1] ?? "chart";
5705
+ if (/is marked decorative/.test(finding2.message)) {
5706
+ return {
5707
+ message: `The ${where} is marked decorative (empty ${code("alt")}), so assistive technology skips it.`,
5708
+ next: `write a sentence in ${code("alt")} if the chart carries information that people need.`
5709
+ };
5710
+ }
5711
+ return {
5712
+ message: `The ${where} has no text alternative.`,
5713
+ next: `write the message it carries in ${code("alt")}, or add text beside it that states what it shows.`
5714
+ };
5715
+ };
5716
+ var missingLanguage = (_finding, context, helpers) => ({
5717
+ message: "The deck doesn't declare its language.",
5718
+ next: (context.format ?? helpers.format) === "json" ? `add ${code('"language": "en-US"')} (or the language the text is written in) to the deck.` : `add ${code("language: en-US")} (or the language the text is written in) to the deck.`
5719
+ });
5720
+ var chartOptionAdapted = (finding2, _context, helpers) => {
5721
+ const at2 = helpers.segments.indexOf("chart");
5722
+ const option = at2 >= 0 ? helpers.segments.slice(at2 + 1).join(".") : lastName(helpers.segments) ?? "";
5723
+ if (!option) return void 0;
5724
+ const type = at2 >= 0 ? helpers.valueAt([...helpers.segments.slice(0, at2 + 1), "type"]) : void 0;
5725
+ const typeName = typeof type === "string" ? type : match(finding2.message, /^A '(.+?)' chart/)?.[1];
5726
+ const adaptation = finding2.message.includes(";") ? cap(us(finding2.message.slice(finding2.message.indexOf(";") + 1).trim())) : "";
5727
+ return {
5728
+ message: `${code(option)} isn't supported by ${typeName ? `a ${typeName}` : "this"} chart and is adapted.${adaptation ? ` ${sentence(adaptation)}` : ""}`,
5729
+ next: `remove ${code(option)}, or choose a chart type that can show it.`
5730
+ };
5731
+ };
5732
+ var textOverflow = (finding2) => {
5733
+ const message = finding2.message;
5734
+ let subject = `Text${onSlide(finding2)} doesn't fit at the smallest allowed size.`;
5735
+ const footnote = match(message, /^Footnote (\d+) does not fit/);
5736
+ if (/^The caption does not fit/.test(message)) subject = `The caption${onSlide(finding2)} doesn't fit its band at the smallest allowed size.`;
5737
+ else if (footnote) subject = `Footnote ${footnote[1]}${onSlide(finding2)} doesn't fit the footnote area.`;
5738
+ return { message: subject, next: "shorten it, split the slide, or choose a roomier layout." };
5739
+ };
5740
+ var unresolvedContent = (finding2) => ({
5741
+ message: keep(finding2.message),
5742
+ next: "supply what the message asks for, or remove the setting that can't be honored."
5743
+ });
5744
+ var layoutFailed = (finding2, _context, helpers) => {
5745
+ const reason = match(finding2.message, /^Layout failed: (.+)$/)?.[1];
5746
+ return {
5747
+ message: `${slideRef(finding2, helpers)} couldn't be laid out${reason ? `: ${sentence(us(reason)).replace(/\.$/, "")}` : ""}, so the geometry checks were skipped.`,
5748
+ next: "fix the composition setting or content named above."
5749
+ };
5750
+ };
5751
+ var minFontSize = (finding2) => {
5752
+ const size = measuredNumber(finding2, "sizePt");
5753
+ const minimum = measuredNumber(finding2, "minimumPt");
5754
+ if (size === void 0 || minimum === void 0) return void 0;
5755
+ const run = /^Text run is set to/.test(finding2.message);
5756
+ const label = run ? "A text run" : match(finding2.message, /^(.+?) is drawn at/)?.[1] ?? "Text";
5757
+ return {
5758
+ message: `${cap(us(label))}${onSlide(finding2)} ${run ? "is set to" : "is drawn at"} ${num(size, 1)} pt, below the ${num(minimum, 1)} pt readable minimum.`,
5759
+ next: run ? `raise ${code("fontSize")} to at least ${num(minimum, 1)}.` : "shorten the text, give it more room or split the slide, so it can be drawn larger."
5760
+ };
5761
+ };
5762
+ function regionNamesOf(slideLayout, helpers) {
5763
+ if (typeof slideLayout !== "string" || !slideLayout) return void 0;
5764
+ const colon = slideLayout.indexOf(":");
5765
+ const group = colon > 0 ? slideLayout.slice(0, colon) : void 0;
5766
+ const id = colon > 0 ? slideLayout.slice(colon + 1) : slideLayout;
5767
+ const record = (group ? [group] : ["custom", "default"]).map((name) => helpers.valueAt(["catalogs", name, "layouts", id])).find(isRecord2);
5768
+ if (!record) return void 0;
5769
+ const names2 = [];
5770
+ const walk2 = (list) => {
5771
+ if (!Array.isArray(list)) return;
5772
+ for (const entry2 of list) {
5773
+ if (!isRecord2(entry2)) continue;
5774
+ if (typeof entry2.name === "string" && !["title", "subtitle", "tag"].includes(entry2.name)) names2.push(entry2.name);
5775
+ walk2(entry2.regions);
5776
+ }
5777
+ };
5778
+ walk2(record.regions);
5779
+ return { id: slideLayout, names: names2 };
5780
+ }
5781
+ var regionUnknown = (finding2, _context, helpers) => {
5782
+ const region = match(finding2.message, /^The layout has no region '(.+?)'/)?.[1];
5783
+ if (!region) return void 0;
5784
+ const slide = slideNumber(finding2);
5785
+ const layout = slide ? regionNamesOf(helpers.valueAt(["slides", String(slide - 1), "layout"]), helpers) : void 0;
5786
+ const known = layout?.names ?? [];
5787
+ return {
5788
+ message: layout ? `Block pinned to region ${code(region)}; layout ${code(layout.id)} has ${andList(known)}.` : `A block is pinned to region ${code(region)}, which the slide's layout doesn't have.`,
5789
+ next: known.length ? chooseNext(region, known, `remove ${code("region")}`) : `pin it to a region the layout has, or remove ${code("region")}.`
5790
+ };
5791
+ };
5792
+ var regionKind = (finding2) => {
5793
+ const found = match(finding2.message, /^Region '(.+?)' does not accept (\S+) content \(it accepts (.+?)\)/);
5794
+ if (!found) return void 0;
5795
+ return {
5796
+ message: `Region ${code(found[1])} doesn't take ${found[2]} content (it takes ${found[3]}).`,
5797
+ next: `remove ${code("region")} so OPF places the block by its kind, or pin it to a region that takes ${found[2]}.`
5798
+ };
5799
+ };
5800
+ var regionFull = (finding2) => {
5801
+ const found = match(finding2.message, /^Region '(.+?)' already holds its (\d+) block/);
5802
+ if (!found) return void 0;
5803
+ return {
5804
+ message: `Region ${code(found[1])} already holds ${found[2]} block${found[2] === "1" ? "" : "s"}, which is all it takes.`,
5805
+ next: `pin the block to another region with room, or remove ${code("region")}.`
5806
+ };
5807
+ };
5808
+ var layoutUnplaced = (finding2, _context, helpers) => {
5809
+ const kind = blockKind(helpers.valueAt(helpers.segments));
5810
+ const what = kind ? `this ${kind} block` : "this block";
5811
+ const overflow = match(finding2.message, /drawn in '(.+?)' beyond its (\d+) block/);
5812
+ return {
5813
+ message: overflow ? `No region has room for ${what}; it is drawn in ${code(overflow[1])}, beyond its ${overflow[2]} block${overflow[2] === "1" ? "" : "s"}.` : `No region has room for ${what}; it is drawn in the overflow area.`,
5814
+ next: "paginate the slide, choose a layout with more room, or remove a block."
5815
+ };
5816
+ };
5817
+ var furnitureWraps = (finding2) => {
5818
+ const found = match(finding2.message, /^The (.+?) text wraps onto (\d+) lines? \(at most (\d+)\)/);
5819
+ const lines = measuredNumber(finding2, "lines") ?? (found ? Number(found[2]) : void 0);
5820
+ const most = measuredNumber(finding2, "maximumLines") ?? (found ? Number(found[3]) : void 0);
5821
+ const zone = found?.[1] ?? "header or footer";
5822
+ if (lines === void 0) return void 0;
5823
+ return {
5824
+ message: `The ${zone} text wraps onto ${lines} lines${most !== void 0 ? ` (${most} at most)` : ""}; headers and footers should be short.`,
5825
+ next: "shorten it, or move part of it to another zone."
5826
+ };
5827
+ };
5828
+ var fontOutsideScheme = (finding2) => {
5829
+ const found = match(finding2.message, /^Text uses ("(?:[^"\\]|\\.)*"), which is not in the font scheme \((.+)\)\.$/);
5830
+ if (!found) return void 0;
5831
+ return {
5832
+ message: `${code(unquote(found[1]))} isn't in this deck's font scheme (${found[2]}), so it won't follow a scheme change.`,
5833
+ next: `remove ${code("fontFamily")} to use the scheme font, or add the font to the font scheme.`
5834
+ };
5835
+ };
5836
+ var imageResolution = (finding2) => {
5837
+ const width = measuredNumber(finding2, "widthPx");
5838
+ const height = measuredNumber(finding2, "heightPx");
5839
+ const ppi = measuredNumber(finding2, "ppi");
5840
+ const minimum = measuredNumber(finding2, "minimumPpi");
5841
+ if (width === void 0 || height === void 0 || ppi === void 0) return void 0;
5842
+ const shown = match(finding2.message, /shown ([\d.]+) x ([\d.]+) in/);
5843
+ const needed = shown && minimum !== void 0 ? ` about ${Math.ceil(Number(shown[1]) * minimum)}\xD7${Math.ceil(Number(shown[2]) * minimum)} px` : "";
5844
+ return {
5845
+ message: `This image is ${width}\xD7${height} px, shown at about ${ppi} ppi${minimum !== void 0 ? `; ${minimum} are needed to look sharp` : ""}.`,
5846
+ next: needed ? `use a file of${needed} or larger, or show it smaller.` : "use a larger file, or show the image smaller."
5847
+ };
5848
+ };
5849
+ var chartValueNotNumeric = (finding2) => {
5850
+ const value = unquote(match(finding2.message, /^chart value ("(?:[^"\\]|\\.)*") is not a number/)?.[1]);
5851
+ const fix = finding2.fixes?.find((entry2) => entry2.id === "store-chart-numbers");
5852
+ if (!value) return void 0;
5853
+ return {
5854
+ message: `${code(value)} is text, so it is plotted as a gap.`,
5855
+ next: fix ? `${sentence((fix.title.charAt(0).toLowerCase() + fix.title.slice(1)).replace(/\.$/, ""))}` : `write a number (12, -3.5, 1e6) and put units, currency or percent in the column ${code("format")}.`
5856
+ };
5857
+ };
5858
+ var chartMappingAdapted = (finding2) => ({
5859
+ message: keep(cap(finding2.message)),
5860
+ next: "remove that mapping entry, or name a different column."
5861
+ });
5862
+ var chartHighlightAdapted = (finding2) => {
5863
+ const name = unquote(match(finding2.message, /^highlight names \w+ ("(?:[^"\\]|\\.)*"), which is not plotted as a series/)?.[1]);
5864
+ if (!name) return void 0;
5865
+ return {
5866
+ message: `${code(name)} is not plotted as a series, so highlighting it changes nothing. It is the category or X column, or the mapping leaves it out.`,
5867
+ next: "name a series the chart plots, or remove the entry."
5868
+ };
5869
+ };
5870
+ var codeHighlightOutOfRange = (finding2) => {
5871
+ const found = match(finding2.message, /line (\d+) is past the last line \(the code has (\d+) lines?\)/);
5872
+ if (!found) return void 0;
5873
+ return {
5874
+ message: `Highlight line ${found[1]} is past the last line (${found[2]}), so it marks nothing.`,
5875
+ next: `change it to a line from 1 to ${found[2]}, or remove the entry.`
5876
+ };
5877
+ };
5878
+ var codeHighlightRangeReversed = (finding2) => {
5879
+ const found = match(finding2.message, /^The highlight range \[(\d+), (\d+)\] ends before it starts.*write \[(\d+), (\d+)\]/);
5880
+ if (!found) return void 0;
5881
+ return {
5882
+ message: `Highlight ${found[1]}\u2013${found[2]} ends before it starts, so it marks no line.`,
5883
+ next: `write [${found[3]}, ${found[4]}].`
5884
+ };
5885
+ };
5886
+ var variableBuiltinMissing = (finding2, _context, helpers) => {
5887
+ const idParam = isRecord2(finding2.validation?.params) ? finding2.validation.params.id : void 0;
5888
+ const id = (typeof idParam === "string" ? idParam : void 0) ?? match(finding2.message, /^Built-in '(.+?)' has no value/)?.[1] ?? match(finding2.message, /'\{\{(.+?)\}\}'/)?.[1];
5889
+ if (!id) return void 0;
5890
+ const root = id.split(".")[0];
5891
+ const field = id.split(".").at(-1) ?? id;
5892
+ const token = code(`{{${id}}}`);
5893
+ if (id === "slide.section") return { message: `${token} has no value: ${slideRef(finding2, helpers)} has no section.`, next: `give the slide a ${code("section")}, or remove the token.` };
5894
+ if (root === "deck") return { message: `${token} has no value in this document.`, next: `add ${code(field)} to the deck.` };
5895
+ if (id === "speakers") return { message: `${token} has no value in this document.`, next: `add a ${code("speaker")} with a ${code("name")}.` };
5896
+ if (root === "speaker" || root === "organization") return { message: `${token} has no value in this document.`, next: `add ${code(field)} to ${code(root)}.` };
5897
+ return { message: `${token} has no value in this document.`, next: "add the field it reads." };
5898
+ };
5899
+ var durationOutsideNarrative2 = (finding2) => {
5900
+ const found = match(finding2.message, /^The target duration of (\d+(?:\.\d+)?) minutes is outside the (.+?) narrative ("(?:[^"\\]|\\.)*") suits\.$/);
5901
+ if (!found) return void 0;
5902
+ return {
5903
+ message: `A ${found[1]}-minute deck is outside the ${found[2]} minutes that narrative ${code(unquote(found[3]))} suits.`.replace("minutes minutes", "minutes"),
5904
+ next: `change ${code("duration")}, or choose a narrative that suits ${found[1]} minutes.`
5905
+ };
5906
+ };
5907
+ var narrativeDurationRange = (_finding, _context, helpers) => {
5908
+ const range = helpers.valueAt(helpers.segments);
5909
+ if (isRecord2(range) && typeof range.min === "number" && typeof range.max === "number") {
5910
+ return {
5911
+ message: `The duration range has min ${range.min} greater than max ${range.max}.`,
5912
+ next: `swap them: ${code(`min: ${range.max}`)} and ${code(`max: ${range.min}`)}.`
5913
+ };
5914
+ }
5915
+ return { message: "The duration range has min greater than max.", next: `swap ${code("min")} and ${code("max")}.` };
5916
+ };
5917
+ var placeholderText = (finding2) => {
5918
+ const found = match(finding2.message, /^Text looks like (.+?): ("(?:[^"\\]|\\.)*")\.$/);
5919
+ if (!found) return void 0;
5920
+ return {
5921
+ message: `${code(brief(unquote(found[2]), 50))} looks like placeholder text (${found[1]}).`,
5922
+ next: "replace it with the real text."
5923
+ };
5924
+ };
5925
+ var emptyText = (finding2) => {
5926
+ const what = match(finding2.message, /^The (.+) is empty\.$/)?.[1];
5927
+ if (!what) return void 0;
5928
+ return {
5929
+ message: `The ${what}${onSlide(finding2)} is empty.`,
5930
+ next: `write the text, or remove the empty ${what}.`
5931
+ };
5932
+ };
5933
+ var unusedBeat = (finding2, _context, helpers) => {
5934
+ const beat = measuredText(finding2, "beat");
5935
+ const position = measuredNumber(finding2, "position");
5936
+ const beats = measuredNumber(finding2, "beats");
5937
+ if (!beat) return void 0;
5938
+ const narrative = isText2(helpers.valueAt(["narrative"])) ? String(helpers.valueAt(["narrative"])) : unquote(match(finding2.message, /of narrative ("(?:[^"\\]|\\.)*")/)?.[1]);
5939
+ return {
5940
+ message: `Beat ${code(beat)}${position !== void 0 && beats !== void 0 ? ` (${position} of ${beats})` : ""} of narrative ${code(narrative)} has no slide.`,
5941
+ next: `give a slide ${code(`beat: "${beat}"`)}, or ignore this if the beat is optional.`
5942
+ };
5943
+ };
5944
+ var emptySlide = (finding2, _context, helpers) => ({
5945
+ message: `${slideRef(finding2, helpers)} has no content.`,
5946
+ next: `add content or a ${code("title")}, remove the slide, or give it the ${code("blank")} layout if it is meant to be empty.`
5947
+ });
5948
+ var languageTag = (finding2, _context, helpers) => {
5949
+ const value = helpers.valueAt(helpers.segments);
5950
+ const tag = typeof value === "string" ? value : match(finding2.message, /^'(.+?)'/)?.[1];
5951
+ if (!tag) return void 0;
5952
+ const fix = finding2.fixes?.find((entry2) => entry2.id === "use-canonical-tag");
5953
+ const replacement = fix?.kind === "patch" && fix.patch?.[0] && "value" in fix.patch[0] && typeof fix.patch[0].value === "string" ? fix.patch[0].value : match(finding2.help ?? "", /^Write '(.+?)'\.$/)?.[1];
5954
+ const region = match(finding2.message, /names the region '(.+?)', which is not an assigned region/)?.[1];
5955
+ const reason = /not a well-formed/.test(finding2.message) ? "is not a well-formed language tag" : region ? `is not a valid tag: ${code(region)} is not an assigned region` : /not in canonical case/.test(finding2.message) ? "is not in canonical case" : /deprecated/.test(finding2.message) ? "uses a deprecated region" : "is not a valid language tag";
5956
+ return {
5957
+ message: `${code(tag)} ${reason}.`,
5958
+ next: replacement ? `did you mean ${code(replacement)}?` : `write a tag like ${code("en-US")}, ${code("pt-BR")} or ${code("es-419")}.`
5959
+ };
5960
+ };
5961
+ var AUDIT_TEXT = {
5962
+ "opf/text-contrast": textContrast,
5963
+ "opf/text-on-image": textOnImage,
5964
+ "opf/missing-alt-text": missingAltText,
5965
+ "opf/poor-alt-text": poorAltText,
5966
+ "opf/missing-slide-title": missingSlideTitle,
5967
+ "opf/duplicate-slide-title": duplicateSlideTitle,
5968
+ "opf/reading-order": readingOrder,
5969
+ "opf/link-text": linkText,
5970
+ "opf/chart-color-only": chartColorOnly,
5971
+ "opf/chart-colors-adjusted": chartColorsAdjusted,
5972
+ "opf/chart-text-alternative": chartTextAlternative,
5973
+ "opf/missing-language": missingLanguage,
5974
+ "opf/chart-option-adapted": chartOptionAdapted,
5975
+ "opf/text-overflow": textOverflow,
5976
+ "opf/unresolved-content": unresolvedContent,
5977
+ "opf/layout-failed": layoutFailed,
5978
+ "opf/min-font-size": minFontSize,
5979
+ "opf/region-unknown": regionUnknown,
5980
+ "opf/region-kind": regionKind,
5981
+ "opf/region-full": regionFull,
5982
+ "opf/layout-unplaced": layoutUnplaced,
5983
+ "opf/furniture-wraps": furnitureWraps,
5984
+ "opf/font-outside-scheme": fontOutsideScheme,
5985
+ "opf/image-resolution": imageResolution,
5986
+ "opf/chart-value-not-numeric": chartValueNotNumeric,
5987
+ "opf/chart-mapping-adapted": chartMappingAdapted,
5988
+ "opf/chart-highlight-adapted": chartHighlightAdapted,
5989
+ "opf/code-highlight-out-of-range": codeHighlightOutOfRange,
5990
+ "opf/code-highlight-range-reversed": codeHighlightRangeReversed,
5991
+ "opf/variable-builtin-missing": variableBuiltinMissing,
5992
+ "opf/duration-outside-narrative": durationOutsideNarrative2,
5993
+ "opf/narrative-duration-range": narrativeDurationRange,
5994
+ "opf/placeholder-text": placeholderText,
5995
+ "opf/empty-text": emptyText,
5996
+ "opf/unused-beat": unusedBeat,
5997
+ "opf/empty-slide": emptySlide,
5998
+ "opf/language-tag": languageTag
5999
+ };
6000
+
6001
+ // src/finding-text-format.ts
6002
+ var params = (finding2) => isRecord2(finding2.validation?.params) ? finding2.validation.params : {};
6003
+ var text = (value) => typeof value === "string" ? value : void 0;
6004
+ var jsonSyntax = (finding2) => ({
6005
+ message: keep(finding2.message),
6006
+ next: helpAsNext(finding2, "fix the syntax error at the marked position.")
6007
+ });
6008
+ var duplicateKey = (finding2, _context, helpers) => {
6009
+ const key2 = helpers.segments.at(-1) ?? unquote(match(finding2.message, /^Duplicate JSON property (".*")\.$/)?.[1]);
6010
+ const owner = helpers.segments.length > 1 ? `in ${code(helpers.segments.at(-2))}` : "in this object";
6011
+ return {
6012
+ message: `${code(key2)} appears twice ${owner}. JSON keeps only the last value, so the earlier one is ignored.`,
6013
+ next: "keep one of them, or merge their values into one."
6014
+ };
6015
+ };
6016
+ var schemaPlain = (finding2) => ({
6017
+ message: keep(finding2.message),
6018
+ next: helpAsNext(finding2, "check the schema reference for this field.")
6019
+ });
6020
+ var referenceIdDuplicate = (finding2, _context, helpers) => {
6021
+ const id = text(params(finding2).id) ?? unquote(match(finding2.message, /^reference id '(.+?)' is used/)?.[1]);
6022
+ const ids = idsAt(helpers, ["references"]);
6023
+ const index = Number(helpers.segments[1]);
6024
+ const first = ids.indexOf(id);
6025
+ const where = first >= 0 && Number.isInteger(index) && first !== index ? ` (references ${first + 1} and ${index + 1})` : "";
6026
+ return {
6027
+ message: `Two references use the id ${code(id)}${where}.`,
6028
+ next: `give the later one another id (for example ${code(freshName(id, ids))}); runs cite references by id.`
6029
+ };
6030
+ };
6031
+ var citeUnknownReference = (finding2, _context, helpers) => {
6032
+ const id = text(params(finding2).id) ?? unquote(match(finding2.message, /^unknown reference '(.+?)'/)?.[1]);
6033
+ const ids = idsAt(helpers, ["references"]);
6034
+ const add = `add a reference with the id ${code(id)} under ${code("references")}`;
6035
+ return {
6036
+ message: `${code(`cite: "${id}"`)} names a reference that does not exist.`,
6037
+ next: chooseNext(id, ids, add)
6038
+ };
6039
+ };
6040
+ var citeUnsupportedLocation = (finding2) => {
6041
+ const where = match(finding2.message, /^cite and footnote are not supported in ([^;]+);/)?.[1];
6042
+ const inReferences = /^reference text cannot cite/.test(finding2.message);
6043
+ if (inReferences)
6044
+ return { message: `${code("cite")} and ${code("footnote")} can't be used inside the text of a reference.`, next: `remove ${code("cite")} and ${code("footnote")} from that run.` };
6045
+ return {
6046
+ message: `${code("cite")} and ${code("footnote")} are drawn only in titles, text, bullets, list items and quotes${where ? `, not in ${where}` : ""}.`,
6047
+ next: "move the cited run into a text block, or remove the citation here."
6048
+ };
6049
+ };
6050
+ var captionUnsupportedPayload = (finding2) => {
6051
+ const type = text(params(finding2).type) ?? match(finding2.message, /; this payload is '(.+?)'/)?.[1];
6052
+ const kind = type ?? (/a group cannot carry one/.test(finding2.message) ? "group" : void 0);
6053
+ const is = kind ? kindPhrase(kind) : "not one of those";
6054
+ return {
6055
+ message: `A ${code("caption")} belongs to one image, chart, table or video; this block is ${is}.`,
6056
+ next: `put the ${code("caption")} on the image, chart, table or video it describes, or remove it.`
6057
+ };
6058
+ };
6059
+ var imageOptionUnsupportedPayload = (finding2) => {
6060
+ const field = text(params(finding2).field) ?? match(finding2.message, /^(\w+) is only valid on an image payload/)?.[1];
6061
+ const type = text(params(finding2).type) ?? match(finding2.message, /; this payload is '(.+?)'/)?.[1];
6062
+ const kind = type ?? (/a group cannot carry them/.test(finding2.message) ? "group" : void 0);
6063
+ if (!field) return void 0;
6064
+ return {
6065
+ message: `${code(field)} only applies to pictures; this block is ${kind ? kindPhrase(kind) : "not a picture"}.`,
6066
+ next: `remove ${code(field)}, or move it onto an image.`
6067
+ };
6068
+ };
6069
+ var imagePlacementInvalid = (finding2) => {
6070
+ const { edge, blocks } = params(finding2);
6071
+ if (typeof edge === "string" && Array.isArray(blocks) && blocks.length === 2) {
6072
+ return {
6073
+ message: `Blocks ${Number(blocks[0]) + 1} and ${Number(blocks[1]) + 1} are both placed on the ${edge} edge, and a slide edge takes one placed image.`,
6074
+ next: `move one of them to another edge, or remove its ${code("placement")}.`
6075
+ };
6076
+ }
6077
+ return {
6078
+ message: `${code("placement")} can't be used here: a block inside a group or a region can't bleed to a slide edge.`,
6079
+ next: `use ${code("placement")} on a top-level block of the slide, one per slide edge, or remove it.`
6080
+ };
6081
+ };
6082
+ var layoutPlaceholderGroup = (finding2) => {
6083
+ const detail = finding2.message.trim();
6084
+ return {
6085
+ message: detail ? `${cap(keep(detail))} A group holds body regions only, not headings.` : "A placeholder group nests at most three levels and holds body regions only, not headings.",
6086
+ next: "flatten the group, and keep the title, subtitle and tag at the layout's top level."
6087
+ };
6088
+ };
6089
+ function datasetOf(helpers) {
6090
+ const at2 = helpers.segments.findIndex((segment) => segment === "fields" || segment === "columns" || segment === "mapping" || segment === "highlight");
6091
+ const parent = at2 > 0 ? helpers.segments.slice(0, at2) : helpers.segments.slice(0, -1);
6092
+ for (const candidate of [parent, [...parent, "data"]]) {
6093
+ const value = helpers.valueAt([...candidate, "dataset"]);
6094
+ if (isText2(value)) return value;
6095
+ }
6096
+ return void 0;
6097
+ }
6098
+ var datasetUnknown = (finding2, _context, helpers) => {
6099
+ const id = unquote(match(finding2.message, /^unknown dataset '(.+?)'/)?.[1]) || String(helpers.valueAt(helpers.segments) ?? "");
6100
+ if (!id) return void 0;
6101
+ const ids = keysAt(helpers, ["datasets"]);
6102
+ const here = ids.length ? `Datasets in this deck: ${plainList(ids)}.` : "This deck has no datasets.";
6103
+ return {
6104
+ message: `${code(`dataset: "${id}"`)} does not exist. ${here}`,
6105
+ next: chooseNext(id, ids, `add ${code(`datasets.${id}`)}`)
6106
+ };
6107
+ };
6108
+ var datasetFieldUnknown = (finding2, _context, helpers) => {
6109
+ const found = match(finding2.message, /^field ("(?:[^"\\]|\\.)*") is not a column of the dataset; use one of (.+)$/);
6110
+ if (!found) return void 0;
6111
+ const field = unquote(found[1]);
6112
+ const columns = unquoteList(found[2]);
6113
+ const dataset = datasetOf(helpers);
6114
+ return {
6115
+ message: `${code(field)} is not a column of ${dataset ? code(dataset) : "the dataset"}. Columns: ${plainList(columns)}.`,
6116
+ next: chooseNext(field, columns)
6117
+ };
6118
+ };
6119
+ var dataColumnDuplicate = (finding2, _context, helpers) => {
6120
+ const name = unquote(match(finding2.message, /^column name ("(?:[^"\\]|\\.)*") is used by an earlier column/)?.[1]);
6121
+ if (!name) return void 0;
6122
+ const columns = helpers.valueAt(helpers.segments.slice(0, -1));
6123
+ const taken = Array.isArray(columns) ? columns.map((entry2) => isRecord2(entry2) ? String(entry2.name) : String(entry2)) : [];
6124
+ return {
6125
+ message: `Two columns are named ${code(name)}. Series and mappings address columns by name.`,
6126
+ next: `rename the second one (for example ${code(freshName(name, taken))}).`
6127
+ };
6128
+ };
6129
+ var chartMappingUnknownColumn = (finding2) => {
6130
+ const found = match(finding2.message, /^mapping names column ("(?:[^"\\]|\\.)*"), which the chart data does not have; use one of (.+)$/);
6131
+ if (!found) return void 0;
6132
+ const name = unquote(found[1]);
6133
+ const columns = unquoteList(found[2]);
6134
+ return {
6135
+ message: `The chart mapping names column ${code(name)}, which the data does not have. Columns: ${plainList(columns)}.`,
6136
+ next: chooseNext(name, columns)
6137
+ };
6138
+ };
6139
+ var numberFormatInvalid = (finding2, _context, helpers) => {
6140
+ const value = helpers.valueAt(helpers.segments);
6141
+ const reason = match(finding2.message, /^Number format '.*?' ((?:needs|scales|has|uses|asks|allows|is)\b.+)$/)?.[1];
6142
+ const subject = typeof value === "string" && value ? `${code(value)} is not a number format OPF can read` : "This is not a number format OPF can read";
6143
+ return {
6144
+ message: `${subject}.${reason ? ` It ${sentence(us(reason))}` : ""}`,
6145
+ next: `write a format such as ${code("0.0")}, ${code("#,##0")}, ${code("0%")} or ${code("$#,##0.00")}.`
6146
+ };
6147
+ };
6148
+ var chartHighlightUnknownName = (finding2) => {
6149
+ const series = match(finding2.message, /^highlight names series ("(?:[^"\\]|\\.)*"), which the chart data does not have as a column; use one of (.+)$/);
6150
+ if (series) {
6151
+ const name = unquote(series[1]);
6152
+ const columns = unquoteList(series[2]);
6153
+ return { message: `${code(`highlight: "${name}"`)} matches no series. Available: ${plainList(columns)}.`, next: chooseNext(name, columns) };
6154
+ }
6155
+ const category = match(finding2.message, /^highlight names category ("(?:[^"\\]|\\.)*"), which no row of the chart data has as its label(?:; the labels are (.+))?$/);
6156
+ if (category) {
6157
+ const name = unquote(category[1]);
6158
+ const labels = unquoteList(category[2]);
6159
+ return {
6160
+ message: `${code(`highlight: "${name}"`)} matches no category.${labels.length ? ` Available: ${plainList(labels)}${/\.\.\.$/.test(category[2] ?? "") ? ", \u2026" : ""}.` : ""}`,
6161
+ next: labels.length ? chooseNext(name, labels) : "name a category that appears in the chart data, or remove the entry."
6162
+ };
6163
+ }
6164
+ return void 0;
6165
+ };
6166
+ var tokenOf = (finding2, id) => finding2.message.includes(`var:${id}`) ? code(`var:${id}`) : code(`{{${id}}}`);
6167
+ var SAMPLE_BY_KIND = {
6168
+ number: "a plain number such as 42",
6169
+ date: "a date such as 2026-10-24",
6170
+ color: "a color such as #1F4E79",
6171
+ url: "a web address such as https://example.com",
6172
+ image: "an `asset:` reference or an https:// address",
6173
+ list: "a list of values",
6174
+ text: "some text"
6175
+ };
6176
+ var variableInvalidValue = (finding2) => {
6177
+ const found = match(finding2.message, /^Value for '(.+?)' \((\w+)\): (.+?)\.?$/);
6178
+ const id = text(params(finding2).id) ?? found?.[1];
6179
+ if (!id) return void 0;
6180
+ const kind = found?.[2] ?? "";
6181
+ return {
6182
+ message: `The value given for ${code(id)} does not fit its type${kind ? ` (${kind})` : ""}${found?.[3] ? `: ${us(found[3])}` : ""}.`,
6183
+ next: `give it ${SAMPLE_BY_KIND[kind] ?? "a value of the declared type"}.`
6184
+ };
6185
+ };
6186
+ var BUILTINS = ["deck.name", "deck.description", "deck.author", "deck.slideCount", "slide.number", "slide.section", "speakers", "speaker.name", "speaker.title", "speaker.email", "speaker.phone", "speaker.bio", "speaker.photo", "organization.name", "organization.legalName", "organization.tagline", "organization.domain", "organization.email", "organization.phone"];
6187
+ var variableUnknownBuiltin = (finding2) => {
6188
+ const id = text(params(finding2).id) ?? match(finding2.message, /^'(.+?)' is not a built-in variable/)?.[1];
6189
+ if (!id) return void 0;
6190
+ const mentioned = [...finding2.message.matchAll(/'((?:deck|slide|speaker|organization)[A-Za-z0-9_.-]*|speakers)'/g)].map((entry2) => entry2[1]).filter((name) => name !== id);
6191
+ const candidates = [.../* @__PURE__ */ new Set([...mentioned, ...BUILTINS])];
6192
+ const { close } = nearest(id, candidates);
6193
+ const whole = /^'var:/.test(finding2.message) || finding2.message.includes(`'var:${id}'`);
6194
+ const message = whole ? `${code(`var:${id}`)} is not supported: ${code(id)} is a slide-scoped built-in, written only as the inline token ${code(`{{${id}}}`)}.` : `${tokenOf(finding2, id)} is not a built-in variable.`;
6195
+ if (whole) return { message, next: `write ${code(`{{${id}}}`)} inside the text instead.` };
6196
+ return { message, next: close.length ? `did you mean ${code(close[0])}?` : `use one of ${codeList(mentioned.length ? mentioned : BUILTINS.slice(0, 4), 4, "or")}, or declare ${code(id)} under ${code("variables")}.` };
6197
+ };
6198
+ var variableFormat = (finding2, _context, helpers) => {
6199
+ const id = text(params(finding2).id);
6200
+ const declared = id ? helpers.valueAt(["variables", id, "format"]) : void 0;
6201
+ const reason = match(finding2.message, /^(?:Number|Date) format '.*?' (.+)$/)?.[1];
6202
+ const kind = id ? helpers.valueAt(["variables", id, "type"]) : void 0;
6203
+ return {
6204
+ message: `The format${typeof declared === "string" ? ` ${code(declared)}` : ""} does not apply to ${kind ? `a ${String(kind)}` : "this"} value${id ? ` (${code(id)})` : ""}${reason ? `: it ${sentence(us(reason)).replace(/\.$/, "")}` : ""}.`,
6205
+ next: `change the ${code("format")} of ${id ? code(id) : "the variable"} to one that fits its values, such as ${kind === "date" ? code("MMMM d, yyyy") : code("#,##0.0")}.`
6206
+ };
6207
+ };
6208
+ var variableUnfilled = (finding2, _context, helpers) => {
6209
+ const token = match(finding2.message, /^Template variable ("\{\{.+\}\}") is still in the text\.$/);
6210
+ if (token) {
6211
+ const literal = unquote(token[1]);
6212
+ const id2 = /\{\{\s*([a-z][a-z0-9-]*)/.exec(literal)?.[1] ?? literal;
6213
+ return {
6214
+ message: `${code(literal)} is still in the text, so it would show literally.`,
6215
+ next: `declare ${code(id2)} under ${code("variables")} and give it a value, or replace the token with the real text.`
6216
+ };
6217
+ }
6218
+ const id = text(params(finding2).id) ?? match(finding2.message, /^(?:required variable '(.+?)'|Template variable ("(?:[^"\\]|\\.)*")) has no value/)?.[1] ?? helpers.segments.at(-1);
6219
+ const inTemplate = /^Template variable/.test(finding2.message);
6220
+ if (!id) return void 0;
6221
+ const name = id.startsWith('"') ? unquote(id) : id;
6222
+ if (inTemplate) {
6223
+ return {
6224
+ message: `Template variable ${code(name)} has no value yet.`,
6225
+ next: `fill it (${code("opf fill")}) before using the deck, or leave it while you edit the template.`
6226
+ };
6227
+ }
6228
+ return {
6229
+ message: `Required variable ${code(name)} has no value, so ${code(`{{${name}}}`)} would show literally.`,
6230
+ next: `give it a ${code("value")}, or mark the deck ${code("template: true")}.`
6231
+ };
6232
+ };
6233
+ var variableUnknown = (finding2, _context, helpers) => {
6234
+ const id = text(params(finding2).id) ?? match(finding2.message, /^'\{\{(.+?)\}\}' names no declared variable/)?.[1];
6235
+ if (!id) return void 0;
6236
+ const declared = keysAt(helpers, ["variables"]);
6237
+ const { close } = nearest(id, declared);
6238
+ return {
6239
+ message: `${code(`{{${id}}}`)} is not declared under ${code("variables")}.`,
6240
+ next: close.length ? `did you mean ${code(close[0])}? Or declare ${code(id)} under ${code("variables")}.` : `declare ${code(id)} under ${code("variables")}, or write ${code("\\{{")} for literal braces.`
6241
+ };
6242
+ };
6243
+ var variableUnknownValue = (finding2, _context, helpers) => {
6244
+ const id = text(params(finding2).id) ?? match(finding2.message, /^A value was supplied for '(.+?)'/)?.[1];
6245
+ if (!id) return void 0;
6246
+ const declared = keysAt(helpers, ["variables"]);
6247
+ const { close } = nearest(id, declared);
6248
+ return {
6249
+ message: `A value was supplied for ${code(id)}, which is not declared.`,
6250
+ next: close.length ? `did you mean ${code(close[0])}? Or declare ${code(id)} under ${code("variables")}.` : `declare ${code(id)} under ${code("variables")}, or remove the value.`
6251
+ };
6252
+ };
6253
+ var variableUnused = (finding2, _context, helpers) => {
6254
+ const id = text(params(finding2).id) ?? helpers.segments.at(-1);
6255
+ if (!id) return void 0;
6256
+ return {
6257
+ message: `Variable ${code(id)} is declared but never used.`,
6258
+ next: `use it as ${code(`{{${id}}}`)} or ${code(`var:${id}`)} where it belongs, or remove its declaration.`
6259
+ };
6260
+ };
6261
+ var variableReferenceUnknown = (finding2, _context, helpers) => {
6262
+ const id = text(params(finding2).id) ?? match(finding2.message, /^unknown variable '(.+?)'/)?.[1];
6263
+ if (!id) return void 0;
6264
+ const declared = keysAt(helpers, ["variables"]);
6265
+ const { close } = nearest(id, declared);
6266
+ return {
6267
+ message: `${code(`var:${id}`)} is not declared, so a default color is used.`,
6268
+ next: close.length ? `did you mean ${code(`var:${close[0]}`)}? Or declare ${code(id)} under ${code("variables")}.` : `declare ${code(id)} under ${code("variables")}, or use another color.`
6269
+ };
6270
+ };
6271
+ var SCHEME_COLORS = ["accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "dark1", "dark2", "light1", "light2", "hyperlink", "followedHyperlink", "primary", "secondary", "accent", "background", "surface", "surfaceAlt", "text", "textSecondary"];
6272
+ var runColorUnrecognized = (finding2, _context, helpers) => {
6273
+ const value = text(params(finding2).color) ?? match(finding2.message, /^run color '(.+?)' is not/)?.[1] ?? text(helpers.valueAt(helpers.segments));
6274
+ if (!value) return void 0;
6275
+ const { close } = nearest(value, SCHEME_COLORS);
6276
+ return {
6277
+ message: `${code(`color: "${value}"`)} is not a hex color, a scheme color or a ${code("var:")} reference, so the theme color is used.`,
6278
+ next: close.length ? `did you mean ${code(close[0])}?` : `write a hex color like ${code("#1F4E79")}, a scheme color such as ${code("accent1")}, or ${code("var:")} and a variable id.`
6279
+ };
6280
+ };
6281
+ var numberingStartIgnored = (_finding, _context, helpers) => {
6282
+ const owner = lastName(helpers.segments);
6283
+ return {
6284
+ message: `${code("start")} has no effect without ${code("numbering")} on ${owner ? `the ${code(owner)} list` : "this list"}.`,
6285
+ next: `add ${code("numbering")} to the list, or remove ${code("start")}.`
6286
+ };
6287
+ };
6288
+ var slideSizeOutOfRange = (finding2) => {
6289
+ const { widthInches, heightInches } = params(finding2);
6290
+ const size = typeof widthInches === "number" && typeof heightInches === "number" ? `A ${widthInches}\xD7${heightInches} in slide` : "This slide size";
6291
+ return {
6292
+ message: `${size} is outside PowerPoint's 1 to 56 inch range, so PowerPoint may reject the export.`,
6293
+ next: `set ${code("design.dimensions")} to sizes between 1 and 56 inches, or to a preset.`
6294
+ };
6295
+ };
6296
+ var undeclaredCatalog = (finding2) => {
6297
+ const found = match(finding2.message, /^Reference ("(?:[^"\\]|\\.)*") names catalog group ("(?:[^"\\]|\\.)*"), which/);
6298
+ if (!found) return void 0;
6299
+ const reference = unquote(found[1]);
6300
+ const group = unquote(found[2]);
6301
+ const bare = reference.includes(":") ? reference.slice(reference.indexOf(":") + 1) : reference;
6302
+ return {
6303
+ message: `${code(reference)} uses catalog group ${code(group)}, which ${code("catalogs")} does not declare, so it can never resolve.`,
6304
+ next: `declare ${code(`catalogs.${group}`)}, or write ${code(bare)}.`
6305
+ };
6306
+ };
6307
+ var assetReference = (finding2, _context, helpers) => {
6308
+ const id = unquote(match(finding2.message, /^Asset ("(?:[^"\\]|\\.)*") is missing/)?.[1]);
6309
+ if (!id) return void 0;
6310
+ const ids = keysAt(helpers, ["assets"]);
6311
+ const suggested = (finding2.suggestions ?? []).map((entry2) => String(entry2.value).replace(/^asset:/, ""));
6312
+ const known = ids.length ? ids : suggested;
6313
+ const { close } = nearest(id, known);
6314
+ const add = `add ${code(`assets.${id}`)}`;
6315
+ return {
6316
+ message: `${code(`asset:${id}`)} is not in ${code("assets")}.${known.length ? ` Assets: ${plainList(known)}.` : " This deck has no assets."}`,
6317
+ next: close.length ? `did you mean ${code(`asset:${close[0]}`)}? Or ${add}.` : known.length ? `pick one of ${plainList(known.slice(0, 4))}, or ${add}.` : `${add}.`
6318
+ };
6319
+ };
6320
+ var assetCycle = (finding2) => {
6321
+ const chainText = match(finding2.message, /^Asset references form a cycle: (.+)\.$/)?.[1];
6322
+ const chain = chainText ? chainText.split(" \u2192 ").map(unquote) : [];
6323
+ const first = chain[0];
6324
+ if (!first || chain.length < 2) return void 0;
6325
+ const through = chain.slice(1, -1);
6326
+ const how = through.length ? ` through ${codeList(through)}` : "";
6327
+ return {
6328
+ message: `Asset ${code(first)} points back to itself${how}.`,
6329
+ next: "give one link a real source (an https:// address or embedded data) instead of an `asset:` reference."
6330
+ };
6331
+ };
6332
+ var KIND_SINGULAR = (kind) => KIND_WORDS[kind] ?? kind.replace(/s$/, "");
6333
+ var catalogRecord = (finding2, context) => {
6334
+ const [root, group, kind, id] = finding2.path.split("/").slice(1).map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"));
6335
+ if (root !== "catalogs" || !kind || !id) return void 0;
6336
+ const inner = describeFinding({ ...finding2, ruleId: "opf/schema" }, context);
6337
+ const registered = finding2.scope === "context";
6338
+ const record = `${registered ? "The registered" : "The embedded"} ${KIND_SINGULAR(kind)} ${code(id)}`;
6339
+ return {
6340
+ message: `${record} is invalid: ${inner.message.charAt(0).toLowerCase()}${inner.message.slice(1)}`,
6341
+ next: inner.next ?? `fix the record; references to it are reported too.`
6342
+ };
6343
+ };
6344
+ function layoutSpot(finding2) {
6345
+ const parts2 = finding2.path.split("/").slice(1).map((part) => part.replaceAll("~1", "/").replaceAll("~0", "~"));
6346
+ if (parts2[0] === "catalogs" && parts2[2] === "layouts") return { id: parts2[3], spot: dotted(parts2.slice(4)) };
6347
+ return { spot: dotted(parts2) };
6348
+ }
6349
+ var layoutTemplate = (finding2) => {
6350
+ const { id, spot } = layoutSpot(finding2);
6351
+ const detail = finding2.message.trim().replace(/\.$/, "");
6352
+ return {
6353
+ message: `The layout's region list is malformed${spot ? ` at ${code(spot)}` : ""}${id ? ` (layout ${code(id)})` : ""}: ${us(detail)}.`,
6354
+ next: spot ? `correct ${code(spot)} as described; the layout is not used until it is valid.` : "correct the layout as described; it is not used until it is valid."
6355
+ };
6356
+ };
6357
+ var layoutRegion = (finding2) => {
6358
+ const { id, spot } = layoutSpot(finding2);
6359
+ const detail = finding2.message.trim().replace(/\.$/, "");
6360
+ const reserved = match(detail, /^'(.+?)' is reserved and cannot name a body region \(reserved: (.+)\)$/);
6361
+ if (reserved) {
6362
+ return {
6363
+ message: `Region name ${code(reserved[1])} is reserved, so it can't name a body region${id ? ` of layout ${code(id)}` : ""}. Reserved names: ${reserved[2]}.`,
6364
+ next: `rename it, for example ${code(`${reserved[1]}-area`)}.`
6365
+ };
6366
+ }
6367
+ const twice = match(detail, /^region name '(.+?)' is used twice/);
6368
+ if (twice) {
6369
+ return {
6370
+ message: `Region name ${code(twice[1])} is used twice; names are unique across the whole layout.`,
6371
+ next: `rename one of them (for example ${code(freshName(twice[1]))}).`
6372
+ };
6373
+ }
6374
+ const badName = match(detail, /^'(.+?)' is not a region name/);
6375
+ if (badName) {
6376
+ const bad = badName[1];
6377
+ return {
6378
+ message: `${code(bad)} is not a valid region name: use lowercase letters, digits and hyphens, starting with a letter.`,
6379
+ next: `rename it, for example ${code(bad.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+|-+$/g, "") || "body")}.`
6380
+ };
6381
+ }
6382
+ return {
6383
+ message: `A region of the layout is not valid${spot ? ` at ${code(spot)}` : ""}${id ? ` (layout ${code(id)})` : ""}: ${us(detail)}.`,
6384
+ next: spot ? `correct ${code(spot)} as described.` : "correct the layout as described."
6385
+ };
6386
+ };
6387
+ var FALLBACK = (kind) => kind === "layouts" ? "the slide is laid out automatically" : "the engine default is used";
6388
+ var unresolvedReference2 = (finding2, _context, helpers) => {
6389
+ const found = match(finding2.message, /^([A-Z][A-Za-z ]*?) '(.+?)' resolves nowhere/);
6390
+ const kind = finding2.lookup?.[2] ?? "";
6391
+ const reference = found?.[2] ?? (() => {
6392
+ const value = helpers.valueAt(helpers.segments);
6393
+ return typeof value === "string" ? value : void 0;
6394
+ })();
6395
+ if (!reference) return void 0;
6396
+ const noun = cap(KIND_SINGULAR(kind || (found?.[1] ?? "catalog record").toLowerCase()));
6397
+ const colon = reference.indexOf(":");
6398
+ const group = colon > 0 ? reference.slice(0, colon) : void 0;
6399
+ const bare = colon > 0 ? reference.slice(colon + 1) : reference;
6400
+ const embeddedIn = group ? [group] : ["custom", "default"];
6401
+ const embedded = kind && embeddedIn.some((name) => isRecord2(helpers.valueAt(["catalogs", name, kind, bare])));
6402
+ const suggestions = (finding2.suggestions ?? []).slice(0, 5).map((entry2) => String(entry2.value));
6403
+ if (embedded) {
6404
+ return {
6405
+ message: `${noun} ${code(reference)} is embedded but invalid, so it is not used and ${FALLBACK(kind)}.`,
6406
+ next: `fix the record named in the other problems, or remove the reference.`
6407
+ };
6408
+ }
6409
+ const message = group && /declares no catalogs\./.test(finding2.message) ? `${noun} ${code(reference)} uses catalog group ${code(group)}, which the deck does not declare, so ${FALLBACK(kind)}.` : `${noun} ${code(reference)} is not embedded or in a registered catalog, so ${FALLBACK(kind)}.`;
6410
+ const { close } = nearest(reference, suggestions);
6411
+ const remove = kind === "layouts" ? "remove the reference to let OPF pick" : "remove the reference";
6412
+ return {
6413
+ message,
6414
+ next: close.length ? `did you mean ${code(close[0])}?` : suggestions.length ? `choose one of ${codeList(suggestions, 5, "or")}, embed the record under ${code("catalogs.custom")}, or ${remove}.` : `embed the record under ${code("catalogs.custom")}, register the catalog that defines it, or ${remove}.`
6415
+ };
6416
+ };
6417
+ var catalogRecordNotInSource = (finding2) => {
6418
+ const found = match(finding2.message, /^Record ("(?:[^"\\]|\\.)*") is embedded under catalogs\.([^.]+)\.(\w+), but the catalog registered for (.+?) has no /);
6419
+ if (!found) return void 0;
6420
+ const id = unquote(found[1]);
6421
+ const group = found[2];
6422
+ return {
6423
+ message: `${code(id)} says it comes from ${found[4]}, but that catalog has no such record. It is treated as your own.`,
6424
+ next: `move it to ${code("catalogs.custom")}${group === "default" ? "" : ` and drop the ${code(`${group}:`)} prefix from its references`}, or use an id that the catalog publishes.`
6425
+ };
6426
+ };
6427
+ var unusedReference = (finding2, _context, helpers) => {
6428
+ const id = text(params(finding2).id) ?? match(finding2.message, /^reference '(.+?)' is never cited/)?.[1];
6429
+ if (!id) return void 0;
6430
+ return {
6431
+ message: `Reference ${code(id)} is never cited.`,
6432
+ next: `cite it from a text, bullet or list item (${code(`cite: "${id}"`)}), or remove it.`
6433
+ };
6434
+ };
6435
+ var unusedDataset = (finding2, _context, helpers) => {
6436
+ const id = helpers.segments.at(-1) ?? unquote(match(finding2.message, /^Dataset ("(?:[^"\\]|\\.)*") is never referenced/)?.[1]);
6437
+ if (!id) return void 0;
6438
+ return {
6439
+ message: `Dataset ${code(id)} is not used by any chart or table.`,
6440
+ next: `point a chart or table at it with ${code(`dataset: "${id}"`)}, or remove it.`
6441
+ };
6442
+ };
6443
+ var unknownBeat = (finding2, _context, helpers) => {
6444
+ const found = match(finding2.message, /^Slide (\d+) names beat ("(?:[^"\\]|\\.)*"), which narrative ("(?:[^"\\]|\\.)*") does not define/);
6445
+ if (!found) return void 0;
6446
+ const beat = unquote(found[2]);
6447
+ const narrative = unquote(found[3]);
6448
+ const beats = (finding2.suggestions ?? []).map((entry2) => String(entry2.value));
6449
+ const { close } = nearest(beat, beats);
6450
+ return {
6451
+ message: `Slide ${found[1]} names beat ${code(beat)}, which narrative ${code(narrative)} does not define.${beats.length ? ` Its beats include ${plainList(beats.slice(0, 5))}.` : ""}`,
6452
+ next: close.length ? `did you mean ${code(close[0])}?` : beats.length ? `use one of ${codeList(beats, 5, "or")}, or remove ${code("beat")}.` : `use a beat the narrative defines, or remove ${code("beat")}.`
6453
+ };
6454
+ };
6455
+ var slideThemeDimensions = (finding2) => {
6456
+ const { slideDimensions, deckDimensions } = params(finding2);
6457
+ const nice = (value) => {
6458
+ const found = /^([\d.]+)x([\d.]+) px$/.exec(String(value ?? ""));
6459
+ return found ? `${Math.round(Number(found[1]))}\xD7${Math.round(Number(found[2]))} px` : void 0;
6460
+ };
6461
+ const slide = nice(slideDimensions);
6462
+ const deck = nice(deckDimensions);
6463
+ return {
6464
+ message: `This slide's theme sets another slide size${slide && deck ? ` (${slide}; the deck is ${deck})` : ""}, which is ignored: a deck has one size.`,
6465
+ next: `set ${code("design.dimensions")} on the deck, or give every slide's theme the same dimensions.`
6466
+ };
6467
+ };
6468
+ var contract = (finding2, _context, helpers) => {
6469
+ const actual = helpers.valueAt(helpers.segments);
6470
+ const allowed = (finding2.suggestions ?? []).filter((entry2) => entry2.origin === "contract").map((entry2) => entry2.value);
6471
+ const standard = match(finding2.message, /^This value is outside the allowed values for (.+)\.$/);
6472
+ const label = helpers.segments.length ? fieldLabel(helpers.segments) : "the deck";
6473
+ const allowedWords = allowed.length ? plainList(allowed.map((value) => typeof value === "string" ? value : JSON.stringify(value)), 6) : void 0;
6474
+ const message = standard ? `${cap(label)} is ${actual === void 0 ? "missing or unset" : describeValue(actual)}, which this host's policy does not allow.${allowedWords ? ` Allowed: ${allowedWords}.` : ""}` : keep(finding2.message);
6475
+ return {
6476
+ message,
6477
+ next: allowed.length ? chooseNext(actual, allowed.map((value) => typeof value === "string" ? value : JSON.stringify(value))) : helpAsNext(finding2, "change it to an allowed value.")
6478
+ };
6479
+ };
6480
+ var FORMAT_TEXT = {
6481
+ "opf/json-syntax": jsonSyntax,
6482
+ "opf/duplicate-key": duplicateKey,
6483
+ "opf/schema": schemaPlain,
6484
+ "opf/reference-id-duplicate": referenceIdDuplicate,
6485
+ "opf/cite-unknown-reference": citeUnknownReference,
6486
+ "opf/cite-unsupported-location": citeUnsupportedLocation,
6487
+ "opf/caption-unsupported-payload": captionUnsupportedPayload,
6488
+ "opf/image-option-unsupported-payload": imageOptionUnsupportedPayload,
6489
+ "opf/image-placement-invalid": imagePlacementInvalid,
6490
+ "opf/layout-placeholder-group": layoutPlaceholderGroup,
6491
+ "opf/dataset-unknown": datasetUnknown,
6492
+ "opf/dataset-field-unknown": datasetFieldUnknown,
6493
+ "opf/data-column-duplicate": dataColumnDuplicate,
6494
+ "opf/chart-mapping-unknown-column": chartMappingUnknownColumn,
6495
+ "opf/number-format-invalid": numberFormatInvalid,
6496
+ "opf/variable-invalid-value": variableInvalidValue,
6497
+ "opf/variable-unknown-builtin": variableUnknownBuiltin,
6498
+ "opf/chart-highlight-unknown-name": chartHighlightUnknownName,
6499
+ "opf/variable-format": variableFormat,
6500
+ "opf/variable-unfilled": variableUnfilled,
6501
+ "opf/run-color-unrecognized": runColorUnrecognized,
6502
+ "opf/numbering-start-ignored": numberingStartIgnored,
6503
+ "opf/slide-size-out-of-range": slideSizeOutOfRange,
6504
+ "opf/undeclared-catalog": undeclaredCatalog,
6505
+ "opf/asset-reference": assetReference,
6506
+ "opf/asset-cycle": assetCycle,
6507
+ "opf/catalog-record": catalogRecord,
6508
+ "opf/layout-template": layoutTemplate,
6509
+ "opf/layout-region": layoutRegion,
6510
+ "opf/unresolved-reference": unresolvedReference2,
6511
+ "opf/catalog-record-not-in-source": catalogRecordNotInSource,
6512
+ "opf/unused-reference": unusedReference,
6513
+ "opf/unused-dataset": unusedDataset,
6514
+ "opf/unknown-beat": unknownBeat,
6515
+ "opf/slide-theme-dimensions": slideThemeDimensions,
6516
+ "opf/variable-unknown": variableUnknown,
6517
+ "opf/variable-unknown-value": variableUnknownValue,
6518
+ "opf/variable-unused": variableUnused,
6519
+ "opf/variable-reference-unknown": variableReferenceUnknown,
6520
+ "opf/contract": contract
6521
+ };
6522
+
6523
+ // src/finding-text-rules.ts
6524
+ var GENERIC_NEXT = "check the rule's documentation for how to fix it.";
6525
+ function safe(handler) {
6526
+ return (finding2, context, helpers) => {
6527
+ try {
6528
+ const text2 = handler(finding2, context, helpers);
6529
+ if (text2?.message && text2.next) return text2;
6530
+ } catch {
6531
+ }
6532
+ return { message: keep(finding2.message || "This finding has no message"), next: helpAsNext(finding2, GENERIC_NEXT) };
6533
+ };
6534
+ }
6535
+ var RULE_TEXT = Object.freeze(
6536
+ Object.fromEntries(Object.entries({ ...FORMAT_TEXT, ...AUDIT_TEXT }).map(([id, handler]) => [id, safe(handler)]))
6537
+ );
6538
+
6539
+ // src/markdown/dialect.ts
6540
+ var SLIDE_OPTION_KEYS = ["id", "type", "layout", "section", "tag", "hidden", "beat"];
6541
+ var BLOCK_OPTION_KEYS = ["id", "type", "as", "region"];
6542
+ var BLOCK_AS_VALUES = ["bullets", "video"];
6543
+ var FENCE_KINDS = ["chart", "metric", "timeline", "opf-block", "opf-slide"];
6544
+ var METRIC_KEYS = ["value", "label", "description", "unit", "delta", "trend", "sentiment"];
6545
+ var FENCE_ATTRIBUTES = { chart: ["alt"], timeline: ["name", "description"], code: ["title"] };
6546
+ var SPAN_FLAGS = ["bold", "italic", "underline", "strike", "sup", "sub"];
6547
+ var SPAN_VALUES = ["color", "size", "font", "lang"];
6548
+ var LINE_KEYWORDS = ["Note:", "Notes:"];
6549
+
6550
+ // src/finding-text-source.ts
6551
+ var keep2 = (finding2, next) => next ? { message: finding2.message, next } : { message: finding2.message };
6552
+ var wholeHelp = (finding2) => keep2(finding2, finding2.help ? asNext(finding2.help) : void 0);
6553
+ var line = (finding2) => finding2.location?.line ?? 1;
6554
+ var quoted = (text2, pattern) => pattern.exec(text2)?.[1];
6555
+ var optionKeys = (kind) => kind === "block" ? BLOCK_OPTION_KEYS : SLIDE_OPTION_KEYS;
6556
+ var options = {
6557
+ "markdown/options-unknown-key": (finding2) => {
6558
+ const kind = quoted(finding2.message, /^Unknown (slide|block) option/) ?? "slide";
6559
+ const key2 = quoted(finding2.message, /option "(.*)"\.$/) ?? "";
6560
+ const guess = finding2.suggestions?.[0]?.value;
6561
+ const keys = optionKeys(kind).join(", ");
6562
+ return {
6563
+ message: `${code(key2)} is not a ${kind} option.`,
6564
+ next: guess !== void 0 ? `did you mean ${code(guess)}? The keys are ${keys}. Anything else goes in an \`opf-${kind}\` fence.` : `use one of ${keys}. Anything else goes in an \`opf-${kind}\` fence.`
6565
+ };
6566
+ },
6567
+ "markdown/options-value": (finding2) => {
6568
+ if (/^hidden takes/.test(finding2.message)) return keep2(finding2, "write `hidden=true`, `hidden=false` or just `hidden`.");
6569
+ const key2 = quoted(finding2.message, /^Option (\w+) needs a value/);
6570
+ if (key2) return keep2(finding2, `write ${code(`${key2}=value`)}, for example ${code(key2 === "layout" ? "layout=title" : key2 === "id" ? "id=cover" : `${key2}=name`)}.`);
6571
+ if (/^as takes/.test(finding2.message)) return keep2(finding2, "write `as=bullets` before a list or `as=video` before an image line.");
6572
+ return keep2(finding2, "put `as=bullets` before a list and `as=video` before an image line, or remove `as`.");
6573
+ },
6574
+ "markdown/options-syntax": (finding2) => keep2(finding2, `write options as ${code("key=value")} pairs, with text in double quotes: ${code('<!-- slide: layout=title section="Part 1" -->')}.`),
6575
+ "markdown/options-duplicate": (finding2) => {
6576
+ const key2 = quoted(finding2.message, /^Option (\w+) is given twice/);
6577
+ if (key2) return keep2(finding2, `keep one ${code(`${key2}=`)} in the comment.`);
6578
+ return keep2(finding2, "merge them into one.");
6579
+ },
6580
+ "markdown/options-orphan": (finding2) => keep2(finding2, "put the `<!-- block: \u2026 -->` comment directly before the block it describes, or remove it."),
6581
+ "markdown/options-trailing": (finding2) => keep2(finding2, "put the content on its own line after the options comment."),
6582
+ "markdown/options-embedded": (finding2) => keep2(finding2, "remove the comment and put `id` and `type` inside the `opf-block` YAML.")
6583
+ };
6584
+ var markdown = {
6585
+ "markdown/front-matter-unterminated": (finding2) => keep2(finding2, "add a line with only `---` after the settings, or remove the opening `---` (a deck without front matter starts with its first slide)."),
6586
+ "markdown/front-matter-slides": () => ({
6587
+ message: "The front matter can't hold `slides`: the slides are the Markdown below it.",
6588
+ next: "remove the `slides` key and write the slides below the front matter, separated by `---`."
6589
+ }),
6590
+ "markdown/front-matter-comments": (finding2) => keep2(finding2, "add the deck properties as `key: value` lines, or remove the first `---` if the file starts with a slide."),
6591
+ "markdown/no-slides": (finding2) => keep2(finding2, "write a `# Title` line, or any content, then `---` between slides."),
6592
+ "markdown/comment-unterminated": (finding2) => ({ message: `The ${code("<!--")} on line ${line(finding2)} has no ${code("-->")}.`, next: "end the comment with `-->`." }),
6593
+ "markdown/fence-unterminated": (finding2) => {
6594
+ const fence = /Add a closing (`{3,}|~{3,}) line/.exec(finding2.help ?? "")?.[1] ?? "```";
6595
+ return { message: `The ${fence} fence opened on line ${line(finding2)} is never closed.`, next: `add a line with ${fence} to close it.` };
6596
+ },
6597
+ "markdown/code-fence": (finding2) => ({ message: `The code fence opened on line ${line(finding2)} could not be read as code.`, next: "close the fence with at least as many backticks as opened it." }),
6598
+ "markdown/chart-type": () => ({ message: "A chart fence needs its type after `chart`.", next: "write ```chart column (also `bar`, `line`, `pie`, \u2026), then the data." }),
6599
+ "markdown/chart-attributes": (finding2) => keep2(finding2, 'write ```chart column alt="What the data shows" (`alt` is optional).'),
6600
+ "markdown/chart-data": wholeHelp,
6601
+ "markdown/chart-ragged": (finding2) => keep2(finding2, "give every row one value per column."),
6602
+ "markdown/metric-block": (finding2) => keep2(finding2, `write ${code("key: value")} lines using ${METRIC_KEYS.join(", ")}; a value is plain text or a JSON string in double quotes.`),
6603
+ "markdown/timeline-attributes": (finding2) => keep2(finding2, 'write ```timeline name="Roadmap" description="\u2026" (both are optional).'),
6604
+ "markdown/timeline-description": (finding2) => keep2(finding2, "put the event first, then its description indented below it."),
6605
+ "markdown/timeline-events": (finding2) => keep2(finding2, "write one event per line, for example `2024 Q1 \u2014 Pilot`."),
6606
+ "markdown/slide-property-conflict": (finding2) => {
6607
+ const field = quoted(finding2.message, /property "(.*)" is set twice/) ?? finding2.path.split("/").at(-1) ?? "field";
6608
+ return { message: `${code(field)} is set both in the Markdown and in the \`opf-slide\` fence.`, next: "keep it in one of the two places." };
6609
+ },
6610
+ "markdown/duplicate-title": (finding2) => keep2(finding2, "use `---` to start a new slide, or `##` for the subtitle."),
6611
+ "markdown/duplicate-subtitle": (finding2) => keep2(finding2, "write further lines as paragraphs; a slide has one subtitle."),
6612
+ "markdown/empty-heading": (finding2) => keep2(finding2, "write the title after the `#`, or remove the line."),
6613
+ "markdown/empty-quote": (finding2) => keep2(finding2, "write the quote after `>`."),
6614
+ "markdown/image-source": (finding2) => keep2(finding2, "write the file or address inside the parentheses: `![alt](path.png)`."),
6615
+ "markdown/heading-demoted": (finding2) => keep2(finding2, "use `#` for the title and `##` for the subtitle; a bold paragraph on purpose silences this."),
6616
+ "markdown/empty-slide": (finding2) => keep2(finding2, "remove the extra `---`, or write `<!-- slide -->` to keep an empty slide."),
6617
+ "markdown/numbered-list": () => ({
6618
+ message: "Numbers in a list aren't kept: the list becomes bullets.",
6619
+ next: "add `numbering` to the slide in an `opf-slide` fence (see docs/numbered-lists.md), write the numbers into the text, or use a bulleted list."
6620
+ }),
6621
+ "markdown/table-ragged": (finding2) => keep2(finding2, "give every row the same number of cells as the header."),
6622
+ "markdown/formatting-dropped": (finding2) => keep2(finding2, "use a plain string here, or put the field in an `opf-slide` fence."),
6623
+ "markdown/front-matter": wholeHelp,
6624
+ "markdown/front-matter-not-mapping": wholeHelp,
6625
+ "markdown/opf-slide": wholeHelp,
6626
+ "markdown/opf-slide-not-mapping": wholeHelp,
6627
+ "markdown/opf-block": wholeHelp,
6628
+ "markdown/opf-block-not-mapping": wholeHelp
6629
+ };
6630
+ var yaml = Object.fromEntries(
6631
+ ["yaml/syntax", "yaml/empty", "yaml/not-mapping", "yaml/multiple-documents", "yaml/duplicate-key", "yaml/key", "yaml/tag", "yaml/number", "yaml/alias", "yaml/alias-limit", "yaml/alias-cycle"].map((id) => [id, wholeHelp])
6632
+ );
6633
+ var SOURCE_TEXT = { ...yaml, ...markdown, ...options };
6634
+ var entry = (category, severity, summary, rationale, fix, problem, fixed, language = "markdown") => ({
6635
+ category,
6636
+ severity,
6637
+ summary,
6638
+ rationale,
6639
+ fix,
6640
+ ...problem !== void 0 && fixed !== void 0 ? { example: { problem, fixed, language } } : {}
6641
+ });
6642
+ var YAML_WHY = "OPF reads JSON-compatible YAML 1.2, so a file that is not valid YAML cannot be opened at all; the finding says where the reader gave up.";
6643
+ var SOURCE_EXPLAIN = {
6644
+ "yaml/syntax": entry("format", "error", "The YAML text cannot be parsed.", YAML_WHY, "Fix the line the message names: indent with spaces, close quotes and brackets, give every key a colon and a value.", "slides:\n - title: a", "slides:\n - title: a", "yaml"),
6645
+ "yaml/empty": entry("format", "error", "The YAML document is empty.", "An empty file (or one that is all comments) holds no deck.", "Write `slides:` and at least one `- title:` line.", "# deck", "slides:\n - title: Title", "yaml"),
6646
+ "yaml/not-mapping": entry("format", "error", "The top level of the YAML is not a mapping.", "A deck is a mapping of properties (`name`, `slides`, ...); a bare list or value has nowhere to put them.", "Start with `key: value` lines, and put a list of slides under `slides:`.", "- title: a", "slides:\n - title: a", "yaml"),
6647
+ "yaml/multiple-documents": entry("format", "error", "The file holds more than one YAML document.", "An OPF file is exactly one document; a second `---` starts a document the reader would silently ignore.", "Remove the extra `---`, or put each deck in its own file.", "a: 1\n---\nb: 2", "a: 1\nb: 2", "yaml"),
6648
+ "yaml/duplicate-key": entry("format", "error", "A YAML mapping repeats a key.", "Parsers keep one value of a repeated key and drop the other, so an author loses content without a trace.", "Keep one of the two, or rename the one you meant as another key.", "name: A\nname: B", "name: B", "yaml"),
6649
+ "yaml/key": entry("format", "error", "A YAML mapping key is not a string.", "JSON keys are always strings; a number, a list or a null as a key cannot be carried.", 'Quote the key: `"1": value`.', "1: x", '"1": x', "yaml"),
6650
+ "yaml/tag": entry("format", "error", "A YAML tag is not allowed.", "OPF reads the YAML core schema; custom tags (`!!binary`, `!!set`) have no JSON form.", "Remove the tag; quote a value to keep it as text.", "a: !!set {x}", "a: x", "yaml"),
6651
+ "yaml/number": entry("format", "error", "A YAML number is not a finite number.", "JSON cannot carry `.inf` or `.nan`.", "Quote the value to keep it as text, or write a finite number.", "n: .inf", 'n: ".inf"', "yaml"),
6652
+ "yaml/alias": entry("format", "error", "A YAML anchor, alias or merge key is not allowed.", "Anchors and aliases make one value stand in for another, which OPF tools do not expand unless asked.", "Write each value out, or read the file with `aliases: true` (CLI: `--aliases`).", "a: &x 1\nb: *x", "a: 1\nb: 1", "yaml"),
6653
+ "yaml/alias-limit": entry("format", "error", "The anchors expand past the limit.", "Aliases that contain aliases grow exponentially; the reader stops at 100.", "Write the repeated values out, or reduce the aliasing.", void 0, void 0, "yaml"),
6654
+ "yaml/alias-cycle": entry("format", "error", "An alias refers to the value that contains it.", "A value that contains itself is infinitely deep and cannot be written as JSON.", "Write the nested value out.", void 0, void 0, "yaml"),
6655
+ "markdown/front-matter-unterminated": entry("format", "error", "The front matter is not closed.", "Without the closing `---` the reader cannot tell where the settings end and the first slide begins.", "Add a line with only `---` after the settings.", "---\nname: x\n\n# A", "---\nname: x\n---\n\n# A"),
6656
+ "markdown/front-matter": entry("format", "error", "The front matter is not valid YAML.", YAML_WHY, "Fix the line the message names.", "---\nname x\n---", "---\nname: x\n---"),
6657
+ "markdown/front-matter-not-mapping": entry("format", "error", "The front matter is not a mapping.", "Front matter holds deck properties as `key: value` lines.", "Write `key: value` lines.", "---\nname x\n---", "---\nname: x\n---"),
6658
+ "markdown/front-matter-slides": entry("format", "error", "The front matter holds `slides`.", "In Markdown the slides are the text below the front matter.", "Remove the `slides` key and write the slides below, separated by `---`.", "---\nslides: []\n---", "---\nname: x\n---"),
6659
+ "markdown/front-matter-comments": entry("format", "warning", "The front matter is only comments.", "A file that opens with `---` then only comments reads as empty front matter, not as slide content.", "Add the deck properties, or remove the first `---`.", void 0, void 0),
6660
+ "markdown/no-slides": entry("format", "error", "The Markdown has no slides.", "A deck needs at least one slide.", "Write a `# Title` line, or any content, then `---` between slides.", "", "# Title"),
6661
+ "markdown/options-syntax": entry("format", "error", "A slide or block options comment is malformed.", "Options are `key=value` pairs; a stray quote or missing value cannot be read.", "Write `<!-- slide: layout=title -->` with values bare or in double quotes.", "<!-- slide: layout= -->", "<!-- slide: layout=title -->"),
6662
+ "markdown/options-unknown-key": entry("format", "error", "An options comment names an option that does not exist.", "The dialect reads a fixed set of keys; an unknown one would be silently lost.", "Use one of the keys, or put other fields in an `opf-slide` fence.", "<!-- slide: layot=cover -->", "<!-- slide: layout=cover -->"),
6663
+ "markdown/options-value": entry("format", "error", "An option has a value it cannot take.", "Each option reads a specific kind of value, for example `hidden` is true or false.", "Write a value the option takes.", "<!-- slide: hidden=maybe -->", "<!-- slide: hidden=true -->"),
6664
+ "markdown/options-duplicate": entry("format", "error", "An option or an options comment is repeated.", "Two comments or two copies of one key leave it unclear which one is meant.", "Merge them into one.", "<!-- slide: layout=a layout=b -->", "<!-- slide: layout=b -->"),
6665
+ "markdown/options-orphan": entry("format", "error", "A block options comment is not followed by a block.", "The comment describes the block right after it.", "Put the comment directly before its block, or remove it."),
6666
+ "markdown/options-trailing": entry("format", "error", "Text follows an options comment on the same line.", "Everything after `-->` on that line would be ignored.", "Put the content on its own line."),
6667
+ "markdown/options-embedded": entry("format", "error", "A block options comment precedes an `opf-block` fence.", "The fence carries its own `id` and `type`.", "Put them inside the fence's YAML."),
6668
+ "markdown/comment-unterminated": entry("format", "error", "An HTML comment is never closed.", "Everything after an unclosed `<!--` would be swallowed into the comment.", "End the comment with `-->`.", "<!-- note", "<!-- note -->"),
6669
+ "markdown/fence-unterminated": entry("format", "error", "A fenced block is never closed.", "Everything after an unclosed fence would be read as its content.", "Add a closing fence line with at least as many backticks as opened it.", "```js\nx", "```js\nx\n```"),
6670
+ "markdown/code-fence": entry("format", "error", "A fenced block could not be read as code.", "A fence closes only with a line of at least as many backticks as opened it.", "Close the fence with at least as many backticks."),
6671
+ "markdown/chart-type": entry("format", "error", "A chart fence has no type.", "A chart needs its type (`column`, `bar`, `line`, `pie`, ...) to be drawn.", "Write the type after `chart`.", "```chart\n```", "```chart column\nCategory,Value\nA,1\n```"),
6672
+ "markdown/chart-attributes": entry("format", "error", "A chart fence has an attribute it cannot take.", "A chart fence takes `alt`, a text alternative in double quotes.", 'Write ```chart column alt="What the data shows".'),
6673
+ "markdown/chart-data": entry("format", "error", "A chart fence has no usable data.", "The chart is drawn from CSV with a header row, or a JSON object with `columns` and `rows`.", "Write CSV with a header row and at least one data row."),
6674
+ "markdown/chart-ragged": entry("format", "warning", "A chart row has a different number of values than the header.", "Missing cells plot as gaps.", "Give every row one value per column."),
6675
+ "markdown/metric-block": entry("format", "error", "A metric fence is malformed.", "A metric is `key: value` lines using a fixed set of keys.", "Write `value:` and, if you like, `label:`, `unit:`, `delta:`, `trend:` and `sentiment:`.", "```metric\nlabel: Revenue\n```", "```metric\nvalue: 42\nlabel: Revenue\n```"),
6676
+ "markdown/timeline-attributes": entry("format", "error", "A timeline fence has an attribute it cannot take.", "A timeline fence takes `name` and `description`.", "Remove the attribute, or use `name=` or `description=`."),
6677
+ "markdown/timeline-description": entry("format", "error", "An indented line has no event above it.", "A description belongs to the event before it.", "Put the event first, then its description indented below it."),
6678
+ "markdown/timeline-events": entry("format", "error", "A timeline fence has no events.", "A timeline is drawn from its events.", "Write one event per line, for example `2024 Q1 \u2014 Pilot`."),
6679
+ "markdown/opf-slide": entry("format", "error", "The YAML of an `opf-slide` fence is not valid.", YAML_WHY, "Fix the line the message names."),
6680
+ "markdown/opf-slide-not-mapping": entry("format", "error", "An `opf-slide` fence is not a mapping.", "The fence holds the slide's fields as `key: value` lines.", "Write `key: value` lines."),
6681
+ "markdown/opf-block": entry("format", "error", "The YAML of an `opf-block` fence is not valid.", YAML_WHY, "Fix the line the message names."),
6682
+ "markdown/opf-block-not-mapping": entry("format", "error", "An `opf-block` fence is not a mapping.", "The fence holds the block's fields as `key: value` lines.", "Write `key: value` lines."),
6683
+ "markdown/slide-property-conflict": entry("format", "error", "A slide field is set in the Markdown and in the `opf-slide` fence.", "Two values for one field leave it unclear which wins.", "Keep it in one place."),
6684
+ "markdown/duplicate-title": entry("format", "error", "A slide has more than one `#` title.", "A slide has one title; a second `#` is the start of the next slide.", "Use `---` to start a new slide, or `##` for the subtitle."),
6685
+ "markdown/duplicate-subtitle": entry("format", "error", "A slide has more than one `##` subtitle.", "A slide has one subtitle.", "Write further lines as paragraphs."),
6686
+ "markdown/empty-heading": entry("format", "error", "A heading has no text.", "An empty title is an empty field.", "Write the title after the `#`, or remove the line."),
6687
+ "markdown/empty-quote": entry("format", "error", "A block quote has no text.", "A quote without text draws nothing.", "Write the quote after `>`."),
6688
+ "markdown/image-source": entry("format", "error", "An image has no source.", "A picture needs a file or an address.", "Write it inside the parentheses: `![alt](path.png)`."),
6689
+ "markdown/heading-demoted": entry("format", "warning", "A heading below level 2 has no OPF form.", "Only `#` (title) and `##` (subtitle) are slide fields.", "Use a bold paragraph on purpose to silence this."),
6690
+ "markdown/empty-slide": entry("format", "warning", "Two `---` lines in a row gave an empty slide.", "The empty slide was skipped.", "Remove the extra `---`, or write `<!-- slide -->` to keep it."),
6691
+ "markdown/numbered-list": entry("format", "warning", "List numbers are not kept.", "The dialect does not read numbers as `numbering`, so each item became a bullet.", "Add `numbering` in an `opf-slide` fence, or use a bulleted list.", "1. one\n2. two", "```opf-slide\nitems:\n - one\n - two\nnumbering: decimal\n```"),
6692
+ "markdown/table-ragged": entry("format", "warning", "A table row has a different number of cells than the header.", "Missing cells are empty.", "Give every row the same number of cells."),
6693
+ "markdown/formatting-dropped": entry("format", "warning", "Inline formatting was dropped.", "The field takes a plain string.", "Use plain text here, or put the field in an `opf-slide` fence.")
6694
+ };
6695
+
6696
+ // src/finding-text.ts
6697
+ var ID = "an id of lowercase letters, digits and hyphens that starts with a letter, like `my-layout`";
6698
+ var ID_NEXT = "use lowercase letters, digits and hyphens, and start with a letter.";
6699
+ var LANGUAGE = "a language tag like `en-US` or `de`";
6700
+ var PATTERN_PHRASES = Object.freeze({
6701
+ "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$": { what: "a hex color like `#1F4E79` (3, 6 or 8 hex digits)", next: "write the color as #RRGGBB." },
6702
+ "^[a-z][a-z0-9-]*$": { what: ID, next: ID_NEXT },
6703
+ "^[a-z0-9][a-z0-9-]*$": { what: "an id of lowercase letters, digits and hyphens, like `my-chart`", next: ID_NEXT },
6704
+ "^(?:[a-z][a-z0-9-]*:)?[a-z][a-z0-9-]*$": { what: "a catalog id like `pillars` or `acme:hero` (lowercase letters, digits and hyphens)", next: "use the id of a record, with the catalog group in front when it has one." },
6705
+ "^[a-zA-Z0-9][a-zA-Z0-9._-]*$": { what: "an id of letters, digits, dots, hyphens and underscores that starts with a letter or digit, like `logo-2`", next: "use letters, digits, dots, hyphens and underscores, without spaces." },
6706
+ "^[a-zA-Z0-9_-]+$": { what: "an id of letters, digits, hyphens and underscores, like `hero_1`", next: "remove spaces and other punctuation." },
6707
+ "^var:[a-z][a-z0-9-]*$": { what: "a variable reference like `var:brand`", next: "write `var:` and the id of a variable under `variables`." },
6708
+ "^var:organization(\\.[A-Za-z0-9_-]+)?\\.logo(\\.(full|stacked|icon|wordmark))?$": { what: "an organization logo reference like `var:organization.logo.full`", next: "write `var:organization.logo` and, if you want, `.full`, `.stacked`, `.icon` or `.wordmark`." },
6709
+ "^\\d{4}-\\d{2}-\\d{2}$": { what: "a date written year-month-day, like `2026-10-24`", next: "write the date as YYYY-MM-DD." },
6710
+ "^(?:https?://|mailto:|tel:)\\S+$": { what: "a web, email or phone address (`https://\u2026`, `mailto:\u2026` or `tel:\u2026`)", next: "start with https://, mailto: or tel:, and leave out spaces." },
6711
+ "^(https?://|mailto:|tel:)\\S+$": { what: "a web, email or phone address (`https://\u2026`, `mailto:\u2026` or `tel:\u2026`)", next: "start with https://, mailto: or tel:, and leave out spaces." },
6712
+ "^(asset:|https://|data:|\\./|\\.\\./)": { what: "a source that starts with `asset:`, `https://`, `data:`, `./` or `../`", next: "register the file under `assets` and write `asset:` and its id, or give an https:// address." },
6713
+ "^(?:https://[^\\s]+|pkg:[^\\s]+)$": { what: "an `https://` address or a `pkg:` reference", next: "write an address that starts with https:// or pkg:, without spaces." },
6714
+ "^(?:[A-Za-z]{2,3}|[iIxX])(?:-[A-Za-z0-9]{1,8})*$": { what: LANGUAGE, next: "write a language and, if needed, a region: `en-US`, `pt-BR`, `de`." },
6715
+ "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$": { what: LANGUAGE, next: "write a language and, if needed, a region: `en-US`, `pt-BR`, `de`." },
6716
+ "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8})*$": { what: LANGUAGE, next: "write a language and, if needed, a region: `en-US`, `pt-BR`, `de`." },
6717
+ "^[0-9a-f]{64}$": { what: "a 64-character lowercase hex digest", next: "paste the SHA-256 digest in lowercase hex." },
6718
+ "^[^/]+\\.json$": { what: "a file name that ends in `.json`, without folders", next: "write a plain file name such as `pillars.json`." },
6719
+ "^[a-z][a-z0-9-]*\\.svg$": { what: "a file name like `pillars.svg`", next: "write the record id and `.svg`." },
6720
+ "^[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*$": { what: "a rule id of two parts like `opf/text-contrast`", next: "write the producer, a slash and the rule name." }
6721
+ });
6722
+ var hasHex = (pattern) => typeof pattern === "string" && /^\^#\(\?:\[0-9a-fA-F\]/.test(pattern);
6723
+ function helpersFor(finding2, context) {
6724
+ const segments = segmentsOf(finding2.path);
6725
+ return {
6726
+ segments,
6727
+ valueAt: (path) => context.valueAt ? context.valueAt([...path]) : readAt(context.document, path),
6728
+ get: (path) => context.valueAt ? context.valueAt([...path]) : readAt(context.document, path),
6729
+ chord: context.chord ?? "Ctrl+Space",
6730
+ format: context.format ?? "json"
6731
+ };
6732
+ }
6733
+ var attempt = (run, fallback) => {
6734
+ try {
6735
+ return run();
6736
+ } catch {
6737
+ return fallback;
6738
+ }
6739
+ };
6740
+ function schemaKeys(segments, context) {
6741
+ if (context.keysOf) return context.keysOf(segments);
6742
+ return attempt(() => [...new Set(schemaVariants(schemaAtPath(context.document, segments)).flatMap((variant) => Object.keys(variant.properties ?? {})))], []);
6743
+ }
6744
+ function starterText(schema, property, format) {
6745
+ const example = schema?.examples?.find((value) => typeof value === "string" && !value.includes("..."));
6746
+ const type = Array.isArray(schema?.type) ? schema?.type[0] : schema?.type;
6747
+ if (example !== void 0) return format === "json" ? JSON.stringify(example) : String(example);
6748
+ if (type === "object") return "{ \u2026 }";
6749
+ if (type === "array") return "[ \u2026 ]";
6750
+ if (type === "number" || type === "integer") return "0";
6751
+ if (type === "boolean") return "false";
6752
+ if (property === "id") return format === "json" ? '"acme"' : "acme";
6753
+ return format === "json" ? '"\u2026"' : "\u2026";
6754
+ }
6755
+ function describeSchema(finding2, context, helpers) {
6756
+ const { keyword, params: params2 = {}, alternatives } = finding2.validation ?? { keyword: "" };
6757
+ const segments = helpers.segments;
6758
+ const label = fieldLabel(segments);
6759
+ const actual = helpers.valueAt(segments);
6760
+ const chord = helpers.chord;
6761
+ const format = helpers.format;
6762
+ switch (keyword) {
6763
+ case "required": {
6764
+ const property = String(params2.missingProperty ?? "");
6765
+ const where = containerName(segments);
6766
+ const schema = context.schemaAt ? context.schemaAt([...segments, property]) : attempt(() => schemaAtPath(context.document, [...segments, property]), void 0);
6767
+ if (format === "json") {
6768
+ const sample2 = property === "slides" ? '[ { "title": "\u2026" } ]' : starterText(schema, property, "json");
6769
+ return { message: `${cap(where)} is missing ${code(property)}.`, next: `add "${property}": ${sample2}.` };
6770
+ }
6771
+ const sample = property === "slides" ? "slides:` with at least one `- title: \u2026" : `${property}: ${starterText(schema, property, "yaml")}`;
6772
+ return { message: `${cap(where)} is missing ${code(property)}.`, next: `add \`${sample}\`.` };
6773
+ }
6774
+ case "type": {
6775
+ const expected = typeWords(params2.type);
6776
+ const next = params2.type === "array" && typeof actual === "string" ? `write [${JSON.stringify(actual)}].` : params2.type === "string" && typeof actual === "number" ? `write ${JSON.stringify(String(actual))}.` : params2.type === "string" && typeof actual === "boolean" ? `write ${JSON.stringify(String(actual))}.` : `write ${expected}.`;
6777
+ return { message: `${cap(label)} should be ${expected}, but it is ${describeKind(actual)}.`, next };
6778
+ }
6779
+ case "oneOf":
6780
+ case "anyOf": {
6781
+ const forms = (alternatives ?? []).map((entry2) => String(entry2).split(":")[0]?.trim() ?? "");
6782
+ const words = forms.length ? [...new Set(forms.map((form) => WORDS[form] ?? form))].slice(0, 3).join(" or ") : "a different kind of value";
6783
+ return { message: `${cap(label)} should be ${words}, but it is ${describeKind(actual)}.`, next: forms.length ? `write ${words}.` : "check the schema reference for this field." };
6784
+ }
6785
+ case "enum": {
6786
+ const values = params2.allowedValues ?? finding2.suggestions?.map((entry2) => entry2.value) ?? [];
6787
+ const guess = nearest(actual, values).close[0];
6788
+ const is = actual === void 0 ? "" : `, but is ${describeValue(actual)}`;
6789
+ return {
6790
+ message: `${cap(label)} must be ${valueList(values)}${is}.`,
6791
+ next: guess !== void 0 ? `did you mean ${code(guess)}?` : values.length > 5 ? `press ${chord} for the ${values.length} values.` : "choose one of these values."
6792
+ };
6793
+ }
6794
+ case "const":
6795
+ return { message: `${cap(label)} must be exactly ${code(params2.allowedValue)}.`, next: `write ${JSON.stringify(params2.allowedValue)}.` };
6796
+ case "pattern": {
6797
+ const pattern = String(params2.pattern ?? "");
6798
+ const kind = context.catalogKind ? context.catalogKind(segments) : attempt(() => catalogKindAt(segments, context.document), void 0);
6799
+ actual === "" ? "it is empty" : `it is ${describeValue(actual)}`;
6800
+ if (kind) {
6801
+ const word = KIND_WORDS[kind] ?? "catalog";
6802
+ const count = context.catalogCount?.(kind);
6803
+ const ids = KIND_EXAMPLES[kind] ?? "`id` or `name:id`";
6804
+ const press = `press ${chord}${count ? ` for the ${count} ${word}s` : " for the available ids"}`;
6805
+ const orRemove = `, or remove it${kind === "layouts" ? " to let OPF pick" : ""}.`;
6806
+ if (actual === "") return { message: `${cap(label)} is empty.`, next: `${press} (an id such as ${ids})${orRemove}` };
6807
+ return {
6808
+ message: `${cap(label)} must be a ${word} id such as ${ids} (lowercase letters, digits and hyphens), but it is ${describeValue(actual)}.`,
6809
+ next: `${press}${orRemove}`
6810
+ };
6811
+ }
6812
+ const phrase = PATTERN_PHRASES[pattern];
6813
+ if (phrase) return { message: `${cap(label)} must be ${phrase.what}, but is ${describeValue(actual)}.`, next: phrase.next };
6814
+ if (hasHex(pattern)) return { message: `${cap(label)} must be a hex color like ${code("#1F4E79")} (3, 6 or 8 hex digits), but is ${describeValue(actual)}.`, next: "write the color as #RRGGBB." };
6815
+ return { message: `${cap(label)} does not match the expected format${typeof actual === "string" ? `: ${describeValue(actual)}` : ""}.`, next: "check the schema reference for this field." };
6816
+ }
6817
+ case "propertyNames": {
6818
+ const name = String(params2.propertyName ?? "");
6819
+ return { message: `${code(name)} is not a valid name here.`, next: "use letters, digits, hyphens and underscores, and start with a letter." };
6820
+ }
6821
+ case "additionalProperties": {
6822
+ const name = String(params2.additionalProperty ?? segments.at(-1) ?? "");
6823
+ const parent = segments.slice(0, -1);
6824
+ const allowed = schemaKeys(parent, context);
6825
+ const hint = nearest(name, allowed).close[0];
6826
+ return {
6827
+ message: `${code(name)} is not a property of ${containerName(parent)}.`,
6828
+ next: `${hint ? `did you mean ${code(hint)}? ` : ""}To keep custom data, put it under ${code("extensions")}.`.trim()
6829
+ };
6830
+ }
6831
+ case "minItems":
6832
+ return { message: `${cap(label)} needs at least ${params2.limit} item${params2.limit === 1 ? "" : "s"}.`, next: label === "`slides`" ? "add a slide." : "add an item." };
6833
+ case "maxItems":
6834
+ return { message: `${cap(label)} can hold at most ${params2.limit} items.`, next: "remove the extra ones." };
6835
+ case "minProperties":
6836
+ return { message: `${cap(label)} needs at least ${params2.limit} propert${params2.limit === 1 ? "y" : "ies"}.`, next: "add one, or remove the empty object." };
6837
+ case "uniqueItems":
6838
+ return { message: `${cap(label)} repeats an entry.`, next: "remove the repeated entry." };
6839
+ case "minLength":
6840
+ return { message: `${cap(label)} is empty.`, next: "write some text, or remove the key." };
6841
+ case "maxLength":
6842
+ return { message: `${cap(label)} is longer than ${params2.limit} characters.`, next: "shorten it." };
6843
+ case "minimum":
6844
+ case "exclusiveMinimum":
6845
+ case "maximum":
6846
+ case "exclusiveMaximum": {
6847
+ const bound = /minimum/i.test(keyword) ? "at least" : "at most";
6848
+ return {
6849
+ message: `${cap(label)} must be ${keyword.startsWith("exclusive") ? bound === "at least" ? "greater than" : "less than" : bound} ${params2.limit}, but is ${describeValue(actual)}.`,
6850
+ next: `write ${params2.limit}${keyword.startsWith("exclusive") ? " or a different number" : ""}.`
6851
+ };
6852
+ }
6853
+ case "format":
6854
+ return { message: `${cap(label)} must be a ${params2.format === "uri" ? "web address like https://example.com" : params2.format}.`, next: "check the value." };
6855
+ default:
6856
+ return void 0;
6857
+ }
6858
+ }
6859
+ function ruleText(finding2, context, helpers) {
6860
+ const handler = RULE_TEXT[finding2.ruleId] ?? SOURCE_TEXT[finding2.ruleId];
6861
+ return handler?.(finding2, context, helpers);
6862
+ }
6863
+ function describeFinding(finding2, context = {}) {
6864
+ const helpers = helpersFor(finding2, context);
6865
+ let described;
6866
+ try {
6867
+ if (finding2.ruleId === "opf/schema" && finding2.validation?.keyword) described = describeSchema(finding2, context, helpers);
6868
+ else described = ruleText(finding2, context, helpers);
6869
+ } catch {
6870
+ described = void 0;
6871
+ }
6872
+ if (described) return described.next === void 0 ? { message: described.message, ...fallbackNext(finding2) } : described;
6873
+ return { message: finding2.message, ...fallbackNext(finding2) };
6874
+ }
6875
+ function fallbackNext(finding2) {
6876
+ const help = firstSentence(finding2.help);
6877
+ return help ? { next: asNext(help) } : {};
6878
+ }
6879
+ function starterValue(document, segments, property) {
6880
+ if (property === "slides" && segments.length === 0) return [{ title: "Slide title" }];
6881
+ try {
6882
+ return createSchemaValue(schemaAtPath(document, [...segments, property]));
6883
+ } catch {
6884
+ return void 0;
6885
+ }
6886
+ }
6887
+
6888
+ // src/schema-fixes.ts
6889
+ var MAX_CHANGES = 5;
6890
+ var patch = (id, title, operations, safe2 = false) => ({ id, title, kind: "patch", safe: safe2, patch: operations });
6891
+ var allowsKey = (document, segments, key2) => {
6892
+ try {
6893
+ return schemaVariants(schemaAtPath(document, segments)).some((variant) => Object.hasOwn(variant.properties ?? {}, key2));
6894
+ } catch {
6895
+ return false;
6896
+ }
6897
+ };
6898
+ function schemaFixes(finding2, document) {
6899
+ const validation = finding2.validation;
6900
+ if (finding2.ruleId !== "opf/schema" || !validation) return [];
6901
+ const segments = segmentsOf(finding2.path);
6902
+ const params2 = validation.params;
6903
+ switch (validation.keyword) {
6904
+ case "required": {
6905
+ const property = String(params2.missingProperty ?? "");
6906
+ if (!property || !isRecord2(readAt(document, segments)) || Object.hasOwn(readAt(document, segments), property)) return [];
6907
+ const value = starterValue(document, segments, property);
6908
+ if (value === void 0) return [];
6909
+ return [patch(`add:${property}`, `Add \`${property}\``, [{ op: "add", path: pointerOf([...segments, property]), value }])];
6910
+ }
6911
+ case "enum": {
6912
+ const allowed = Array.isArray(params2.allowedValues) ? params2.allowedValues : [];
6913
+ if (!allowed.length || readAt(document, segments) === void 0) return [];
6914
+ const current = readAt(document, segments);
6915
+ const values = [.../* @__PURE__ */ new Set([...nearest(current, allowed, MAX_CHANGES).close, ...allowed])].slice(0, MAX_CHANGES);
6916
+ return values.map((value) => patch(`change-to:${String(value)}`, `Change to ${String(value)}`, [{ op: "replace", path: finding2.path, value }]));
6917
+ }
6918
+ case "additionalProperties": {
6919
+ const name = String(params2.additionalProperty ?? segments.at(-1) ?? "");
6920
+ const parent = segments.slice(0, -1);
6921
+ const owner = readAt(document, parent);
6922
+ if (!name || !isRecord2(owner) || !Object.hasOwn(owner, name)) return [];
6923
+ const fixes = [];
6924
+ for (const suggestion of finding2.suggestions ?? []) {
6925
+ const target = String(suggestion.value);
6926
+ if (!Object.hasOwn(owner, target)) fixes.push(patch(`rename-to:${target}`, `Rename to ${target}`, [{ op: "move", from: finding2.path, path: pointerOf([...parent, target]) }]));
6927
+ }
6928
+ if (allowsKey(document, parent, "extensions") && name !== "extensions") {
6929
+ const existing = owner.extensions;
6930
+ if (existing === void 0) fixes.push(patch("move-to-extensions", "Move into extensions", [{ op: "add", path: pointerOf([...parent, "extensions"]), value: {} }, { op: "move", from: finding2.path, path: pointerOf([...parent, "extensions", name]) }]));
6931
+ else if (isRecord2(existing) && !Object.hasOwn(existing, name)) fixes.push(patch("move-to-extensions", "Move into extensions", [{ op: "move", from: finding2.path, path: pointerOf([...parent, "extensions", name]) }]));
6932
+ }
6933
+ return fixes;
6934
+ }
6935
+ case "type": {
6936
+ const current = readAt(document, segments);
6937
+ if (current === void 0) return [];
6938
+ if (params2.type === "array" && (typeof current === "string" || isRecord2(current))) return [patch("wrap-in-list", "Wrap in a list", [{ op: "replace", path: finding2.path, value: [current] }], typeof current === "string")];
6939
+ if (params2.type === "string" && (typeof current === "number" || typeof current === "boolean")) return [patch("convert-to-string", "Convert to string", [{ op: "replace", path: finding2.path, value: String(current) }], true)];
6940
+ return [];
6941
+ }
6942
+ case "oneOf":
6943
+ case "anyOf": {
6944
+ const current = readAt(document, segments);
6945
+ const forms = (validation.alternatives ?? []).map((entry2) => String(entry2).split(":")[0]?.trim());
6946
+ if (forms.includes("string") && (typeof current === "number" || typeof current === "boolean")) return [patch("convert-to-string", "Convert to string", [{ op: "replace", path: finding2.path, value: String(current) }], true)];
6947
+ return [];
6948
+ }
6949
+ case "minItems":
6950
+ if (finding2.path === "/slides" && Array.isArray(readAt(document, segments)) && params2.limit === 1) return [patch("add-slide", "Add a slide", [{ op: "add", path: "/slides/0", value: { title: "Slide title" } }])];
6951
+ return [];
6952
+ default:
6953
+ return [];
6954
+ }
6955
+ }
6956
+
5356
6957
  // src/script-measurement.ts
5357
- function scriptProfile(presentation, slideIndex, options, language) {
6958
+ function scriptProfile(presentation, slideIndex, options2, language) {
5358
6959
  let resolved;
5359
6960
  try {
5360
- resolved = resolveScriptFonts(presentation, { slideIndex, ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, ...language === void 0 ? {} : { language } });
6961
+ resolved = resolveScriptFonts(presentation, { slideIndex, ...options2.catalogs !== void 0 ? { catalogs: options2.catalogs } : {}, ...language === void 0 ? {} : { language } });
5361
6962
  } catch {
5362
6963
  resolved = void 0;
5363
6964
  }
5364
6965
  const slots = (role) => {
5365
- const latin = options.fontFamilies[role];
6966
+ const latin = options2.fontFamilies[role];
5366
6967
  const slot = (key2) => !resolved || resolved.sources[key2] === "latin" || resolved.sources[key2] === "schemeFamily" ? latin : resolved[role][key2] ?? latin;
5367
6968
  return { latin, eastAsian: slot("eastAsian"), complexScript: slot("complexScript") };
5368
6969
  };
@@ -5373,13 +6974,13 @@ function scriptProfile(presentation, slideIndex, options, language) {
5373
6974
  script: resolved?.script ?? "Zzzz",
5374
6975
  ...resolved ? { scriptRole: resolved.scriptRole, bcp47: resolved.bcp47, lang: resolved.lang, languageSource: resolved.languageSource, direction: resolved.direction } : {},
5375
6976
  rtl: resolved?.rtl === true,
5376
- serif: options.serif
6977
+ serif: options2.serif
5377
6978
  };
5378
6979
  }
5379
- function slideScriptMeasurement(presentation, slideIndex, measurement, options) {
6980
+ function slideScriptMeasurement(presentation, slideIndex, measurement, options2) {
5380
6981
  const forScripts = measurement.forScripts;
5381
6982
  if (typeof forScripts !== "function") return measurement;
5382
- const deckProfile = scriptProfile(presentation, slideIndex, options);
6983
+ const deckProfile = scriptProfile(presentation, slideIndex, options2);
5383
6984
  const deck = forScripts.call(measurement, deckProfile);
5384
6985
  const languages = /* @__PURE__ */ new Map();
5385
6986
  const forLanguage = (lang) => {
@@ -5387,7 +6988,7 @@ function slideScriptMeasurement(presentation, slideIndex, measurement, options)
5387
6988
  if (!languages.has(lang)) {
5388
6989
  let own4;
5389
6990
  try {
5390
- const profile = scriptProfile(presentation, slideIndex, options, lang);
6991
+ const profile = scriptProfile(presentation, slideIndex, options2, lang);
5391
6992
  own4 = profile.languageSource === "option" && profile.bcp47?.toLowerCase() !== deckProfile.bcp47?.toLowerCase() ? forScripts.call(measurement, profile) : void 0;
5392
6993
  } catch {
5393
6994
  own4 = void 0;
@@ -5397,31 +6998,31 @@ function slideScriptMeasurement(presentation, slideIndex, measurement, options)
5397
6998
  return languages.get(lang);
5398
6999
  };
5399
7000
  const pick = (style) => forLanguage(style?.lang) ?? deck;
5400
- const scripted = { ...deck, measure: (text, size, style) => pick(style).measure(text, size, style) };
7001
+ const scripted = { ...deck, measure: (text2, size, style) => pick(style).measure(text2, size, style) };
5401
7002
  if (typeof deck.resolveStyle === "function") scripted.resolveStyle = (style) => deck.resolveStyle.call(deck, style);
5402
7003
  if (typeof deck.outlineBounds === "function")
5403
- scripted.outlineBounds = (text, size, style) => {
7004
+ scripted.outlineBounds = (text2, size, style) => {
5404
7005
  const own4 = pick(style);
5405
- return typeof own4.outlineBounds === "function" ? own4.outlineBounds(text, size, style) : deck.outlineBounds.call(deck, text, size, style);
7006
+ return typeof own4.outlineBounds === "function" ? own4.outlineBounds(text2, size, style) : deck.outlineBounds.call(deck, text2, size, style);
5406
7007
  };
5407
7008
  return scripted;
5408
7009
  }
5409
- function resolveSlideScriptMeasurement(presentation, slideIndex, measurement, options = {}) {
7010
+ function resolveSlideScriptMeasurement(presentation, slideIndex, measurement, options2 = {}) {
5410
7011
  if (typeof measurement.forScripts !== "function") return measurement;
5411
- const { fontScheme } = resolveDesignRecords(presentation, slideIndex, options.catalogs !== void 0 ? { catalogs: options.catalogs } : {});
7012
+ const { fontScheme } = resolveDesignRecords(presentation, slideIndex, options2.catalogs !== void 0 ? { catalogs: options2.catalogs } : {});
5412
7013
  const resolved = resolveFontFamilies(fontScheme);
5413
- const fontFamilies = { heading: options.fontFamilies?.heading ?? resolved.heading, body: options.fontFamilies?.body ?? resolved.body };
5414
- return slideScriptMeasurement(presentation, slideIndex, measurement, { ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, fontFamilies, serif: fontScheme.type === "serif" });
7014
+ const fontFamilies = { heading: options2.fontFamilies?.heading ?? resolved.heading, body: options2.fontFamilies?.body ?? resolved.body };
7015
+ return slideScriptMeasurement(presentation, slideIndex, measurement, { ...options2.catalogs !== void 0 ? { catalogs: options2.catalogs } : {}, fontFamilies, serif: fontScheme.type === "serif" });
5415
7016
  }
5416
7017
 
5417
7018
  // src/slide-context.ts
5418
7019
  var rec2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
5419
- function resolveSlideContext(presentation, index, options = {}) {
7020
+ function resolveSlideContext(presentation, index, options2 = {}) {
5420
7021
  const deck = rec2(presentation);
5421
7022
  const slides = Array.isArray(deck.slides) ? deck.slides : [];
5422
7023
  if (!Number.isInteger(index) || index < 0 || index >= slides.length) throw new RangeError(`Slide index ${String(index)} is outside the presentation's ${slides.length} slides.`);
5423
7024
  const slide = rec2(slides[index]);
5424
- const catalogs = { catalogs: checkCatalogsOption(options.catalogs, "resolveSlideContext") };
7025
+ const catalogs = { catalogs: checkCatalogsOption(options2.catalogs, "resolveSlideContext") };
5425
7026
  const records = resolveDesignRecords(presentation, index, catalogs);
5426
7027
  const diagnostics = [...records.diagnostics];
5427
7028
  const { theme, colorScheme, fontScheme } = records;
@@ -5435,30 +7036,30 @@ function resolveSlideContext(presentation, index, options = {}) {
5435
7036
  layoutProvenance = provenanceOf(found);
5436
7037
  } else diagnostics.push(unresolvedReference(presentation, "layouts", slide.layout, `slides.${index}.layout`, catalogs));
5437
7038
  }
5438
- if (options.strictReferences && diagnostics.length) throw new OPFUnresolvedReferenceError(diagnostics);
7039
+ if (options2.strictReferences && diagnostics.length) throw new OPFUnresolvedReferenceError(diagnostics);
5439
7040
  const roles = resolveColorRoles(colorScheme, colorChoice([rec2(slide.design), rec2(deck.design), theme], colorScheme, rec2(deck.variables)));
5440
7041
  const composeOptions = {
5441
7042
  ...resolveCanvasDimensions(design.dimensions ?? theme.dimensions),
5442
7043
  ...layout ? { layout } : {},
5443
7044
  presentation,
5444
7045
  slideIndex: index,
5445
- slideNumber: options.slideNumber ?? index + 1,
5446
- slideCount: options.slideCount ?? slides.length,
7046
+ slideNumber: options2.slideNumber ?? index + 1,
7047
+ slideCount: options2.slideCount ?? slides.length,
5447
7048
  fontFamilies: resolveFontFamilies(fontScheme),
5448
7049
  darkBackground: roles.dark,
5449
7050
  ...theme.background !== void 0 ? { themeBackground: theme.background } : {},
5450
7051
  // OPF 0.19: a theme may carry the shared design keys (between the deck's design and the layout record's).
5451
7052
  ...theme.design !== void 0 && rec2(theme.design) === theme.design ? { themeDesign: theme.design } : {},
5452
- ...options.fonts?.textMeasurement ? { textMeasurement: options.fonts.textMeasurement } : {},
5453
- ...options.date !== void 0 ? { date: options.date } : {}
7053
+ ...options2.fonts?.textMeasurement ? { textMeasurement: options2.fonts.textMeasurement } : {},
7054
+ ...options2.date !== void 0 ? { date: options2.date } : {}
5454
7055
  };
5455
7056
  const resolvedSlide = resolveSlideVariables(slide, { slideNumber: composeOptions.slideNumber, slideCount: composeOptions.slideCount, presentation, darkBackground: composeOptions.darkBackground === true });
5456
7057
  return { slide: resolvedSlide, options: composeOptions, diagnostics, resolved: { theme, colorScheme, roles, fontScheme, fontSchemePath: records.fontSchemePath, ...layout ? { layout } : {}, provenance: { ...layoutProvenance ? { layout: layoutProvenance } : {}, ...records.provenance } } };
5457
7058
  }
5458
7059
 
5459
7060
  // src/rule-slides.ts
5460
- var measurementFor = (options, index) => {
5461
- const measurement = options.fonts?.textMeasurement;
7061
+ var measurementFor = (options2, index) => {
7062
+ const measurement = options2.fonts?.textMeasurement;
5462
7063
  return typeof measurement === "function" ? measurement(index) : measurement;
5463
7064
  };
5464
7065
  var lazy = (compute) => {
@@ -5485,16 +7086,16 @@ function relax(value) {
5485
7086
  walk2(copy);
5486
7087
  return copy;
5487
7088
  }
5488
- function buildSlides(document, options) {
7089
+ function buildSlides(document, options2) {
5489
7090
  const slides = Array.isArray(document.slides) ? document.slides : [];
5490
7091
  return slides.map((slide, index) => {
5491
7092
  const path = pointer2("slides", index);
5492
- const baseContext = lazy(() => resolveSlideContext(document, index, { catalogs: options.catalogs, date: "2000-01-01" }));
7093
+ const baseContext = lazy(() => resolveSlideContext(document, index, { catalogs: options2.catalogs, date: "2000-01-01" }));
5493
7094
  const measurement = lazy(() => {
5494
- const host = measurementFor(options, index);
7095
+ const host = measurementFor(options2, index);
5495
7096
  if (!host) return void 0;
5496
7097
  const { resolved: resolved2 } = baseContext();
5497
- return slideScriptMeasurement(document, index, host, { catalogs: options.catalogs, fontFamilies: resolveFontFamilies(resolved2.fontScheme), serif: resolved2.fontScheme.type === "serif" });
7098
+ return slideScriptMeasurement(document, index, host, { catalogs: options2.catalogs, fontFamilies: resolveFontFamilies(resolved2.fontScheme), serif: resolved2.fontScheme.type === "serif" });
5498
7099
  });
5499
7100
  const resolved = lazy(() => {
5500
7101
  const textMeasurement = measurement();
@@ -5549,7 +7150,6 @@ function buildSlides(document, options) {
5549
7150
  // src/validator.ts
5550
7151
  var SEVERITIES = ["error", "warning", "info"];
5551
7152
  var OPTION_KEYS = ["only", "ignore", "severity", "ignorePaths", "catalogs", "contracts", "thresholds", "fonts", "chartPalette", "template", "values"];
5552
- var DOCS = "https://github.com/OpenPresentation/opf/blob/main/docs/validate.md";
5553
7153
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
5554
7154
  var OPFValidationError = class extends Error {
5555
7155
  /** The full report that failed. */
@@ -5557,7 +7157,7 @@ var OPFValidationError = class extends Error {
5557
7157
  /** The error-severity findings of the report. */
5558
7158
  findings;
5559
7159
  constructor(report2) {
5560
- const errors = report2.findings.filter((entry) => entry.severity === "error");
7160
+ const errors = report2.findings.filter((entry2) => entry2.severity === "error");
5561
7161
  const first = errors[0];
5562
7162
  super(first ? `OPF validation failed at ${first.path || "/"}: ${first.message}` : "OPF validation failed");
5563
7163
  this.name = "OPFValidationError";
@@ -5573,7 +7173,7 @@ function ruleFor(name, what) {
5573
7173
  }
5574
7174
  function names(list, what) {
5575
7175
  if (list === void 0) return void 0;
5576
- if (!Array.isArray(list) || !list.every((entry) => typeof entry === "string")) throw new TypeError(`Validate ${what} must be an array of rule ids or category names.`);
7176
+ if (!Array.isArray(list) || !list.every((entry2) => typeof entry2 === "string")) throw new TypeError(`Validate ${what} must be an array of rule ids or category names.`);
5577
7177
  const result = { rules: /* @__PURE__ */ new Set(), categories: /* @__PURE__ */ new Set() };
5578
7178
  for (const name of list) {
5579
7179
  const found = ruleFor(name, what);
@@ -5582,65 +7182,65 @@ function names(list, what) {
5582
7182
  }
5583
7183
  return result;
5584
7184
  }
5585
- function resolveOptions(options) {
5586
- if (!isObject2(options)) throw new TypeError("Validate options must be an object.");
5587
- for (const key2 of Object.keys(options)) if (!OPTION_KEYS.includes(key2)) throw new TypeError(`Unknown validate option ${JSON.stringify(key2)}. Options: ${OPTION_KEYS.join(", ")}.`);
5588
- checkDocumentOptions(options);
5589
- const only = names(options.only, "only");
5590
- const ignore = names(options.ignore, "ignore");
7185
+ function resolveOptions(options2) {
7186
+ if (!isObject2(options2)) throw new TypeError("Validate options must be an object.");
7187
+ for (const key2 of Object.keys(options2)) if (!OPTION_KEYS.includes(key2)) throw new TypeError(`Unknown validate option ${JSON.stringify(key2)}. Options: ${OPTION_KEYS.join(", ")}.`);
7188
+ checkDocumentOptions(options2);
7189
+ const only = names(options2.only, "only");
7190
+ const ignore = names(options2.ignore, "ignore");
5591
7191
  const severity = /* @__PURE__ */ new Map();
5592
- if (options.severity !== void 0) {
5593
- if (!isObject2(options.severity)) throw new TypeError('Validate severity must map rule ids or category names to a severity or "off".');
5594
- for (const [name, value] of Object.entries(options.severity)) {
7192
+ if (options2.severity !== void 0) {
7193
+ if (!isObject2(options2.severity)) throw new TypeError('Validate severity must map rule ids or category names to a severity or "off".');
7194
+ for (const [name, value] of Object.entries(options2.severity)) {
5595
7195
  if (typeof value !== "string" || value !== "off" && !SEVERITIES.includes(value)) throw new TypeError(`Validate severity for ${JSON.stringify(name)} must be error, warning, info or off.`);
5596
7196
  const found = ruleFor(name, "severity");
5597
7197
  severity.set(typeof found === "string" ? found : found.id, value);
5598
7198
  }
5599
7199
  }
5600
7200
  const ignorePaths = [];
5601
- if (options.ignorePaths !== void 0) {
5602
- if (!Array.isArray(options.ignorePaths)) throw new TypeError("Validate ignorePaths must be an array of {rule, path}.");
5603
- for (const entry of options.ignorePaths) {
5604
- if (!isObject2(entry) || typeof entry.rule !== "string" || typeof entry.path !== "string" || entry.path !== "" && !entry.path.startsWith("/"))
7201
+ if (options2.ignorePaths !== void 0) {
7202
+ if (!Array.isArray(options2.ignorePaths)) throw new TypeError("Validate ignorePaths must be an array of {rule, path}.");
7203
+ for (const entry2 of options2.ignorePaths) {
7204
+ if (!isObject2(entry2) || typeof entry2.rule !== "string" || typeof entry2.path !== "string" || entry2.path !== "" && !entry2.path.startsWith("/"))
5605
7205
  throw new TypeError('Each validate ignorePaths entry needs a rule id, category name or "*", and a JSON Pointer path.');
5606
- const found = entry.rule === "*" ? "*" : ruleFor(entry.rule, "ignorePaths");
7206
+ const found = entry2.rule === "*" ? "*" : ruleFor(entry2.rule, "ignorePaths");
5607
7207
  ignorePaths.push({
5608
7208
  match: (info) => found === "*" || (typeof found === "string" ? found === info.category : found.id === info.id),
5609
- path: splitPointer(entry.path)
7209
+ path: splitPointer(entry2.path)
5610
7210
  });
5611
7211
  }
5612
7212
  }
5613
7213
  const thresholds = { ...DEFAULT_VALIDATION_THRESHOLDS };
5614
- if (options.thresholds !== void 0) {
5615
- if (!isObject2(options.thresholds)) throw new TypeError("Validate thresholds must be an object.");
5616
- for (const [key2, value] of Object.entries(options.thresholds)) {
7214
+ if (options2.thresholds !== void 0) {
7215
+ if (!isObject2(options2.thresholds)) throw new TypeError("Validate thresholds must be an object.");
7216
+ for (const [key2, value] of Object.entries(options2.thresholds)) {
5617
7217
  if (!(key2 in DEFAULT_VALIDATION_THRESHOLDS)) throw new TypeError(`Unknown validate threshold ${JSON.stringify(key2)}. Thresholds: ${Object.keys(DEFAULT_VALIDATION_THRESHOLDS).join(", ")}.`);
5618
7218
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new TypeError(`Validate threshold ${key2} must be a positive number.`);
5619
7219
  thresholds[key2] = value;
5620
7220
  }
5621
7221
  }
5622
- if (options.fonts !== void 0) {
5623
- if (!isObject2(options.fonts)) throw new TypeError("Validate fonts must be a fonts object with a textMeasurement.");
5624
- const measurement = options.fonts.textMeasurement;
7222
+ if (options2.fonts !== void 0) {
7223
+ if (!isObject2(options2.fonts)) throw new TypeError("Validate fonts must be a fonts object with a textMeasurement.");
7224
+ const measurement = options2.fonts.textMeasurement;
5625
7225
  if (measurement !== void 0 && typeof measurement !== "function" && !isObject2(measurement)) throw new TypeError("Validate fonts.textMeasurement must be a text measurement, or a function of the slide index.");
5626
7226
  }
5627
- if (options.chartPalette !== void 0 && (!Array.isArray(options.chartPalette) || !options.chartPalette.every((color) => typeof color === "string" && /^#[0-9a-fA-F]{6}$/.test(color))))
7227
+ if (options2.chartPalette !== void 0 && (!Array.isArray(options2.chartPalette) || !options2.chartPalette.every((color) => typeof color === "string" && /^#[0-9a-fA-F]{6}$/.test(color))))
5628
7228
  throw new TypeError("Validate chartPalette must be an array of #RRGGBB colours.");
5629
- if (options.template !== void 0 && typeof options.template !== "boolean") throw new TypeError("Validate template must be true or false.");
5630
- if (options.values !== void 0 && !isObject2(options.values)) throw new TypeError("Validate values must be an object keyed by variable id.");
7229
+ if (options2.template !== void 0 && typeof options2.template !== "boolean") throw new TypeError("Validate template must be true or false.");
7230
+ if (options2.values !== void 0 && !isObject2(options2.values)) throw new TypeError("Validate values must be an object keyed by variable id.");
5631
7231
  const level = (info) => severity.get(info.id) ?? severity.get(info.category);
5632
7232
  const off = (info) => level(info) === "off" || !!ignore?.rules.has(info.id) || !!ignore?.categories.has(info.category);
5633
7233
  return {
5634
- options,
7234
+ options: options2,
5635
7235
  off,
5636
7236
  enabled: (info) => !off(info) && (!only || only.rules.has(info.id) || only.categories.has(info.category)),
5637
7237
  severity: (info) => {
5638
7238
  const value = level(info);
5639
7239
  return value === "off" ? void 0 : value;
5640
7240
  },
5641
- ignored: (info, path) => ignorePaths.some((entry) => entry.match(info) && entry.path.every((part, index) => path[index] === part)),
7241
+ ignored: (info, path) => ignorePaths.some((entry2) => entry2.match(info) && entry2.path.every((part, index) => path[index] === part)),
5642
7242
  thresholds,
5643
- chartPalette: options.chartPalette
7243
+ chartPalette: options2.chartPalette
5644
7244
  };
5645
7245
  }
5646
7246
  var order = new Map(validationRules.map((info, index) => [info.id, index]));
@@ -5648,25 +7248,25 @@ var categoryOrder = (category) => {
5648
7248
  const index = validationCategories.indexOf(category);
5649
7249
  return index < 0 ? validationCategories.length : index;
5650
7250
  };
5651
- var auditInfos = ruleImplementations.flatMap((entry) => [entry.info, ...entry.also ?? []]);
7251
+ var auditInfos = ruleImplementations.flatMap((entry2) => [entry2.info, ...entry2.also ?? []]);
5652
7252
  var isLayoutRule = (info) => info.category === "layout" && info.cost === "composition";
5653
7253
  function slideOf(path) {
5654
- const match = /^\/slides\/(\d+)(?:\/|$)/.exec(path);
5655
- return match ? Number(match[1]) : void 0;
7254
+ const match2 = /^\/slides\/(\d+)(?:\/|$)/.exec(path);
7255
+ return match2 ? Number(match2[1]) : void 0;
5656
7256
  }
5657
7257
  function counts(findings) {
5658
7258
  const result = { error: 0, warning: 0, info: 0 };
5659
- for (const entry of findings) result[entry.severity]++;
7259
+ for (const entry2 of findings) result[entry2.severity]++;
5660
7260
  return result;
5661
7261
  }
5662
7262
  var FIXED = { backgroundPixels: "not-read", imageBytes: "embedded-only", nativeExport: "not-checked" };
5663
7263
  function syntaxReport(parsed, run) {
5664
7264
  const findings = [];
5665
- for (const entry of parsed.syntax) {
5666
- const info = findValidationRule(entry.ruleId);
7265
+ for (const entry2 of parsed.syntax) {
7266
+ const info = findValidationRule(entry2.ruleId);
5667
7267
  if (info && run.off(info)) continue;
5668
7268
  const severity = info ? run.severity(info) : void 0;
5669
- findings.push(severity ? { ...entry, severity } : entry);
7269
+ findings.push(severity ? { ...entry2, severity } : entry2);
5670
7270
  }
5671
7271
  const checks = {
5672
7272
  syntax: "checked",
@@ -5693,11 +7293,11 @@ function validateDocument(document, run, parsed) {
5693
7293
  if (enabledRule(findValidationRule("opf/variable-unfilled"))) findings.push(...variableFindings(document, engine, schemaValid));
5694
7294
  if (enabledRule(findValidationRule("opf/undeclared-catalog"))) findings.push(...undeclaredCatalogFindings(document));
5695
7295
  if (parsed) {
5696
- findings.push(...parsed.duplicates.filter((entry) => enabledRule(findValidationRule(entry.ruleId))));
7296
+ findings.push(...parsed.duplicates.filter((entry2) => enabledRule(findValidationRule(entry2.ruleId))));
5697
7297
  }
5698
7298
  const referencesWanted = validationRules.some((info) => info.category === "references" && run.enabled(info));
5699
7299
  if (referencesWanted) {
5700
- findings.push(...referenceFindings(document, engine, run.options).filter((entry) => enabledRule(findValidationRule(entry.ruleId))));
7300
+ findings.push(...referenceFindings(document, engine, run.options).filter((entry2) => enabledRule(findValidationRule(entry2.ruleId))));
5701
7301
  }
5702
7302
  const contracts = run.options.contracts ?? [];
5703
7303
  const contractRule = findValidationRule("opf/contract");
@@ -5716,7 +7316,7 @@ function validateDocument(document, run, parsed) {
5716
7316
  chartPalette: run.chartPalette,
5717
7317
  report(info, input) {
5718
7318
  const severity = run.severity(info) ?? input.severity ?? info.severity;
5719
- const entry = {
7319
+ const entry2 = {
5720
7320
  ruleId: info.id,
5721
7321
  severity,
5722
7322
  category: info.category,
@@ -5724,28 +7324,28 @@ function validateDocument(document, run, parsed) {
5724
7324
  path: input.path,
5725
7325
  message: input.message,
5726
7326
  help: input.help,
5727
- definition: `${DOCS}#${info.id.replace("/", "")}`,
7327
+ definition: ruleDocumentation(info.id),
5728
7328
  ...input.slide ? { slide: input.slide.index, ...input.slide.id ? { slideId: input.slide.id } : {} } : {},
5729
7329
  ...input.measured ? { measured: input.measured } : {},
5730
7330
  ...input.fixes?.length ? { fixes: input.fixes } : {},
5731
7331
  ...input.suggestions ? { suggestions: input.suggestions } : {}
5732
7332
  };
5733
- if (run.enabled(info)) findings.push(entry);
5734
- return entry;
7333
+ if (run.enabled(info)) findings.push(entry2);
7334
+ return entry2;
5735
7335
  }
5736
7336
  };
5737
- for (const entry of ruleImplementations) {
5738
- const all = [entry.info, ...entry.also ?? []];
7337
+ for (const entry2 of ruleImplementations) {
7338
+ const all = [entry2.info, ...entry2.also ?? []];
5739
7339
  if (!all.some((info) => run.enabled(info))) continue;
5740
7340
  for (const info of all) if (run.enabled(info)) auditRan.add(info.id);
5741
- entry.run(context);
7341
+ entry2.run(context);
5742
7342
  }
5743
7343
  }
5744
7344
  const finished = [];
5745
- for (const entry of findings) {
5746
- const info = findValidationRule(entry.ruleId);
7345
+ for (const entry2 of findings) {
7346
+ const info = findValidationRule(entry2.ruleId);
5747
7347
  const override = info ? run.severity(info) : void 0;
5748
- const next = override && override !== entry.severity ? { ...entry, severity: override } : entry;
7348
+ const next = override && override !== entry2.severity ? { ...entry2, severity: override } : entry2;
5749
7349
  if (info && run.ignored(info, splitPointer(next.path))) continue;
5750
7350
  const index = slideOf(next.path);
5751
7351
  if (next.slide === void 0 && index !== void 0 && next.scope !== "context") {
@@ -5757,6 +7357,10 @@ function validateDocument(document, run, parsed) {
5757
7357
  const location = parsed.locate(next.path);
5758
7358
  if (location) next.location = location;
5759
7359
  }
7360
+ if (next.ruleId === "opf/schema" && next.fixes === void 0 && next.scope !== "context") {
7361
+ const fixes = schemaFixes(next, document);
7362
+ if (fixes.length) next.fixes = fixes;
7363
+ }
5760
7364
  finished.push(next);
5761
7365
  }
5762
7366
  finished.sort(
@@ -5784,17 +7388,17 @@ function validateDocument(document, run, parsed) {
5784
7388
  ...engine.template !== void 0 ? { template: engine.template, unfilledVariables: engine.unfilledVariables } : {}
5785
7389
  };
5786
7390
  }
5787
- function validate(input, options = {}) {
5788
- const run = resolveOptions(options);
7391
+ function validate(input, options2 = {}) {
7392
+ const run = resolveOptions(options2);
5789
7393
  if (typeof input === "string") {
5790
7394
  const parsed = parseSource(input);
5791
7395
  return parsed.syntax.length ? syntaxReport(parsed, run) : validateDocument(parsed.value, run, parsed);
5792
7396
  }
5793
7397
  return validateDocument(input, run);
5794
7398
  }
5795
- function assertValid(value, options = {}) {
7399
+ function assertValid(value, options2 = {}) {
5796
7400
  if (typeof value === "string") throw new TypeError("assertValid takes a parsed presentation. Use validate(text) for JSON text.");
5797
- const report2 = validate(value, options);
7401
+ const report2 = validate(value, options2);
5798
7402
  if (!report2.valid) throw new OPFValidationError(report2);
5799
7403
  }
5800
7404
  function validateCatalogRecord(kind, value) {
@@ -5820,4 +7424,4 @@ function assertValidCatalogRecord(kind, value) {
5820
7424
  if (!report2.valid) throw new OPFValidationError(report2);
5821
7425
  }
5822
7426
 
5823
- export { CATALOG_SCHEMA, COMMON_KEYS, DEFAULT_VALIDATION_THRESHOLDS, OPFPatchError, OPFPatchValidationError, OPFValidationError, UNRANKED_PRIORITY, activeSchema, analyzeJsonSyntax, applyPatch, applyPatchWithInverse, applyTextEdits, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogKindAt, catalogSchemaNames, createLocator, createSchemaValue, describeField, escapePointerToken, findValidationRule, formatPointer, getAtPointer, hasPointer, invertPatch, isPromotedRegionKey, jsonEqual, listSchemaFields, normalizePatch, parsePointer, parseSource, pointerFromPath, readPointer, rememberByText, resolveSchema, resolveSlideContext, resolveSlideScriptMeasurement, schemaAtPath, schemaCatalogKinds, schemaLabel, schemaType, schemaVariants, splitOpfPath, splitPointer2 as splitPointer, unescapePointerToken, validate, validateCatalogRecord, validationCategories, validationRules };
7427
+ 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 };