@openpresentation/opf 0.15.1 → 0.16.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 (73) hide show
  1. package/README.md +4 -0
  2. package/dist/{catalog-refs-BSfGELpt.d.ts → catalog-refs-_anZs-Qz.d.ts} +1 -1
  3. package/dist/{catalog-schemas-CinPh5WA.d.ts → catalog-schemas-BZCjgAxu.d.ts} +1 -1
  4. package/dist/catalog.d.ts +2 -2
  5. package/dist/catalog.js +609 -121
  6. package/dist/{chunk-5FTUIDUH.js → chunk-3XDRQYWZ.js} +1 -1
  7. package/dist/{chunk-O744SXXA.js → chunk-5PDN7BN6.js} +202 -95
  8. package/dist/{chunk-VGAOJEY7.js → chunk-5TO6I3GB.js} +1 -1
  9. package/dist/{chunk-3RKB4RDJ.js → chunk-5UEUAW57.js} +4 -2
  10. package/dist/{chunk-ZPU46TFA.js → chunk-6OR326EA.js} +1 -1
  11. package/dist/{chunk-UBFJ2QHH.js → chunk-BSYQDWT4.js} +12 -2
  12. package/dist/{chunk-3JG4YXWF.js → chunk-FQSIIADJ.js} +12 -5
  13. package/dist/chunk-K4DQQIT4.js +319 -0
  14. package/dist/chunk-KINQFU6X.js +51 -0
  15. package/dist/{chunk-374R35TJ.js → chunk-NAWQR7MN.js} +1 -1
  16. package/dist/{chunk-QDI75P2M.js → chunk-PX5AWL7Y.js} +141 -13
  17. package/dist/{chunk-U4J75TRG.js → chunk-SMLMI6IZ.js} +5 -2
  18. package/dist/{chunk-TWQJLI6E.js → chunk-UM24JFUE.js} +2 -2
  19. package/dist/{chunk-2CJPC4HI.js → chunk-WEDGVC5W.js} +185 -11
  20. package/dist/{composition-D2tghM82.d.ts → composition-D4ADF5c-.d.ts} +88 -9
  21. package/dist/composition.d.ts +2 -2
  22. package/dist/composition.js +1 -1
  23. package/dist/convert.d.ts +3 -3
  24. package/dist/convert.js +7 -7
  25. package/dist/data.d.ts +27 -10
  26. package/dist/data.js +2 -2
  27. package/dist/deck.d.ts +77 -0
  28. package/dist/deck.js +12 -0
  29. package/dist/diff.js +5 -5
  30. package/dist/docs.js +24 -24
  31. package/dist/examples.d.ts +1 -1
  32. package/dist/examples.js +2992 -748
  33. package/dist/format.js +2 -2
  34. package/dist/index.d.ts +9 -8
  35. package/dist/index.js +15 -13
  36. package/dist/markdown.d.ts +6 -7
  37. package/dist/markdown.js +7 -7
  38. package/dist/{pagination-wedbe7eq.d.ts → pagination-DzZPFheM.d.ts} +2 -2
  39. package/dist/pagination.d.ts +3 -3
  40. package/dist/pagination.js +4 -4
  41. package/dist/patch.js +4 -4
  42. package/dist/{presentation-CDKQVkyI.d.ts → presentation-COrFuj2g.d.ts} +16 -8
  43. package/dist/repo-readme.js +1 -1
  44. package/dist/schemas.d.ts +14 -0
  45. package/dist/schemas.js +1 -1
  46. package/dist/spec/catalogs/layouts/business-model-canvas.json +66 -1
  47. package/dist/spec/catalogs/layouts/capability-map.json +53 -3
  48. package/dist/spec/catalogs/layouts/chart-2x.json +32 -8
  49. package/dist/spec/catalogs/layouts/chart-3x-bottom-vertical-title-left.json +16 -10
  50. package/dist/spec/catalogs/layouts/chart-3x-left-title-center.json +16 -10
  51. package/dist/spec/catalogs/layouts/chart-3x-left.json +16 -10
  52. package/dist/spec/catalogs/layouts/chart-3x-right-title-center.json +16 -10
  53. package/dist/spec/catalogs/layouts/chart-3x-right.json +16 -10
  54. package/dist/spec/catalogs/layouts/chart-3x-top-vertical-title-left.json +16 -10
  55. package/dist/spec/catalogs/layouts/chart-3x.json +48 -12
  56. package/dist/spec/catalogs/layouts/dashboard.json +37 -5
  57. package/dist/spec/catalogs/layouts/image-2x.json +32 -8
  58. package/dist/spec/catalogs/layouts/image-3x.json +48 -12
  59. package/dist/spec/catalogs/layouts/index.json +1 -1
  60. package/dist/spec/catalogs/layouts/kpi-scorecard.json +48 -3
  61. package/dist/spec/catalogs/layouts/logic-tree.json +69 -0
  62. package/dist/spec/catalogs/layouts/market-size.json +26 -2
  63. package/dist/spec/catalogs/layouts/org-chart.json +53 -6
  64. package/dist/spec/catalogs/manifest.json +3 -3
  65. package/dist/spec/schemas/layout.schema.json +172 -5
  66. package/dist/spec/schemas/opf.schema.json +11 -6
  67. package/dist/types.d.ts +9 -31
  68. package/dist/{validation-types-BOcY9GTs.d.ts → validation-types-HxIgJfKx.d.ts} +2 -2
  69. package/dist/validator.d.ts +6 -6
  70. package/dist/validator.js +3 -3
  71. package/dist/yaml.d.ts +4 -4
  72. package/dist/yaml.js +8 -321
  73. package/package.json +5 -1
@@ -1,4 +1,4 @@
1
- import { validate } from './chunk-QDI75P2M.js';
1
+ import { validate } from './chunk-PX5AWL7Y.js';
2
2
 
3
3
  // src/convert/shared.ts
4
4
  var CONTENT_KEYS = ["text", "items", "bullets", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"];
@@ -1,6 +1,8 @@
1
- import { isRecord, same } from './chunk-VGAOJEY7.js';
1
+ import { isRecord, same } from './chunk-5TO6I3GB.js';
2
2
  import { parseDocument, YAMLMap, Scalar, stringify } from 'yaml';
3
3
 
4
+ // src/not-checked.ts
5
+ var NOT_CHECKED = { syntax: "checked", schema: "not-run", references: "not-run", policy: "not-run", accessibility: "not-run", content: "not-run", layout: "not-run", backgroundPixels: "not-read", imageBytes: "embedded-only", nativeExport: "not-checked" };
4
6
  function splitLines(source) {
5
7
  const lines = [];
6
8
  let start = 0;
@@ -189,4 +191,4 @@ var OPFMarkdownError = class extends Error {
189
191
  }
190
192
  };
191
193
 
192
- export { Ctx, DECIMAL, OPFMarkdownError, csvField, lineRange, readCsv, readYamlMapping, scalarText, splitLines, writeYaml };
194
+ export { Ctx, DECIMAL, NOT_CHECKED, OPFMarkdownError, csvField, lineRange, readCsv, readYamlMapping, scalarText, splitLines, writeYaml };
@@ -1,4 +1,4 @@
1
- import { presentation } from './chunk-2CJPC4HI.js';
1
+ import { presentation } from './chunk-WEDGVC5W.js';
2
2
 
3
3
  // src/format.ts
4
4
  var OPFFormatError = class extends Error {
@@ -1,5 +1,5 @@
1
- import { assertValid, resolveSlideContext } from './chunk-QDI75P2M.js';
2
- import { composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, sliceNumberedItems } from './chunk-O744SXXA.js';
1
+ import { assertValid, resolveSlideContext } from './chunk-PX5AWL7Y.js';
2
+ import { composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, sliceNumberedItems } from './chunk-5PDN7BN6.js';
3
3
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
4
4
 
5
5
  // src/pagination.ts
@@ -105,6 +105,16 @@ function paginateSlide(input, options = {}) {
105
105
  let evaluations = 0;
106
106
  const { fonts, minFontSize: _minFontSize, maxSlides: _maxSlides, reservedIds: _reservedIds, ...engineOptions } = options;
107
107
  const composeOptions = { ...engineOptions, ...fonts?.textMeasurement ? { textMeasurement: fonts.textMeasurement } : {} };
108
+ const readableLayout = (layout) => {
109
+ if (!isRecord(layout) || !Array.isArray(layout.placeholders) || !layout.placeholders.some((entry) => isRecord(entry) && entry.type === "group")) return layout;
110
+ const visit = (entries) => entries.map((entry) => {
111
+ if (!isRecord(entry) || entry.type !== "group") return entry;
112
+ const own = isRecord(entry.composition) ? entry.composition : {};
113
+ return { ...entry, composition: { ...own, ...typeof own.minFontSize === "number" ? { minFontSize: Math.max(minFontSize, own.minFontSize) } : {}, overflow: "warn" }, placeholders: Array.isArray(entry.placeholders) ? visit(entry.placeholders) : entry.placeholders };
114
+ });
115
+ return { ...layout, placeholders: visit(layout.placeholders) };
116
+ };
117
+ if (options.layout !== void 0) composeOptions.layout = readableLayout(options.layout);
108
118
  const geometry = (slide, pageIndex = 0) => {
109
119
  if (++evaluations > 2e4) throw new OPFPaginationError("Pagination exceeded its layout evaluation limit. Split the input into smaller sections.");
110
120
  return composeSlide(withReadability(slide, true), { ...composeOptions, slideNumber: (options.slideNumber ?? sourceIndex + 1) + pageIndex });
@@ -1,7 +1,7 @@
1
- import { Ctx, splitLines, lineRange, readYamlMapping, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-3RKB4RDJ.js';
2
- import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-374R35TJ.js';
3
- import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-VGAOJEY7.js';
4
- import { validate } from './chunk-QDI75P2M.js';
1
+ import { Ctx, splitLines, lineRange, readYamlMapping, NOT_CHECKED, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-5UEUAW57.js';
2
+ import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-NAWQR7MN.js';
3
+ import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-5TO6I3GB.js';
4
+ import { validate } from './chunk-PX5AWL7Y.js';
5
5
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
6
 
7
7
  // src/markdown/canon.ts
@@ -1757,6 +1757,7 @@ function fromMarkdown(markdown, options = {}) {
1757
1757
  let lines = splitLines(source);
1758
1758
  if (lines[0]?.text.startsWith("\uFEFF")) lines = [{ ...lines[0], text: lines[0].text.slice(1), start: lines[0].start + 1 }, ...lines.slice(1)];
1759
1759
  const front = {};
1760
+ let report = { schemaValid: null, checks: NOT_CHECKED };
1760
1761
  let body = lines;
1761
1762
  const matter = frontMatter(lines);
1762
1763
  if (matter === "unterminated") {
@@ -1792,6 +1793,12 @@ function fromMarkdown(markdown, options = {}) {
1792
1793
  if (!slides.length) ctx.error("no-slides", "The Markdown has no slides.", "Write at least one slide: a # title or any content, with --- between slides.", { start: 0, end: Math.min(1, source.length) }, "/slides");
1793
1794
  else if (options.validate !== false) {
1794
1795
  const checked = validate(document, options.validate === true || options.validate === void 0 ? { only: ["format", "references"], ...options.catalogs ? { catalogs: options.catalogs } : {} } : options.validate);
1796
+ report = {
1797
+ schemaValid: checked.schemaValid,
1798
+ checks: { ...checked.checks, syntax: "checked" },
1799
+ ...checked.template === void 0 ? {} : { template: checked.template },
1800
+ ...checked.unfilledVariables === void 0 ? {} : { unfilledVariables: checked.unfilledVariables }
1801
+ };
1795
1802
  for (const found of checked.findings) {
1796
1803
  const range = ctx.rangeOf(found.path) ?? ctx.rangeOf("") ?? { start: 0, end: 0 };
1797
1804
  ctx.findings.push({ ...found, location: ctx.location(range) });
@@ -1800,7 +1807,7 @@ function fromMarkdown(markdown, options = {}) {
1800
1807
  const findings = [...ctx.findings].sort((a, b) => a.location.offset - b.location.offset);
1801
1808
  const counts = { error: 0, warning: 0, info: 0 };
1802
1809
  for (const entry of findings) counts[entry.severity]++;
1803
- return { presentation: document, valid: counts.error === 0, findings, counts };
1810
+ return { presentation: document, valid: counts.error === 0, findings, counts, ...report };
1804
1811
  }
1805
1812
  function toMarkdown(document, options = {}) {
1806
1813
  const checked = validate(document, { only: ["format"] });
@@ -0,0 +1,319 @@
1
+ import { NOT_CHECKED, Ctx } from './chunk-5UEUAW57.js';
2
+ import { sortPresentationKeys } from './chunk-6OR326EA.js';
3
+ import { same } from './chunk-5TO6I3GB.js';
4
+ import { validate } from './chunk-PX5AWL7Y.js';
5
+ import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
+ import { presentation } from './chunk-WEDGVC5W.js';
7
+ import { Parser, parseDocument, isMap, Document, visit, isScalar, isAlias, isSeq } from 'yaml';
8
+
9
+ var MAX_ALIAS_COUNT = 100;
10
+ var CORE_TAGS = new Set(["map", "seq", "str", "int", "float", "bool", "null"].map((name) => `tag:yaml.org,2002:${name}`));
11
+ var ALIAS_HELP = "Write each value out instead of using anchors (&name), aliases (*name) and merge keys (<<). Pass aliases: true (CLI: --aliases) to expand them.";
12
+ var TAG_HELP = "Custom tags are not accepted. Remove the tag; quote a value to keep it as text.";
13
+ var CYCLE_HELP = "An alias must not appear inside its own anchor. Write the nested value out.";
14
+ var decode = (part) => part.replaceAll("~1", "/").replaceAll("~0", "~");
15
+ var encode = (part) => part.replaceAll("~", "~0").replaceAll("/", "~1");
16
+ var pointerOf = (parts) => parts.length ? `/${parts.map(encode).join("/")}` : "";
17
+ function rangeOfPointer(doc, pointer) {
18
+ const root = doc?.contents;
19
+ if (!root?.range) return void 0;
20
+ const parts = pointer === "" ? [] : pointer.slice(1).split("/").map(decode);
21
+ let node = root;
22
+ let key;
23
+ for (const part of parts) {
24
+ if (isAlias(node)) break;
25
+ let child;
26
+ let childKey;
27
+ if (isMap(node)) {
28
+ const pairs = node.items;
29
+ const pair = pairs.find((item) => isScalar(item.key) && String(item.key.value) === part) ?? pairs.find((item) => isMergeKey(item.key) && item.value);
30
+ if (pair) {
31
+ childKey = pair.key;
32
+ child = pair.value ?? childKey;
33
+ }
34
+ } else if (isSeq(node)) child = node.items[/^(?:0|[1-9]\d*)$/.test(part) ? Number(part) : -1];
35
+ if (!child?.range) break;
36
+ node = child;
37
+ key = childKey;
38
+ }
39
+ const [start, end] = node.range;
40
+ if (key?.range && (isMap(node) || isSeq(node) || isAlias(node))) return { start: key.range[0], end };
41
+ return { start, end: Math.max(start, end) };
42
+ }
43
+ function isMergeKey(key) {
44
+ return isScalar(key) && (typeof key.value === "symbol" || key.value === "<<" && key.type === "PLAIN");
45
+ }
46
+ function describeError(error) {
47
+ const text = error.message.split("\n")[0] ?? error.message;
48
+ switch (error.code) {
49
+ case "DUPLICATE_KEY":
50
+ return { rule: "duplicate-key", message: `YAML: ${text}`, help: "Remove or rename one of the repeated keys. JSON-compatible YAML needs every key once in each mapping." };
51
+ case "MULTIPLE_DOCS":
52
+ return { rule: "multiple-documents", message: "The input holds more than one YAML document.", help: "An OPF file is exactly one document: remove the extra --- separators, or put each deck in its own file." };
53
+ case "TAG_RESOLVE_FAILED":
54
+ return { rule: "tag", message: `YAML: ${text}`, help: TAG_HELP };
55
+ case "BAD_ALIAS":
56
+ return { rule: "alias", message: `YAML: ${text}`, help: ALIAS_HELP };
57
+ default:
58
+ return { rule: "syntax", message: `YAML: ${text}`, help: "Fix the YAML syntax at this position. An OPF file holds JSON-compatible YAML 1.2: mappings, sequences and scalars." };
59
+ }
60
+ }
61
+ function keyRangeAt(doc, start) {
62
+ let found;
63
+ visit(doc, {
64
+ Pair(_key, pair) {
65
+ const key = pair.key;
66
+ if (!found && key?.range && key.range[0] === start) found = { start, end: key.range[1] };
67
+ }
68
+ });
69
+ return found;
70
+ }
71
+ function readYamlDocument(source, aliases, rootKind = "mapping") {
72
+ const bom = source.startsWith("\uFEFF") ? 1 : 0;
73
+ const text = source.slice(bom);
74
+ const ctx = new Ctx(text);
75
+ const findings = [];
76
+ const locate = (range) => {
77
+ const location = ctx.location(range);
78
+ return { ...location, offset: location.offset + bom };
79
+ };
80
+ const add = (rule, message, help, range, path = "") => {
81
+ findings.push({ ruleId: `yaml/${rule}`, severity: "error", category: "format", scope: "document", path, message, help, location: locate(range) });
82
+ };
83
+ const doc = parseDocument(text, { schema: "core", uniqueKeys: true, merge: aliases, prettyErrors: false });
84
+ const finish = (value2) => ({
85
+ value: value2,
86
+ locatePointer: (pointer) => {
87
+ const range = rangeOfPointer(doc, pointer);
88
+ return range ? locate(range) : void 0;
89
+ },
90
+ findings,
91
+ errors: findings.length
92
+ });
93
+ for (const error of [...doc.errors, ...doc.warnings.filter((warning) => warning.code === "TAG_RESOLVE_FAILED")]) {
94
+ const described = describeError(error);
95
+ const [start, end] = error.pos;
96
+ add(described.rule, described.message, described.help, (error.code === "DUPLICATE_KEY" ? keyRangeAt(doc, start) : void 0) ?? { start, end: Math.max(start, end) });
97
+ }
98
+ if (findings.length) return finish({});
99
+ const root = doc.contents;
100
+ if (root === null || !root.range) {
101
+ add("empty", "The input holds no YAML document.", "An OPF file is one YAML mapping: write the deck properties (name, slides, ...) as key: value lines.", { start: 0, end: Math.min(1, text.length) });
102
+ return finish({});
103
+ }
104
+ const rootRange = { start: root.range[0], end: root.range[1] };
105
+ if (rootKind === "mapping" && !isMap(root)) {
106
+ add("not-mapping", "The document is not a mapping.", "An OPF deck is a YAML mapping of keys and values (name:, slides:, ...), not a list or a single value.", rootRange);
107
+ return finish({});
108
+ }
109
+ checkTree({ doc, text, aliases, add }, root, [], []);
110
+ if (findings.length) return finish({});
111
+ let value;
112
+ try {
113
+ value = doc.toJS({ maxAliasCount: aliases ? MAX_ALIAS_COUNT : 0 });
114
+ } catch (error) {
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ if (/excessive alias/i.test(message)) add("alias-limit", `The anchors and aliases expand beyond the limit of ${MAX_ALIAS_COUNT} aliases.`, "Aliases that contain aliases grow exponentially. Write the repeated values out, or reduce the aliasing.", rootRange);
117
+ else add("alias", `YAML: ${message}`, ALIAS_HELP, rootRange);
118
+ return finish({});
119
+ }
120
+ if (isCircular(value)) {
121
+ add("alias-cycle", "An alias refers to the value that contains it, so the document is infinitely deep.", CYCLE_HELP, rootRange);
122
+ return finish({});
123
+ }
124
+ const bad = nonJson(value);
125
+ if (bad) {
126
+ add("number", `The YAML holds ${bad}, which JSON cannot carry.`, "Quote it to keep it as text, or write a finite number.", rootRange);
127
+ return finish({});
128
+ }
129
+ return finish(value);
130
+ }
131
+ function propertyRange(text, nodeStart, token, fallback) {
132
+ const found = text.lastIndexOf(token, nodeStart);
133
+ return found >= 0 && found < nodeStart ? { start: found, end: found + token.length } : fallback;
134
+ }
135
+ function checkTree(env, node, trail, ancestors) {
136
+ const { doc, text, aliases, add } = env;
137
+ const path = pointerOf(trail);
138
+ const range = (target) => ({ start: target.range?.[0] ?? 0, end: Math.max(target.range?.[0] ?? 0, target.range?.[1] ?? 0) });
139
+ if (isAlias(node)) {
140
+ const name = node.source;
141
+ if (!aliases) add("alias", `The alias *${name} is not allowed.`, ALIAS_HELP, range(node), path);
142
+ else {
143
+ const target = node.resolve(doc);
144
+ if (target && ancestors.includes(target)) add("alias-cycle", `The alias *${name} refers to a value that contains it.`, CYCLE_HELP, range(node), path);
145
+ }
146
+ return;
147
+ }
148
+ const anchor = node.anchor;
149
+ if (anchor !== void 0 && !aliases) add("alias", `The anchor &${anchor} is not allowed.`, ALIAS_HELP, propertyRange(text, node.range?.[0] ?? 0, `&${anchor}`, range(node)), path);
150
+ const tag = node.tag;
151
+ if (tag !== void 0 && !CORE_TAGS.has(tag)) {
152
+ let start = text.lastIndexOf("!", node.range?.[0] ?? 0);
153
+ while (start > 0 && text[start - 1] === "!") start--;
154
+ const written = start >= 0 ? /^!\S*/.exec(text.slice(start))?.[0] : void 0;
155
+ add("tag", `The tag ${written ?? tag} is not allowed.`, TAG_HELP, written && start < (node.range?.[0] ?? 0) ? { start, end: start + written.length } : range(node), path);
156
+ }
157
+ if (isScalar(node)) {
158
+ const value = node.value;
159
+ if (typeof value === "number" && !Number.isFinite(value)) add("number", `${String(node.source ?? value)} is not a finite number, and JSON cannot carry it.`, 'Quote it (".inf", ".nan") to keep it as text, or write a finite number.', range(node), path);
160
+ return;
161
+ }
162
+ const inside = [...ancestors, node];
163
+ if (isMap(node)) {
164
+ for (const pair of node.items) {
165
+ const key = pair.key;
166
+ if (key && isMergeKey(key)) {
167
+ if (!aliases) add("alias", "The merge key << is not allowed.", ALIAS_HELP, range(key), path);
168
+ else {
169
+ const source = pair.value;
170
+ if (source && isSeq(source)) for (const item of source.items) checkTree(env, item, [...trail, "<<"], inside);
171
+ else if (source) checkTree(env, source, [...trail, "<<"], inside);
172
+ }
173
+ continue;
174
+ }
175
+ const resolved = key && isAlias(key) ? key.resolve(doc) : key;
176
+ const name = resolved && isScalar(resolved) && typeof resolved.value === "string" ? resolved.value : void 0;
177
+ if (key) checkTree(env, key, trail, inside);
178
+ if (name === void 0) {
179
+ add("key", "A mapping key is not a string.", 'JSON keys are always strings. Quote the key, for example "1": value.', key ? range(key) : range(node), path);
180
+ continue;
181
+ }
182
+ if (pair.value) checkTree(env, pair.value, [...trail, name], inside);
183
+ }
184
+ return;
185
+ }
186
+ if (isSeq(node)) {
187
+ const items = node.items;
188
+ for (let index = 0; index < items.length; index++) checkTree(env, items[index], [...trail, String(index)], inside);
189
+ }
190
+ }
191
+ function nonJson(value) {
192
+ if (typeof value === "number") return Number.isFinite(value) ? void 0 : "a number that is not finite";
193
+ if (value === null || typeof value === "string" || typeof value === "boolean") return void 0;
194
+ if (Array.isArray(value)) return value.map(nonJson).find(Boolean);
195
+ if (typeof value === "object") {
196
+ const prototype = Object.getPrototypeOf(value);
197
+ if (prototype !== Object.prototype && prototype !== null) return `a ${value.constructor?.name ?? "non-JSON"} value`;
198
+ return Object.values(value).map(nonJson).find(Boolean);
199
+ }
200
+ return `a ${typeof value}`;
201
+ }
202
+ function isCircular(value, stack = /* @__PURE__ */ new Set()) {
203
+ if (typeof value !== "object" || value === null) return false;
204
+ if (stack.has(value)) return true;
205
+ stack.add(value);
206
+ const found = Object.values(value).some((child) => isCircular(child, stack));
207
+ stack.delete(value);
208
+ return found;
209
+ }
210
+ function scanYamlComments(source) {
211
+ const text = source.startsWith("\uFEFF") ? source.slice(1) : source;
212
+ let count = 0;
213
+ let modeline;
214
+ const visit3 = (value) => {
215
+ if (Array.isArray(value)) value.forEach(visit3);
216
+ else if (value && typeof value === "object") {
217
+ const token = value;
218
+ if (token.type === "comment" && typeof token.source === "string") {
219
+ if (modeline === void 0 && token.offset === 0 && /^#\s*yaml-language-server:/.test(token.source)) modeline = token.source;
220
+ else count++;
221
+ } else for (const child of Object.values(value)) visit3(child);
222
+ }
223
+ };
224
+ for (const token of new Parser().parse(text)) visit3(token);
225
+ return { count, ...modeline === void 0 ? {} : { modeline } };
226
+ }
227
+ var AMBIGUOUS = [
228
+ /^(?:y|n|yes|no|on|off|true|false|null|~|=|<<)$/i,
229
+ /^[-+]?(?:0[0-7_]+|0b[01_]+|0x[0-9a-f_]+|[0-9][0-9_]*(?:\.[0-9_]*)?(?:e[-+]?[0-9]+)?|\.[0-9_]+(?:e[-+]?[0-9]+)?|\.inf)$/i,
230
+ /^\.nan$/i,
231
+ /^\d{4}-\d{1,2}-\d{1,2}(?:[Tt ].*)?$/,
232
+ /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$/
233
+ ];
234
+ var isAmbiguous = (text) => AMBIGUOUS.some((pattern) => pattern.test(text));
235
+ function writeCanonicalYaml(value, reads) {
236
+ const expected = JSON.stringify(value);
237
+ for (const blockQuote of [true, false]) {
238
+ const doc = new Document(value, { schema: "core" });
239
+ visit(doc, {
240
+ Scalar(_key, node) {
241
+ if (isScalar(node) && typeof node.value === "string" && isAmbiguous(node.value)) node.type = "QUOTE_DOUBLE";
242
+ }
243
+ });
244
+ const text = doc.toString({ indent: 2, lineWidth: 0, minContentWidth: 0, blockQuote, defaultStringType: "PLAIN", defaultKeyType: "PLAIN" });
245
+ const out = text.endsWith("\n") ? text : `${text}
246
+ `;
247
+ let back;
248
+ try {
249
+ back = reads(out);
250
+ } catch {
251
+ continue;
252
+ }
253
+ if (JSON.stringify(back) === expected) return out;
254
+ }
255
+ return void 0;
256
+ }
257
+
258
+ // src/yaml.ts
259
+ var YAML_SCHEMA_URL = presentation.$id;
260
+ function fromYaml(yaml, options = {}) {
261
+ if (typeof yaml !== "string") throw new TypeError("fromYaml expects a string.");
262
+ checkCatalogsOption(options.catalogs, "fromYaml");
263
+ const parsed = readYamlDocument(yaml, options.aliases === true);
264
+ const findings = [...parsed.findings];
265
+ let report = { schemaValid: null, checks: NOT_CHECKED };
266
+ if (!parsed.errors && options.validate !== false) {
267
+ const checked = validate(parsed.value, options.validate === true || options.validate === void 0 ? { only: ["format", "references"], ...options.catalogs ? { catalogs: options.catalogs } : {} } : options.validate);
268
+ report = {
269
+ schemaValid: checked.schemaValid,
270
+ checks: { ...checked.checks, syntax: "checked" },
271
+ ...checked.template === void 0 ? {} : { template: checked.template },
272
+ ...checked.unfilledVariables === void 0 ? {} : { unfilledVariables: checked.unfilledVariables }
273
+ };
274
+ for (const found of checked.findings) {
275
+ const location = parsed.locatePointer(found.path) ?? parsed.locatePointer("") ?? { offset: 0, length: 0, line: 1, column: 1 };
276
+ findings.push({ ...found, location });
277
+ }
278
+ }
279
+ findings.sort((a, b) => a.location.offset - b.location.offset);
280
+ const counts = { error: 0, warning: 0, info: 0 };
281
+ for (const found of findings) counts[found.severity]++;
282
+ return { presentation: parsed.value, valid: counts.error === 0, findings, counts, ...report };
283
+ }
284
+ var OPFYamlError = class extends Error {
285
+ code;
286
+ details;
287
+ constructor(code, message, details = {}) {
288
+ super(message);
289
+ this.name = "OPFYamlError";
290
+ this.code = code;
291
+ this.details = details;
292
+ }
293
+ };
294
+ function toYaml(document, options = {}) {
295
+ const checked = validate(document, { only: ["format"] });
296
+ if (!checked.valid) {
297
+ const errors = checked.findings.filter((found) => found.severity === "error");
298
+ const first = errors[0];
299
+ throw new OPFYamlError("invalid-document", `The document is not valid OPF: ${first?.message ?? "unknown error"}${first?.path ? ` (${first.path})` : ""}.`, { findings: errors });
300
+ }
301
+ const sorted = sortPresentationKeys(document);
302
+ const text = writeCanonicalYaml(sorted, (out) => {
303
+ const back = readYamlDocument(out, false);
304
+ if (back.errors) throw new Error("The YAML does not read back.");
305
+ return back.value;
306
+ });
307
+ if (text === void 0) throw new OPFYamlError("not-representable", "The document holds a value that YAML cannot carry exactly.");
308
+ if (!same(JSON.parse(JSON.stringify(sorted)), readYamlDocument(text, false).value)) throw new OPFYamlError("not-representable", "The document does not read back from its YAML.");
309
+ const schema = typeof sorted.$schema === "string" && !/[\r\n]/.test(sorted.$schema) ? sorted.$schema : YAML_SCHEMA_URL;
310
+ return { yaml: options.schemaComment ? `# yaml-language-server: $schema=${schema}
311
+ ${text}` : text };
312
+ }
313
+ function parseYamlData(yaml, options = {}) {
314
+ if (typeof yaml !== "string") throw new TypeError("parseYamlData expects a string.");
315
+ const parsed = readYamlDocument(yaml, options.aliases === true, "any");
316
+ return { value: parsed.errors ? null : parsed.value, valid: parsed.errors === 0, findings: parsed.findings };
317
+ }
318
+
319
+ export { OPFYamlError, YAML_SCHEMA_URL, fromYaml, parseYamlData, scanYamlComments, toYaml };
@@ -0,0 +1,51 @@
1
+ import { fromMarkdown, toMarkdown } from './chunk-FQSIIADJ.js';
2
+ import { fromYaml, toYaml } from './chunk-K4DQQIT4.js';
3
+ import { NOT_CHECKED } from './chunk-5UEUAW57.js';
4
+ import { parseSource, validate } from './chunk-PX5AWL7Y.js';
5
+ import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
+
7
+ // src/deck.ts
8
+ var DECK_FORMATS = ["json", "yaml", "markdown"];
9
+ function deckFormatOf(filename) {
10
+ if (filename === void 0) return "json";
11
+ if (/\.opf\.md$/i.test(filename)) return "markdown";
12
+ if (/\.ya?ml$/i.test(filename)) return "yaml";
13
+ return "json";
14
+ }
15
+ function resolveFormat(caller, options) {
16
+ if (options.format === void 0) return deckFormatOf(options.filename);
17
+ if (!DECK_FORMATS.includes(options.format)) throw new TypeError(`${caller}: unknown format ${JSON.stringify(options.format)}. Formats: ${DECK_FORMATS.join(", ")}.`);
18
+ return options.format;
19
+ }
20
+ function readDeck(text, options = {}) {
21
+ if (typeof text !== "string") throw new TypeError("readDeck expects a string.");
22
+ checkCatalogsOption(options.catalogs, "readDeck");
23
+ const format = resolveFormat("readDeck", options);
24
+ const { catalogs } = options;
25
+ const check = options.validate ?? true;
26
+ const validateOptions = typeof check === "object" ? { ...catalogs && check.catalogs === void 0 ? { catalogs } : {}, ...check } : { only: ["format", "references"], ...catalogs ? { catalogs } : {} };
27
+ const checked = check === false ? false : validateOptions;
28
+ if (format === "yaml") return { ...fromYaml(text, { validate: checked, catalogs }), format };
29
+ if (format === "markdown") return { ...fromMarkdown(text, { validate: checked, catalogs }), format };
30
+ if (checked === false) {
31
+ const parsed = parseSource(text);
32
+ return { ...report(parsed.syntax), presentation: parsed.syntax.length ? {} : parsed.value, format };
33
+ }
34
+ const result = validate(text, checked);
35
+ const broken = result.findings.some((found) => found.ruleId === "opf/json-syntax");
36
+ return { ...result, presentation: broken ? {} : JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text), format };
37
+ }
38
+ function report(findings) {
39
+ const counts = { error: 0, warning: 0, info: 0 };
40
+ for (const found of findings) counts[found.severity]++;
41
+ return { valid: counts.error === 0, schemaValid: null, findings, counts, checks: NOT_CHECKED };
42
+ }
43
+ function writeDeck(presentation, options = {}) {
44
+ const format = resolveFormat("writeDeck", options);
45
+ if (format === "yaml") return toYaml(presentation, { schemaComment: options.schemaComment === true }).yaml;
46
+ if (format === "markdown") return toMarkdown(presentation).markdown;
47
+ return `${JSON.stringify(presentation, null, 2)}
48
+ `;
49
+ }
50
+
51
+ export { DECK_FORMATS, deckFormatOf, readDeck, writeDeck };
@@ -1,4 +1,4 @@
1
- import { runText } from './chunk-VGAOJEY7.js';
1
+ import { runText } from './chunk-5TO6I3GB.js';
2
2
 
3
3
  // src/convert/text-lines.ts
4
4
  var LIST_PREFIX = /^([ \t]*)(?:([-*•+]|\d{1,3}[.)])[ \t]+)?/;