@tinta-lang/engine 0.0.0-stage → 0.1.0-dev.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.
@@ -0,0 +1,236 @@
1
+ import { Budget, fail, scalarType } from "./common.js";
2
+ export function tableType(type, expr) {
3
+ if (typeof type === "string")
4
+ fail("E_TYPE", expr.span);
5
+ return type;
6
+ }
7
+ export function infer(expr, get, budget, columns) {
8
+ budget.charge(expr.span);
9
+ const visit = (child) => infer(child, get, budget, columns);
10
+ const requireType = (child, expected) => {
11
+ if (visit(child) !== expected)
12
+ fail("E_TYPE", child.span);
13
+ };
14
+ switch (expr.kind) {
15
+ case "literal":
16
+ return scalarType(expr.value);
17
+ case "name":
18
+ return get(expr.name, expr);
19
+ case "row-field": {
20
+ if (!columns)
21
+ fail("E_ROW_SCOPE", expr.span);
22
+ const column = columns.find((c) => c.name === expr.field);
23
+ if (!column)
24
+ fail("E_COLUMN_UNKNOWN", expr.span);
25
+ return column.type;
26
+ }
27
+ case "list": {
28
+ let item = "never";
29
+ for (const child of expr.items) {
30
+ const type = visit(child);
31
+ if (type !== "text" && type !== "number" && type !== "boolean")
32
+ fail("E_TYPE", child.span);
33
+ if (item !== "never" && item !== type)
34
+ fail("E_TYPE", child.span);
35
+ item = type;
36
+ }
37
+ return `list:${item}`;
38
+ }
39
+ case "unary": {
40
+ const type = expr.operator === "not" ? "boolean" : "number";
41
+ requireType(expr.operand, type);
42
+ return type;
43
+ }
44
+ case "binary": {
45
+ const a = visit(expr.left), b = visit(expr.right);
46
+ if (expr.operator === "==" || expr.operator === "!=") {
47
+ if (a !== b || (a !== "number" && a !== "text" && a !== "boolean"))
48
+ fail("E_TYPE", expr.span);
49
+ return "boolean";
50
+ }
51
+ const logical = expr.operator === "and" || expr.operator === "or";
52
+ if (a !== (logical ? "boolean" : "number") || a !== b)
53
+ fail("E_TYPE", expr.span);
54
+ return logical || ["<", "<=", ">", ">="].includes(expr.operator)
55
+ ? "boolean"
56
+ : "number";
57
+ }
58
+ case "call": {
59
+ const args = expr.arguments;
60
+ const arity = expr.name === "if"
61
+ ? 3
62
+ : ["count", "percent", "money"].includes(expr.name)
63
+ ? 1
64
+ : 2;
65
+ if (args.length !== arity &&
66
+ !(expr.name === "round" && args.length === 1))
67
+ fail("E_FUNCTION_ARITY", expr.span);
68
+ if (expr.name === "if") {
69
+ requireType(args[0], "boolean");
70
+ const a = visit(args[1]), b = visit(args[2]);
71
+ if (typeof a !== "string" || typeof b !== "string")
72
+ fail("E_TYPE", expr.span);
73
+ if (a === b)
74
+ return a;
75
+ if (a === "list:never" && b.startsWith("list:"))
76
+ return b;
77
+ if (b === "list:never" && a.startsWith("list:"))
78
+ return a;
79
+ fail("E_TYPE", expr.span);
80
+ }
81
+ if (expr.name === "count") {
82
+ const type = visit(args[0]);
83
+ if (typeof type === "string" && !type.startsWith("list:"))
84
+ fail("E_TYPE", expr.span);
85
+ return "number";
86
+ }
87
+ if (expr.name === "sum" || expr.name === "mean") {
88
+ const schema = tableType(visit(args[0]), args[0]);
89
+ const column = args[1];
90
+ if (column.kind !== "literal" || typeof column.value !== "string")
91
+ fail("E_TYPE", column.span);
92
+ const found = schema.find((c) => c.name === column.value);
93
+ if (!found)
94
+ fail("E_COLUMN_UNKNOWN", column.span);
95
+ if (found.type !== "number")
96
+ fail("E_TYPE", column.span);
97
+ return "number";
98
+ }
99
+ args.forEach((arg) => requireType(arg, "number"));
100
+ return ["fixed", "percent", "money"].includes(expr.name)
101
+ ? "text"
102
+ : "number";
103
+ }
104
+ }
105
+ }
106
+ export function numeric(value, expr) {
107
+ if (!Number.isFinite(value))
108
+ fail("E_NUMBER_RANGE", expr.span, "evaluate");
109
+ return value === 0 ? 0 : value;
110
+ }
111
+ export function format(value, metadata) {
112
+ if (typeof value !== "number")
113
+ return String(value);
114
+ if (Math.abs(value) >= 1e21 || (value !== 0 && Math.abs(value) < 1e-10))
115
+ return String(value);
116
+ return new Intl.NumberFormat(metadata.locale, {
117
+ useGrouping: false,
118
+ maximumFractionDigits: 10,
119
+ }).format(value);
120
+ }
121
+ export function evaluate(expr, env) {
122
+ env.budget.charge(expr.span);
123
+ const visit = (child) => evaluate(child, env);
124
+ const number = (child) => visit(child);
125
+ switch (expr.kind) {
126
+ case "literal":
127
+ return expr.value;
128
+ case "name":
129
+ return env.get(expr.name);
130
+ case "row-field":
131
+ return env.row.get(expr.field);
132
+ case "list":
133
+ return expr.items.map((item) => visit(item));
134
+ case "unary":
135
+ return expr.operator === "not"
136
+ ? !visit(expr.operand)
137
+ : numeric((expr.operator === "-" ? -1 : 1) * number(expr.operand), expr);
138
+ case "binary": {
139
+ const a = visit(expr.left);
140
+ if (expr.operator === "and")
141
+ return a ? visit(expr.right) : false;
142
+ if (expr.operator === "or")
143
+ return a ? true : visit(expr.right);
144
+ const b = visit(expr.right);
145
+ if (expr.operator === "==")
146
+ return a === b;
147
+ if (expr.operator === "!=")
148
+ return a !== b;
149
+ const x = a, y = b;
150
+ switch (expr.operator) {
151
+ case "+":
152
+ return numeric(x + y, expr);
153
+ case "-":
154
+ return numeric(x - y, expr);
155
+ case "*":
156
+ return numeric(x * y, expr);
157
+ case "/":
158
+ case "%":
159
+ if (y === 0)
160
+ fail("E_DIV_ZERO", expr.span, "evaluate");
161
+ return numeric(expr.operator === "/" ? x / y : x % y, expr);
162
+ case "<":
163
+ return x < y;
164
+ case "<=":
165
+ return x <= y;
166
+ case ">":
167
+ return x > y;
168
+ case ">=":
169
+ return x >= y;
170
+ }
171
+ throw new Error("Unreachable validated binary operator.");
172
+ }
173
+ case "call": {
174
+ const args = expr.arguments;
175
+ if (expr.name === "if")
176
+ return visit(args[0]) ? visit(args[1]) : visit(args[2]);
177
+ if (expr.name === "count") {
178
+ const value = visit(args[0]);
179
+ return "rows" in value ? value.rows.length : value.length;
180
+ }
181
+ if (expr.name === "sum" || expr.name === "mean") {
182
+ const table = visit(args[0]);
183
+ const index = table.columns.findIndex((c) => c.name === visit(args[1]));
184
+ if (expr.name === "mean" && !table.rows.length)
185
+ fail("E_EMPTY_MEAN", expr.span, "evaluate");
186
+ let total = 0;
187
+ for (const row of table.rows) {
188
+ env.budget.charge(expr.span);
189
+ total = numeric(total + row.cells[index], expr);
190
+ }
191
+ return expr.name === "sum"
192
+ ? total
193
+ : numeric(total / table.rows.length, expr);
194
+ }
195
+ const a = number(args[0]);
196
+ const b = args[1] ? number(args[1]) : 0;
197
+ if ((expr.name === "round" || expr.name === "fixed") &&
198
+ (!Number.isInteger(b) || b < 0 || b > 10))
199
+ fail("E_DIGITS_RANGE", expr.span, "evaluate");
200
+ switch (expr.name) {
201
+ case "min":
202
+ return Math.min(a, b);
203
+ case "max":
204
+ return Math.max(a, b);
205
+ case "pow":
206
+ return numeric(Math.pow(a, b), expr);
207
+ case "round":
208
+ return numeric(Math.round(numeric(a * 10 ** b, expr)) / 10 ** b, expr);
209
+ case "fixed":
210
+ return new Intl.NumberFormat(env.metadata.locale, {
211
+ useGrouping: false,
212
+ minimumFractionDigits: b,
213
+ maximumFractionDigits: b,
214
+ }).format(a);
215
+ case "percent":
216
+ return new Intl.NumberFormat(env.metadata.locale, {
217
+ style: "percent",
218
+ useGrouping: false,
219
+ minimumFractionDigits: 0,
220
+ maximumFractionDigits: 1,
221
+ }).format(a);
222
+ case "money": {
223
+ const digits = env.metadata.currency === "JPY" ? 0 : 2;
224
+ return new Intl.NumberFormat(env.metadata.locale, {
225
+ style: "currency",
226
+ currency: env.metadata.currency,
227
+ currencyDisplay: "symbol",
228
+ useGrouping: true,
229
+ minimumFractionDigits: digits,
230
+ maximumFractionDigits: digits,
231
+ }).format(a);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
package/dist/grid.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { ControlDefinition, SliderDefinition, Span } from "@tinta-lang/types";
2
+ /** Decimal tokens become exact millionths before any binary64 grid arithmetic. */
3
+ export declare function units(raw: string): bigint | undefined;
4
+ export declare function grid(min: string, max: string, step: string, span: Span): SliderDefinition["grid"];
5
+ export declare function inDomain(control: ControlDefinition, value: unknown): boolean;
package/dist/grid.js ADDED
@@ -0,0 +1,57 @@
1
+ import { fail } from "./common.js";
2
+ /** Decimal tokens become exact millionths before any binary64 grid arithmetic. */
3
+ export function units(raw) {
4
+ const match = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(raw);
5
+ if (!match)
6
+ return undefined;
7
+ const fraction = match[3] ?? "";
8
+ if (/^0+$/.test(match[2] + fraction))
9
+ return 0n;
10
+ const exponent = Number(match[4] ?? 0) + 6 - fraction.length;
11
+ if (Math.abs(exponent) > 400)
12
+ return undefined;
13
+ let value = BigInt(match[2] + fraction);
14
+ if (exponent >= 0)
15
+ value *= 10n ** BigInt(exponent);
16
+ else {
17
+ const divisor = 10n ** BigInt(-exponent);
18
+ if (value % divisor !== 0n)
19
+ return undefined;
20
+ value /= divisor;
21
+ }
22
+ return match[1] === "-" ? -value : value;
23
+ }
24
+ export function grid(min, max, step, span) {
25
+ const a = units(min), b = units(max), s = units(step);
26
+ if (a === undefined ||
27
+ b === undefined ||
28
+ s === undefined ||
29
+ a >= b ||
30
+ s < 1n ||
31
+ a < -1000000000000000n ||
32
+ b > 1000000000000000n ||
33
+ (b - a) % s !== 0n ||
34
+ (b - a) / s > 100000n)
35
+ fail("E_STATE_DOMAIN", span);
36
+ return {
37
+ scale: 1000000,
38
+ minUnits: String(a),
39
+ stepUnits: String(s),
40
+ ticks: Number((b - a) / s),
41
+ };
42
+ }
43
+ export function inDomain(control, value) {
44
+ if (control.kind === "toggle")
45
+ return typeof value === "boolean";
46
+ if (control.kind === "choice")
47
+ return typeof value === "string" && control.options.includes(value);
48
+ if (typeof value !== "number" || !Number.isFinite(value))
49
+ return false;
50
+ const n = units(String(value));
51
+ if (n === undefined)
52
+ return false;
53
+ const offset = n - BigInt(control.grid.minUnits), step = BigInt(control.grid.stepUnits);
54
+ return (offset >= 0n &&
55
+ offset <= step * BigInt(control.grid.ticks) &&
56
+ offset % step === 0n);
57
+ }
@@ -0,0 +1,4 @@
1
+ import type { DataImport, DataImportOptions, Result } from "@tinta-lang/types";
2
+ export declare function importCsv(bytes: Uint8Array, options: DataImportOptions): Result<DataImport>;
3
+ /** A bounded JSON reader detects repeated keys before they can be erased. */
4
+ export declare function importJson(bytes: Uint8Array, options: DataImportOptions): Result<DataImport>;
@@ -0,0 +1,293 @@
1
+ import { decodeUtf8, encodeUtf8, parse } from "@tinta-lang/syntax";
2
+ import { attempt, Budget, Failure, fail, scalarType } from "./common.js";
3
+ import { registry } from "./registry.js";
4
+ const span = {
5
+ uri: "tinta:import",
6
+ start: { offset: 0, line: 1, column: 1 },
7
+ end: { offset: 0, line: 1, column: 1 },
8
+ };
9
+ const numberToken = /^[+-]?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/;
10
+ const validName = (name) => /^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(name) &&
11
+ !registry.reservedIdentifiers.includes(name);
12
+ function decode(bytes, budget) {
13
+ budget.limit("importBytes", bytes.length);
14
+ try {
15
+ return decodeUtf8(bytes).replace(/^\uFEFF/, "");
16
+ }
17
+ catch {
18
+ fail("E_ENCODING", span, "decode");
19
+ }
20
+ }
21
+ function mapping(headers) {
22
+ const used = new Set();
23
+ return headers.map((header) => {
24
+ let name = header
25
+ .normalize("NFKD")
26
+ .replace(/\p{M}/gu, "")
27
+ .replace(/[^A-Za-z0-9_]+/g, "_")
28
+ .replace(/^_+|_+$/g, "") || "column";
29
+ if (/^[0-9]/.test(name) ||
30
+ registry.reservedIdentifiers.includes(name))
31
+ name = "column_" + name;
32
+ name = name.slice(0, 56);
33
+ let candidate = name, index = 2;
34
+ while (used.has(candidate))
35
+ candidate = `${name}_${index++}`;
36
+ used.add(candidate);
37
+ return candidate;
38
+ });
39
+ }
40
+ function candidate(headers, rows, options, budget, json) {
41
+ if (!validName(options.name) ||
42
+ !headers.length ||
43
+ headers.some((h) => !h.length) ||
44
+ new Set(headers).size !== headers.length)
45
+ fail("E_IMPORT_SCHEMA", span);
46
+ budget.limit("tableColumns", headers.length);
47
+ budget.limit("tableRows", rows.length);
48
+ budget.limit("totalCells", rows.length * headers.length);
49
+ const names = mapping(headers);
50
+ const inferred = headers.map((input, i) => {
51
+ const cells = rows.map((row) => row[i]);
52
+ let type = "text";
53
+ if (json) {
54
+ type = scalarType(cells[0]);
55
+ if (cells.some((c) => scalarType(c) !== type))
56
+ fail("E_IMPORT_SCHEMA", span);
57
+ }
58
+ else if (cells.length &&
59
+ cells.every((c) => c === "true" || c === "false"))
60
+ type = "boolean";
61
+ else if (cells.length &&
62
+ cells.every((c) => typeof c === "string" &&
63
+ numberToken.test(c) &&
64
+ c.length <= 128 &&
65
+ Number.isFinite(Number(c))))
66
+ type = "number";
67
+ return { input, name: names[i], type };
68
+ });
69
+ const columns = options.columns ? [...options.columns] : inferred;
70
+ if (columns.length !== headers.length ||
71
+ new Set(columns.map((c) => c.name)).size !== columns.length ||
72
+ columns.some((c, i) => c.input !== headers[i] ||
73
+ !validName(c.name) ||
74
+ !["text", "number", "boolean"].includes(c.type)))
75
+ fail("E_IMPORT_SCHEMA", span);
76
+ const lines = [
77
+ `@data ${options.name}`,
78
+ columns.map((c) => `${c.name}:${c.type}`).join(" | "),
79
+ ];
80
+ for (const row of rows) {
81
+ budget.charge();
82
+ if (row.length !== headers.length)
83
+ fail("E_IMPORT_SCHEMA", span);
84
+ lines.push(columns
85
+ .map((column, i) => {
86
+ const value = row[i];
87
+ if (json &&
88
+ column.type !== "text" &&
89
+ scalarType(value) !== column.type)
90
+ fail("E_IMPORT_SCHEMA", span);
91
+ if (column.type === "text") {
92
+ const text = String(value);
93
+ budget.limit("textValueUnits", text.length);
94
+ return JSON.stringify(text);
95
+ }
96
+ if (column.type === "boolean") {
97
+ if (value !== true &&
98
+ value !== false &&
99
+ value !== "true" &&
100
+ value !== "false")
101
+ fail("E_IMPORT_SCHEMA", span);
102
+ return String(value);
103
+ }
104
+ const token = String(value);
105
+ if (!numberToken.test(token) ||
106
+ token.length > 128 ||
107
+ !Number.isFinite(Number(token)))
108
+ fail("E_IMPORT_SCHEMA", span);
109
+ return String(Number(token));
110
+ })
111
+ .join(" | "));
112
+ }
113
+ lines.push("@end", "");
114
+ const source = lines.join("\n");
115
+ // A candidate must already fit the source parser's limits and string grammar.
116
+ const parsed = parse({ uri: span.uri, profile: "tinta", text: source });
117
+ if (!parsed.ok)
118
+ fail("E_IMPORT_SCHEMA", span);
119
+ return { source, columns, rowCount: rows.length };
120
+ }
121
+ export function importCsv(bytes, options) {
122
+ return attempt(() => {
123
+ const budget = new Budget(span, "decode");
124
+ const source = decode(bytes, budget), records = [];
125
+ let row = [], field = "", quoted = false, closed = false, start = true;
126
+ const cell = () => {
127
+ budget.limit("textValueUnits", field.length);
128
+ row.push(field);
129
+ budget.limit("tableColumns", row.length);
130
+ field = "";
131
+ closed = false;
132
+ start = true;
133
+ };
134
+ const record = () => {
135
+ const blank = row.length === 0 && field === "" && !closed;
136
+ cell();
137
+ if (blank)
138
+ fail("E_IMPORT_SCHEMA", span);
139
+ records.push(row);
140
+ budget.limit("tableRows", records.length - 1);
141
+ budget.limit("totalCells", Math.max(0, records.length - 1) * row.length);
142
+ row = [];
143
+ };
144
+ for (let i = 0; i < source.length; i++) {
145
+ budget.charge();
146
+ const char = source[i];
147
+ if (quoted) {
148
+ if (char === '"') {
149
+ if (source[i + 1] === '"') {
150
+ field += '"';
151
+ i++;
152
+ }
153
+ else {
154
+ quoted = false;
155
+ closed = true;
156
+ }
157
+ }
158
+ else
159
+ field += char;
160
+ }
161
+ else if (char === ",")
162
+ cell();
163
+ else if (char === "\n" || char === "\r") {
164
+ record();
165
+ if (char === "\r" && source[i + 1] === "\n")
166
+ i++;
167
+ }
168
+ else if (char === '"' && start) {
169
+ quoted = true;
170
+ start = false;
171
+ }
172
+ else {
173
+ if (closed || char === '"')
174
+ fail("E_IMPORT_SCHEMA", span);
175
+ field += char;
176
+ start = false;
177
+ }
178
+ budget.limit("textValueUnits", field.length);
179
+ }
180
+ if (quoted)
181
+ fail("E_IMPORT_SCHEMA", span);
182
+ if (field.length || row.length || closed)
183
+ record();
184
+ const headers = records.shift();
185
+ if (!headers || records.some((r) => r.length !== headers.length))
186
+ fail("E_IMPORT_SCHEMA", span);
187
+ return candidate(headers, records, options, budget, false);
188
+ });
189
+ }
190
+ /** A bounded JSON reader detects repeated keys before they can be erased. */
191
+ export function importJson(bytes, options) {
192
+ return attempt(() => {
193
+ const budget = new Budget(span, "decode");
194
+ const source = decode(bytes, budget);
195
+ let offset = 0;
196
+ const space = () => {
197
+ while (/[ \t\r\n]/.test(source[offset] ?? "x"))
198
+ offset++;
199
+ };
200
+ const token = (expected) => {
201
+ space();
202
+ if (source[offset++] !== expected)
203
+ fail("E_IMPORT_SCHEMA", span);
204
+ };
205
+ const string = () => {
206
+ space();
207
+ const begin = offset;
208
+ token('"');
209
+ let escaped = false;
210
+ while (offset < source.length) {
211
+ budget.charge();
212
+ const char = source[offset++];
213
+ if (char === '"' && !escaped) {
214
+ try {
215
+ const value = JSON.parse(source.slice(begin, offset));
216
+ if (typeof value !== "string")
217
+ fail("E_IMPORT_SCHEMA", span);
218
+ encodeUtf8(value);
219
+ budget.limit("textValueUnits", value.length);
220
+ return value;
221
+ }
222
+ catch (error) {
223
+ if (error instanceof Failure)
224
+ throw error;
225
+ fail("E_IMPORT_SCHEMA", span);
226
+ }
227
+ }
228
+ escaped = char === "\\" && !escaped;
229
+ }
230
+ fail("E_IMPORT_SCHEMA", span);
231
+ };
232
+ const scalar = () => {
233
+ space();
234
+ if (source[offset] === '"')
235
+ return string();
236
+ const begin = offset;
237
+ while (offset < source.length && !/[,}\s]/.test(source[offset]))
238
+ offset++;
239
+ const raw = source.slice(begin, offset);
240
+ if (raw === "true" || raw === "false")
241
+ return raw === "true";
242
+ if (!/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/.test(raw) ||
243
+ raw.length > 128 ||
244
+ !Number.isFinite(Number(raw)))
245
+ fail("E_IMPORT_SCHEMA", span);
246
+ return Number(raw) || 0;
247
+ };
248
+ const rows = [];
249
+ let headers;
250
+ token("[");
251
+ space();
252
+ if (source[offset] === "]")
253
+ fail("E_IMPORT_SCHEMA", span);
254
+ while (true) {
255
+ token("{");
256
+ space();
257
+ const fields = new Map();
258
+ if (source[offset] === "}")
259
+ fail("E_IMPORT_SCHEMA", span);
260
+ while (true) {
261
+ const key = string();
262
+ if (fields.has(key))
263
+ fail("E_IMPORT_SCHEMA", span);
264
+ token(":");
265
+ fields.set(key, scalar());
266
+ budget.limit("tableColumns", fields.size);
267
+ space();
268
+ if (source[offset] === "}") {
269
+ offset++;
270
+ break;
271
+ }
272
+ token(",");
273
+ }
274
+ headers ??= [...fields.keys()];
275
+ if (fields.size !== headers.length ||
276
+ headers.some((key) => !fields.has(key)))
277
+ fail("E_IMPORT_SCHEMA", span);
278
+ rows.push(headers.map((key) => fields.get(key)));
279
+ budget.limit("tableRows", rows.length);
280
+ budget.limit("totalCells", rows.length * headers.length);
281
+ space();
282
+ if (source[offset] === "]") {
283
+ offset++;
284
+ break;
285
+ }
286
+ token(",");
287
+ }
288
+ space();
289
+ if (offset !== source.length)
290
+ fail("E_IMPORT_SCHEMA", span);
291
+ return candidate(headers, rows, options, budget, true);
292
+ });
293
+ }
@@ -0,0 +1,7 @@
1
+ export { compile } from "./compiler.js";
2
+ export { createSession } from "./session.js";
3
+ export { encodeProgram, decodeProgram } from "./codec.js";
4
+ export { importCsv, importJson } from "./imports.js";
5
+ export { chartPresentation, legendKey, numericDomain, } from "./chart-presentation.js";
6
+ export { analyzeProgram } from "./analysis.js";
7
+ export type { AnalyzedBinding } from "./analysis.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { compile } from "./compiler.js";
2
+ export { createSession } from "./session.js";
3
+ export { encodeProgram, decodeProgram } from "./codec.js";
4
+ export { importCsv, importJson } from "./imports.js";
5
+ export { chartPresentation, legendKey, numericDomain, } from "./chart-presentation.js";
6
+ export { analyzeProgram } from "./analysis.js";
@@ -0,0 +1,3 @@
1
+ import { Budget } from "./common.js";
2
+ /** Reject cycles, accessors and excessive structure before recursive schema traversal. */
3
+ export declare function inspect(value: unknown, budget: Budget): void;
@@ -0,0 +1,42 @@
1
+ import { Budget, fail } from "./common.js";
2
+ /** Reject cycles, accessors and excessive structure before recursive schema traversal. */
3
+ export function inspect(value, budget) {
4
+ const stack = [{ value, depth: 0 }], seen = new Set();
5
+ let nodes = 0, units = 0;
6
+ while (stack.length) {
7
+ const item = stack.pop();
8
+ if (item.leave) {
9
+ seen.delete(item.value);
10
+ continue;
11
+ }
12
+ budget.charge();
13
+ if (++nodes > 2000000 || item.depth > 256)
14
+ fail("E_LIMIT", budget.span, "decode");
15
+ if (typeof item.value === "string") {
16
+ units += item.value.length;
17
+ if (units > 41943040)
18
+ fail("E_LIMIT", budget.span, "decode");
19
+ }
20
+ if (!item.value || typeof item.value !== "object")
21
+ continue;
22
+ if (seen.has(item.value))
23
+ fail("E_PROTOCOL", budget.span, "decode");
24
+ seen.add(item.value);
25
+ stack.push({ ...item, leave: true });
26
+ const proto = Object.getPrototypeOf(item.value);
27
+ if (proto !== Object.prototype &&
28
+ proto !== Array.prototype &&
29
+ proto !== null)
30
+ fail("E_PROTOCOL", budget.span, "decode");
31
+ for (const key of Reflect.ownKeys(item.value)) {
32
+ if (typeof key !== "string")
33
+ fail("E_PROTOCOL", budget.span, "decode");
34
+ if (Array.isArray(item.value) && key === "length")
35
+ continue;
36
+ const descriptor = Object.getOwnPropertyDescriptor(item.value, key);
37
+ if (!Object.hasOwn(descriptor, "value") || !descriptor.enumerable)
38
+ fail("E_PROTOCOL", budget.span, "decode");
39
+ stack.push({ value: descriptor.value, depth: item.depth + 1 });
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,19 @@
1
+ export function validate(data: any, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors, }?: {
2
+ instancePath?: string | undefined;
3
+ rootData?: any;
4
+ dynamicAnchors?: {} | undefined;
5
+ }): boolean;
6
+ export namespace validate {
7
+ namespace evaluated {
8
+ let props: boolean;
9
+ let dynamicProps: boolean;
10
+ let dynamicItems: boolean;
11
+ }
12
+ }
13
+ export default validate20;
14
+ declare function validate20(data: any, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors, }?: {
15
+ instancePath?: string | undefined;
16
+ rootData?: any;
17
+ dynamicAnchors?: {} | undefined;
18
+ }): boolean;
19
+ declare namespace validate20 { }