@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.
- package/dist/browser.js +16 -16
- package/dist/{chunk-Z75YB54S.js → chunk-2X4ZG2CN.js} +5 -5
- package/dist/{chunk-WD4W7KKK.js → chunk-6UBIR2U3.js} +54 -11
- package/dist/{chunk-DYGYMBBW.js → chunk-7YFMWEPQ.js} +2 -2
- package/dist/{chunk-7PQZDC2H.js → chunk-ARLOTG23.js} +1 -1
- package/dist/{chunk-VFMHM2IL.js → chunk-BWWGYOOG.js} +22 -0
- package/dist/{chunk-IDMQWS6O.js → chunk-D2XIDBUB.js} +6 -6
- package/dist/{chunk-5SQ6CIWR.js → chunk-D7WZVSNV.js} +5 -5
- package/dist/{chunk-OIGCC6VA.js → chunk-EFICNDGV.js} +2 -2
- package/dist/{chunk-YOLDSF7U.js → chunk-THEUCQZZ.js} +1 -1
- package/dist/{chunk-S4FR7ATF.js → chunk-U55BQQKU.js} +1 -1
- package/dist/{chunk-V66KJA3H.js → chunk-VLFDWV7L.js} +1 -1
- package/dist/{chunk-52GKVP3I.js → chunk-W62CX2WG.js} +22 -12
- package/dist/{chunk-URUNMZFO.js → chunk-WZPFIPOO.js} +2 -2
- package/dist/{chunk-XAYMPBNE.js → chunk-X6SB52RP.js} +333 -149
- package/dist/{chunk-2M7U4WL3.js → chunk-XQARTOZJ.js} +260 -104
- package/dist/{chunk-TZ5IZETE.js → chunk-YD44KIRA.js} +1 -1
- package/dist/{chunk-TCBEF5OY.js → chunk-YZ7RZ4MU.js} +1 -1
- package/dist/{composition-QtS1rnhv.d.ts → composition-DzAV0cfU.d.ts} +133 -3
- package/dist/composition.d.ts +1 -1
- package/dist/composition.js +1 -1
- package/dist/conversion-524P44NX.js +19 -0
- package/dist/{conversion-Cqwu0Ds9.d.ts → conversion-CtTg2ivR.d.ts} +6 -5
- package/dist/convert.d.ts +3 -3
- package/dist/convert.js +7 -7
- package/dist/data.d.ts +1 -1
- package/dist/data.js +2 -2
- package/dist/{deck-report-BdG5H3Qj.d.ts → deck-report-971nEzTY.d.ts} +2 -2
- package/dist/deck.d.ts +6 -6
- package/dist/deck.js +10 -10
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +5 -5
- package/dist/docs.js +16 -10
- package/dist/examples.d.ts +1 -1
- package/dist/{finding-DqgwzUmr.d.ts → finding-l9dlQ2NF.d.ts} +4 -0
- package/dist/format.js +2 -2
- package/dist/index.d.ts +21 -15
- package/dist/index.js +20 -20
- package/dist/markdown.d.ts +4 -4
- package/dist/markdown.js +7 -7
- package/dist/node-engine.d.ts +30 -9
- package/dist/node-engine.js +20 -20
- package/dist/{pagination-CAX6mHG6.d.ts → pagination-BTqRj7dM.d.ts} +2 -2
- package/dist/pagination.d.ts +2 -2
- package/dist/pagination.js +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.js +4 -4
- package/dist/{presentation-BG4QCcdB.d.ts → presentation-ZNcXSpvm.d.ts} +50 -4
- package/dist/repo-readme.js +1 -1
- package/dist/schemas.js +1 -1
- package/dist/spec/schemas/finding.schema.json +6 -1
- package/dist/spec/schemas/opf.schema.json +8 -0
- package/dist/types.d.ts +2 -2
- package/dist/{validation-types-DRaNnieo.d.ts → validation-types-uLDSrJgM.d.ts} +3 -3
- package/dist/{validator-BJtXHJKh.d.ts → validator-DxAjRPmE.d.ts} +4 -10
- package/dist/validator.d.ts +5 -5
- package/dist/validator.js +3 -3
- package/dist/yaml.d.ts +4 -4
- package/dist/yaml.js +7 -7
- package/package.json +1 -1
- 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,
|
|
2
|
-
import {
|
|
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 [
|
|
111
|
-
const segment =
|
|
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,
|
|
272
|
-
return Object.hasOwn(value,
|
|
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(
|
|
395
|
-
if (
|
|
396
|
-
const [rowPart, columnPart] =
|
|
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 (
|
|
403
|
-
const columns = columnSpans[
|
|
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 (
|
|
407
|
-
const rows = rowSpans[
|
|
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((
|
|
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
|
|
432
|
-
const value = slide[
|
|
551
|
+
for (const key2 of regionKeys) {
|
|
552
|
+
const value = slide[key2];
|
|
433
553
|
if (isRecord(value)) {
|
|
434
|
-
issues.push(...validateContentPayload(value, pathFor(slidePath,
|
|
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 = (
|
|
459
|
-
const field = value[
|
|
460
|
-
if (Array.isArray(field)) return field.flatMap((entry, index) => isRecord(entry) ? [{ entry, path: `/${
|
|
461
|
-
return isRecord(field) ? [{ entry: field, path: `/${
|
|
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
|
|
584
|
+
for (const key2 of ["organization", "speaker"]) {
|
|
465
585
|
const seen = /* @__PURE__ */ new Set();
|
|
466
|
-
for (const { entry, path } of entries(
|
|
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`, `${
|
|
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 (
|
|
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
|
|
625
|
-
if (seen.has(
|
|
626
|
-
seen.add(
|
|
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
|
|
812
|
-
if (promotedRegionKeySet.has(
|
|
813
|
-
issues.push(...chartTypeWarnings(slide[
|
|
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
|
|
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
|
|
894
|
-
if (Array.isArray(current)) current = current[Number(
|
|
895
|
-
else if (isRecord(current) && hasOwn(current,
|
|
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
|
|
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
|
|
1360
|
-
const value = metric[
|
|
1361
|
-
if (typeof value === "string") push(at2("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
|
|
1599
|
-
const known = aggregated.get(
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
2212
|
-
if (deckLevel.has(
|
|
2213
|
-
deckLevel.add(
|
|
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,
|
|
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,
|
|
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
|
-
(
|
|
2829
|
+
(key2) => ![
|
|
2674
2830
|
"path",
|
|
2675
2831
|
"allowedValues",
|
|
2676
2832
|
"severity",
|
|
2677
2833
|
"message",
|
|
2678
2834
|
"documentation"
|
|
2679
|
-
].includes(
|
|
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 [
|
|
2758
|
-
if (own(value,
|
|
2759
|
-
result += own(child, "const") ? child.const === value[
|
|
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
|
|
2763
|
-
if (typeof
|
|
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,
|
|
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" ?
|
|
2795
|
-
(pattern) => new RegExp(pattern).test(
|
|
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 [
|
|
2855
|
-
if (entry.path.length === 0 &&
|
|
2856
|
-
const cursor = childCursor(cursors,
|
|
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,
|
|
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,
|
|
3007
|
-
if (
|
|
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)],
|
|
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 [
|
|
3030
|
-
if (path.length === 0 && ["variables", "extensions", "catalogs", "assets"].includes(
|
|
3031
|
-
walk(entry, [...path,
|
|
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
|
-
(_,
|
|
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
|
|
3400
|
+
const key2 = pattern[entry.depth];
|
|
3245
3401
|
for (const [name, value] of Object.entries(entry.value))
|
|
3246
|
-
if (
|
|
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
|
|
3320
|
-
if (!
|
|
3321
|
-
if (keys.has(
|
|
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(
|
|
3325
|
-
message: `Duplicate JSON property ${JSON.stringify(
|
|
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(
|
|
3483
|
+
location: location(key2.offset, key2.length)
|
|
3328
3484
|
})
|
|
3329
3485
|
);
|
|
3330
|
-
keys.add(
|
|
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 = (
|
|
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
|
|
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 [
|
|
3621
|
-
if (!(
|
|
3622
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new TypeError(`Validate threshold ${
|
|
3623
|
-
thresholds[
|
|
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
|
|
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,
|
|
3983
|
+
export { CATALOG_SCHEMA, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogSchemaNames, findValidationRule, parseSource, resolveSlideContext, resolveSlideScriptMeasurement, validate, validateCatalogRecord, validationCategories, validationRules };
|