@openpresentation/opf 0.11.4 → 0.12.1
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/README.md +16 -0
- package/dist/audit.d.ts +156 -0
- package/dist/audit.js +7 -0
- package/dist/{catalogs-CUClB3nl.d.ts → catalogs-BnI7JKcn.d.ts} +3 -3
- package/dist/catalogs.d.ts +2 -2
- package/dist/catalogs.js +2 -1
- package/dist/{chunk-KJKASEPE.js → chunk-4LAM3N6S.js} +753 -42
- package/dist/chunk-6LZK2IZB.js +1690 -0
- package/dist/{chunk-NSVVQ52O.js → chunk-AUM5MXBR.js} +8 -6
- package/dist/{chunk-AEUSVEUY.js → chunk-E34DJ5LL.js} +50 -14
- package/dist/chunk-FYLJVF7X.js +18448 -0
- package/dist/{chunk-HJL64ETN.js → chunk-GJKJL4YY.js} +14 -0
- package/dist/{chunk-3H2U7FDR.js → chunk-HOBSL7AJ.js} +1867 -247
- package/dist/chunk-JKVKYVWL.js +1332 -0
- package/dist/{chunk-3OJN7QAW.js → chunk-MWPMJE6P.js} +18 -4
- package/dist/chunk-RUC7L5DJ.js +261 -0
- package/dist/chunk-S5W34SIJ.js +1 -0
- package/dist/chunk-V2537ENL.js +198 -0
- package/dist/{chunk-RXNFDGPC.js → chunk-WXSPETQ6.js} +311 -40
- package/dist/{composition-Dl-d9FRj.d.ts → composition-DWbCiMwF.d.ts} +559 -10
- package/dist/composition.d.ts +1 -1
- package/dist/composition.js +2 -1
- package/dist/convert.d.ts +261 -0
- package/dist/convert.js +1107 -0
- package/dist/diff.d.ts +145 -0
- package/dist/diff.js +604 -0
- package/dist/docs.js +72 -18
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +112 -112
- package/dist/font-policy.d.ts +15 -0
- package/dist/font-policy.js +1 -1
- package/dist/format.d.ts +20 -0
- package/dist/format.js +83 -0
- package/dist/index.d.ts +173 -24
- package/dist/index.js +926 -262
- package/dist/lint.d.ts +4 -4
- package/dist/lint.js +6 -5
- package/dist/markdown.d.ts +74 -0
- package/dist/markdown.js +1941 -0
- package/dist/pagination.d.ts +1 -1
- package/dist/pagination.js +6 -5
- package/dist/patch.d.ts +99 -0
- package/dist/patch.js +6 -0
- package/dist/{presentation-j5PVrqfV.d.ts → presentation-DcF-MZcz.d.ts} +452 -17
- package/dist/repo-readme.js +1 -1
- package/dist/{schemas-X_NniU4A.d.ts → schemas-BSjlG8-R.d.ts} +28 -0
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/dist/spec/catalogs/audiences/customers.json +5 -1
- package/dist/spec/catalogs/audiences/executives.json +5 -1
- package/dist/spec/catalogs/audiences/index.json +20 -8
- package/dist/spec/catalogs/audiences/investors.json +5 -1
- package/dist/spec/catalogs/audiences/marketing-team.json +5 -1
- package/dist/spec/catalogs/audiences/regulators.json +5 -1
- package/dist/spec/catalogs/audiences/sales-team.json +5 -1
- package/dist/spec/catalogs/manifest.json +2 -2
- package/dist/spec/reference/engine-defaults.json +1 -1
- package/dist/spec/reference/font-policy.json +311 -40
- package/dist/spec/reference/font-policy.schema.json +40 -0
- package/dist/spec/reference/symbol-font-encodings.json +18354 -0
- package/dist/spec/reference/symbol-font-encodings.schema.json +156 -0
- package/dist/spec/schemas/audience.schema.json +7 -7
- package/dist/spec/schemas/narrative.schema.json +1 -1
- package/dist/spec/schemas/opf.schema.json +597 -28
- package/dist/spec-files.d.ts +1 -1
- package/dist/spec-files.js +1 -1
- package/dist/symbol-font-encodings.d.ts +128 -0
- package/dist/symbol-font-encodings.js +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/validator-DfG9yxba.d.ts +172 -0
- package/dist/validator.d.ts +4 -36
- package/dist/validator.js +5 -4
- package/package.json +32 -3
- package/dist/chunk-FDCWQHWO.js +0 -1366
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { validatePresentation, validationDefinition, validateCatalogRecord } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { validatePresentation, unusedReferenceWarnings, validationDefinition, validateCatalogRecord } from './chunk-JKVKYVWL.js';
|
|
2
|
+
import { catalogKinds, catalogEntries, catalogSchemaNames } from './chunk-E34DJ5LL.js';
|
|
3
|
+
import { schemas, schemaEntries } from './chunk-4LAM3N6S.js';
|
|
4
4
|
import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
|
|
5
5
|
|
|
6
6
|
var own = (value, key) => Object.hasOwn(value, key);
|
|
@@ -367,7 +367,9 @@ function distance(a, b) {
|
|
|
367
367
|
}
|
|
368
368
|
function lintPresentation(document, options = {}) {
|
|
369
369
|
optionsChecked(options);
|
|
370
|
-
const validation = validatePresentation(document), diagnostics = validation.errors.map(
|
|
370
|
+
const validation = validatePresentation(document), diagnostics = validation.errors.map(
|
|
371
|
+
(issue) => typeof issue.params.code === "string" && issue.keyword === "opf" ? { ...schemaDiagnostic(issue), ruleId: `opf/${issue.params.code}` } : schemaDiagnostic(issue)
|
|
372
|
+
);
|
|
371
373
|
const catalogs = catalogContext(document, options, diagnostics), seen = /* @__PURE__ */ new Set();
|
|
372
374
|
const assetValues = object(document) && object(document.assets) ? document.assets : {};
|
|
373
375
|
for (const field of fields(document)) {
|
|
@@ -515,6 +517,18 @@ function lintPresentation(document, options = {}) {
|
|
|
515
517
|
}
|
|
516
518
|
}
|
|
517
519
|
}
|
|
520
|
+
for (const issue of unusedReferenceWarnings(document))
|
|
521
|
+
diagnostics.push({
|
|
522
|
+
ruleId: "opf/unused-reference",
|
|
523
|
+
severity: "warning",
|
|
524
|
+
scope: "document",
|
|
525
|
+
path: issue.path,
|
|
526
|
+
message: issue.message,
|
|
527
|
+
help: "Cite the reference from a text, bullet or list item run ({ text, cite: id }) so it is listed in that slide's footnote area, or remove the entry. Nothing is drawn for an uncited reference.",
|
|
528
|
+
definition: schemas.presentation.$id + "#/$defs/Reference",
|
|
529
|
+
lookup: ["opf", "schema", "presentation", "/$defs/Reference"],
|
|
530
|
+
validation: issue
|
|
531
|
+
});
|
|
518
532
|
return report(diagnostics, validation.valid, "not-applicable");
|
|
519
533
|
}
|
|
520
534
|
function lintSource(source, options = {}) {
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { validatePresentation } from './chunk-JKVKYVWL.js';
|
|
2
|
+
|
|
3
|
+
// src/patch.ts
|
|
4
|
+
var PatchError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
/** The JSON Pointer the failing operation addressed, when known. */
|
|
7
|
+
path;
|
|
8
|
+
/** The index of the failing operation within the patch, when known. */
|
|
9
|
+
index;
|
|
10
|
+
/** The failing operation, when known. */
|
|
11
|
+
operation;
|
|
12
|
+
constructor(code, message, details = {}) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "PatchError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
if (details.path !== void 0) this.path = details.path;
|
|
17
|
+
if (details.index !== void 0) this.index = details.index;
|
|
18
|
+
if (details.operation !== void 0) this.operation = details.operation;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var PatchValidationError = class extends PatchError {
|
|
22
|
+
validation;
|
|
23
|
+
constructor(validation) {
|
|
24
|
+
super("patch-invalid-document", "The patched document is not a valid OPF presentation.");
|
|
25
|
+
this.name = "PatchValidationError";
|
|
26
|
+
this.validation = validation;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
30
|
+
var isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
var clone = (value) => structuredClone(value);
|
|
32
|
+
function jsonEqual(a, b) {
|
|
33
|
+
if (a === b) return true;
|
|
34
|
+
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
35
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
36
|
+
if (Array.isArray(a)) {
|
|
37
|
+
const other = b;
|
|
38
|
+
return a.length === other.length && a.every((item, i) => jsonEqual(item, other[i]));
|
|
39
|
+
}
|
|
40
|
+
const left = a, right = b;
|
|
41
|
+
const keys = Object.keys(left);
|
|
42
|
+
return keys.length === Object.keys(right).length && keys.every((key) => own(right, key) && jsonEqual(left[key], right[key]));
|
|
43
|
+
}
|
|
44
|
+
function escapePointerToken(token) {
|
|
45
|
+
return String(token).replaceAll("~", "~0").replaceAll("/", "~1");
|
|
46
|
+
}
|
|
47
|
+
function unescapePointerToken(token) {
|
|
48
|
+
return token.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
49
|
+
}
|
|
50
|
+
function parsePointer(pointer) {
|
|
51
|
+
if (typeof pointer !== "string" || pointer !== "" && !pointer.startsWith("/") || /~(?![01])/u.test(pointer)) {
|
|
52
|
+
throw new PatchError("invalid-json-pointer", "Expected a JSON Pointer (empty string or /path with ~0 and ~1 escapes).", { path: typeof pointer === "string" ? pointer : void 0 });
|
|
53
|
+
}
|
|
54
|
+
return pointer === "" ? [] : pointer.slice(1).split("/").map(unescapePointerToken);
|
|
55
|
+
}
|
|
56
|
+
function formatPointer(tokens) {
|
|
57
|
+
return tokens.length ? `/${tokens.map(escapePointerToken).join("/")}` : "";
|
|
58
|
+
}
|
|
59
|
+
function pointerFromPath(path) {
|
|
60
|
+
if (Array.isArray(path)) return formatPointer(path);
|
|
61
|
+
if (typeof path !== "string") throw new PatchError("invalid-json-pointer", "A path must be a string or an array of segments.");
|
|
62
|
+
if (path === "") return "";
|
|
63
|
+
if (path.startsWith("/")) {
|
|
64
|
+
parsePointer(path);
|
|
65
|
+
return path;
|
|
66
|
+
}
|
|
67
|
+
return formatPointer(path.split(".").filter(Boolean));
|
|
68
|
+
}
|
|
69
|
+
function splitPointer(pointer) {
|
|
70
|
+
const tokens = parsePointer(pointer);
|
|
71
|
+
if (!tokens.length) throw new PatchError("invalid-json-pointer", "The root pointer has no parent.", { path: pointer });
|
|
72
|
+
return { parent: formatPointer(tokens.slice(0, -1)), token: tokens[tokens.length - 1] };
|
|
73
|
+
}
|
|
74
|
+
function readPointer(document, pointer) {
|
|
75
|
+
const tokens = typeof pointer === "string" ? parsePointer(pointer) : pointer;
|
|
76
|
+
let value = document;
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
if (!/^(0|[1-9][0-9]*)$/.test(token)) return { found: false };
|
|
80
|
+
const at = Number(token);
|
|
81
|
+
if (!Number.isSafeInteger(at) || at >= value.length) return { found: false };
|
|
82
|
+
value = value[at];
|
|
83
|
+
} else if (value && typeof value === "object" && own(value, token)) value = value[token];
|
|
84
|
+
else return { found: false };
|
|
85
|
+
}
|
|
86
|
+
return { found: true, value };
|
|
87
|
+
}
|
|
88
|
+
function getAtPointer(document, pointer) {
|
|
89
|
+
const result = readPointer(document, pointer);
|
|
90
|
+
if (!result.found) {
|
|
91
|
+
const shown = typeof pointer === "string" ? pointer : formatPointer(pointer);
|
|
92
|
+
throw new PatchError("patch-path-missing", `Patch path does not exist: ${shown || "/"}`, { path: shown });
|
|
93
|
+
}
|
|
94
|
+
return result.value;
|
|
95
|
+
}
|
|
96
|
+
var hasPointer = (document, pointer) => readPointer(document, pointer).found;
|
|
97
|
+
function arrayIndex(token, length, allowEnd, path) {
|
|
98
|
+
if (allowEnd && token === "-") return length;
|
|
99
|
+
if (!/^(0|[1-9][0-9]*)$/.test(token)) throw new PatchError("invalid-array-index", `Invalid array index: ${token}`, { path });
|
|
100
|
+
const at = Number(token);
|
|
101
|
+
if (!Number.isSafeInteger(at) || at > length || !allowEnd && at >= length) throw new PatchError("invalid-array-index", `Array index out of bounds: ${token}`, { path });
|
|
102
|
+
return at;
|
|
103
|
+
}
|
|
104
|
+
function normalizePatch(patch) {
|
|
105
|
+
if (!Array.isArray(patch)) throw new PatchError("invalid-patch", "A JSON Patch must be an array of operations.");
|
|
106
|
+
return patch.map((operation, index) => normalizeOperation(operation, index));
|
|
107
|
+
}
|
|
108
|
+
function normalizeOperation(operation, index) {
|
|
109
|
+
const fail = (code, message, path) => new PatchError(code, index === void 0 ? message : `Operation ${index}: ${message}`, { index, operation, path });
|
|
110
|
+
if (!isObject(operation)) throw fail("invalid-patch-operation", "Expected an operation object.");
|
|
111
|
+
const op = operation.op;
|
|
112
|
+
if (typeof op !== "string" || !["add", "remove", "replace", "move", "copy", "test"].includes(op)) {
|
|
113
|
+
throw fail("unsupported-patch-operation", `Unknown operation: ${String(op)}`);
|
|
114
|
+
}
|
|
115
|
+
if (typeof operation.path !== "string") throw fail("invalid-patch-operation", "An operation needs a string path.");
|
|
116
|
+
try {
|
|
117
|
+
parsePointer(operation.path);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw fail("invalid-json-pointer", error.message, operation.path);
|
|
120
|
+
}
|
|
121
|
+
if (op === "move" || op === "copy") {
|
|
122
|
+
if (typeof operation.from !== "string") throw fail("invalid-patch-operation", `${op} requires a string from.`, operation.path);
|
|
123
|
+
try {
|
|
124
|
+
parsePointer(operation.from);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw fail("invalid-json-pointer", error.message, operation.from);
|
|
127
|
+
}
|
|
128
|
+
return { op, from: operation.from, path: operation.path };
|
|
129
|
+
}
|
|
130
|
+
if (op === "remove") return { op, path: operation.path };
|
|
131
|
+
if (!own(operation, "value")) throw fail("invalid-patch-operation", `${op} requires value.`, operation.path);
|
|
132
|
+
return { op, path: operation.path, value: clone(operation.value) };
|
|
133
|
+
}
|
|
134
|
+
function parentOf(document, tokens, path) {
|
|
135
|
+
const key = tokens[tokens.length - 1];
|
|
136
|
+
const found = readPointer(document, tokens.slice(0, -1));
|
|
137
|
+
if (!found.found || !found.value || typeof found.value !== "object") {
|
|
138
|
+
throw new PatchError("patch-parent-missing", `Patch parent does not exist for ${path}.`, { path });
|
|
139
|
+
}
|
|
140
|
+
return { parent: found.value, key };
|
|
141
|
+
}
|
|
142
|
+
var NONE = /* @__PURE__ */ Symbol("none");
|
|
143
|
+
function addAt(document, path, value) {
|
|
144
|
+
const tokens = parsePointer(path);
|
|
145
|
+
if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) } };
|
|
146
|
+
const { parent, key } = parentOf(document, tokens, path);
|
|
147
|
+
if (Array.isArray(parent)) {
|
|
148
|
+
const at = arrayIndex(key, parent.length, true, path);
|
|
149
|
+
parent.splice(at, 0, clone(value));
|
|
150
|
+
return { document, inverse: { op: "remove", path: formatPointer([...tokens.slice(0, -1), at]) } };
|
|
151
|
+
}
|
|
152
|
+
const record = parent;
|
|
153
|
+
const previous = own(record, key) ? record[key] : NONE;
|
|
154
|
+
Object.defineProperty(record, key, { value: clone(value), enumerable: true, configurable: true, writable: true });
|
|
155
|
+
return { document, inverse: previous === NONE ? { op: "remove", path } : { op: "replace", path, value: clone(previous) } };
|
|
156
|
+
}
|
|
157
|
+
function replaceAt(document, path, value) {
|
|
158
|
+
const tokens = parsePointer(path);
|
|
159
|
+
if (!tokens.length) return { document: clone(value), inverse: { op: "replace", path: "", value: clone(document) } };
|
|
160
|
+
const { parent, key } = parentOf(document, tokens, path);
|
|
161
|
+
let previous;
|
|
162
|
+
if (Array.isArray(parent)) {
|
|
163
|
+
const at = arrayIndex(key, parent.length, false, path);
|
|
164
|
+
previous = parent[at];
|
|
165
|
+
parent[at] = clone(value);
|
|
166
|
+
} else {
|
|
167
|
+
const record = parent;
|
|
168
|
+
if (!own(record, key)) throw new PatchError("patch-path-missing", `Patch path does not exist: ${path}.`, { path });
|
|
169
|
+
previous = record[key];
|
|
170
|
+
Object.defineProperty(record, key, { value: clone(value), enumerable: true, configurable: true, writable: true });
|
|
171
|
+
}
|
|
172
|
+
return { document, inverse: { op: "replace", path, value: clone(previous) } };
|
|
173
|
+
}
|
|
174
|
+
function removeAt(document, path) {
|
|
175
|
+
const tokens = parsePointer(path);
|
|
176
|
+
if (!tokens.length) throw new PatchError("patch-root-remove", "The document root cannot be removed.", { path });
|
|
177
|
+
const { parent, key } = parentOf(document, tokens, path);
|
|
178
|
+
let previous;
|
|
179
|
+
if (Array.isArray(parent)) {
|
|
180
|
+
const at = arrayIndex(key, parent.length, false, path);
|
|
181
|
+
previous = parent[at];
|
|
182
|
+
parent.splice(at, 1);
|
|
183
|
+
return { document, inverse: { op: "add", path: formatPointer([...tokens.slice(0, -1), at]), value: clone(previous) } };
|
|
184
|
+
}
|
|
185
|
+
const record = parent;
|
|
186
|
+
if (!own(record, key)) throw new PatchError("patch-path-missing", `Patch path does not exist: ${path}.`, { path });
|
|
187
|
+
previous = record[key];
|
|
188
|
+
delete record[key];
|
|
189
|
+
return { document, inverse: { op: "add", path, value: clone(previous) } };
|
|
190
|
+
}
|
|
191
|
+
function applyOne(document, operation) {
|
|
192
|
+
switch (operation.op) {
|
|
193
|
+
case "add": {
|
|
194
|
+
const r = addAt(document, operation.path, operation.value);
|
|
195
|
+
return { document: r.document, inverse: [r.inverse] };
|
|
196
|
+
}
|
|
197
|
+
case "replace": {
|
|
198
|
+
const r = replaceAt(document, operation.path, operation.value);
|
|
199
|
+
return { document: r.document, inverse: [r.inverse] };
|
|
200
|
+
}
|
|
201
|
+
case "remove": {
|
|
202
|
+
const r = removeAt(document, operation.path);
|
|
203
|
+
return { document: r.document, inverse: [r.inverse] };
|
|
204
|
+
}
|
|
205
|
+
case "test": {
|
|
206
|
+
const found = readPointer(document, operation.path);
|
|
207
|
+
if (!found.found || !jsonEqual(found.value, operation.value)) throw new PatchError("patch-test-failed", `Test failed at ${operation.path || "/"}: the value changed.`, { path: operation.path });
|
|
208
|
+
return { document, inverse: [] };
|
|
209
|
+
}
|
|
210
|
+
case "copy": {
|
|
211
|
+
const value = clone(getAtPointer(document, operation.from));
|
|
212
|
+
const r = addAt(document, operation.path, value);
|
|
213
|
+
return { document: r.document, inverse: [r.inverse] };
|
|
214
|
+
}
|
|
215
|
+
case "move": {
|
|
216
|
+
const from = parsePointer(operation.from), to = parsePointer(operation.path);
|
|
217
|
+
const value = clone(getAtPointer(document, from));
|
|
218
|
+
if (from.length === to.length && from.every((token, i) => token === to[i])) return { document, inverse: [] };
|
|
219
|
+
if (to.length > from.length && from.every((token, i) => to[i] === token)) {
|
|
220
|
+
throw new PatchError("patch-invalid-move", "Cannot move a value into its descendant.", { path: operation.path });
|
|
221
|
+
}
|
|
222
|
+
const removed = removeAt(document, operation.from);
|
|
223
|
+
const added = addAt(removed.document, operation.path, value);
|
|
224
|
+
return { document: added.document, inverse: [added.inverse, removed.inverse] };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function applyPatchWithInverse(document, patch, options = {}) {
|
|
229
|
+
const operations = normalizePatch(patch);
|
|
230
|
+
let working = clone(document);
|
|
231
|
+
const inverse = [];
|
|
232
|
+
for (const [index, operation] of operations.entries()) {
|
|
233
|
+
try {
|
|
234
|
+
const step = applyOne(working, operation);
|
|
235
|
+
working = step.document;
|
|
236
|
+
inverse.unshift(...step.inverse);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error instanceof PatchError && error.index === void 0) {
|
|
239
|
+
const wrapped = new PatchError(error.code, `Operation ${index}: ${error.message}`, { path: error.path, index, operation });
|
|
240
|
+
throw wrapped;
|
|
241
|
+
}
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const result = { document: working, inverse, patch: operations };
|
|
246
|
+
if (options.validate) {
|
|
247
|
+
const validator = typeof options.validate === "function" ? options.validate : (value) => validatePresentation(value);
|
|
248
|
+
const validation = validator(working);
|
|
249
|
+
if (!validation.valid || options.strict && Array.isArray(validation.warnings) && validation.warnings.length) throw new PatchValidationError(validation);
|
|
250
|
+
result.validation = validation;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
function applyPatch(document, patch, options = {}) {
|
|
255
|
+
return applyPatchWithInverse(document, patch, options).document;
|
|
256
|
+
}
|
|
257
|
+
function invertPatch(document, patch) {
|
|
258
|
+
return applyPatchWithInverse(document, patch).inverse;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export { PatchError, PatchValidationError, applyPatch, applyPatchWithInverse, escapePointerToken, formatPointer, getAtPointer, hasPointer, invertPatch, jsonEqual, normalizePatch, parsePointer, pointerFromPath, readPointer, splitPointer, unescapePointerToken };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { validatePresentation } from './chunk-JKVKYVWL.js';
|
|
2
|
+
|
|
3
|
+
// src/convert/shared.ts
|
|
4
|
+
var CONTENT_KEYS = ["text", "items", "bullets", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"];
|
|
5
|
+
var SLIDE_ONLY_KEYS = ["title", "subtitle", "tag", "layout", "design", "notes", "section", "hidden", "beat"];
|
|
6
|
+
var OPFConversionError = class extends Error {
|
|
7
|
+
code;
|
|
8
|
+
details;
|
|
9
|
+
constructor(code, message, details = {}) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "OPFConversionError";
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.details = details;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var refuse = (message, details = {}) => new OPFConversionError("not-convertible", message, details);
|
|
17
|
+
var Loss = class {
|
|
18
|
+
list = [];
|
|
19
|
+
note(message) {
|
|
20
|
+
if (!this.list.includes(message)) this.list.push(message);
|
|
21
|
+
}
|
|
22
|
+
merge(other) {
|
|
23
|
+
for (const message of other) this.note(message);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var report = (loss) => ({ lossless: loss.length === 0, loss: [...loss] });
|
|
27
|
+
var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
28
|
+
var clone = (value) => structuredClone(value);
|
|
29
|
+
var own = (value, key) => Object.hasOwn(value, key);
|
|
30
|
+
var same = (a, b) => JSON.stringify(canonical(a)) === JSON.stringify(canonical(b));
|
|
31
|
+
function canonical(value) {
|
|
32
|
+
if (Array.isArray(value)) return value.map(canonical);
|
|
33
|
+
if (isRecord(value)) return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
var isSlideLike = (payload) => SLIDE_ONLY_KEYS.some((key) => own(payload, key));
|
|
37
|
+
var contentKeysOf = (payload) => CONTENT_KEYS.filter((key) => payload[key] !== void 0);
|
|
38
|
+
function assertValidOutput(value, as = "block") {
|
|
39
|
+
const slides = as === "slides" ? value : as === "slide" ? [value] : [{ blocks: [value] }];
|
|
40
|
+
const result = validatePresentation({ slides });
|
|
41
|
+
if (!result.valid) {
|
|
42
|
+
const first = result.errors[0];
|
|
43
|
+
throw new OPFConversionError("invalid-output", `The converted content is not valid OPF: ${first?.message ?? "unknown error"}${first?.path ? ` (${first.path})` : ""}.`, { issues: result.errors });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var assertValidOwner = (owner) => assertValidOutput(owner, isSlideLike(owner) ? "slide" : "block");
|
|
47
|
+
var FORMATTING = ["bold", "italic", "underline", "strikethrough", "color", "fontSize", "fontFamily", "link", "superscript", "subscript"];
|
|
48
|
+
var isPlainRun = (run) => typeof run === "string" || isRecord(run) && !FORMATTING.some((key) => run[key] !== void 0);
|
|
49
|
+
var runText = (run) => typeof run === "string" ? run : run.text ?? "";
|
|
50
|
+
var runsOf = (value) => typeof value === "string" ? [value] : Array.isArray(value) ? value : [];
|
|
51
|
+
var plainOf = (value) => runsOf(value).map(runText).join("");
|
|
52
|
+
var hasFormatting = (value) => runsOf(value).some((run) => !isPlainRun(run));
|
|
53
|
+
var NEWLINE = /\r\n|\r|\n/;
|
|
54
|
+
function splitRuns(runs) {
|
|
55
|
+
const lines = [[]];
|
|
56
|
+
for (const run of runs) {
|
|
57
|
+
const parts = runText(run).split(NEWLINE);
|
|
58
|
+
parts.forEach((part, index) => {
|
|
59
|
+
if (index > 0) lines.push([]);
|
|
60
|
+
if (part === "") return;
|
|
61
|
+
lines[lines.length - 1].push(typeof run === "string" ? part : { ...run, text: part });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return lines;
|
|
65
|
+
}
|
|
66
|
+
function mergeRuns(runs) {
|
|
67
|
+
const merged = [];
|
|
68
|
+
for (const run of runs) {
|
|
69
|
+
if (typeof run === "string" && typeof merged.at(-1) === "string") merged[merged.length - 1] += run;
|
|
70
|
+
else merged.push(run);
|
|
71
|
+
}
|
|
72
|
+
return merged;
|
|
73
|
+
}
|
|
74
|
+
var linePlain = (line) => line.map(runText).join("");
|
|
75
|
+
var lineIsPlain = (line) => line.every(isPlainRun);
|
|
76
|
+
var lineValue = (line) => lineIsPlain(line) ? linePlain(line) : mergeRuns(line);
|
|
77
|
+
function joinLines(lines) {
|
|
78
|
+
if (lines.every(lineIsPlain)) return lines.map(linePlain).join("\n");
|
|
79
|
+
const runs = [];
|
|
80
|
+
lines.forEach((line, index) => {
|
|
81
|
+
if (index > 0) runs.push("\n");
|
|
82
|
+
runs.push(...line);
|
|
83
|
+
});
|
|
84
|
+
return mergeRuns(runs);
|
|
85
|
+
}
|
|
86
|
+
function concatText(a, b) {
|
|
87
|
+
if (typeof a === "string" && typeof b === "string") return a + b;
|
|
88
|
+
return mergeRuns([...runsOf(a), ...runsOf(b)]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/convert/text-lines.ts
|
|
92
|
+
var LIST_PREFIX = /^([ \t]*)(?:([-*•+]|\d{1,3}[.)])[ \t]+)?/;
|
|
93
|
+
var indentWidth = (space) => [...space].reduce((width, char) => width + (char === " " ? 4 : 1), 0);
|
|
94
|
+
function parseListLine(line) {
|
|
95
|
+
const first = line[0];
|
|
96
|
+
if (first === void 0) return { line, indent: 0, numbered: false, marked: false };
|
|
97
|
+
const text = runText(first);
|
|
98
|
+
const match = LIST_PREFIX.exec(text);
|
|
99
|
+
const rest = text.slice(match[0].length);
|
|
100
|
+
const next = [...line];
|
|
101
|
+
if (rest === "") next.shift();
|
|
102
|
+
else next[0] = typeof first === "string" ? rest : { ...first, text: rest };
|
|
103
|
+
return { line: next, indent: indentWidth(match[1] ?? ""), numbered: /^\d/.test(match[2] ?? ""), marked: match[2] !== void 0 };
|
|
104
|
+
}
|
|
105
|
+
function levelsFromIndents(indents) {
|
|
106
|
+
const stack = [];
|
|
107
|
+
return indents.map((width) => {
|
|
108
|
+
while (stack.length && stack.at(-1) > width) stack.pop();
|
|
109
|
+
if (!stack.length || stack.at(-1) < width) stack.push(width);
|
|
110
|
+
return stack.length - 1;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
var STRONG_DASH = /^\s*(?:—|–|―|--|~)\s*(\S.*?)\s*$/;
|
|
114
|
+
var WEAK_DASH = /^\s*-\s+(\S.*?)\s*$/;
|
|
115
|
+
var MAX_ATTRIBUTION = 120;
|
|
116
|
+
var OPEN_QUOTE = /^\s*[“"‘«„]/;
|
|
117
|
+
var INLINE_ATTRIBUTION = /^(.*[”"’»“])\s+[—–]\s+(\S.*?)\s*$/;
|
|
118
|
+
function parseQuoteLines(lines) {
|
|
119
|
+
const attributionOf = (line, body) => {
|
|
120
|
+
const strong = STRONG_DASH.exec(line);
|
|
121
|
+
const weak = strong ? void 0 : WEAK_DASH.exec(line);
|
|
122
|
+
const text = (strong ?? weak)?.[1];
|
|
123
|
+
if (!text || text.length > MAX_ATTRIBUTION) return void 0;
|
|
124
|
+
if (weak && body.length && body.every((entry) => /^\s*-\s+/.test(entry))) return void 0;
|
|
125
|
+
return text;
|
|
126
|
+
};
|
|
127
|
+
if (lines.length >= 2) {
|
|
128
|
+
const last = attributionOf(lines.at(-1), lines.slice(0, -1));
|
|
129
|
+
if (last !== void 0) {
|
|
130
|
+
if (lines.length >= 3) {
|
|
131
|
+
const middle = attributionOf(lines.at(-2), lines.slice(0, -2));
|
|
132
|
+
if (middle !== void 0) return { text: lines.slice(0, -2), attribution: middle, source: last };
|
|
133
|
+
}
|
|
134
|
+
return { text: lines.slice(0, -1), attribution: last };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const final = lines.at(-1);
|
|
138
|
+
if (final !== void 0) {
|
|
139
|
+
const inline = INLINE_ATTRIBUTION.exec(final);
|
|
140
|
+
if (inline && OPEN_QUOTE.test(lines[0]) && inline[2].length <= MAX_ATTRIBUTION && !/[—–]/.test(inline[2])) return { text: [...lines.slice(0, -1), inline[1]], attribution: inline[2] };
|
|
141
|
+
}
|
|
142
|
+
return { text: lines };
|
|
143
|
+
}
|
|
144
|
+
var MONTH = "(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|June?|July?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\\.?";
|
|
145
|
+
var YEAR = "(?:1[5-9]\\d\\d|2[01]\\d\\d)";
|
|
146
|
+
var WHEN = new RegExp(
|
|
147
|
+
`^(?:${[
|
|
148
|
+
`${YEAR}(?:-\\d{2}(?:-\\d{2})?)?`,
|
|
149
|
+
`${YEAR}[\u2013-]${YEAR}`,
|
|
150
|
+
`Q[1-4](?:\\s*(?:'\\d{2}|${YEAR}))?`,
|
|
151
|
+
`${YEAR}\\s*Q[1-4]`,
|
|
152
|
+
`H[12](?:\\s*${YEAR})?`,
|
|
153
|
+
`${YEAR}\\s*H[12]`,
|
|
154
|
+
`${MONTH}(?:\\s+\\d{1,2}(?:st|nd|rd|th)?)?(?:,?\\s+${YEAR})?`,
|
|
155
|
+
`\\d{1,2}\\s+${MONTH}(?:\\s+${YEAR})?`,
|
|
156
|
+
"(?:Week|Wk|Day|Month|Sprint|Phase|Step|Stage)\\s+\\d{1,3}"
|
|
157
|
+
].join("|")})$`,
|
|
158
|
+
"i"
|
|
159
|
+
);
|
|
160
|
+
var WHEN_SEPARATOR = /\s*:\s+|\s+[–—-]\s+|\s*[—–]\s*/g;
|
|
161
|
+
var looksLikeWhen = (text) => text.length <= 24 && WHEN.test(text.trim());
|
|
162
|
+
function splitWhen(text, loose = false) {
|
|
163
|
+
for (const match of text.matchAll(WHEN_SEPARATOR)) {
|
|
164
|
+
const when = text.slice(0, match.index).trim();
|
|
165
|
+
const what = text.slice(match.index + match[0].length).trim();
|
|
166
|
+
if (!when || !what) continue;
|
|
167
|
+
if (looksLikeWhen(when) || loose && match[0].trim() === ":" && when.length <= 24) return { when, what };
|
|
168
|
+
}
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
var FENCE_OPEN = /^\s*(`{3,}|~{3,})[ \t]*([^`\r\n]*?)[ \t]*$/;
|
|
172
|
+
var fenceLine = (char, length) => new RegExp(`^\\s*${char === "`" ? "`" : "~"}{${length},}\\s*$`);
|
|
173
|
+
function parseCodeFence(lines) {
|
|
174
|
+
if (lines.length < 2) return void 0;
|
|
175
|
+
const open = FENCE_OPEN.exec(lines[0]);
|
|
176
|
+
if (!open) return void 0;
|
|
177
|
+
const fence = open[1];
|
|
178
|
+
const closing = fenceLine(fence[0], fence.length);
|
|
179
|
+
if (!closing.test(lines.at(-1))) return void 0;
|
|
180
|
+
const body = lines.slice(1, -1);
|
|
181
|
+
if (body.some((line) => closing.test(line))) return void 0;
|
|
182
|
+
const info = open[2] ?? "";
|
|
183
|
+
const result = { source: body.join("\n") };
|
|
184
|
+
const attribute = /(?:^|\s)(?:title|filename|file)\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+))/.exec(info);
|
|
185
|
+
const filename = attribute ? attribute[1] ?? attribute[2] ?? attribute[3] : void 0;
|
|
186
|
+
const language = /^(?:title|filename|file)\s*=/.test(info) ? void 0 : /^\{?\.?([A-Za-z0-9_+#.-]+)/.exec(info)?.[1];
|
|
187
|
+
if (language) result.language = language;
|
|
188
|
+
if (filename) result.filename = filename;
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
function fenceCode(source, language, filename) {
|
|
192
|
+
const longest = Math.max(2, ...[...source.matchAll(/`+/g)].map((match) => match[0].length));
|
|
193
|
+
const fence = "`".repeat(longest + 1);
|
|
194
|
+
const info = [language, filename ? `title="${filename.replaceAll('"', "'")}"` : void 0].filter(Boolean).join(" ");
|
|
195
|
+
return [`${fence}${info}`, ...source.split(/\r\n|\r|\n/), fence];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { CONTENT_KEYS, FORMATTING, Loss, OPFConversionError, assertValidOutput, assertValidOwner, clone, concatText, contentKeysOf, fenceCode, hasFormatting, isPlainRun, isRecord, isSlideLike, joinLines, levelsFromIndents, lineIsPlain, linePlain, lineValue, parseCodeFence, parseListLine, parseQuoteLines, plainOf, refuse, report, runText, runsOf, same, splitRuns, splitWhen };
|