@openpresentation/opf 0.3.0 → 0.4.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/LICENSE +1 -1
- package/README.md +2 -0
- package/dist/catalogs-x1oFFQnd.d.ts +1198 -0
- package/dist/catalogs.d.ts +2 -32413
- package/dist/catalogs.js +1 -18505
- package/dist/chunk-75N5I6M4.js +229 -0
- package/dist/chunk-EMTMQRX4.js +1007 -0
- package/dist/chunk-GCXOU5MZ.js +6515 -0
- package/dist/chunk-HJNJHB5H.js +454 -0
- package/dist/chunk-OZULJBSG.js +3368 -0
- package/dist/chunk-PIQXLBBJ.js +445 -0
- package/dist/chunk-SOSOBKQ2.js +145 -0
- package/dist/chunk-TWMRZ43O.js +18576 -0
- package/dist/composition.d.ts +190 -0
- package/dist/composition.js +1 -0
- package/dist/data.d.ts +32 -0
- package/dist/data.js +1 -0
- package/dist/docs.js +69 -5
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +112 -2
- package/dist/index.d.ts +6 -4
- package/dist/index.js +8 -29555
- package/dist/pagination.d.ts +52 -0
- package/dist/pagination.js +5 -0
- package/dist/{presentation-C8BOK0-5.d.ts → presentation-BjP4yTQ1.d.ts} +86 -3
- package/dist/previews.js +1 -447
- package/dist/repo-readme.js +1 -3
- package/dist/schemas-C1DxapX2.d.ts +206 -0
- package/dist/schemas.d.ts +1 -12373
- package/dist/schemas.js +1 -6275
- package/dist/spec/README.md +18 -3
- package/dist/spec/catalogs/audiences/index.json +1 -1
- package/dist/spec/catalogs/chart-types/index.json +1 -1
- package/dist/spec/catalogs/color-schemes/index.json +1 -1
- package/dist/spec/catalogs/font-schemes/index.json +1 -1
- package/dist/spec/catalogs/languages/index.json +1 -1
- package/dist/spec/catalogs/layouts/chart-1x.json +4 -1
- package/dist/spec/catalogs/layouts/chart-2x.json +5 -1
- package/dist/spec/catalogs/layouts/chart-3x.json +5 -1
- package/dist/spec/catalogs/layouts/code-1x.json +4 -1
- package/dist/spec/catalogs/layouts/extract/canonical.json +94 -21
- package/dist/spec/catalogs/layouts/image-1x.json +4 -1
- package/dist/spec/catalogs/layouts/image-2x.json +5 -1
- package/dist/spec/catalogs/layouts/image-3x.json +5 -1
- package/dist/spec/catalogs/layouts/index.json +1 -1
- package/dist/spec/catalogs/layouts/list-2x.json +4 -1
- package/dist/spec/catalogs/layouts/list-3x.json +4 -1
- package/dist/spec/catalogs/layouts/list-4x.json +5 -1
- package/dist/spec/catalogs/layouts/list-5x.json +5 -1
- package/dist/spec/catalogs/layouts/list-6x.json +5 -1
- package/dist/spec/catalogs/layouts/media-1x.json +4 -1
- package/dist/spec/catalogs/layouts/number-2x.json +4 -1
- package/dist/spec/catalogs/layouts/number-3x.json +4 -1
- package/dist/spec/catalogs/layouts/number-4x.json +5 -1
- package/dist/spec/catalogs/layouts/number-5x.json +5 -1
- package/dist/spec/catalogs/layouts/number-6x.json +5 -1
- package/dist/spec/catalogs/layouts/table-1x.json +4 -1
- package/dist/spec/catalogs/layouts/text-2x.json +4 -1
- package/dist/spec/catalogs/layouts/text-3x.json +4 -1
- package/dist/spec/catalogs/narratives/index.json +2 -2
- package/dist/spec/catalogs/purposes/index.json +1 -1
- package/dist/spec/catalogs/social-platforms/index.json +1 -1
- package/dist/spec/catalogs/themes/index.json +1 -1
- package/dist/spec/catalogs/tones/index.json +1 -1
- package/dist/spec/openapi.yaml +54 -14
- package/dist/spec/previews/layouts/index.json +71 -71
- package/dist/spec/schemas/catalog-index.schema.json +53 -0
- package/dist/spec/schemas/layout-preview-index.schema.json +54 -0
- package/dist/spec/schemas/layout.schema.json +63 -0
- package/dist/spec/schemas/opf.schema.json +182 -3
- package/dist/spec-files.d.ts +14 -2391
- package/dist/spec-files.js +1 -3356
- package/dist/types.d.ts +3 -1102
- package/dist/types.js +0 -2
- package/dist/validator.d.ts +3 -10
- package/dist/validator.js +4 -7243
- package/package.json +16 -3
- package/dist/catalogs.js.map +0 -1
- package/dist/docs.js.map +0 -1
- package/dist/examples.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/previews.js.map +0 -1
- package/dist/repo-readme.js.map +0 -1
- package/dist/schemas.js.map +0 -1
- package/dist/spec-files.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validator.js.map +0 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// src/data.ts
|
|
2
|
+
var OPFDataImportError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "OPFDataImportError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var fail = (message) => {
|
|
9
|
+
throw new OPFDataImportError(message);
|
|
10
|
+
};
|
|
11
|
+
var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
12
|
+
function cell(value, location) {
|
|
13
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return value;
|
|
14
|
+
return fail(`${location}: expected a string, finite number, boolean, or null. Flatten nested JSON fields first.`);
|
|
15
|
+
}
|
|
16
|
+
function labels(values) {
|
|
17
|
+
if (!values.length || values.some((value) => typeof value !== "string" || !value.trim())) fail("Column names must be nonempty strings.");
|
|
18
|
+
const names = values;
|
|
19
|
+
if (new Set(names).size !== names.length) fail("Duplicate column names. Rename them before importing.");
|
|
20
|
+
return [...names];
|
|
21
|
+
}
|
|
22
|
+
function csv(text, delimiter) {
|
|
23
|
+
if (delimiter.length !== 1 || /["\r\n]/.test(delimiter)) fail("Delimiter must be one character other than a quote or newline.");
|
|
24
|
+
const rows = [];
|
|
25
|
+
let row = [], value = "", quoted = false, closed = false;
|
|
26
|
+
for (let i = 0; i < text.length; i++) {
|
|
27
|
+
const char = text[i];
|
|
28
|
+
if (quoted) {
|
|
29
|
+
if (char === '"') {
|
|
30
|
+
if (text[i + 1] === '"') {
|
|
31
|
+
value += '"';
|
|
32
|
+
i++;
|
|
33
|
+
} else {
|
|
34
|
+
quoted = false;
|
|
35
|
+
closed = true;
|
|
36
|
+
}
|
|
37
|
+
} else value += char;
|
|
38
|
+
} else if (char === delimiter || char === "\r" || char === "\n") {
|
|
39
|
+
row.push(value);
|
|
40
|
+
value = "";
|
|
41
|
+
closed = false;
|
|
42
|
+
if (char !== delimiter) {
|
|
43
|
+
rows.push(row);
|
|
44
|
+
row = [];
|
|
45
|
+
if (char === "\r" && text[i + 1] === "\n") i++;
|
|
46
|
+
}
|
|
47
|
+
} else if (char === '"' && !value && !closed) quoted = true;
|
|
48
|
+
else {
|
|
49
|
+
if (closed || char === '"') fail(`Malformed CSV near character ${i + 1}. Check quoted fields.`);
|
|
50
|
+
value += char;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (quoted) fail("Unclosed quoted CSV field.");
|
|
54
|
+
if (value || row.length || closed) {
|
|
55
|
+
row.push(value);
|
|
56
|
+
rows.push(row);
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
function matrix(input, header) {
|
|
61
|
+
if (!input.length || !input.every(Array.isArray)) return fail("Expected a nonempty array of rows.");
|
|
62
|
+
const rows = input;
|
|
63
|
+
const columns = header ? labels(rows[0]) : labels(rows[0].map((_, i) => `Column ${i + 1}`));
|
|
64
|
+
return rectangular(columns, header ? rows.slice(1) : rows);
|
|
65
|
+
}
|
|
66
|
+
function rectangular(columns, input) {
|
|
67
|
+
return { columns, rows: input.map((row, i) => {
|
|
68
|
+
if (!Array.isArray(row) || row.length !== columns.length) return fail(`Row ${i + 1}: expected ${columns.length} cells; found ${Array.isArray(row) ? row.length : "a non-array value"}.`);
|
|
69
|
+
return row.map((value, j) => cell(value, `Row ${i + 1}, column ${columns[j]}`));
|
|
70
|
+
}) };
|
|
71
|
+
}
|
|
72
|
+
function parseTabularData(input, options = {}) {
|
|
73
|
+
let value = input;
|
|
74
|
+
const format = options.format ?? (typeof input === "string" && !/^[\s\uFEFF]*[\[{]/.test(input) ? "csv" : "json");
|
|
75
|
+
if (!["csv", "tsv", "json"].includes(format)) fail(`Unsupported data format: ${format}`);
|
|
76
|
+
let data;
|
|
77
|
+
if (format !== "json") {
|
|
78
|
+
if (typeof input !== "string") return fail("CSV and TSV input must be text.");
|
|
79
|
+
data = matrix(csv(input.replace(/^\uFEFF/, ""), options.delimiter ?? (format === "tsv" ? " " : ",")), options.header !== false);
|
|
80
|
+
} else {
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
try {
|
|
83
|
+
value = JSON.parse(value.replace(/^\uFEFF/, ""));
|
|
84
|
+
} catch {
|
|
85
|
+
fail("Invalid JSON data.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
if (!value.length) return fail("Data contains no rows.");
|
|
90
|
+
if (Array.isArray(value[0])) data = matrix(value, options.header !== false);
|
|
91
|
+
else {
|
|
92
|
+
if (!value.every((row) => row && typeof row === "object" && !Array.isArray(row))) return fail("JSON records must all be objects.");
|
|
93
|
+
const columns = labels([...new Set(value.flatMap((row) => Object.keys(row)))]);
|
|
94
|
+
data = rectangular(columns, value.map((row) => columns.map((key) => own(row, key) ? row[key] : null)));
|
|
95
|
+
}
|
|
96
|
+
} else if (value && typeof value === "object" && "columns" in value && "rows" in value && Array.isArray(value.columns) && Array.isArray(value.rows)) {
|
|
97
|
+
data = rectangular(labels(value.columns), value.rows);
|
|
98
|
+
} else return fail("JSON data must be records, a row matrix, or {columns, rows}.");
|
|
99
|
+
}
|
|
100
|
+
if (options.columns) {
|
|
101
|
+
const selected = labels(options.columns), indices = selected.map((name) => {
|
|
102
|
+
const index = data.columns.indexOf(name);
|
|
103
|
+
if (index < 0) fail(`Unknown column: ${name}`);
|
|
104
|
+
return index;
|
|
105
|
+
});
|
|
106
|
+
data = { columns: selected, rows: data.rows.map((row) => indices.map((i) => row[i])) };
|
|
107
|
+
}
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
function measure(value, location) {
|
|
111
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
112
|
+
if (typeof value === "string" && /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(value.trim())) {
|
|
113
|
+
const number = Number(value.trim());
|
|
114
|
+
if (Number.isFinite(number) && (!Number.isInteger(number) || Number.isSafeInteger(number))) return number;
|
|
115
|
+
}
|
|
116
|
+
return fail(`${location}: expected a numeric chart value; found ${JSON.stringify(value)}. Clean the value or select another series.`);
|
|
117
|
+
}
|
|
118
|
+
function createDataContent(input, options) {
|
|
119
|
+
const data = parseTabularData(input, options);
|
|
120
|
+
if (options.as === "table") return { table: data };
|
|
121
|
+
if (options.as !== "chart") return fail("Choose table or chart.");
|
|
122
|
+
const category = options.category ?? data.columns[0];
|
|
123
|
+
const series = options.series ?? data.columns.filter((name) => name !== category);
|
|
124
|
+
if (!series.length || !data.rows.length) return fail("Charts need at least one category, numeric series, and data row.");
|
|
125
|
+
const names = labels([category, ...series]), indices = names.map((name) => {
|
|
126
|
+
const index = data.columns.indexOf(name);
|
|
127
|
+
if (index < 0) fail(`Unknown column: ${name}`);
|
|
128
|
+
return index;
|
|
129
|
+
});
|
|
130
|
+
const circular = ["pie", "donut", "doughnut"].includes(options.chartType ?? "column");
|
|
131
|
+
if (circular && series.length !== 1) return fail("Pie and donut charts require exactly one numeric series.");
|
|
132
|
+
const rows = data.rows.map((row, i) => indices.map((index, j) => {
|
|
133
|
+
const value = row[index];
|
|
134
|
+
if (!j) {
|
|
135
|
+
if (value === null || value === "") fail(`Row ${i + 1}: missing category ${category}.`);
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
const numeric = measure(value, `Row ${i + 1}, column ${names[j]}`);
|
|
139
|
+
if (circular && numeric < 0) fail(`Row ${i + 1}: pie and donut values cannot be negative.`);
|
|
140
|
+
return numeric;
|
|
141
|
+
}));
|
|
142
|
+
return { chart: { type: options.chartType ?? "column", data: { columns: names, rows } } };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { OPFDataImportError, createDataContent, parseTabularData };
|