@openpresentation/opf 0.20.4 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/browser.js +16 -16
  2. package/dist/{chunk-Z75YB54S.js → chunk-2X4ZG2CN.js} +5 -5
  3. package/dist/{chunk-WD4W7KKK.js → chunk-6UBIR2U3.js} +54 -11
  4. package/dist/{chunk-DYGYMBBW.js → chunk-7YFMWEPQ.js} +2 -2
  5. package/dist/{chunk-7PQZDC2H.js → chunk-ARLOTG23.js} +1 -1
  6. package/dist/{chunk-VFMHM2IL.js → chunk-BWWGYOOG.js} +22 -0
  7. package/dist/{chunk-IDMQWS6O.js → chunk-D2XIDBUB.js} +6 -6
  8. package/dist/{chunk-5SQ6CIWR.js → chunk-D7WZVSNV.js} +5 -5
  9. package/dist/{chunk-OIGCC6VA.js → chunk-EFICNDGV.js} +2 -2
  10. package/dist/{chunk-YOLDSF7U.js → chunk-THEUCQZZ.js} +1 -1
  11. package/dist/{chunk-S4FR7ATF.js → chunk-U55BQQKU.js} +1 -1
  12. package/dist/{chunk-V66KJA3H.js → chunk-VLFDWV7L.js} +1 -1
  13. package/dist/{chunk-52GKVP3I.js → chunk-W62CX2WG.js} +22 -12
  14. package/dist/{chunk-URUNMZFO.js → chunk-WZPFIPOO.js} +2 -2
  15. package/dist/{chunk-XAYMPBNE.js → chunk-X6SB52RP.js} +333 -149
  16. package/dist/{chunk-2M7U4WL3.js → chunk-XQARTOZJ.js} +260 -104
  17. package/dist/{chunk-TZ5IZETE.js → chunk-YD44KIRA.js} +1 -1
  18. package/dist/{chunk-TCBEF5OY.js → chunk-YZ7RZ4MU.js} +1 -1
  19. package/dist/{composition-QtS1rnhv.d.ts → composition-DzAV0cfU.d.ts} +133 -3
  20. package/dist/composition.d.ts +1 -1
  21. package/dist/composition.js +1 -1
  22. package/dist/conversion-524P44NX.js +19 -0
  23. package/dist/{conversion-Cqwu0Ds9.d.ts → conversion-CtTg2ivR.d.ts} +6 -5
  24. package/dist/convert.d.ts +3 -3
  25. package/dist/convert.js +7 -7
  26. package/dist/data.d.ts +1 -1
  27. package/dist/data.js +2 -2
  28. package/dist/{deck-report-BdG5H3Qj.d.ts → deck-report-971nEzTY.d.ts} +2 -2
  29. package/dist/deck.d.ts +6 -6
  30. package/dist/deck.js +10 -10
  31. package/dist/diff.d.ts +1 -1
  32. package/dist/diff.js +5 -5
  33. package/dist/docs.js +16 -10
  34. package/dist/examples.d.ts +1 -1
  35. package/dist/{finding-DqgwzUmr.d.ts → finding-l9dlQ2NF.d.ts} +4 -0
  36. package/dist/format.js +2 -2
  37. package/dist/index.d.ts +21 -15
  38. package/dist/index.js +20 -20
  39. package/dist/markdown.d.ts +4 -4
  40. package/dist/markdown.js +7 -7
  41. package/dist/node-engine.d.ts +30 -9
  42. package/dist/node-engine.js +20 -20
  43. package/dist/{pagination-CAX6mHG6.d.ts → pagination-BTqRj7dM.d.ts} +2 -2
  44. package/dist/pagination.d.ts +2 -2
  45. package/dist/pagination.js +4 -4
  46. package/dist/patch.d.ts +1 -1
  47. package/dist/patch.js +4 -4
  48. package/dist/{presentation-BG4QCcdB.d.ts → presentation-ZNcXSpvm.d.ts} +50 -4
  49. package/dist/repo-readme.js +1 -1
  50. package/dist/schemas.js +1 -1
  51. package/dist/spec/schemas/finding.schema.json +6 -1
  52. package/dist/spec/schemas/opf.schema.json +8 -0
  53. package/dist/types.d.ts +2 -2
  54. package/dist/{validation-types-DRaNnieo.d.ts → validation-types-uLDSrJgM.d.ts} +3 -3
  55. package/dist/{validator-BJtXHJKh.d.ts → validator-DxAjRPmE.d.ts} +4 -10
  56. package/dist/validator.d.ts +5 -5
  57. package/dist/validator.js +3 -3
  58. package/dist/yaml.d.ts +4 -4
  59. package/dist/yaml.js +7 -7
  60. package/package.json +1 -1
  61. package/dist/conversion-DRCJIMWS.js +0 -19
@@ -1,5 +1,5 @@
1
- import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-XAYMPBNE.js';
2
- import { schemaEntries, schemas } from './chunk-VFMHM2IL.js';
1
+ import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, chartSeriesPalette, normalizeHexColor, resolveChartColors, colorContrast, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-X6SB52RP.js';
2
+ import { schemas, schemaEntries } from './chunk-BWWGYOOG.js';
3
3
  import Ajv2020 from 'ajv/dist/2020.js';
4
4
  import addFormats from 'ajv-formats';
5
5
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
@@ -87,6 +87,125 @@ function unusedReferenceWarnings(value) {
87
87
  return value.references.flatMap((reference, index) => isRecord(reference) && typeof reference.id === "string" && unused.includes(reference.id) ? [issue(`/references/${index}`, `reference '${reference.id}' is never cited; cite it with a run's cite field or remove it`, { code: "unused-reference", id: reference.id })] : []);
88
88
  }
89
89
 
90
+ // src/union-errors.ts
91
+ var compiled = /* @__PURE__ */ new WeakMap();
92
+ var origin = /* @__PURE__ */ new WeakMap();
93
+ var isUnion = (error) => (error.keyword === "oneOf" || error.keyword === "anyOf") && Array.isArray(error.schema);
94
+ var key = (error) => `${error.instancePath}\0${error.keyword}\0${error.message}\0${JSON.stringify(error.params)}`;
95
+ var depth = (pointer4) => pointer4 ? pointer4.split("/").length - 1 : 0;
96
+ var original = (node) => {
97
+ let current = node;
98
+ while (current && typeof current === "object" && origin.has(current)) current = origin.get(current);
99
+ return current;
100
+ };
101
+ function qualify(node, rootId) {
102
+ if (Array.isArray(node)) {
103
+ const copy = node.map((item) => qualify(item, rootId));
104
+ origin.set(copy, node);
105
+ return copy;
106
+ }
107
+ if (node && typeof node === "object") {
108
+ const copy = {};
109
+ for (const [name, value] of Object.entries(node)) copy[name] = name === "$ref" && typeof value === "string" && value.startsWith("#") ? rootId + value : qualify(value, rootId);
110
+ origin.set(copy, node);
111
+ return copy;
112
+ }
113
+ return node;
114
+ }
115
+ function branchValidator(ajv2, rootId, branch) {
116
+ let validate2 = compiled.get(branch);
117
+ if (!validate2) {
118
+ validate2 = ajv2.compile(qualify(branch, rootId));
119
+ compiled.set(branch, validate2);
120
+ }
121
+ return validate2;
122
+ }
123
+ function branchLabel(branch, index) {
124
+ if (typeof branch.$ref === "string") return branch.$ref.slice(branch.$ref.lastIndexOf("/") + 1);
125
+ if (typeof branch.type === "string") return branch.type;
126
+ if (Array.isArray(branch.type)) return branch.type.join("|");
127
+ if ("const" in branch) return JSON.stringify(branch.const);
128
+ return `option ${index + 1}`;
129
+ }
130
+ function describe(errors, base) {
131
+ const types = [...new Set(errors.filter((error) => depth(error.instancePath) === base && error.keyword === "type").flatMap((error) => [error.params.type].flat()))];
132
+ if (types.length) return `must be ${types.join(" or ")}`;
133
+ const shallow = [...errors].filter((error) => !isUnion(error) || errors.every(isUnion)).sort((a, b) => depth(a.instancePath) - depth(b.instancePath))[0];
134
+ if (!shallow) return "does not match";
135
+ const rest = shallow.instancePath.split("/").slice(base + 1).join("/");
136
+ return depth(shallow.instancePath) === base || !rest ? shallow.message ?? "does not match" : `${shallow.message} at /${rest}`;
137
+ }
138
+ function bestMatchErrors(ajv2, rootId, errors) {
139
+ const unions = errors.filter(isUnion);
140
+ if (!unions.length) return errors;
141
+ const analyses = [];
142
+ for (const union of unions) {
143
+ const analysis = analyze(ajv2, rootId, union);
144
+ if (analysis) analyses.push(analysis);
145
+ }
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));
148
+ const outer = analyses.filter((analysis) => !analyses.some((other) => within(analysis, other)));
149
+ const replaced = new Map(outer.map((analysis) => [analysis.union, analysis]));
150
+ const drop = new Set(outer.flatMap((analysis) => [...analysis.span]));
151
+ const result = [];
152
+ for (const error of errors) {
153
+ const analysis = replaced.get(error);
154
+ if (analysis) result.push(...analysis.replacement);
155
+ else if (!drop.has(key(error))) result.push(error);
156
+ }
157
+ return result;
158
+ }
159
+ function analyze(ajv2, rootId, union) {
160
+ const branches = union.schema;
161
+ const base = depth(union.instancePath);
162
+ const results = [];
163
+ for (const [index, branch] of branches.entries()) {
164
+ if (!branch || typeof branch !== "object") return void 0;
165
+ const validate2 = branchValidator(ajv2, rootId, branch);
166
+ if (validate2(union.data) === true) return void 0;
167
+ const raw = (validate2.errors ?? []).map((error) => ({ ...error, instancePath: union.instancePath + error.instancePath }));
168
+ results.push({ label: branchLabel(branch, index), errors: raw });
169
+ }
170
+ const span = /* @__PURE__ */ new Set([key(union), ...results.flatMap((result) => result.errors.map(key))]);
171
+ const reduced = results.map((result) => ({ label: result.label, errors: bestMatchErrors(ajv2, rootId, result.errors) }));
172
+ const mismatch = (error) => {
173
+ const level = depth(error.instancePath) - base;
174
+ return level === 0 && (error.keyword === "type" || isUnion(error)) || level === 1 && error.keyword === "const";
175
+ };
176
+ const matched = reduced.filter((result) => result.errors.length && !result.errors.some(mismatch));
177
+ const reach = (errors) => Math.min(...errors.map((error) => depth(error.instancePath)));
178
+ const ranked = [...matched].sort((a, b) => reach(b.errors) - reach(a.errors));
179
+ const summary = (list) => list.map((result) => `${result.label}: ${describe(result.errors, base)}`);
180
+ const named = (list) => list.length > 1 ? `${list.slice(0, -1).join(", ")} or ${list.at(-1)}` : list[0] ?? "";
181
+ const lead = ranked[0];
182
+ if (!lead) {
183
+ const rootTypes = reduced.map((result) => result.errors.filter((error) => depth(error.instancePath) === base && error.keyword === "type"));
184
+ const plain = rootTypes.every((list, index) => list.length === reduced[index]?.errors.length && list.length > 0);
185
+ const types = [...new Set(rootTypes.flatMap((list) => list.flatMap((error) => [error.params.type].flat())))].map(String);
186
+ return { union, span, replacement: [{ ...union, message: plain ? `must be ${named(types)}` : union.message, alternatives: summary(reduced) }] };
187
+ }
188
+ const top = ranked.filter((result) => reach(result.errors) === reach(lead.errors));
189
+ const others = reduced.filter((result) => !top.includes(result));
190
+ const single = (result) => {
191
+ const only = result.errors[0];
192
+ return result.errors.length === 1 && only && depth(only.instancePath) === base ? only : void 0;
193
+ };
194
+ const singles = top.map(single);
195
+ const keyword = singles[0]?.keyword;
196
+ if (top.length > 1 && (keyword === "format" || keyword === "required") && singles.every((error) => error?.keyword === keyword)) {
197
+ const values = singles.map((error) => JSON.stringify(keyword === "format" ? error?.params.format : error?.params.missingProperty).replace(/^"|"$/g, keyword === "format" ? '"' : "'"));
198
+ const message = keyword === "format" ? `must match format ${named(values)}` : `must have required property ${named(values)}`;
199
+ return { union, span, replacement: [{ ...singles[0], message, alternatives: summary(top) }] };
200
+ }
201
+ const chosen = top.flatMap((result) => result.errors).map((error, index) => {
202
+ const mapped = { ...error, parentSchema: original(error.parentSchema), schema: original(error.schema) };
203
+ if (index === 0 && others.length) mapped.alternatives = summary(others);
204
+ return mapped;
205
+ });
206
+ return { union, span, replacement: chosen };
207
+ }
208
+
90
209
  // src/validation-definitions.ts
91
210
  var schemaLocations;
92
211
  var issueLocations = /* @__PURE__ */ new WeakMap();
@@ -107,8 +226,8 @@ function rememberValidationDefinition(issue2, parentSchema, keyword) {
107
226
  pointer: current.pointer,
108
227
  uri: `${entry.schema.$id}#${current.pointer}`
109
228
  });
110
- for (const [key, value] of Object.entries(current.value)) {
111
- const segment = key.replaceAll("~", "~0").replaceAll("/", "~1");
229
+ for (const [key2, value] of Object.entries(current.value)) {
230
+ const segment = key2.replaceAll("~", "~0").replaceAll("/", "~1");
112
231
  stack.push({ value, pointer: `${current.pointer}/${segment}` });
113
232
  }
114
233
  }
@@ -254,7 +373,8 @@ function toIssue(error) {
254
373
  message: slideDimensions ? "a slide's design cannot set 'dimensions': a PPTX has one slide size, so set design.dimensions on the deck (or its theme)" : error.message ?? "failed validation",
255
374
  keyword: error.keyword,
256
375
  schemaPath: error.schemaPath,
257
- params: error.params
376
+ params: error.params,
377
+ ...error.alternatives?.length ? { alternatives: error.alternatives } : {}
258
378
  };
259
379
  rememberValidationDefinition(issue2, error.parentSchema, error.keyword);
260
380
  return issue2;
@@ -268,8 +388,8 @@ function semanticIssue(path, message, params = {}) {
268
388
  params
269
389
  };
270
390
  }
271
- function hasOwn(value, key) {
272
- return Object.hasOwn(value, key);
391
+ function hasOwn(value, key2) {
392
+ return Object.hasOwn(value, key2);
273
393
  }
274
394
  function isEnUkTag(value) {
275
395
  return typeof value === "string" && value.toLowerCase() === "en-uk";
@@ -391,20 +511,20 @@ function validateContentPayload(value, path, options = {}) {
391
511
  }
392
512
  return issues;
393
513
  }
394
- function slideRegion(key) {
395
- if (key.includes(":")) {
396
- const [rowPart, columnPart] = key.split(":");
514
+ function slideRegion(key2) {
515
+ if (key2.includes(":")) {
516
+ const [rowPart, columnPart] = key2.split(":");
397
517
  if (!rowPart || !columnPart) return void 0;
398
518
  const rows = rowSpans[rowPart];
399
519
  const columns = columnSpans[columnPart];
400
520
  return rows && columns ? { rows, columns } : void 0;
401
521
  }
402
- if (key in columnSpans) {
403
- const columns = columnSpans[key];
522
+ if (key2 in columnSpans) {
523
+ const columns = columnSpans[key2];
404
524
  return columns ? { rows: [0, 1, 2], columns } : void 0;
405
525
  }
406
- if (key in rowSpans) {
407
- const rows = rowSpans[key];
526
+ if (key2 in rowSpans) {
527
+ const rows = rowSpans[key2];
408
528
  return rows ? { rows, columns: [0, 1, 2] } : void 0;
409
529
  }
410
530
  return void 0;
@@ -417,7 +537,7 @@ function regionsOverlap(left, right) {
417
537
  }
418
538
  function validateSlideRegions(slide, slidePath) {
419
539
  const issues = [];
420
- const regionKeys = Object.keys(slide).filter((key) => promotedRegionKeySet.has(key));
540
+ const regionKeys = Object.keys(slide).filter((key2) => promotedRegionKeySet.has(key2));
421
541
  const rootFields = presentFields(slide, rootPayloadFields);
422
542
  if (regionKeys.length > 0 && rootFields.length > 0) {
423
543
  issues.push(semanticIssue(slidePath, "root content payload fields cannot be mixed with promoted region keys", {
@@ -428,10 +548,10 @@ function validateSlideRegions(slide, slidePath) {
428
548
  if (regionKeys.length === 0 && rootFields.length > 0) {
429
549
  issues.push(...validateContentPayload(slide, slidePath, { slideRoot: true }));
430
550
  }
431
- for (const key of regionKeys) {
432
- const value = slide[key];
551
+ for (const key2 of regionKeys) {
552
+ const value = slide[key2];
433
553
  if (isRecord(value)) {
434
- issues.push(...validateContentPayload(value, pathFor(slidePath, key)));
554
+ issues.push(...validateContentPayload(value, pathFor(slidePath, key2)));
435
555
  }
436
556
  }
437
557
  for (let leftIndex = 0; leftIndex < regionKeys.length; leftIndex += 1) {
@@ -455,19 +575,19 @@ function validateSlideRegions(slide, slidePath) {
455
575
  }
456
576
  function deckMetadataIssues(value) {
457
577
  const issues = [];
458
- const entries = (key) => {
459
- const field = value[key];
460
- if (Array.isArray(field)) return field.flatMap((entry, index) => isRecord(entry) ? [{ entry, path: `/${key}/${index}` }] : []);
461
- return isRecord(field) ? [{ entry: field, path: `/${key}` }] : [];
578
+ const entries = (key2) => {
579
+ const field = value[key2];
580
+ if (Array.isArray(field)) return field.flatMap((entry, index) => isRecord(entry) ? [{ entry, path: `/${key2}/${index}` }] : []);
581
+ return isRecord(field) ? [{ entry: field, path: `/${key2}` }] : [];
462
582
  };
463
583
  const organizationIds = /* @__PURE__ */ new Set();
464
- for (const key of ["organization", "speaker"]) {
584
+ for (const key2 of ["organization", "speaker"]) {
465
585
  const seen = /* @__PURE__ */ new Set();
466
- for (const { entry, path } of entries(key)) {
586
+ for (const { entry, path } of entries(key2)) {
467
587
  if (typeof entry.id !== "string") continue;
468
- if (seen.has(entry.id)) issues.push(semanticIssue(`${path}/id`, `${key} ids must be unique within a presentation`, { id: entry.id }));
588
+ if (seen.has(entry.id)) issues.push(semanticIssue(`${path}/id`, `${key2} ids must be unique within a presentation`, { id: entry.id }));
469
589
  seen.add(entry.id);
470
- if (key === "organization") organizationIds.add(entry.id);
590
+ if (key2 === "organization") organizationIds.add(entry.id);
471
591
  }
472
592
  }
473
593
  for (const { entry, path } of entries("speaker")) {
@@ -621,9 +741,9 @@ function dataIssues(value) {
621
741
  const seen = /* @__PURE__ */ new Set();
622
742
  const add = (diagnostics) => {
623
743
  for (const diagnostic of diagnostics) {
624
- const key = `${diagnostic.code}|${diagnostic.path}`;
625
- if (seen.has(key)) continue;
626
- seen.add(key);
744
+ const key2 = `${diagnostic.code}|${diagnostic.path}`;
745
+ if (seen.has(key2)) continue;
746
+ seen.add(key2);
627
747
  (diagnostic.severity === "error" ? out.errors : out.warnings).push(semanticIssue(diagnostic.path, diagnostic.message, { code: diagnostic.code }));
628
748
  }
629
749
  };
@@ -808,9 +928,9 @@ function presentationReferenceWarnings(value, catalogs) {
808
928
  };
809
929
  highlightWarnings(slide, slidePath);
810
930
  visitContentPayloads(slide, slidePath, highlightWarnings);
811
- for (const key of Object.keys(slide)) {
812
- if (promotedRegionKeySet.has(key)) {
813
- issues.push(...chartTypeWarnings(slide[key], pathFor(slidePath, key)));
931
+ for (const key2 of Object.keys(slide)) {
932
+ if (promotedRegionKeySet.has(key2)) {
933
+ issues.push(...chartTypeWarnings(slide[key2], pathFor(slidePath, key2)));
814
934
  }
815
935
  }
816
936
  });
@@ -835,7 +955,7 @@ function contentDepthIssues(value) {
835
955
  if (Array.isArray(slide.blocks)) slide.blocks.forEach((block, i) => {
836
956
  stack.push({ value: block, path: `/slides/${index}/blocks/${i}`, depth: 0, ancestors: [] });
837
957
  });
838
- for (const key of promotedRegionKeys) if (hasOwn(slide, key)) stack.push({ value: slide[key], path: `/slides/${index}/${key}`, depth: 0, ancestors: [] });
958
+ for (const key2 of promotedRegionKeys) if (hasOwn(slide, key2)) stack.push({ value: slide[key2], path: `/slides/${index}/${key2}`, depth: 0, ancestors: [] });
839
959
  });
840
960
  while (stack.length) {
841
961
  const entry = stack.pop();
@@ -890,9 +1010,9 @@ function typedVariableErrors(errors, value) {
890
1010
  function pointerValue(root, pointer4) {
891
1011
  let current = root;
892
1012
  for (const token of pointer4.split("/").slice(1)) {
893
- const key = token.replaceAll("~1", "/").replaceAll("~0", "~");
894
- if (Array.isArray(current)) current = current[Number(key)];
895
- else if (isRecord(current) && hasOwn(current, key)) current = current[key];
1013
+ const key2 = token.replaceAll("~1", "/").replaceAll("~0", "~");
1014
+ if (Array.isArray(current)) current = current[Number(key2)];
1015
+ else if (isRecord(current) && hasOwn(current, key2)) current = current[key2];
896
1016
  else return void 0;
897
1017
  }
898
1018
  return current;
@@ -955,7 +1075,8 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options = {
955
1075
  }
956
1076
  const valid = resolved.validate(subject) === true;
957
1077
  const ajvErrors = valid ? [] : typedVariableErrors(resolved.validate.errors ?? [], subject);
958
- const mapped = (resolved.schemaName === "presentation" ? dataUnionErrors(ajvErrors, subject) : ajvErrors).map(toIssue);
1078
+ const specific = resolved.schemaName === "presentation" ? dataUnionErrors(ajvErrors, subject) : ajvErrors;
1079
+ const mapped = bestMatchErrors(getAjv(), schemas[resolved.schemaName].$id, specific).map(toIssue);
959
1080
  const errors = mapped;
960
1081
  if (resolved.schemaName === "layout" && valid) for (const issue2 of layoutTemplateIssues(subject)) errors.push(semanticIssue(issue2.path, issue2.message, { code: issue2.code }));
961
1082
  const warnings = [];
@@ -1283,20 +1404,6 @@ function readableColor(candidates, sample, minimum) {
1283
1404
  if (worstContrast(candidate, sample).ratio >= minimum) return candidate;
1284
1405
  return void 0;
1285
1406
  }
1286
- var DEFAULT_CHART_PALETTE = [
1287
- "#2874A6",
1288
- "#1B4F72",
1289
- "#5499C7",
1290
- "#7BDBB2",
1291
- "#3AC67A",
1292
- "#24A89E",
1293
- "#F59E0B",
1294
- "#EF4444",
1295
- "#8B5CF6",
1296
- "#14B8A6",
1297
- "#0F172A",
1298
- "#64748B"
1299
- ];
1300
1407
 
1301
1408
  // src/rule-content.ts
1302
1409
  var pointer2 = (...parts2) => parts2.length ? "/" + parts2.map((part) => String(part).replaceAll("~", "~0").replaceAll("/", "~1")).join("/") : "";
@@ -1356,9 +1463,9 @@ function textValues(slide, slidePath) {
1356
1463
  const metric = node.metric;
1357
1464
  if (typeof metric === "string") push(at2("metric"), metric, "metric", "metric");
1358
1465
  else if (metric && typeof metric === "object")
1359
- for (const key of ["value", "label", "unit", "delta"]) {
1360
- const value = metric[key];
1361
- if (typeof value === "string") push(at2("metric", key), value, "metric", "metric");
1466
+ for (const key2 of ["value", "label", "unit", "delta"]) {
1467
+ const value = metric[key2];
1468
+ if (typeof value === "string") push(at2("metric", key2), value, "metric", "metric");
1362
1469
  }
1363
1470
  const timeline = node.timeline, events = Array.isArray(timeline) ? timeline : Array.isArray(rec(timeline).events) ? rec(timeline).events : [];
1364
1471
  if (typeof rec(timeline).name === "string") push(at2("timeline", "name"), rec(timeline).name, "timeline", "timeline");
@@ -1595,8 +1702,8 @@ var contrastRules = [
1595
1702
  }
1596
1703
  continue;
1597
1704
  }
1598
- const key = `${sample.source === "default" ? "default" : sample.path}|${sample.color}|${worst.against}|${large}`;
1599
- const known = aggregated.get(key);
1705
+ const key2 = `${sample.source === "default" ? "default" : sample.path}|${sample.color}|${worst.against}|${large}`;
1706
+ const known = aggregated.get(key2);
1600
1707
  if (known && sample.source === "default") {
1601
1708
  known.extra++;
1602
1709
  continue;
@@ -1617,7 +1724,7 @@ var contrastRules = [
1617
1724
  measured: { ratio: round(worst.ratio), needed, large, sizePt: round(sample.sizePx * PX_TO_PT), textColor: sample.color, backgroundColor: worst.against },
1618
1725
  fixes
1619
1726
  });
1620
- if (sample.source === "default") aggregated.set(key, { diagnostic, extra: 0 });
1727
+ if (sample.source === "default") aggregated.set(key2, { diagnostic, extra: 0 });
1621
1728
  }
1622
1729
  for (const { diagnostic, extra } of aggregated.values())
1623
1730
  if (extra) {
@@ -1893,7 +2000,7 @@ var chartColorRule = rule(
1893
2000
  {
1894
2001
  standard: "WCAG 2.2 SC 1.4.1 Use of Color, level A",
1895
2002
  thresholds: ["minSeriesColorDifference"],
1896
- approximations: "Uses the engine's series palette (ValidateOptions.chartPalette; default the opf-render/opf-pptx palette, adjusted for the card surface like the preview does) in series order: series i takes colour i, pie/doughnut/treemap/funnel slices take colours per category. Pairs are compared by CIE76 distance after simulating protanopia, deuteranopia, tritanopia (Machado 2009, severity 1) and greyscale. Single-series charts and chart types without a series legend are skipped."
2003
+ approximations: "Uses the engine's series palette (ValidateOptions.chartPalette; default chartSeriesPalette of each slide colour scheme (accent1 to accent6, then their tints), adjusted for the card surface like the preview does) in series order: series i takes colour i, pie/doughnut/treemap/funnel slices take colours per category. Pairs are compared by CIE76 distance after simulating protanopia, deuteranopia, tritanopia (Machado 2009, severity 1) and greyscale. Single-series charts and chart types without a series legend are skipped."
1897
2004
  }
1898
2005
  );
1899
2006
  var chartAltRule = rule(
@@ -1904,6 +2011,21 @@ var chartAltRule = rule(
1904
2011
  "A chart conveys a message; people who cannot see it need the message and ideally the numbers in text. The chart's alt field is that text alternative (the preview exposes it as the chart's accessible name and the PowerPoint export writes it as the frame's alternative text); a sentence or table beside the chart also serves. An empty alt marks a chart decorative, which is reported as info so the choice is reviewed: a chart rarely carries no message.",
1905
2012
  { standard: "WCAG 2.2 SC 1.1.1 Non-text Content, level A", approximations: `A chart passes when chart.alt has text. Without alt, it passes when the slide has any other text, list, table, quote or metric content besides title and tag, or a subtitle. It does not judge whether alt or that text states the chart's point (see opf/poor-alt-text for generic alt text). alt: "" is reported as a decorative chart, whatever else is on the slide.` }
1906
2013
  );
2014
+ var chartColorsRule = rule(
2015
+ "chart-colors-adjusted",
2016
+ "accessibility",
2017
+ "warning",
2018
+ "A chart colour cannot be used as authored.",
2019
+ "`chart.colors` sets the colour of series 1, 2, 3 and so on. A colour that cannot be resolved (a `var:` colour variable the deck does not define, or a name that is not a colour) is ignored and the deck palette colour of that position is used. A colour with under 3:1 contrast against the chart panel is kept for its hue but drawn lighter or darker, so the chart does not show the colour that was written.",
2020
+ {
2021
+ standard: "WCAG 2.2 SC 1.4.11 Non-text Contrast, level AA",
2022
+ approximations: "Resolves each entry like any colour reference (hex, colour-scheme slot or role, `var:` variable) against the slide's scheme and compares it with the chart panel (the slide surface colour) by WCAG contrast; 3:1 is the threshold the engines enforce. Translucent colours and unresolved panels are not measured. Colours that look alike to colour-vision deficiency are reported by opf/chart-color-only, which judges the authored colours."
2023
+ }
2024
+ );
2025
+ var chartColorContext = (design) => ({
2026
+ roles: { primary: design.colors.primary, secondary: design.colors.secondary, accent: design.colors.accent, background: design.colors.background, surface: design.colors.surface, text: design.colors.text, textSecondary: design.colors.mutedText },
2027
+ variables: design.variables
2028
+ });
1907
2029
  var chartSkip = /(histogram|box|pareto|waterfall|world|map)/;
1908
2030
  var perCategory = /(pie|doughnut|treemap|funnel)/;
1909
2031
  var chartRules = [
@@ -1922,7 +2044,7 @@ var chartRules = [
1922
2044
  const count = slices ? data.rows.length : data.columns.length - 1;
1923
2045
  if (count < 2) continue;
1924
2046
  const surface = slide.design.colors.surface;
1925
- const palette = chartPaletteForFill(surface, context.chartPalette);
2047
+ const palette = chartPaletteForFill(surface, context.chartPalette ?? chartSeriesPalette(slide.design.colorScheme, chart.colors, chartColorContext(slide.design)));
1926
2048
  if (!palette.length) continue;
1927
2049
  const used = Array.from({ length: Math.min(count, 24) }, (_, i) => palette[i % palette.length]);
1928
2050
  const pairs = [];
@@ -1939,17 +2061,51 @@ var chartRules = [
1939
2061
  if (!pairs.length && !cycles) continue;
1940
2062
  pairs.sort((x, y) => x.difference - y.difference);
1941
2063
  const names2 = slices ? data.rows.map((row) => String(row?.[0] ?? "")) : data.columns.slice(1).map(String);
1942
- const describe = (p) => `${JSON.stringify(names2[p.a] ?? `#${p.a + 1}`)} and ${JSON.stringify(names2[p.b] ?? `#${p.b + 1}`)} (${p.model}, difference ${p.difference.toFixed(1)})`;
2064
+ const describe2 = (p) => `${JSON.stringify(names2[p.a] ?? `#${p.a + 1}`)} and ${JSON.stringify(names2[p.b] ?? `#${p.b + 1}`)} (${p.model}, difference ${p.difference.toFixed(1)})`;
1943
2065
  context.report(chartColorRule, {
1944
2066
  path: `${payload.path === slide.path ? slide.path : payload.path}/chart`,
1945
2067
  slide,
1946
- message: `${slices ? "Slices" : "Series"} may be hard to tell apart without colour: ${pairs.slice(0, 3).map(describe).join("; ")}${cycles ? `${pairs.length ? "; " : ""}${count} ${slices ? "slices" : "series"} exceed the ${palette.length}-colour palette, so colours repeat` : ""}.`,
2068
+ message: `${slices ? "Slices" : "Series"} may be hard to tell apart without colour: ${pairs.slice(0, 3).map(describe2).join("; ")}${cycles ? `${pairs.length ? "; " : ""}${count} ${slices ? "slices" : "series"} exceed the ${palette.length}-colour palette, so colours repeat` : ""}.`,
1947
2069
  help: "Show fewer series per chart (split into two charts), state the key values in the slide text or a table next to the chart, or choose a chart type whose categories sit on an axis instead of in a legend.",
1948
2070
  measured: { series: count, confusablePairs: pairs.length, minDifference: pairs[0] ? round(pairs[0].difference) : null }
1949
2071
  });
1950
2072
  }
1951
2073
  }
1952
2074
  },
2075
+ {
2076
+ info: chartColorsRule,
2077
+ run(context) {
2078
+ for (const slide of context.slides)
2079
+ for (const payload of slidePayloads(slide.slide, slide.path)) {
2080
+ const chart = rec(payload.node.chart);
2081
+ if (!Array.isArray(chart.colors)) continue;
2082
+ const surface = normalizeHexColor(slide.design.colors.surface);
2083
+ const resolved = resolveChartColors(chart.colors, slide.design.colorScheme, chartColorContext(slide.design));
2084
+ resolved.forEach((color, index) => {
2085
+ const path = `${payload.path === slide.path ? slide.path : payload.path}/chart/colors/${index}`;
2086
+ const entry = chart.colors[index];
2087
+ if (color === void 0) {
2088
+ context.report(chartColorsRule, {
2089
+ path,
2090
+ slide,
2091
+ message: `Chart colour ${index + 1} (${JSON.stringify(entry)}) cannot be resolved, so the palette colour of that position is used.`,
2092
+ help: 'Use a hex colour, a colour-scheme name such as "accent2", or the id of a colour variable the deck defines (var:<id>).'
2093
+ });
2094
+ return;
2095
+ }
2096
+ const contrast = surface ? colorContrast(color, surface) : void 0;
2097
+ if (contrast === void 0 || contrast >= 3) return;
2098
+ context.report(chartColorsRule, {
2099
+ path,
2100
+ slide,
2101
+ message: `Chart colour ${index + 1} (${color}) has ${contrast.toFixed(1)}:1 contrast against the chart panel (${surface}); it is drawn lighter or darker to reach 3:1.`,
2102
+ help: "Choose a colour that already has at least 3:1 contrast against the slide surface, so the chart shows the colour you wrote.",
2103
+ measured: { contrast: round(contrast), panel: surface }
2104
+ });
2105
+ });
2106
+ }
2107
+ }
2108
+ },
1953
2109
  {
1954
2110
  info: chartAltRule,
1955
2111
  run(context) {
@@ -2208,9 +2364,9 @@ var layoutRules = [
2208
2364
  for (const { diagnostic } of slide.layoutDiagnostics) {
2209
2365
  const path = pointerOfDotted(diagnostic.path);
2210
2366
  if (!path.startsWith("/slides/")) {
2211
- const key = `${diagnostic.code}|${path}|${diagnostic.message}`;
2212
- if (deckLevel.has(key)) continue;
2213
- deckLevel.add(key);
2367
+ const key2 = `${diagnostic.code}|${path}|${diagnostic.message}`;
2368
+ if (deckLevel.has(key2)) continue;
2369
+ deckLevel.add(key2);
2214
2370
  }
2215
2371
  if (diagnostic.code === "text-overflow")
2216
2372
  context.report(overflowRule, { path, slide, message: diagnostic.message, help: "Shorten the text, give it more space (fewer blocks, a different composition) or split the slide (paginate); raising the readable minimum makes it worse." });
@@ -2648,7 +2804,7 @@ function ruleInfo(id) {
2648
2804
  }
2649
2805
 
2650
2806
  // src/check-document.ts
2651
- var own = (value, key) => Object.hasOwn(value, key);
2807
+ var own = (value, key2) => Object.hasOwn(value, key2);
2652
2808
  var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2653
2809
  var primitive = (value) => value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
2654
2810
  var pointer3 = (parts2) => parts2.length ? "/" + parts2.map((part) => String(part).replaceAll("~", "~0").replaceAll("/", "~1")).join("/") : "";
@@ -2660,7 +2816,7 @@ function parts(path) {
2660
2816
  }
2661
2817
  function at(value, path) {
2662
2818
  return parts(path).reduce(
2663
- (current, key) => object(current) || Array.isArray(current) ? own(current, key) ? current[key] : void 0 : void 0,
2819
+ (current, key2) => object(current) || Array.isArray(current) ? own(current, key2) ? current[key2] : void 0 : void 0,
2664
2820
  value
2665
2821
  );
2666
2822
  }
@@ -2670,13 +2826,13 @@ function checkDocumentOptions(options) {
2670
2826
  throw new TypeError("Validate contracts must be an array.");
2671
2827
  for (const contract of options.contracts ?? []) {
2672
2828
  if (!object(contract) || Object.keys(contract).some(
2673
- (key) => ![
2829
+ (key2) => ![
2674
2830
  "path",
2675
2831
  "allowedValues",
2676
2832
  "severity",
2677
2833
  "message",
2678
2834
  "documentation"
2679
- ].includes(key)
2835
+ ].includes(key2)
2680
2836
  ) || 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")
2681
2837
  throw new TypeError(
2682
2838
  "Invalid contract: provide a path and primitive allowedValues, with optional severity, message and documentation."
@@ -2754,13 +2910,13 @@ function score(schema, value) {
2754
2910
  if (own(schema, "const")) return schema.const === value ? 100 : -1e4;
2755
2911
  let result = 1;
2756
2912
  if (object(value) && object(schema.properties)) {
2757
- for (const [key, child] of Object.entries(schema.properties))
2758
- if (own(value, key) && object(child))
2759
- result += own(child, "const") ? child.const === value[key] ? 100 : -1e3 : 1;
2913
+ for (const [key2, child] of Object.entries(schema.properties))
2914
+ if (own(value, key2) && object(child))
2915
+ result += own(child, "const") ? child.const === value[key2] ? 100 : -1e3 : 1;
2760
2916
  }
2761
2917
  if (Array.isArray(schema.required)) {
2762
- for (const key of schema.required)
2763
- if (typeof key === "string" && (!object(value) || !own(value, key)))
2918
+ for (const key2 of schema.required)
2919
+ if (typeof key2 === "string" && (!object(value) || !own(value, key2)))
2764
2920
  result -= 2;
2765
2921
  }
2766
2922
  return result;
@@ -2782,7 +2938,7 @@ function active(cursor, value) {
2782
2938
  const selected = candidates.sort((a, b) => score(b.schema, value) - score(a.schema, value))[0];
2783
2939
  return selected ? { ...selected, baseSchemas: [...selected.baseSchemas ?? [], resolved] } : resolved;
2784
2940
  }
2785
- function childCursor(cursors, key) {
2941
+ function childCursor(cursors, key2) {
2786
2942
  for (const keyword of [
2787
2943
  "properties",
2788
2944
  "patternProperties",
@@ -2791,8 +2947,8 @@ function childCursor(cursors, key) {
2791
2947
  for (const cursor of cursors) {
2792
2948
  const declarations = cursor.schema[keyword];
2793
2949
  if (!object(declarations)) continue;
2794
- const name = keyword === "properties" ? key : keyword === "patternProperties" ? Object.keys(declarations).find(
2795
- (pattern) => new RegExp(pattern).test(key)
2950
+ const name = keyword === "properties" ? key2 : keyword === "patternProperties" ? Object.keys(declarations).find(
2951
+ (pattern) => new RegExp(pattern).test(key2)
2796
2952
  ) : void 0;
2797
2953
  const schema = keyword === "additionalProperties" ? declarations : name === void 0 ? void 0 : declarations[name];
2798
2954
  if (object(schema))
@@ -2851,13 +3007,13 @@ function fields(document) {
2851
3007
  });
2852
3008
  });
2853
3009
  else if (object(entry.value))
2854
- for (const [key, value] of Object.entries(entry.value)) {
2855
- if (entry.path.length === 0 && key === "catalogs") continue;
2856
- const cursor = childCursor(cursors, key);
3010
+ for (const [key2, value] of Object.entries(entry.value)) {
3011
+ if (entry.path.length === 0 && key2 === "catalogs") continue;
3012
+ const cursor = childCursor(cursors, key2);
2857
3013
  if (!cursor) continue;
2858
3014
  stack.push({
2859
3015
  value,
2860
- path: [...entry.path, key],
3016
+ path: [...entry.path, key2],
2861
3017
  cursor,
2862
3018
  ancestors
2863
3019
  });
@@ -3003,8 +3159,8 @@ function variableFindings(document, engine, schemaValid) {
3003
3159
  })
3004
3160
  );
3005
3161
  if (engine.template !== void 0 || !schemaValid) return findings;
3006
- const walk = (value, path, depth) => {
3007
- if (depth > 64) return;
3162
+ const walk = (value, path, depth2) => {
3163
+ if (depth2 > 64) return;
3008
3164
  if (typeof value === "string") {
3009
3165
  if (value.startsWith("data:") || !value.includes("{{")) return;
3010
3166
  for (const match of value.matchAll(VARIABLE_TOKEN))
@@ -3021,14 +3177,14 @@ function variableFindings(document, engine, schemaValid) {
3021
3177
  }
3022
3178
  if (Array.isArray(value)) {
3023
3179
  value.forEach((entry, index) => {
3024
- walk(entry, [...path, String(index)], depth + 1);
3180
+ walk(entry, [...path, String(index)], depth2 + 1);
3025
3181
  });
3026
3182
  return;
3027
3183
  }
3028
3184
  if (value && typeof value === "object")
3029
- for (const [key, entry] of Object.entries(value)) {
3030
- if (path.length === 0 && ["variables", "extensions", "catalogs", "assets"].includes(key)) continue;
3031
- walk(entry, [...path, key], depth + 1);
3185
+ for (const [key2, entry] of Object.entries(value)) {
3186
+ if (path.length === 0 && ["variables", "extensions", "catalogs", "assets"].includes(key2)) continue;
3187
+ walk(entry, [...path, key2], depth2 + 1);
3032
3188
  }
3033
3189
  };
3034
3190
  walk(document, [], 0);
@@ -3223,7 +3379,7 @@ function contractFindings(document, contracts) {
3223
3379
  };
3224
3380
  const message = (contract.message ?? "This value is outside the allowed values for {{path}}.").replace(
3225
3381
  /\{\{(path|value|allowed|file)\}\}/g,
3226
- (_, key) => values[key] ?? ""
3382
+ (_, key2) => values[key2] ?? ""
3227
3383
  );
3228
3384
  findings.push(
3229
3385
  finding("opf/contract", {
@@ -3241,9 +3397,9 @@ function contractFindings(document, contracts) {
3241
3397
  })
3242
3398
  );
3243
3399
  } else if (object(entry.value) || Array.isArray(entry.value)) {
3244
- const key = pattern[entry.depth];
3400
+ const key2 = pattern[entry.depth];
3245
3401
  for (const [name, value] of Object.entries(entry.value))
3246
- if (key === "*" || key === name)
3402
+ if (key2 === "*" || key2 === name)
3247
3403
  stack.push({
3248
3404
  value,
3249
3405
  path: [...entry.path, name],
@@ -3316,18 +3472,18 @@ function parseSource(source) {
3316
3472
  if (node.type === "object") {
3317
3473
  const keys = /* @__PURE__ */ new Set();
3318
3474
  for (const property of node.children ?? []) {
3319
- const key = property.children?.[0];
3320
- if (!key) continue;
3321
- if (keys.has(key.value))
3475
+ const key2 = property.children?.[0];
3476
+ if (!key2) continue;
3477
+ if (keys.has(key2.value))
3322
3478
  duplicates.push(
3323
3479
  finding("opf/duplicate-key", {
3324
- path: pointer3(getNodePath(key)),
3325
- message: `Duplicate JSON property ${JSON.stringify(key.value)}.`,
3480
+ path: pointer3(getNodePath(key2)),
3481
+ message: `Duplicate JSON property ${JSON.stringify(key2.value)}.`,
3326
3482
  help: "Resolve the duplicate explicitly, preserving the intended content from both occurrences. JSON.parse would otherwise hide an earlier value.",
3327
- location: location(key.offset, key.length)
3483
+ location: location(key2.offset, key2.length)
3328
3484
  })
3329
3485
  );
3330
- keys.add(key.value);
3486
+ keys.add(key2.value);
3331
3487
  }
3332
3488
  }
3333
3489
  stack.push(...node.children ?? []);
@@ -3368,7 +3524,7 @@ function scriptProfile(presentation, slideIndex, options, language) {
3368
3524
  }
3369
3525
  const slots = (role) => {
3370
3526
  const latin = options.fontFamilies[role];
3371
- const slot = (key) => !resolved || resolved.sources[key] === "latin" || resolved.sources[key] === "schemeFamily" ? latin : resolved[role][key] ?? latin;
3527
+ const slot = (key2) => !resolved || resolved.sources[key2] === "latin" || resolved.sources[key2] === "schemeFamily" ? latin : resolved[role][key2] ?? latin;
3372
3528
  return { latin, eastAsian: slot("eastAsian"), complexScript: slot("complexScript") };
3373
3529
  };
3374
3530
  return {
@@ -3588,7 +3744,7 @@ function names(list, what) {
3588
3744
  }
3589
3745
  function resolveOptions(options) {
3590
3746
  if (!isObject(options)) throw new TypeError("Validate options must be an object.");
3591
- for (const key of Object.keys(options)) if (!OPTION_KEYS.includes(key)) throw new TypeError(`Unknown validate option ${JSON.stringify(key)}. Options: ${OPTION_KEYS.join(", ")}.`);
3747
+ 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(", ")}.`);
3592
3748
  checkDocumentOptions(options);
3593
3749
  const only = names(options.only, "only");
3594
3750
  const ignore = names(options.ignore, "ignore");
@@ -3617,10 +3773,10 @@ function resolveOptions(options) {
3617
3773
  const thresholds = { ...DEFAULT_VALIDATION_THRESHOLDS };
3618
3774
  if (options.thresholds !== void 0) {
3619
3775
  if (!isObject(options.thresholds)) throw new TypeError("Validate thresholds must be an object.");
3620
- for (const [key, value] of Object.entries(options.thresholds)) {
3621
- if (!(key in DEFAULT_VALIDATION_THRESHOLDS)) throw new TypeError(`Unknown validate threshold ${JSON.stringify(key)}. Thresholds: ${Object.keys(DEFAULT_VALIDATION_THRESHOLDS).join(", ")}.`);
3622
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new TypeError(`Validate threshold ${key} must be a positive number.`);
3623
- thresholds[key] = value;
3776
+ for (const [key2, value] of Object.entries(options.thresholds)) {
3777
+ if (!(key2 in DEFAULT_VALIDATION_THRESHOLDS)) throw new TypeError(`Unknown validate threshold ${JSON.stringify(key2)}. Thresholds: ${Object.keys(DEFAULT_VALIDATION_THRESHOLDS).join(", ")}.`);
3778
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new TypeError(`Validate threshold ${key2} must be a positive number.`);
3779
+ thresholds[key2] = value;
3624
3780
  }
3625
3781
  }
3626
3782
  if (options.fonts !== void 0) {
@@ -3644,7 +3800,7 @@ function resolveOptions(options) {
3644
3800
  },
3645
3801
  ignored: (info, path) => ignorePaths.some((entry) => entry.match(info) && entry.path.every((part, index) => path[index] === part)),
3646
3802
  thresholds,
3647
- chartPalette: options.chartPalette ?? DEFAULT_CHART_PALETTE
3803
+ chartPalette: options.chartPalette
3648
3804
  };
3649
3805
  }
3650
3806
  var order = new Map(validationRules.map((info, index) => [info.id, index]));
@@ -3824,4 +3980,4 @@ function assertValidCatalogRecord(kind, value) {
3824
3980
  if (!report2.valid) throw new OPFValidationError(report2);
3825
3981
  }
3826
3982
 
3827
- export { CATALOG_SCHEMA, DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogSchemaNames, findValidationRule, parseSource, resolveSlideContext, resolveSlideScriptMeasurement, validate, validateCatalogRecord, validationCategories, validationRules };
3983
+ export { CATALOG_SCHEMA, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogSchemaNames, findValidationRule, parseSource, resolveSlideContext, resolveSlideScriptMeasurement, validate, validateCatalogRecord, validationCategories, validationRules };