@phin-tech/dottie 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/dottie.js CHANGED
@@ -1,84 +1,334 @@
1
- /*! dottie.js 0.10.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.11.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
4
4
  var __publicField = (obj, key, value2) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value2);
5
5
 
6
- // src/state.ts
6
+ // src/expression.ts
7
+ var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
8
+ var FUNCTIONS = { min: null, max: null, abs: 1, floor: 1, ceil: 1, round: 1, clamp: 3 };
7
9
  var MAX_NAME = 64;
8
- function isName(name) {
9
- return name.length <= MAX_NAME && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
10
+ var truthy = (v) => typeof v === "boolean" ? v : v !== 0 && !Number.isNaN(v);
11
+ var num = (v) => typeof v === "boolean" ? v ? 1 : 0 : v;
12
+ var parsed = /* @__PURE__ */ new Map();
13
+ function parseExpr(text) {
14
+ const hit = parsed.get(text);
15
+ if (hit !== void 0) return hit;
16
+ const p = new Parser(text);
17
+ let e = p.or();
18
+ if (e && !p.atEnd()) e = null;
19
+ if (parsed.size > 4096) parsed.clear();
20
+ parsed.set(text, e);
21
+ return e;
10
22
  }
11
- var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
12
- var OPS = [">=", "<=", "==", "!=", ">", "<"];
13
- function metricTerm(term) {
14
- const t = trim(term);
15
- if (!t.includes("(")) {
16
- const op2 = t.search(/[>=<!]/);
17
- if (op2 < 0) return null;
18
- const name2 = trim(t.slice(0, op2));
19
- if (!METRIC_NAMES.includes(name2)) return null;
20
- const m = metricTerm(name2 + "() " + t.slice(op2));
21
- return m ? { ...m, target: "" } : null;
22
- }
23
- const open = t.indexOf("("), close = t.indexOf(")");
24
- if (open < 0 || close < 0 || open >= close) return null;
25
- const name = trim(t.slice(0, open)), target = trim(t.slice(open + 1, close));
26
- let rest = trim(t.slice(close + 1));
27
- if (!METRIC_NAMES.includes(name) || target.includes("(")) return null;
28
- const op = OPS.find((o) => rest.startsWith(o));
29
- if (op === void 0) return null;
30
- rest = trim(rest.slice(op.length));
31
- if (!/^-?[0-9]+(\.[0-9]+)?$/.test(rest)) return null;
32
- return { metric: name, target, op, value: Number(rest) };
33
- }
34
- function passes(m, v) {
35
- switch (m.op) {
36
- case ">":
37
- return v > m.value;
38
- case ">=":
39
- return v >= m.value;
40
- case "<":
41
- return v < m.value;
42
- case "<=":
43
- return v <= m.value;
44
- case "==":
45
- return v === m.value;
46
- default:
47
- return v !== m.value;
23
+ function evaluate(e, state, metric = null) {
24
+ switch (e.k) {
25
+ case "lit":
26
+ return e.v;
27
+ case "name":
28
+ return state[e.name] ?? false;
29
+ case "metric":
30
+ return metric?.(e.metric, e.target) ?? NaN;
31
+ case "not":
32
+ return !truthy(evaluate(e.e, state, metric));
33
+ case "neg":
34
+ return -num(evaluate(e.e, state, metric));
35
+ case "call": {
36
+ const xs = e.args.map((a) => num(evaluate(a, state, metric)));
37
+ const fold = (better) => {
38
+ if (xs.some(Number.isNaN)) return NaN;
39
+ let out = xs[0];
40
+ for (const x of xs.slice(1)) if (better(x, out)) out = x;
41
+ return out;
42
+ };
43
+ switch (e.fn) {
44
+ case "min":
45
+ return fold((a, b) => a < b);
46
+ case "max":
47
+ return fold((a, b) => a > b);
48
+ case "abs":
49
+ return Math.abs(xs[0]);
50
+ case "floor":
51
+ return Math.floor(xs[0]);
52
+ case "ceil":
53
+ return Math.ceil(xs[0]);
54
+ case "round":
55
+ return Math.floor(xs[0] + 0.5);
56
+ default:
57
+ if (xs.some(Number.isNaN)) return NaN;
58
+ return xs[0] < xs[1] ? xs[1] : xs[0] > xs[2] ? xs[2] : xs[0];
59
+ }
60
+ }
61
+ case "bin": {
62
+ if (e.op === "&&") return truthy(evaluate(e.l, state, metric)) && truthy(evaluate(e.r, state, metric));
63
+ if (e.op === "||") return truthy(evaluate(e.l, state, metric)) || truthy(evaluate(e.r, state, metric));
64
+ const a = evaluate(e.l, state, metric), b = evaluate(e.r, state, metric);
65
+ const x = num(a), y = num(b);
66
+ switch (e.op) {
67
+ case "+":
68
+ return x + y;
69
+ case "-":
70
+ return x - y;
71
+ case "*":
72
+ return x * y;
73
+ case "/":
74
+ return x / y;
75
+ case "%":
76
+ return x % y;
77
+ }
78
+ if (Number.isNaN(x) || Number.isNaN(y)) return false;
79
+ switch (e.op) {
80
+ case "<":
81
+ return x < y;
82
+ case "<=":
83
+ return x <= y;
84
+ case ">":
85
+ return x > y;
86
+ case ">=":
87
+ return x >= y;
88
+ case "==":
89
+ return typeof a === "boolean" && typeof b === "boolean" ? a === b : x === y;
90
+ default:
91
+ return typeof a === "boolean" && typeof b === "boolean" ? a !== b : x !== y;
92
+ }
93
+ }
48
94
  }
49
95
  }
50
- function mentionsMetric(condition) {
51
- return condition.split("||").some((any) => any.split("&&").some((all) => metricTerm(all) !== null));
96
+ function exprNames(e) {
97
+ const out = [];
98
+ const walk = (x) => {
99
+ switch (x.k) {
100
+ case "name":
101
+ if (!out.includes(x.name)) out.push(x.name);
102
+ break;
103
+ case "not":
104
+ case "neg":
105
+ walk(x.e);
106
+ break;
107
+ case "bin":
108
+ walk(x.l);
109
+ walk(x.r);
110
+ break;
111
+ case "call":
112
+ x.args.forEach(walk);
113
+ break;
114
+ }
115
+ };
116
+ walk(e);
117
+ return out;
52
118
  }
53
- function conditionNames(condition) {
119
+ function exprMetrics(e) {
54
120
  const out = [];
55
- for (const any of condition.split("||")) {
56
- for (const all of any.split("&&")) {
57
- if (metricTerm(all) !== null) continue;
58
- let term = trim(all);
59
- if (term.startsWith("!")) term = trim(term.slice(1));
60
- if (!isName(term)) return null;
61
- out.push(term);
121
+ const walk = (x) => {
122
+ switch (x.k) {
123
+ case "metric":
124
+ out.push({ metric: x.metric, target: x.target });
125
+ break;
126
+ case "not":
127
+ case "neg":
128
+ walk(x.e);
129
+ break;
130
+ case "bin":
131
+ walk(x.l);
132
+ walk(x.r);
133
+ break;
134
+ case "call":
135
+ x.args.forEach(walk);
136
+ break;
62
137
  }
63
- }
138
+ };
139
+ walk(e);
64
140
  return out;
65
141
  }
142
+ var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
143
+ var isLetter = (c) => c !== void 0 && (c === "_" || c >= "a" && c <= "z" || c >= "A" && c <= "Z");
144
+ var Parser = class {
145
+ constructor(text) {
146
+ __publicField(this, "s");
147
+ __publicField(this, "i", 0);
148
+ this.s = [...text];
149
+ }
150
+ atEnd() {
151
+ this.skip();
152
+ return this.i === this.s.length;
153
+ }
154
+ skip() {
155
+ while (this.i < this.s.length && " \n\r".includes(this.s[this.i])) this.i++;
156
+ }
157
+ eat(token) {
158
+ this.skip();
159
+ const t = [...token];
160
+ if (this.i + t.length > this.s.length) return false;
161
+ for (let k = 0; k < t.length; k++) if (this.s[this.i + k] !== t[k]) return false;
162
+ if (t.length === 1 && this.s[this.i + 1] === "=" && "<>!=".includes(t[0])) return false;
163
+ this.i += t.length;
164
+ return true;
165
+ }
166
+ or() {
167
+ let l = this.and();
168
+ if (!l) return null;
169
+ while (this.eat("||")) {
170
+ const r = this.and();
171
+ if (!r) return null;
172
+ l = { k: "bin", op: "||", l, r };
173
+ }
174
+ return l;
175
+ }
176
+ and() {
177
+ let l = this.compare();
178
+ if (!l) return null;
179
+ while (this.eat("&&")) {
180
+ const r = this.compare();
181
+ if (!r) return null;
182
+ l = { k: "bin", op: "&&", l, r };
183
+ }
184
+ return l;
185
+ }
186
+ compare() {
187
+ const l = this.sum();
188
+ if (!l) return null;
189
+ for (const op of ["<=", ">=", "==", "!=", "<", ">"]) {
190
+ if (this.eat(op)) {
191
+ const r = this.sum();
192
+ return r ? { k: "bin", op, l, r } : null;
193
+ }
194
+ }
195
+ return l;
196
+ }
197
+ sum() {
198
+ let l = this.product();
199
+ if (!l) return null;
200
+ for (; ; ) {
201
+ const op = this.eat("+") ? "+" : this.eat("-") ? "-" : null;
202
+ if (!op) return l;
203
+ const r = this.product();
204
+ if (!r) return null;
205
+ l = { k: "bin", op, l, r };
206
+ }
207
+ }
208
+ product() {
209
+ let l = this.unary();
210
+ if (!l) return null;
211
+ for (; ; ) {
212
+ const op = ["*", "/", "%"].find((o) => this.eat(o));
213
+ if (!op) return l;
214
+ const r = this.unary();
215
+ if (!r) return null;
216
+ l = { k: "bin", op, l, r };
217
+ }
218
+ }
219
+ unary() {
220
+ if (this.eat("!")) {
221
+ const e = this.unary();
222
+ return e ? { k: "not", e } : null;
223
+ }
224
+ if (this.eat("-")) {
225
+ const e = this.unary();
226
+ return e ? { k: "neg", e } : null;
227
+ }
228
+ return this.primary();
229
+ }
230
+ primary() {
231
+ this.skip();
232
+ if (this.i >= this.s.length) return null;
233
+ if (this.eat("(")) {
234
+ const e = this.or();
235
+ return e && this.eat(")") ? e : null;
236
+ }
237
+ const s = this.s;
238
+ const c = s[this.i];
239
+ if (isDigit(c)) {
240
+ const start3 = this.i;
241
+ while (isDigit(s[this.i])) this.i++;
242
+ if (s[this.i] === "." && isDigit(s[this.i + 1])) {
243
+ this.i++;
244
+ while (isDigit(s[this.i])) this.i++;
245
+ }
246
+ return { k: "lit", v: Number(s.slice(start3, this.i).join("")) };
247
+ }
248
+ if (!isLetter(c)) return null;
249
+ const start2 = this.i;
250
+ while (isLetter(s[this.i]) || isDigit(s[this.i])) this.i++;
251
+ const name = s.slice(start2, this.i).join("");
252
+ if (name.length > MAX_NAME) return null;
253
+ let j = this.i;
254
+ while (s[j] === " " || s[j] === " ") j++;
255
+ const called = s[j] === "(";
256
+ if (METRIC_NAMES.includes(name)) {
257
+ if (!called) return { k: "metric", metric: name, target: "" };
258
+ this.eat("(");
259
+ const from = this.i;
260
+ while (this.i < s.length && s[this.i] !== ")" && s[this.i] !== "(") this.i++;
261
+ if (s[this.i] !== ")") return null;
262
+ const target = s.slice(from, this.i).join("").replace(/^[ \t]+|[ \t]+$/g, "");
263
+ this.i++;
264
+ return { k: "metric", metric: name, target };
265
+ }
266
+ if (name in FUNCTIONS) {
267
+ if (!called || !this.eat("(")) return null;
268
+ const args = [];
269
+ if (!this.eat(")")) {
270
+ do {
271
+ const a = this.or();
272
+ if (!a) return null;
273
+ args.push(a);
274
+ } while (this.eat(","));
275
+ if (!this.eat(")")) return null;
276
+ }
277
+ const arity = FUNCTIONS[name];
278
+ if (arity !== null ? args.length !== arity : args.length === 0) return null;
279
+ return { k: "call", fn: name, args };
280
+ }
281
+ if (called) return null;
282
+ if (name === "true") return { k: "lit", v: true };
283
+ if (name === "false") return { k: "lit", v: false };
284
+ return { k: "name", name };
285
+ }
286
+ };
287
+ function describeVar(v) {
288
+ return typeof v === "boolean" ? v ? "true" : "false" : formatTwo(v);
289
+ }
290
+ function formatTwo(v) {
291
+ const n = Math.floor(Math.abs(v) * 100 + 0.5);
292
+ if (!(n < 1e15)) return v < 0 ? "-\u221E" : "\u221E";
293
+ const whole = Math.floor(n / 100), part = n - whole * 100;
294
+ let s = String(whole);
295
+ if (part > 0) s += "." + String(part).padStart(2, "0").replace(/0+$/, "");
296
+ return (v < 0 && n > 0 ? "-" : "") + s;
297
+ }
298
+ function filling(text, values) {
299
+ if (!text.includes("{")) return text;
300
+ let out = "", rest = text;
301
+ for (let open = rest.indexOf("{"); open >= 0; open = rest.indexOf("{")) {
302
+ out += rest.slice(0, open);
303
+ const close = rest.indexOf("}", open);
304
+ if (close < 0) {
305
+ out += rest.slice(open);
306
+ rest = "";
307
+ break;
308
+ }
309
+ const inner = rest.slice(open + 1, close);
310
+ const e = inner.includes("{") ? null : parseExpr(inner);
311
+ const names = e ? exprNames(e) : [];
312
+ if (e && exprMetrics(e).length === 0 && names.length > 0 && names.every((n) => values[n] !== void 0)) out += describeVar(evaluate(e, values));
313
+ else out += rest.slice(open, close + 1);
314
+ rest = rest.slice(close + 1);
315
+ }
316
+ return out + rest;
317
+ }
318
+
319
+ // src/state.ts
320
+ var MAX_NAME2 = 64;
321
+ function isName(name) {
322
+ return name.length <= MAX_NAME2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
323
+ }
324
+ function mentionsMetric(condition) {
325
+ const e = parseExpr(condition);
326
+ return e !== null && exprMetrics(e).length > 0;
327
+ }
66
328
  function holds(condition, state, metric = null) {
67
- if (conditionNames(condition) === null) return false;
68
- return condition.split("||").some((any) => any.split("&&").every((all) => {
69
- const m = metricTerm(all);
70
- if (m !== null) {
71
- const v = metric ? metric(m) : null;
72
- return v !== null && passes(m, v);
73
- }
74
- let term = trim(all);
75
- let negated = false;
76
- if (term.startsWith("!")) {
77
- negated = true;
78
- term = trim(term.slice(1));
79
- }
80
- return (state[term] ?? false) !== negated;
81
- }));
329
+ const e = parseExpr(condition);
330
+ if (!e) return false;
331
+ return truthy(evaluate(e, state, metric ? (m, target) => metric({ metric: m, target, op: "", value: 0 }) : null));
82
332
  }
83
333
  function stateValues(page, choice) {
84
334
  if (choice === "all") return null;
@@ -117,7 +367,7 @@ function applyOverride(o, e) {
117
367
  return out;
118
368
  }
119
369
  function changesState(c) {
120
- return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
370
+ return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0 || !!c.run);
121
371
  }
122
372
  function applyClick(c, state, initial = {}) {
123
373
  let out = { ...state };
@@ -126,12 +376,15 @@ function applyClick(c, state, initial = {}) {
126
376
  for (const k of Object.keys(state)) out[k] = false;
127
377
  Object.assign(out, initial);
128
378
  }
129
- for (const [k, v] of Object.entries(c.set ?? {})) out[k] = v;
130
- for (const k of c.toggle ?? []) out[k] = !(out[k] ?? false);
379
+ const before = { ...out };
380
+ for (const [k, v] of Object.entries(c.set ?? {})) out[k] = clickValue(v, before, before[k]);
381
+ for (const k of c.toggle ?? []) out[k] = !truthy(out[k] ?? false);
131
382
  return out;
132
383
  }
133
- function trim(s) {
134
- return s.replace(/^[ \t ]+|[ \t ]+$/g, "");
384
+ function clickValue(v, state, current) {
385
+ if (typeof v !== "string") return v;
386
+ const e = parseExpr(v);
387
+ return e ? evaluate(e, state) : current ?? false;
135
388
  }
136
389
 
137
390
  // src/theme.ts
@@ -193,12 +446,12 @@ function resolvingPalette(page) {
193
446
  }
194
447
 
195
448
  // src/model.ts
196
- var SUPPORTED_VERSION = 2;
449
+ var SUPPORTED_VERSION = 3;
197
450
  var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
198
451
  var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
199
452
  var KNOWN_FIELDS = {
200
453
  file: ["version", "pages", "images"],
201
- page: ["id", "name", "layers", "elements", "theme", "customTheme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
454
+ page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
202
455
  flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
203
456
  flowOverride: ["when", "volume"],
204
457
  symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
@@ -267,7 +520,7 @@ var KNOWN_FIELDS = {
267
520
  repeat: ["start", "end", "count"],
268
521
  cameraKey: ["t", "x", "y", "width", "height", "ease"],
269
522
  marker: ["t", "name", "loop"],
270
- click: ["scene", "url", "set", "toggle", "reset"],
523
+ click: ["scene", "url", "set", "toggle", "reset", "run"],
271
524
  hover: ["stroke", "fill", "opacity"],
272
525
  override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
273
526
  };
@@ -281,7 +534,7 @@ var inkOnDark = { r: 0.93, g: 0.93, b: 0.94, a: 1 };
281
534
  var DottieFormatError = class extends Error {
282
535
  };
283
536
  var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
284
- var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
537
+ var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
285
538
  var str = (v) => typeof v === "string" ? v : void 0;
286
539
  var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
287
540
  var bool = (v) => typeof v === "boolean" ? v : void 0;
@@ -305,9 +558,9 @@ function webLink(url) {
305
558
  }
306
559
  function rgba(v) {
307
560
  if (!isObj(v)) return void 0;
308
- const r = num(v.r), g = num(v.g), b = num(v.b);
561
+ const r = num2(v.r), g = num2(v.g), b = num2(v.b);
309
562
  if (r === void 0 || g === void 0 || b === void 0) return void 0;
310
- return { r, g, b, a: num(v.a) ?? 1 };
563
+ return { r, g, b, a: num2(v.a) ?? 1 };
311
564
  }
312
565
  var Reader = class {
313
566
  constructor() {
@@ -336,15 +589,15 @@ var Reader = class {
336
589
  stroke: rgba(s.stroke) ?? { ...black },
337
590
  fill: rgba(s.fill) ?? { ...clear },
338
591
  fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
339
- strokeWidth: num(s.strokeWidth) ?? 2,
592
+ strokeWidth: num2(s.strokeWidth) ?? 2,
340
593
  dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
341
- roughness: num(s.roughness) ?? 1,
342
- bowing: num(s.bowing) ?? 1,
343
- fontSize: num(s.fontSize) ?? 18,
594
+ roughness: num2(s.roughness) ?? 1,
595
+ bowing: num2(s.bowing) ?? 1,
596
+ fontSize: num2(s.fontSize) ?? 18,
344
597
  font: str(s.font) || null,
345
- gridSize: num(s.gridSize) ?? null,
346
- opacity: num(s.opacity) ?? null,
347
- dashOffset: num(s.dashOffset) ?? null,
598
+ gridSize: num2(s.gridSize) ?? null,
599
+ opacity: num2(s.opacity) ?? null,
600
+ dashOffset: num2(s.dashOffset) ?? null,
348
601
  textColor: rgba(s.textColor) ?? null,
349
602
  named: this.colorNames(s.named)
350
603
  };
@@ -378,13 +631,13 @@ var Reader = class {
378
631
  endpoint(v) {
379
632
  const e = isObj(v) ? v : {};
380
633
  const port = str(e.port);
381
- const portIndex = num(e.portIndex);
634
+ const portIndex = num2(e.portIndex);
382
635
  return {
383
636
  elementID: uuid(e.elementID) ?? null,
384
637
  port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
385
638
  portIndex: portIndex !== void 0 && Number.isInteger(portIndex) && portIndex >= 0 ? portIndex : null,
386
- x: num(e.x) ?? 0,
387
- y: num(e.y) ?? 0
639
+ x: num2(e.x) ?? 0,
640
+ y: num2(e.y) ?? 0
388
641
  };
389
642
  }
390
643
  connector(v) {
@@ -403,7 +656,7 @@ var Reader = class {
403
656
  endHead: head(v.endHead, "arrow"),
404
657
  waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
405
658
  this.fields(w, "waypoint");
406
- return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
659
+ return { x: num2(w.x) ?? 0, y: num2(w.y) ?? 0 };
407
660
  }) : null,
408
661
  hops: typeof v.hops === "boolean" ? v.hops : null,
409
662
  avoid: typeof v.avoid === "boolean" ? v.avoid : null,
@@ -439,20 +692,20 @@ var Reader = class {
439
692
  id,
440
693
  kind,
441
694
  layerID,
442
- x: num(v.x) ?? 0,
443
- y: num(v.y) ?? 0,
444
- width: num(v.width) ?? 0,
445
- height: num(v.height) ?? 0,
695
+ x: num2(v.x) ?? 0,
696
+ y: num2(v.y) ?? 0,
697
+ width: num2(v.width) ?? 0,
698
+ height: num2(v.height) ?? 0,
446
699
  style: this.style(v.style),
447
- seed: (num(v.seed) ?? 1) >>> 0,
700
+ seed: (num2(v.seed) ?? 1) >>> 0,
448
701
  text: str(v.text) ?? null,
449
702
  connector: this.connector(v.connector),
450
703
  groupID: uuid(v.groupID) ?? null,
451
- points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num(p.x) ?? 0, y: num(p.y) ?? 0 })) : null,
704
+ points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num2(p.x) ?? 0, y: num2(p.y) ?? 0 })) : null,
452
705
  path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
453
706
  subpaths,
454
707
  evenOdd: bool(v.evenOdd) ?? null,
455
- rotation: num(v.rotation) ?? null,
708
+ rotation: num2(v.rotation) ?? null,
456
709
  link: uuid(v.link) ?? null,
457
710
  url: str(v.url) ?? null,
458
711
  anchor: str(v.anchor) ?? null,
@@ -462,18 +715,18 @@ var Reader = class {
462
715
  overrides: this.overrides(v.overrides),
463
716
  symbol: uuid(v.symbol) ?? null,
464
717
  playback: this.playback(v.playback),
465
- offset: num(v.offset) ?? null,
718
+ offset: num2(v.offset) ?? null,
466
719
  args: strings(v.args),
467
720
  image: this.image(v.image, images),
468
- reveal: num(v.reveal) ?? null,
721
+ reveal: num2(v.reveal) ?? null,
469
722
  ports: Array.isArray(v.ports) ? v.ports.map((p) => {
470
723
  const o = isObj(p) ? p : {};
471
- return { x: num(o.x) ?? 0, y: num(o.y) ?? 0 };
724
+ return { x: num2(o.x) ?? 0, y: num2(o.y) ?? 0 };
472
725
  }) : null,
473
726
  labelRect: this.labelRect(v.labelRect),
474
727
  extension: this.extension(v.extension),
475
- capacity: num(v.capacity) ?? null,
476
- delay: num(v.delay) ?? null,
728
+ capacity: num2(v.capacity) ?? null,
729
+ delay: num2(v.delay) ?? null,
477
730
  queue: v.queue === true ? true : null,
478
731
  gauge: this.gauge(v.gauge),
479
732
  containerID: uuid(v.containerID) ?? null,
@@ -498,7 +751,7 @@ var Reader = class {
498
751
  labelRect(v) {
499
752
  if (!isObj(v)) return null;
500
753
  this.fields(v, "normalizedRect");
501
- return { x: num(v.x) ?? 0, y: num(v.y) ?? 0, width: num(v.width) ?? 0, height: num(v.height) ?? 0 };
754
+ return { x: num2(v.x) ?? 0, y: num2(v.y) ?? 0, width: num2(v.width) ?? 0, height: num2(v.height) ?? 0 };
502
755
  }
503
756
  extension(v) {
504
757
  if (!isObj(v)) return null;
@@ -519,7 +772,7 @@ var Reader = class {
519
772
  variables(v) {
520
773
  if (!isObj(v)) return null;
521
774
  const out = {};
522
- for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
775
+ for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
523
776
  return Object.keys(out).length > 0 ? out : null;
524
777
  }
525
778
  click(v) {
@@ -531,12 +784,22 @@ var Reader = class {
531
784
  let set = null;
532
785
  if (isObj(v.set)) {
533
786
  set = {};
534
- for (const [k, b] of Object.entries(v.set)) if (typeof b === "boolean" && isName(k)) set[k] = b;
787
+ for (const [k, b] of Object.entries(v.set)) {
788
+ if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
789
+ }
535
790
  if (Object.keys(set).length === 0) set = null;
536
791
  }
537
792
  const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
538
793
  const toggle = raw ? raw.filter(isName) : null;
539
- return { scene: str(v.scene) ?? null, url: safe, set, toggle: toggle && toggle.length > 0 ? toggle : null, reset: v.reset === true ? true : null };
794
+ const run = str(v.run);
795
+ return {
796
+ scene: str(v.scene) ?? null,
797
+ url: safe,
798
+ set,
799
+ toggle: toggle && toggle.length > 0 ? toggle : null,
800
+ reset: v.reset === true ? true : null,
801
+ run: run !== void 0 && isName(run) ? run : null
802
+ };
540
803
  }
541
804
  playback(v) {
542
805
  const s = str(v);
@@ -552,7 +815,7 @@ var Reader = class {
552
815
  return null;
553
816
  }
554
817
  const size = (x) => {
555
- const n = num(x);
818
+ const n = num2(x);
556
819
  return Math.max(1, n ?? 100);
557
820
  };
558
821
  const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
@@ -589,8 +852,8 @@ var Reader = class {
589
852
  stroke: rgba(o.stroke) ?? null,
590
853
  fill: rgba(o.fill) ?? null,
591
854
  textColor: rgba(o.textColor) ?? null,
592
- opacity: num(o.opacity) ?? null,
593
- strokeWidth: num(o.strokeWidth) ?? null,
855
+ opacity: num2(o.opacity) ?? null,
856
+ strokeWidth: num2(o.strokeWidth) ?? null,
594
857
  dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
595
858
  text: str(o.text) ?? null,
596
859
  reroute,
@@ -604,7 +867,7 @@ var Reader = class {
604
867
  hover(v) {
605
868
  if (!isObj(v)) return null;
606
869
  this.fields(v, "hover");
607
- return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num(v.opacity) ?? null };
870
+ return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num2(v.opacity) ?? null };
608
871
  }
609
872
  ease(v) {
610
873
  const s = str(v);
@@ -618,11 +881,11 @@ var Reader = class {
618
881
  keyframe(v) {
619
882
  if (!isObj(v)) return null;
620
883
  this.fields(v, "keyframe");
621
- const t = num(v.t);
884
+ const t = num2(v.t);
622
885
  if (t === void 0) return null;
623
886
  const k = { t };
624
887
  for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
625
- const n = num(v[p]);
888
+ const n = num2(v[p]);
626
889
  if (n !== void 0) k[p] = n;
627
890
  }
628
891
  const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
@@ -640,39 +903,39 @@ var Reader = class {
640
903
  const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
641
904
  stableSortBy(keys, (k) => k.t);
642
905
  const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
643
- const path = uuid(f.path), start2 = num(f.start), end = num(f.end);
906
+ const path = uuid(f.path), start2 = num2(f.start), end = num2(f.end);
644
907
  if (!path || start2 === void 0 || end === void 0 || start2 < 0 || end > MAX_DURATION) return [];
645
- return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num(f.laps) ?? null }];
908
+ return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num2(f.laps) ?? null }];
646
909
  }) : null;
647
910
  const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
648
- const start2 = num(r.start), end = num(r.end);
911
+ const start2 = num2(r.start), end = num2(r.end);
649
912
  if (start2 === void 0 || end === void 0) return [];
650
- const count = num(r.count);
913
+ const count = num2(r.count);
651
914
  const rep = { start: start2, end, count: count === void 0 ? null : Math.trunc(count) };
652
915
  return repeatIsValid(rep) ? [rep] : [];
653
916
  }) : null;
654
- return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num(v.march)) };
917
+ return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num2(v.march)) };
655
918
  }
656
919
  timeline(v) {
657
920
  if (!isObj(v)) return null;
658
921
  this.fields(v, "timeline");
659
- const boil = num(v.boil);
922
+ const boil = num2(v.boil);
660
923
  const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
661
- const t = num(c.t), x = num(c.x), y = num(c.y), width = num(c.width), height = num(c.height);
924
+ const t = num2(c.t), x = num2(c.x), y = num2(c.y), width = num2(c.width), height = num2(c.height);
662
925
  if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
663
926
  if (!(width > 1 && height > 1 && t >= 0)) return [];
664
927
  return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
665
928
  }) : null;
666
929
  if (camera) stableSortBy(camera, (c) => c.t);
667
930
  const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
668
- const t = num(m.t);
931
+ const t = num2(m.t);
669
932
  return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
670
933
  }) : null;
671
934
  if (markers) stableSortBy(markers, (m) => m.t);
672
- const poster = num(v.poster);
935
+ const poster = num2(v.poster);
673
936
  return {
674
- fps: clampFPS(num(v.fps) ?? 12),
675
- duration: clampTime(num(v.duration) ?? 0),
937
+ fps: clampFPS(num2(v.fps) ?? 12),
938
+ duration: clampTime(num2(v.duration) ?? 0),
676
939
  boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
677
940
  settle: bool(v.settle) ?? null,
678
941
  poster: poster === void 0 ? null : clampTime(poster),
@@ -699,6 +962,7 @@ var Reader = class {
699
962
  elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
700
963
  theme: str(v.theme) ?? null,
701
964
  customTheme: this.customTheme(v.customTheme),
965
+ logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
702
966
  timeline: this.timeline(v.timeline),
703
967
  symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
704
968
  variables: this.variables(v.variables),
@@ -710,7 +974,7 @@ var Reader = class {
710
974
  this.fields(f, "flow");
711
975
  const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
712
976
  const finite = (x) => {
713
- const n = num(x);
977
+ const n = num2(x);
714
978
  return n !== void 0 && Number.isFinite(n) ? n : null;
715
979
  };
716
980
  return {
@@ -750,7 +1014,7 @@ function parseDottie(source) {
750
1014
  }
751
1015
  }
752
1016
  if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
753
- const version = num(json.version);
1017
+ const version = num2(json.version);
754
1018
  if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
755
1019
  if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
756
1020
  const reader = new Reader();
@@ -2998,7 +3262,7 @@ function hopCrossings(e, r, page, index) {
2998
3262
  for (const samples of segments) {
2999
3263
  const along = runningLengths(samples);
3000
3264
  const length = along[along.length - 1];
3001
- const found = [];
3265
+ const found2 = [];
3002
3266
  const near = [];
3003
3267
  const whole = boxOf(samples);
3004
3268
  for (let n = 0; n < count; n++) if (touching(whole, lines[n].box)) near.push(lines[n]);
@@ -3015,12 +3279,12 @@ function hopCrossings(e, r, page, index) {
3015
3279
  if (Math.abs(det) < 1e-9) continue;
3016
3280
  const wx = a.x - p.x, wy = a.y - p.y;
3017
3281
  const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
3018
- if (t > 0 && t < 1 && s >= 0 && s <= 1) found.push(along[i] + t * (along[i + 1] - along[i]));
3282
+ if (t > 0 && t < 1 && s >= 0 && s <= 1) found2.push(along[i] + t * (along[i + 1] - along[i]));
3019
3283
  }
3020
3284
  }
3021
3285
  }
3022
3286
  const kept = [];
3023
- for (const at of found.sort((x, y) => x - y)) {
3287
+ for (const at of found2.sort((x, y) => x - y)) {
3024
3288
  if (at < rad + 2 || at > length - rad - 2) continue;
3025
3289
  if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
3026
3290
  kept.push(at);
@@ -3234,7 +3498,7 @@ var lerpColor = (a, b, u) => ({
3234
3498
  b: mix01(a.b, b.b, u),
3235
3499
  a: mix01(a.a, b.a, u)
3236
3500
  });
3237
- function num2(track, t, p, base2) {
3501
+ function num3(track, t, p, base2) {
3238
3502
  return value(track, t, (k) => k[p], base2, lerpNum);
3239
3503
  }
3240
3504
  function isChanging(track, t) {
@@ -3260,20 +3524,20 @@ function isVisible(track, t) {
3260
3524
  function applyTrack(track, e, t) {
3261
3525
  e = cloneElement(e);
3262
3526
  if (track.keys.some((k) => k.reveal !== void 0)) {
3263
- const r = num2(track, t, "reveal", e.reveal ?? 1);
3527
+ const r = num3(track, t, "reveal", e.reveal ?? 1);
3264
3528
  e.reveal = r >= 1 ? null : Math.max(0, r);
3265
3529
  }
3266
3530
  if (track.keys.some((k) => k.opacity !== void 0)) {
3267
- const o = Math.min(Math.max(num2(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
3531
+ const o = Math.min(Math.max(num3(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
3268
3532
  e.style.opacity = o >= 1 ? null : o;
3269
3533
  }
3270
3534
  if (e.kind === "connector") return e;
3271
- e.x = num2(track, t, "x", e.x);
3272
- e.y = num2(track, t, "y", e.y);
3273
- e.width = Math.max(0, num2(track, t, "width", e.width));
3274
- e.height = Math.max(0, num2(track, t, "height", e.height));
3275
- if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num2(track, t, "rotation", angle(e));
3276
- e.style.strokeWidth = Math.max(0, num2(track, t, "strokeWidth", e.style.strokeWidth));
3535
+ e.x = num3(track, t, "x", e.x);
3536
+ e.y = num3(track, t, "y", e.y);
3537
+ e.width = Math.max(0, num3(track, t, "width", e.width));
3538
+ e.height = Math.max(0, num3(track, t, "height", e.height));
3539
+ if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num3(track, t, "rotation", angle(e));
3540
+ e.style.strokeWidth = Math.max(0, num3(track, t, "strokeWidth", e.style.strokeWidth));
3277
3541
  e.style.stroke = value(track, t, (k) => k.stroke, e.style.stroke, lerpColor);
3278
3542
  e.style.fill = value(track, t, (k) => k.fill, e.style.fill, lerpColor);
3279
3543
  return e;
@@ -3349,7 +3613,8 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
3349
3613
  }
3350
3614
  const flowing = (page.flows ?? []).length > 0;
3351
3615
  const measuring = flowing || page.elements.some((e) => e.gauge || (e.overrides ?? []).some((o) => mentionsMetric(o.when)));
3352
- const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring ? null : EMPTY_TIMELINE);
3616
+ const fills = values !== null && Object.keys(values).length > 0 && page.elements.some((e) => e.text?.includes("{") || (e.overrides ?? []).some((o) => o.text?.includes("{")));
3617
+ const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring && !fills ? null : EMPTY_TIMELINE);
3353
3618
  if (!timeline) return { frame: page, metrics: emptyMetrics() };
3354
3619
  t = clampTime(t);
3355
3620
  const tracks = trackMap(timeline);
@@ -3385,6 +3650,10 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
3385
3650
  const track = tracks.get(e.id);
3386
3651
  let out = track ? applyTrack(track, e, t) : e;
3387
3652
  for (const o of active.get(e.id) ?? []) out = applyOverride(o, out);
3653
+ if (fills && values && out.text) {
3654
+ const text = filling(out.text, values);
3655
+ if (text !== out.text) out = { ...out, text };
3656
+ }
3388
3657
  const list = active.get(e.id);
3389
3658
  if (list) {
3390
3659
  const caps = list.map((o) => o.capacity).filter((c) => c !== null && Number.isFinite(c));
@@ -3702,7 +3971,12 @@ function clickTargets(page, file) {
3702
3971
  let pageOf = null;
3703
3972
  for (const e of page.elements) {
3704
3973
  const c = e.click;
3705
- const state = c && changesState(c) ? { ...c.set ? { set: c.set } : {}, ...c.toggle ? { toggle: c.toggle } : {}, ...c.reset ? { reset: true } : {} } : null;
3974
+ const state = c && changesState(c) ? {
3975
+ ...c.set ? { set: c.set } : {},
3976
+ ...c.toggle ? { toggle: c.toggle } : {},
3977
+ ...c.reset ? { reset: true } : {},
3978
+ ...c.run ? { run: c.run } : {}
3979
+ } : null;
3706
3980
  if (c?.scene) {
3707
3981
  const i = sceneNamed(c.scene, markers);
3708
3982
  if (i >= 0) {
@@ -4725,6 +4999,40 @@ function start() {
4725
4999
  return w;
4726
5000
  }
4727
5001
 
5002
+ // src/logic-loader.ts
5003
+ var scriptSrc = typeof document !== "undefined" ? document.currentScript?.src ?? null : null;
5004
+ var found = null;
5005
+ var present = () => globalThis.DottieLogic;
5006
+ function logicEngine() {
5007
+ found ?? (found = (async () => {
5008
+ if (!present()) {
5009
+ const here = import.meta.url;
5010
+ const base2 = here ?? scriptSrc;
5011
+ if (!base2) throw new Error("No dottie-logic.min.js on the page, and no address to find it from");
5012
+ const url = new URL("./dottie-logic.min.js", base2).href;
5013
+ if (here) await import(
5014
+ /* @vite-ignore */
5015
+ url
5016
+ );
5017
+ else await new Promise((resolve2, reject) => {
5018
+ const s = document.createElement("script");
5019
+ s.src = url;
5020
+ s.onload = () => resolve2();
5021
+ s.onerror = () => reject(new Error(`Couldn't load ${url}`));
5022
+ document.head.append(s);
5023
+ });
5024
+ }
5025
+ const engine = present();
5026
+ if (!engine) throw new Error("dottie-logic.min.js didn't load");
5027
+ await engine.loadLogic();
5028
+ return engine;
5029
+ })());
5030
+ found.catch(() => {
5031
+ found = null;
5032
+ });
5033
+ return found;
5034
+ }
5035
+
4728
5036
  // src/player.ts
4729
5037
  var WHITE = { r: 1, g: 1, b: 1, a: 1 };
4730
5038
  function pageIndex(file, spec) {
@@ -5066,6 +5374,9 @@ var DottiePlayer = class extends ElementBase {
5066
5374
  __publicField(this, "stillTime", null);
5067
5375
  __publicField(this, "visible", true);
5068
5376
  __publicField(this, "observer", null);
5377
+ /** Runs handler `handler` from the page's logic for a click on `id` (DottieDocument.clicked). Clicks take turns, so
5378
+ * each handler sees the state the one before left. */
5379
+ __publicField(this, "logicQueue", Promise.resolve());
5069
5380
  this.root = this.attachShadow({ mode: "open" });
5070
5381
  this.root.innerHTML = `<style>${STYLE}</style><div class="stage" part="stage" role="img"></div>
5071
5382
  <div class="bar" part="controls" hidden>
@@ -5292,7 +5603,9 @@ var DottiePlayer = class extends ElementBase {
5292
5603
  this.scheduleReplay();
5293
5604
  }
5294
5605
  if (!click) return;
5295
- if (click.set || click.toggle || click.reset) {
5606
+ if (click.run && p.page.logic && this.hasAttribute("allow-logic")) {
5607
+ this.runLogic(click.run, id, click.reset === true);
5608
+ } else if (click.set || click.toggle || click.reset) {
5296
5609
  this.setState(applyClick(
5297
5610
  { scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null, reset: click.reset ?? null },
5298
5611
  p.state,
@@ -5314,6 +5627,35 @@ var DottiePlayer = class extends ElementBase {
5314
5627
  }
5315
5628
  }
5316
5629
  }
5630
+ runLogic(handler, id, reset) {
5631
+ const p = this.playback;
5632
+ if (!p) return;
5633
+ const e = p.page.elements.find((x) => x.id === id);
5634
+ const s = p.sceneAt(p.frame);
5635
+ const event = {
5636
+ id,
5637
+ anchor: e?.anchor ?? null,
5638
+ text: e?.text?.split("\n")[0] ?? null,
5639
+ time: this.currentTime,
5640
+ scene: s < 0 ? null : p.sceneNames[s]
5641
+ };
5642
+ this.logicQueue = this.logicQueue.then(async () => {
5643
+ const engine = await logicEngine();
5644
+ const now = this.playback;
5645
+ if (!now || now !== p || !now.page.logic) return;
5646
+ let start2 = now.state;
5647
+ if (reset) {
5648
+ start2 = {};
5649
+ for (const k of Object.keys(now.state)) start2[k] = false;
5650
+ Object.assign(start2, now.page.variables ?? {});
5651
+ }
5652
+ const r = engine.runLogic(now.page.logic, handler, start2, event);
5653
+ if (r.ok) this.setState({ ...start2, ...r.set });
5654
+ else this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: r.error } }));
5655
+ }).catch((err) => {
5656
+ this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: err.message } }));
5657
+ });
5658
+ }
5317
5659
  /** The page's variables now. */
5318
5660
  get variables() {
5319
5661
  return { ...this.playback?.state ?? {} };
@@ -5420,7 +5762,7 @@ var DottiePlayer = class extends ElementBase {
5420
5762
  if (handled) e.preventDefault();
5421
5763
  }
5422
5764
  };
5423
- __publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label"]);
5765
+ __publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label", "allow-logic"]);
5424
5766
  function clickable(target) {
5425
5767
  const el = target;
5426
5768
  return el && typeof el.closest === "function" ? el.closest(".dottie-a") : null;
@@ -5525,7 +5867,7 @@ function render(source, options = {}) {
5525
5867
  function isFile(v) {
5526
5868
  return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
5527
5869
  }
5528
- var VERSION = "0.10.0";
5870
+ var VERSION = "0.11.0";
5529
5871
  export {
5530
5872
  BOIL_DRAWINGS,
5531
5873
  BUNDLED_FONTS,