@yaoxiu/marketing-dsl 1.2.0 → 1.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/dist/index.js CHANGED
@@ -1,308 +1,5 @@
1
- // src/expression.ts
2
- var KEYWORD_LITERALS = {
3
- true: true,
4
- false: false,
5
- null: null,
6
- undefined: void 0
7
- };
8
- var PUNCTUATORS = [
9
- "===",
10
- "!==",
11
- "<=",
12
- ">=",
13
- "==",
14
- "!=",
15
- "&&",
16
- "||",
17
- "(",
18
- ")",
19
- "[",
20
- "]",
21
- ".",
22
- "?",
23
- ":",
24
- "<",
25
- ">",
26
- "!",
27
- "+",
28
- "-",
29
- "*",
30
- "/",
31
- "%"
32
- ];
33
- function tokenize(input) {
34
- const tokens = [];
35
- let i = 0;
36
- while (i < input.length) {
37
- const ch = input[i];
38
- if (/\s/.test(ch)) {
39
- i++;
40
- continue;
41
- }
42
- if (ch === '"' || ch === "'") {
43
- const quote = ch;
44
- let value = "";
45
- i++;
46
- while (i < input.length && input[i] !== quote) {
47
- if (input[i] === "\\") {
48
- value += input[i + 1] || "";
49
- i += 2;
50
- } else {
51
- value += input[i];
52
- i++;
53
- }
54
- }
55
- if (i >= input.length) throw new Error("\u5B57\u7B26\u4E32\u6CA1\u6709\u95ED\u5408");
56
- i++;
57
- tokens.push({ type: "string", value });
58
- continue;
59
- }
60
- if (/[0-9]/.test(ch) || ch === "." && /[0-9]/.test(input[i + 1] || "")) {
61
- let raw = "";
62
- while (i < input.length && /[0-9.]/.test(input[i])) {
63
- raw += input[i];
64
- i++;
65
- }
66
- tokens.push({ type: "number", value: Number(raw) });
67
- continue;
68
- }
69
- if (/[A-Za-z_$]/.test(ch)) {
70
- let name = "";
71
- while (i < input.length && /[A-Za-z0-9_$]/.test(input[i])) {
72
- name += input[i];
73
- i++;
74
- }
75
- tokens.push({ type: "ident", value: name });
76
- continue;
77
- }
78
- const punctuator = PUNCTUATORS.find((p) => input.startsWith(p, i));
79
- if (!punctuator) throw new Error(`\u65E0\u6CD5\u8BC6\u522B\u7684\u5B57\u7B26 "${ch}"`);
80
- tokens.push({ type: "punc", value: punctuator });
81
- i += punctuator.length;
82
- }
83
- return tokens;
84
- }
85
- function createParser(tokens) {
86
- let pos = 0;
87
- const peek = () => tokens[pos];
88
- const isPunc = (v) => {
89
- const token = peek();
90
- return !!token && token.type === "punc" && token.value === v;
91
- };
92
- const eat = (v) => {
93
- if (!isPunc(v)) throw new Error(`\u7F3A\u5C11 "${v}"`);
94
- pos++;
95
- };
96
- const binaryLevels = [
97
- ["||"],
98
- ["&&"],
99
- ["===", "!==", "==", "!="],
100
- ["<", ">", "<=", ">="],
101
- ["+", "-"],
102
- ["*", "/", "%"]
103
- ];
104
- function parseExpression() {
105
- return parseTernary();
106
- }
107
- function parseTernary() {
108
- const test = parseBinary(0);
109
- if (!isPunc("?")) return test;
110
- eat("?");
111
- const consequent = parseTernary();
112
- eat(":");
113
- const alternate = parseTernary();
114
- return { kind: "ternary", test, consequent, alternate };
115
- }
116
- function parseBinary(level) {
117
- if (level >= binaryLevels.length) return parseUnary();
118
- let left = parseBinary(level + 1);
119
- for (; ; ) {
120
- const token = peek();
121
- if (!token || token.type !== "punc") break;
122
- if (binaryLevels[level].indexOf(String(token.value)) === -1) break;
123
- const operator = String(token.value);
124
- pos++;
125
- const right = parseBinary(level + 1);
126
- left = { kind: "binary", operator, left, right };
127
- }
128
- return left;
129
- }
130
- function parseUnary() {
131
- if (isPunc("!") || isPunc("-")) {
132
- const operator = String(peek().value);
133
- pos++;
134
- return { kind: "unary", operator, argument: parseUnary() };
135
- }
136
- return parseMember();
137
- }
138
- function parseMember() {
139
- let object = parsePrimary();
140
- for (; ; ) {
141
- if (isPunc(".")) {
142
- eat(".");
143
- const token = peek();
144
- if (!token || token.type !== "ident") throw new Error('"." \u540E\u9762\u9700\u8981\u5C5E\u6027\u540D');
145
- pos++;
146
- object = {
147
- kind: "member",
148
- object,
149
- property: { kind: "literal", value: token.value }
150
- };
151
- } else if (isPunc("[")) {
152
- eat("[");
153
- const property = parseExpression();
154
- eat("]");
155
- object = { kind: "member", object, property };
156
- } else {
157
- return object;
158
- }
159
- }
160
- }
161
- function parsePrimary() {
162
- const token = peek();
163
- if (!token) throw new Error("\u8868\u8FBE\u5F0F\u4E0D\u5B8C\u6574");
164
- if (isPunc("(")) {
165
- eat("(");
166
- const node = parseExpression();
167
- eat(")");
168
- return node;
169
- }
170
- if (token.type === "number" || token.type === "string") {
171
- pos++;
172
- return { kind: "literal", value: token.value };
173
- }
174
- if (token.type === "ident") {
175
- pos++;
176
- const name = String(token.value);
177
- if (Object.prototype.hasOwnProperty.call(KEYWORD_LITERALS, name)) {
178
- return { kind: "literal", value: KEYWORD_LITERALS[name] };
179
- }
180
- return { kind: "identifier", name };
181
- }
182
- throw new Error(`\u610F\u5916\u7684\u7B26\u53F7 "${token.value}"`);
183
- }
184
- const ast = parseExpression();
185
- if (pos < tokens.length) throw new Error(`\u8868\u8FBE\u5F0F\u7ED3\u5C3E\u6709\u591A\u4F59\u5185\u5BB9 "${tokens[pos].value}"`);
186
- return ast;
187
- }
188
- var astCache = /* @__PURE__ */ new Map();
189
- function parse(source) {
190
- const cached = astCache.get(source);
191
- if (cached) return cached;
192
- const ast = createParser(tokenize(source));
193
- astCache.set(source, ast);
194
- return ast;
195
- }
196
- var BLOCKED_KEYS = ["__proto__", "constructor", "prototype"];
197
- function readProperty(object, key) {
198
- if (object === null || object === void 0) return void 0;
199
- if (BLOCKED_KEYS.indexOf(String(key)) > -1) return void 0;
200
- const value = object[String(key)];
201
- return typeof value === "function" ? void 0 : value;
202
- }
203
- function evaluateNode(node, context) {
204
- switch (node.kind) {
205
- case "literal":
206
- return node.value;
207
- case "identifier":
208
- return readProperty(context, node.name);
209
- case "member":
210
- return readProperty(
211
- evaluateNode(node.object, context),
212
- evaluateNode(node.property, context)
213
- );
214
- case "unary": {
215
- const value = evaluateNode(node.argument, context);
216
- return node.operator === "!" ? !value : -value;
217
- }
218
- case "ternary":
219
- return evaluateNode(node.test, context) ? evaluateNode(node.consequent, context) : evaluateNode(node.alternate, context);
220
- case "binary": {
221
- if (node.operator === "&&") {
222
- return evaluateNode(node.left, context) && evaluateNode(node.right, context);
223
- }
224
- if (node.operator === "||") {
225
- return evaluateNode(node.left, context) || evaluateNode(node.right, context);
226
- }
227
- const left = evaluateNode(node.left, context);
228
- const right = evaluateNode(node.right, context);
229
- switch (node.operator) {
230
- case "===":
231
- return left === right;
232
- case "!==":
233
- return left !== right;
234
- case "==":
235
- return left == right;
236
- case "!=":
237
- return left != right;
238
- case "<":
239
- return left < right;
240
- case ">":
241
- return left > right;
242
- case "<=":
243
- return left <= right;
244
- case ">=":
245
- return left >= right;
246
- case "+":
247
- return left + right;
248
- case "-":
249
- return left - right;
250
- case "*":
251
- return left * right;
252
- case "/":
253
- return left / right;
254
- case "%":
255
- return left % right;
256
- default:
257
- throw new Error(`\u4E0D\u652F\u6301\u7684\u8FD0\u7B97\u7B26 "${node.operator}"`);
258
- }
259
- }
260
- default:
261
- throw new Error("\u4E0D\u652F\u6301\u7684\u8282\u70B9\u7C7B\u578B");
262
- }
263
- }
264
- function evaluate(source, context) {
265
- try {
266
- return evaluateNode(parse(source), context);
267
- } catch (e) {
268
- return void 0;
269
- }
270
- }
271
- function check(source) {
272
- try {
273
- parse(source);
274
- return "";
275
- } catch (error) {
276
- return error.message;
277
- }
278
- }
279
- var INTERPOLATION_RE = /\{\{([\s\S]+?)\}\}/g;
280
- function interpolate(input, context) {
281
- if (typeof input !== "string" || input.indexOf("{{") === -1) return input;
282
- const trimmed = input.trim();
283
- const tokens = trimmed.match(INTERPOLATION_RE) || [];
284
- if (tokens.length === 1 && tokens[0] === trimmed) {
285
- return evaluate(trimmed.slice(2, -2).trim(), context);
286
- }
287
- return input.replace(INTERPOLATION_RE, (_, expr) => {
288
- const value = evaluate(expr.trim(), context);
289
- return value === void 0 || value === null ? "" : String(value);
290
- });
291
- }
292
- function interpolateDeep(input, context) {
293
- if (typeof input === "string") return interpolate(input, context);
294
- if (Array.isArray(input)) {
295
- return input.map((item) => interpolateDeep(item, context));
296
- }
297
- if (input && typeof input === "object") {
298
- const result = {};
299
- Object.keys(input).forEach((key) => {
300
- result[key] = interpolateDeep(input[key], context);
301
- });
302
- return result;
303
- }
304
- return input;
305
- }
1
+ import { normalizeViews, createTicker, interpolateDeep, interpolate, evaluate, resolveNodeStyle, toLength, toCssStyle, parseEndTime, computeParts, formatParts } from './chunk-FKZKLCVV.js';
2
+ export { ACTION_TYPES, ALLOWED_STYLE_KEYS, CLOSE_POSITIONS, DSL_VERSION, NODE_TYPES, SINGLE_VIEW_NAME, TRACK_EVENT_PATTERN, check, computeParts, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, isValidEndTime, normalizeViews, parseEndTime, toCssStyle, toLength, validate } from './chunk-FKZKLCVV.js';
306
3
 
307
4
  // src/url.ts
308
5
  var SAFE_PROTOCOLS = ["http:", "https:", "mailto:", "tel:"];
@@ -390,215 +87,6 @@ function createDispatcher(options) {
390
87
  return dispatch;
391
88
  }
392
89
 
393
- // src/countdown.ts
394
- function pad(value, len = 2) {
395
- return String(value).padStart(len, "0");
396
- }
397
- function parseEndTime(to) {
398
- if (typeof to === "number") return to;
399
- if (to === void 0 || to === null || to === "") return 0;
400
- const raw = String(to).trim();
401
- if (/^\d+$/.test(raw)) return Number(raw);
402
- const time = new Date(raw.replace(/-/g, "/")).getTime();
403
- return isNaN(time) ? 0 : time;
404
- }
405
- function computeParts(endTime, now = Date.now()) {
406
- const raw = endTime - now;
407
- const remain = raw > 0 ? raw : 0;
408
- const cs = Math.floor(remain / 10);
409
- const sec = Math.floor(cs / 100);
410
- return {
411
- d: String(Math.floor(sec / 86400)),
412
- // h 是 0~23;跨天还想连着数小时的用 hAll(如 143:58:07)
413
- h: pad(Math.floor(sec % 86400 / 3600)),
414
- hAll: pad(Math.floor(sec / 3600)),
415
- m: pad(Math.floor(sec % 3600 / 60)),
416
- s: pad(sec % 60),
417
- cs: pad(cs % 100),
418
- ended: !endTime || remain <= 0
419
- };
420
- }
421
- function formatParts(parts, format) {
422
- return (format || "{d}\u5929 {h}:{m}:{s}").replace("{d}", parts.d).replace("{hAll}", parts.hAll).replace("{h}", parts.h).replace("{m}", parts.m).replace("{s}", parts.s).replace("{cs}", parts.cs);
423
- }
424
- function createTicker(options) {
425
- let timer = null;
426
- let raf = 0;
427
- let running = false;
428
- let precision = "s";
429
- let endTimes = [];
430
- const hasRaf = typeof requestAnimationFrame === "function";
431
- function allEnded() {
432
- const now = Date.now();
433
- return endTimes.every((end) => !end || end - now <= 0);
434
- }
435
- function tick() {
436
- options.onTick();
437
- if (allEnded()) {
438
- stop();
439
- if (options.onAllEnded) options.onAllEnded();
440
- }
441
- }
442
- function stop() {
443
- running = false;
444
- if (timer) {
445
- clearInterval(timer);
446
- timer = null;
447
- }
448
- if (raf) {
449
- cancelAnimationFrame(raf);
450
- raf = 0;
451
- }
452
- }
453
- function sync(nextEndTimes, nextPrecision) {
454
- const changed = nextPrecision !== precision || nextEndTimes.length !== endTimes.length || nextEndTimes.some((end, i) => end !== endTimes[i]);
455
- endTimes = nextEndTimes;
456
- precision = nextPrecision;
457
- if (!changed && running) return;
458
- stop();
459
- if (!endTimes.length || allEnded()) return;
460
- running = true;
461
- if (precision === "cs" && hasRaf) {
462
- const loop = () => {
463
- tick();
464
- if (running) raf = requestAnimationFrame(loop);
465
- };
466
- raf = requestAnimationFrame(loop);
467
- } else {
468
- timer = setInterval(tick, 1e3);
469
- }
470
- }
471
- return { sync, stop };
472
- }
473
-
474
- // src/views.ts
475
- var SINGLE_VIEW_NAME = "main";
476
- function normalizeViews(dsl) {
477
- if (!dsl || typeof dsl !== "object") {
478
- return { entry: SINGLE_VIEW_NAME, views: {}, isMulti: false };
479
- }
480
- if (dsl.views && typeof dsl.views === "object" && !Array.isArray(dsl.views)) {
481
- const names = Object.keys(dsl.views);
482
- return {
483
- entry: dsl.entry && dsl.views[dsl.entry] ? dsl.entry : names[0],
484
- views: dsl.views,
485
- isMulti: true
486
- };
487
- }
488
- return {
489
- entry: SINGLE_VIEW_NAME,
490
- views: {
491
- [SINGLE_VIEW_NAME]: {
492
- type: dsl.type,
493
- stage: dsl.stage,
494
- nodes: dsl.nodes
495
- }
496
- },
497
- isMulti: false
498
- };
499
- }
500
-
501
- // src/style.ts
502
- function toLength(value) {
503
- return typeof value === "number" ? `${value}px` : String(value);
504
- }
505
- var px = toLength;
506
- var LENGTH_RE = /^-?(\d+(\.\d+)?)(px|%|em|rem|vw|vh)$/;
507
- function isLength(value) {
508
- if (typeof value === "number") return isFinite(value);
509
- if (typeof value !== "string") return false;
510
- const trimmed = value.trim();
511
- if (trimmed === "auto" || trimmed === "0") return true;
512
- if (/^calc\([^;{}]*\)$/.test(trimmed)) return true;
513
- return LENGTH_RE.test(trimmed);
514
- }
515
- var STYLE_MAP = {
516
- // 文字
517
- color: ["color", String],
518
- fontSize: ["fontSize", px],
519
- fontWeight: ["fontWeight", String],
520
- fontFamily: ["fontFamily", String],
521
- lineHeight: ["lineHeight", (v) => typeof v === "number" && v > 4 ? px(v) : String(v)],
522
- letterSpacing: ["letterSpacing", px],
523
- textAlign: ["textAlign", String],
524
- textDecoration: ["textDecoration", String],
525
- whiteSpace: ["whiteSpace", String],
526
- // 倒计时这类逐帧变化的数字必须等宽,否则数字会左右抖动
527
- fontVariantNumeric: ["fontVariantNumeric", String],
528
- // 背景
529
- background: ["background", String],
530
- backgroundImage: ["backgroundImage", (v) => `url(${JSON.stringify(String(v))})`],
531
- backgroundSize: ["backgroundSize", String],
532
- backgroundPosition: ["backgroundPosition", String],
533
- backgroundRepeat: ["backgroundRepeat", String],
534
- // 边框与圆角
535
- border: ["border", String],
536
- borderColor: ["borderColor", String],
537
- borderWidth: ["borderWidth", px],
538
- borderStyle: ["borderStyle", String],
539
- radius: ["borderRadius", px],
540
- // 盒模型
541
- padding: ["padding", px],
542
- margin: ["margin", px],
543
- marginTop: ["marginTop", px],
544
- marginBottom: ["marginBottom", px],
545
- marginLeft: ["marginLeft", px],
546
- marginRight: ["marginRight", px],
547
- overflow: ["overflow", String],
548
- opacity: ["opacity", String],
549
- shadow: ["boxShadow", String],
550
- cursor: ["cursor", String],
551
- // flex 容器
552
- direction: ["flexDirection", (v) => v === "column" ? "column" : "row"],
553
- gap: ["gap", px],
554
- wrap: ["flexWrap", (v) => v ? "wrap" : "nowrap"],
555
- align: ["alignItems", String],
556
- justify: ["justifyContent", String],
557
- // flex 子项
558
- flex: ["flex", String],
559
- // 不写时子项会被拉满父级交叉轴,标签这类「按内容宽度」的元素要显式声明
560
- alignSelf: ["alignSelf", String],
561
- width: ["width", px],
562
- height: ["height", px],
563
- minWidth: ["minWidth", px],
564
- minHeight: ["minHeight", px],
565
- maxWidth: ["maxWidth", px],
566
- maxHeight: ["maxHeight", px],
567
- // 图片
568
- objectFit: ["objectFit", String],
569
- // 宽度给百分比、高度按比例算。整图 banner 的热区必须靠它才能用百分比定位:
570
- // 容器没有真实高度时,子节点的百分比 rect 算出来全是 0。
571
- aspectRatio: ["aspectRatio", String]
572
- };
573
- var ALLOWED_STYLE_KEYS = Object.keys(STYLE_MAP);
574
- function toCssStyle(style) {
575
- const css = {};
576
- if (!style) return css;
577
- Object.keys(style).forEach((key) => {
578
- const rule = STYLE_MAP[key];
579
- if (!rule) return;
580
- const value = style[key];
581
- if (value === void 0 || value === null || value === "") return;
582
- css[rule[0]] = rule[1](value);
583
- });
584
- return css;
585
- }
586
- function toRectStyle(rect) {
587
- if (!Array.isArray(rect)) return {};
588
- const [x, y, w, h] = rect;
589
- const css = { position: "absolute", left: px(x || 0), top: px(y || 0) };
590
- if (w !== void 0 && w !== null && w !== "auto") css.width = px(w);
591
- if (h !== void 0 && h !== null && h !== "auto") css.height = px(h);
592
- return css;
593
- }
594
- function resolveNodeStyle(node, layout) {
595
- return Object.assign(
596
- {},
597
- layout === "absolute" ? toRectStyle(node.rect) : {},
598
- toCssStyle(node.style)
599
- );
600
- }
601
-
602
90
  // src/resolve.ts
603
91
  function resolveTree(input) {
604
92
  const { normalized, viewStack, ready } = input;
@@ -714,11 +202,12 @@ function resolveCloseButton(config, layerName, closeTop) {
714
202
  const offset = config.offset || [8, 8];
715
203
  const size = config.size === void 0 ? 26 : config.size;
716
204
  const position = config.position || "top-right";
205
+ const image = config.image ? safeImageUrl(config.image) : "";
206
+ const iconMode = image ? "image" : config.icon ? "text" : "svg";
717
207
  const style = {
718
208
  position: "absolute",
719
209
  width: toLength(size),
720
210
  height: toLength(size),
721
- lineHeight: toLength(size),
722
211
  display: "flex",
723
212
  alignItems: "center",
724
213
  justifyContent: "center",
@@ -726,12 +215,15 @@ function resolveCloseButton(config, layerName, closeTop) {
726
215
  borderRadius: "50%",
727
216
  background: "rgba(0, 0, 0, 0.35)",
728
217
  color: "#fff",
729
- fontSize: "18px",
730
- textAlign: "center",
731
218
  cursor: "pointer",
732
219
  userSelect: "none",
733
220
  zIndex: "1"
734
221
  };
222
+ if (iconMode !== "svg") {
223
+ style.lineHeight = toLength(size);
224
+ style.fontSize = "18px";
225
+ style.textAlign = "center";
226
+ }
735
227
  if (position.indexOf("bottom") === 0) style.bottom = toLength(offset[1]);
736
228
  else style.top = toLength(offset[1]);
737
229
  if (position.indexOf("center") > -1) {
@@ -744,8 +236,7 @@ function resolveCloseButton(config, layerName, closeTop) {
744
236
  }
745
237
  Object.assign(style, toCssStyle(config.style));
746
238
  const onClick = () => closeTop("close-button");
747
- const image = config.image ? safeImageUrl(config.image) : "";
748
- if (image) {
239
+ if (iconMode === "image") {
749
240
  return {
750
241
  key: `${layerName}-close`,
751
242
  tag: "div",
@@ -768,15 +259,81 @@ function resolveCloseButton(config, layerName, closeTop) {
768
259
  ]
769
260
  };
770
261
  }
262
+ if (iconMode === "text") {
263
+ return {
264
+ key: `${layerName}-close`,
265
+ tag: "div",
266
+ className: "dsl-close",
267
+ style,
268
+ onClick,
269
+ text: config.icon
270
+ };
271
+ }
771
272
  return {
772
273
  key: `${layerName}-close`,
773
274
  tag: "div",
774
275
  className: "dsl-close",
775
276
  style,
776
277
  onClick,
777
- text: config.icon || "\xD7"
278
+ children: [closeIcon(`${layerName}-close-icon`, size)]
279
+ };
280
+ }
281
+ function closeIcon(key, size) {
282
+ const box = clamp(Math.round(toPx(size, 26) * 0.42), 8, 18);
283
+ const stroke = Math.max(1.2, round2(box * 0.15));
284
+ const a = round2(stroke / 2);
285
+ const b = round2(box - stroke / 2);
286
+ const lineAttrs = {
287
+ stroke: "currentColor",
288
+ "stroke-width": String(stroke),
289
+ "stroke-linecap": "round"
290
+ };
291
+ return {
292
+ key,
293
+ tag: "svg",
294
+ className: "dsl-close-icon",
295
+ style: { display: "block" },
296
+ attrs: {
297
+ width: String(box),
298
+ height: String(box),
299
+ viewBox: `0 0 ${box} ${box}`,
300
+ fill: "none",
301
+ "aria-hidden": "true"
302
+ },
303
+ children: [
304
+ {
305
+ key: `${key}-1`,
306
+ tag: "line",
307
+ className: "dsl-close-icon-line",
308
+ style: {},
309
+ attrs: Object.assign(
310
+ { x1: String(a), y1: String(a), x2: String(b), y2: String(b) },
311
+ lineAttrs
312
+ )
313
+ },
314
+ {
315
+ key: `${key}-2`,
316
+ tag: "line",
317
+ className: "dsl-close-icon-line",
318
+ style: {},
319
+ attrs: Object.assign(
320
+ { x1: String(b), y1: String(a), x2: String(a), y2: String(b) },
321
+ lineAttrs
322
+ )
323
+ }
324
+ ]
778
325
  };
779
326
  }
327
+ function toPx(value, fallback) {
328
+ const num = typeof value === "number" ? value : parseFloat(String(value));
329
+ return isFinite(num) && num > 0 ? num : fallback;
330
+ }
331
+ function clamp(value, min, max) {
332
+ return Math.min(max, Math.max(min, value));
333
+ }
334
+ function round2(value) {
335
+ return Math.round(value * 100) / 100;
336
+ }
780
337
  var BASE_STYLE = { boxSizing: "border-box" };
781
338
  var NODE_CLASS = {
782
339
  box: "dsl-box",
@@ -954,10 +511,13 @@ function resolveCountdown(node, key, style, context, input, countdowns) {
954
511
  const parts = computeParts(endTime);
955
512
  countdowns.endTimes.push(endTime);
956
513
  if (node.precision === "cs") countdowns.precision = "cs";
957
- if (parts.ended && endTime && node.onEnd) {
514
+ if (endTime && node.onEnd) {
958
515
  const token = `${key}@${endTime}`;
959
- if (!input.firedEnds.has(token)) {
960
- input.firedEnds.add(token);
516
+ const status = input.countdownEnds.get(token);
517
+ if (status === void 0) {
518
+ input.countdownEnds.set(token, parts.ended ? "done" : "waiting");
519
+ } else if (status === "waiting" && parts.ended) {
520
+ input.countdownEnds.set(token, "done");
961
521
  input.dispatch(node.onEnd, context);
962
522
  }
963
523
  }
@@ -1017,7 +577,7 @@ function createRuntime(dsl, options = {}) {
1017
577
  let loadFailed = false;
1018
578
  let destroyed = false;
1019
579
  const listeners = /* @__PURE__ */ new Set();
1020
- const firedEnds = /* @__PURE__ */ new Set();
580
+ const countdownEnds = /* @__PURE__ */ new Map();
1021
581
  const notify = () => {
1022
582
  if (destroyed) return;
1023
583
  listeners.forEach((fn) => fn());
@@ -1151,7 +711,7 @@ function createRuntime(dsl, options = {}) {
1151
711
  dispatch,
1152
712
  setState,
1153
713
  closeTop,
1154
- firedEnds
714
+ countdownEnds
1155
715
  });
1156
716
  ticker.sync(tree.countdownEndTimes, tree.countdownPrecision);
1157
717
  return tree;
@@ -1178,427 +738,4 @@ function createRuntime(dsl, options = {}) {
1178
738
  };
1179
739
  }
1180
740
 
1181
- // src/validate.ts
1182
- var LENGTH_STYLE_KEYS = [
1183
- "width",
1184
- "height",
1185
- "minWidth",
1186
- "minHeight",
1187
- "maxWidth",
1188
- "maxHeight",
1189
- "radius",
1190
- "padding",
1191
- "margin",
1192
- "marginTop",
1193
- "marginBottom",
1194
- "marginLeft",
1195
- "marginRight",
1196
- "gap",
1197
- "fontSize",
1198
- "letterSpacing",
1199
- "borderWidth"
1200
- ];
1201
- var DSL_VERSION = 1;
1202
- var NODE_TYPES = [
1203
- "box",
1204
- "flex",
1205
- "repeat",
1206
- "image",
1207
- "text",
1208
- "button",
1209
- "tabs",
1210
- "countdown"
1211
- ];
1212
- var ACTION_TYPES = [
1213
- "navigate",
1214
- "close",
1215
- "closeAll",
1216
- "open",
1217
- "setState",
1218
- "track",
1219
- "call",
1220
- "sequence"
1221
- ];
1222
- var CLOSE_POSITIONS = [
1223
- "top-right",
1224
- "top-left",
1225
- "top-center",
1226
- "bottom-right",
1227
- "bottom-left",
1228
- "bottom-center"
1229
- ];
1230
- var CONTAINER_TYPES = ["box", "flex"];
1231
- function validate(dsl) {
1232
- const errors = [];
1233
- const warnings = [];
1234
- const add = (path, message) => errors.push({ path, message });
1235
- const warn = (path, message) => warnings.push({ path, message });
1236
- if (!dsl || typeof dsl !== "object" || Array.isArray(dsl)) {
1237
- return {
1238
- valid: false,
1239
- errors: [{ path: "", message: "\u6839\u8282\u70B9\u5FC5\u987B\u662F\u4E00\u4E2A\u5BF9\u8C61" }],
1240
- warnings
1241
- };
1242
- }
1243
- const doc = dsl;
1244
- if (doc.version !== DSL_VERSION) {
1245
- add(
1246
- "version",
1247
- `version \u5FC5\u987B\u662F ${DSL_VERSION}\uFF0C\u5F53\u524D\u4E3A ${JSON.stringify(doc.version)}`
1248
- );
1249
- }
1250
- const dataKeys = validateData(doc.data, add);
1251
- const stateKeys = validateState(doc.state, add);
1252
- validateDerived(doc.derived, dataKeys, stateKeys, add);
1253
- const isMulti = doc.views !== void 0;
1254
- if (isMulti) validateMultiView(doc, add);
1255
- const { views } = normalizeViews(doc);
1256
- const viewNames = Object.keys(views);
1257
- const ctx = { add, warn, viewNames };
1258
- viewNames.forEach((name) => {
1259
- validateView(views[name], isMulti ? `views.${name}` : "", ctx);
1260
- });
1261
- return { valid: errors.length === 0, errors, warnings };
1262
- }
1263
- function validateMultiView(dsl, add) {
1264
- if (typeof dsl.views !== "object" || dsl.views === null || Array.isArray(dsl.views)) {
1265
- add("views", 'views \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5F62\u5982 { "main": { type, stage, nodes } }');
1266
- return;
1267
- }
1268
- if (!Object.keys(dsl.views).length) {
1269
- add("views", "views \u81F3\u5C11\u8981\u6709\u4E00\u4E2A\u89C6\u56FE");
1270
- return;
1271
- }
1272
- if (dsl.entry === void 0) {
1273
- add("entry", "\u591A\u89C6\u56FE\u5FC5\u987B\u6307\u5B9A entry\uFF08\u9996\u4E2A\u5C55\u793A\u7684\u89C6\u56FE\u540D\uFF09");
1274
- } else if (!dsl.views[dsl.entry]) {
1275
- add("entry", `entry "${dsl.entry}" \u5728 views \u91CC\u4E0D\u5B58\u5728`);
1276
- }
1277
- ["stage", "nodes"].forEach((key) => {
1278
- if (dsl[key] !== void 0) {
1279
- add(key, `\u7528\u4E86 views \u4E4B\u540E\uFF0C${key} \u8981\u5199\u5728\u6BCF\u4E2A\u89C6\u56FE\u91CC\uFF0C\u4E0D\u80FD\u7559\u5728\u9876\u5C42`);
1280
- }
1281
- });
1282
- }
1283
- function validateView(view, prefix, ctx) {
1284
- const { add, warn } = ctx;
1285
- const at = (key) => prefix ? `${prefix}.${key}` : key;
1286
- if (!view || typeof view !== "object" || Array.isArray(view)) {
1287
- add(prefix || "view", "\u89C6\u56FE\u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5305\u542B type / stage / nodes");
1288
- return;
1289
- }
1290
- if (["popup", "banner", "notice"].indexOf(view.type) === -1) {
1291
- add(at("type"), "type \u5FC5\u987B\u662F popup / banner / notice \u4E4B\u4E00");
1292
- }
1293
- validateStage(
1294
- view.stage,
1295
- (path, message) => add(prefix ? `${prefix}.${path}` : path, message),
1296
- warn,
1297
- ctx,
1298
- prefix
1299
- );
1300
- if (!Array.isArray(view.nodes) || view.nodes.length === 0) {
1301
- add(at("nodes"), "nodes \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4");
1302
- return;
1303
- }
1304
- const rootLayout = (view.stage || {}).layout === "flow" ? "flow" : "absolute";
1305
- view.nodes.forEach((node, index) => {
1306
- validateNode(node, at(`nodes[${index}]`), rootLayout, ctx);
1307
- });
1308
- }
1309
- function checkLength(value) {
1310
- if (typeof value === "string" && value.indexOf("{{") > -1) return true;
1311
- if (typeof value === "number") return isLength(value);
1312
- if (typeof value !== "string") return false;
1313
- if (isLength(value)) return true;
1314
- return value.trim().split(/\s+/).every(isLength);
1315
- }
1316
- function checkAspectRatio(value) {
1317
- if (typeof value === "string" && value.indexOf("{{") > -1) return true;
1318
- if (typeof value === "number") return isFinite(value) && value > 0;
1319
- if (typeof value !== "string") return false;
1320
- return /^\s*\d+(\.\d+)?\s*(\/\s*\d+(\.\d+)?\s*)?$/.test(value);
1321
- }
1322
- function validateStage(stage, add, warn, ctx, prefix) {
1323
- if (stage === void 0) {
1324
- add("stage", "\u7F3A\u5C11 stage");
1325
- return;
1326
- }
1327
- if (typeof stage !== "object" || stage === null) {
1328
- add("stage", "stage \u5FC5\u987B\u662F\u5BF9\u8C61");
1329
- return;
1330
- }
1331
- if (!checkLength(stage.width)) {
1332
- add(
1333
- "stage.width",
1334
- "stage.width \u5FC5\u987B\u662F\u6570\u5B57(px)\u6216\u957F\u5EA6\u5B57\u7B26\u4E32\uFF0C\u5982 460 / '100%' / 'calc(100% - 32px)'"
1335
- );
1336
- }
1337
- if (!checkLength(stage.height)) {
1338
- add("stage.height", "stage.height \u5FC5\u987B\u662F\u6570\u5B57(px)\u6216\u957F\u5EA6\u5B57\u7B26\u4E32\uFF0C\u5982 520 / 'auto'");
1339
- }
1340
- if (stage.layout !== void 0 && ["absolute", "flow"].indexOf(stage.layout) === -1) {
1341
- add("stage.layout", "stage.layout \u53EA\u80FD\u662F 'absolute'\uFF08\u9ED8\u8BA4\uFF09\u6216 'flow'");
1342
- }
1343
- validateCloseButton(stage.closeButton, add, warn);
1344
- const at = (key) => prefix ? `${prefix}.stage.${key}` : `stage.${key}`;
1345
- if (stage.onShow !== void 0) validateAction(stage.onShow, at("onShow"), ctx);
1346
- if (stage.onClose !== void 0) validateAction(stage.onClose, at("onClose"), ctx);
1347
- if (stage.height === "auto" && stage.layout !== "flow") {
1348
- add(
1349
- "stage.height",
1350
- "height: 'auto' \u9700\u8981\u540C\u65F6\u8BBE\u7F6E stage.layout: 'flow'\uFF0C\u5426\u5219\u9AD8\u5EA6\u4F1A\u584C\u6210 0"
1351
- );
1352
- }
1353
- }
1354
- function validateCloseButton(config, add, warn) {
1355
- if (config === void 0) return;
1356
- if (typeof config !== "object" || config === null || Array.isArray(config)) {
1357
- add("stage.closeButton", "closeButton \u5FC5\u987B\u662F\u5BF9\u8C61");
1358
- return;
1359
- }
1360
- if (config.position !== void 0 && CLOSE_POSITIONS.indexOf(config.position) === -1) {
1361
- add("stage.closeButton.position", `position \u53EA\u80FD\u662F ${CLOSE_POSITIONS.join(" / ")}`);
1362
- }
1363
- if (config.offset !== void 0) {
1364
- if (!Array.isArray(config.offset) || config.offset.length !== 2) {
1365
- add(
1366
- "stage.closeButton.offset",
1367
- "offset \u5FC5\u987B\u662F [x, y] \u4E24\u9879\u6570\u7EC4\uFF0C\u8D1F\u503C\u4F1A\u628A\u6309\u94AE\u79FB\u5230\u5F39\u7A97\u5916\u9762"
1368
- );
1369
- } else {
1370
- config.offset.forEach((value, index) => {
1371
- if (!checkLength(value)) {
1372
- add(`stage.closeButton.offset[${index}]`, `"${value}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6`);
1373
- }
1374
- });
1375
- }
1376
- }
1377
- if (config.size !== void 0 && !checkLength(config.size)) {
1378
- add("stage.closeButton.size", `"${config.size}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6`);
1379
- }
1380
- if (config.icon !== void 0 && typeof config.icon !== "string") {
1381
- add("stage.closeButton.icon", "icon \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
1382
- }
1383
- if (config.image !== void 0 && typeof config.image !== "string") {
1384
- add("stage.closeButton.image", "image \u5FC5\u987B\u662F\u56FE\u7247\u94FE\u63A5\u5B57\u7B26\u4E32");
1385
- }
1386
- if (config.style) {
1387
- Object.keys(config.style).forEach((key) => {
1388
- if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
1389
- warn(
1390
- `stage.closeButton.style.${key}`,
1391
- `\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`
1392
- );
1393
- }
1394
- });
1395
- }
1396
- }
1397
- function validateData(data, add) {
1398
- if (data === void 0) return [];
1399
- if (typeof data !== "object" || data === null || Array.isArray(data)) {
1400
- add("data", "data \u5FC5\u987B\u662F\u5BF9\u8C61");
1401
- return [];
1402
- }
1403
- Object.keys(data).forEach((key) => {
1404
- const value = data[key];
1405
- if (value && typeof value === "object" && !Array.isArray(value) && value.$source) {
1406
- if (typeof value.$source !== "string") {
1407
- add(`data.${key}.$source`, "$source \u5FC5\u987B\u662F\u6570\u636E\u6E90\u540D\u79F0\u5B57\u7B26\u4E32");
1408
- }
1409
- if (value.params !== void 0 && (typeof value.params !== "object" || value.params === null)) {
1410
- add(`data.${key}.params`, "params \u5FC5\u987B\u662F\u5BF9\u8C61");
1411
- }
1412
- }
1413
- });
1414
- return Object.keys(data);
1415
- }
1416
- function validateState(state, add) {
1417
- if (state === void 0) return [];
1418
- if (typeof state !== "object" || state === null || Array.isArray(state)) {
1419
- add("state", "state \u5FC5\u987B\u662F\u5BF9\u8C61");
1420
- return [];
1421
- }
1422
- return Object.keys(state);
1423
- }
1424
- function validateDerived(derived, dataKeys, stateKeys, add) {
1425
- if (derived === void 0) return;
1426
- if (typeof derived !== "object" || derived === null || Array.isArray(derived)) {
1427
- add("derived", "derived \u5FC5\u987B\u662F\u5BF9\u8C61");
1428
- return;
1429
- }
1430
- Object.keys(derived).forEach((key) => {
1431
- const item = derived[key] || {};
1432
- if (typeof item.list !== "string") {
1433
- add(`derived.${key}.list`, "list \u5FC5\u987B\u662F data \u4E2D\u7684\u6570\u7EC4\u5B57\u6BB5\u540D");
1434
- } else if (dataKeys.indexOf(item.list) === -1) {
1435
- add(`derived.${key}.list`, `data \u4E2D\u4E0D\u5B58\u5728 "${item.list}"`);
1436
- }
1437
- if (typeof item.indexBy !== "string") {
1438
- add(`derived.${key}.indexBy`, "indexBy \u5FC5\u987B\u662F state \u4E2D\u7684\u5B57\u6BB5\u540D");
1439
- } else if (stateKeys.indexOf(item.indexBy) === -1) {
1440
- add(`derived.${key}.indexBy`, `state \u4E2D\u4E0D\u5B58\u5728 "${item.indexBy}"`);
1441
- }
1442
- });
1443
- }
1444
- function validateNode(node, path, layout, ctx) {
1445
- const { add, warn } = ctx;
1446
- if (!node || typeof node !== "object" || Array.isArray(node)) {
1447
- add(path, "\u8282\u70B9\u5FC5\u987B\u662F\u5BF9\u8C61");
1448
- return;
1449
- }
1450
- if (NODE_TYPES.indexOf(node.type) === -1) {
1451
- add(`${path}.type`, `\u672A\u77E5\u8282\u70B9\u7C7B\u578B "${node.type}"\uFF0C\u53EF\u7528\uFF1A${NODE_TYPES.join(" / ")}`);
1452
- return;
1453
- }
1454
- if (layout === "absolute" && !Array.isArray(node.rect)) {
1455
- add(`${path}.rect`, "\u5904\u4E8E\u7EDD\u5BF9\u5B9A\u4F4D\u5BB9\u5668\u4E2D\uFF0C\u5FC5\u987B\u63D0\u4F9B rect: [x, y, w, h]");
1456
- }
1457
- if (node.rect !== void 0) {
1458
- if (!Array.isArray(node.rect)) {
1459
- add(`${path}.rect`, "rect \u5FC5\u987B\u662F [x, y, w, h] \u6570\u7EC4");
1460
- } else {
1461
- node.rect.forEach((value, index) => {
1462
- if (value !== void 0 && value !== null && !checkLength(value)) {
1463
- add(
1464
- `${path}.rect[${index}]`,
1465
- `"${value}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
1466
- );
1467
- }
1468
- });
1469
- }
1470
- }
1471
- if (node.style) {
1472
- Object.keys(node.style).forEach((key) => {
1473
- if (ALLOWED_STYLE_KEYS.indexOf(key) === -1) {
1474
- warn(`${path}.style.${key}`, `\u6837\u5F0F\u5C5E\u6027 "${key}" \u4E0D\u5728\u767D\u540D\u5355\u5185\uFF0C\u6E32\u67D3\u65F6\u4F1A\u88AB\u5FFD\u7565`);
1475
- return;
1476
- }
1477
- if (LENGTH_STYLE_KEYS.indexOf(key) > -1 && !checkLength(node.style[key])) {
1478
- add(
1479
- `${path}.style.${key}`,
1480
- `"${node.style[key]}" \u4E0D\u662F\u5408\u6CD5\u957F\u5EA6\uFF0C\u652F\u6301\u6570\u5B57(px) / '100%' / 'auto' \u7B49`
1481
- );
1482
- }
1483
- if (key === "aspectRatio" && !checkAspectRatio(node.style[key])) {
1484
- add(
1485
- `${path}.style.aspectRatio`,
1486
- "aspectRatio \u8981\u5199\u6210 '750 / 200' \u6216 1.5 \u8FD9\u6837\u7684\u5BBD\u9AD8\u6BD4"
1487
- );
1488
- }
1489
- });
1490
- }
1491
- if (node.visibleWhen !== void 0) {
1492
- if (typeof node.visibleWhen !== "string") {
1493
- add(`${path}.visibleWhen`, "visibleWhen \u5FC5\u987B\u662F\u8868\u8FBE\u5F0F\u5B57\u7B26\u4E32");
1494
- } else {
1495
- const error = check(node.visibleWhen);
1496
- if (error) add(`${path}.visibleWhen`, `\u8868\u8FBE\u5F0F\u6709\u8BEF\uFF1A${error}`);
1497
- }
1498
- }
1499
- if (node.action !== void 0) validateAction(node.action, `${path}.action`, ctx);
1500
- validateNodeByType(node, path, ctx);
1501
- }
1502
- function validateNodeByType(node, path, ctx) {
1503
- const { add } = ctx;
1504
- if (node.type === "image" && !node.src) {
1505
- add(`${path}.src`, "image \u5FC5\u987B\u63D0\u4F9B src");
1506
- }
1507
- if (node.type === "text" && node.content === void 0) {
1508
- add(`${path}.content`, "text \u5FC5\u987B\u63D0\u4F9B content");
1509
- }
1510
- if (node.type === "button" && node.text === void 0) {
1511
- add(`${path}.text`, "button \u5FC5\u987B\u63D0\u4F9B text");
1512
- }
1513
- if (node.type === "tabs") {
1514
- if (typeof node.bind !== "string") {
1515
- add(`${path}.bind`, "tabs \u5FC5\u987B\u63D0\u4F9B bind\uFF08data \u4E2D\u7684\u6570\u7EC4\u5B57\u6BB5\u540D\uFF09");
1516
- }
1517
- if (typeof node.stateKey !== "string")
1518
- add(`${path}.stateKey`, "tabs \u5FC5\u987B\u63D0\u4F9B stateKey");
1519
- if (node.labelField !== void 0 && typeof node.labelField !== "string") {
1520
- add(`${path}.labelField`, "labelField \u5FC5\u987B\u662F\u5B57\u7B26\u4E32");
1521
- }
1522
- }
1523
- if (node.type === "countdown") {
1524
- if (!node.to) add(`${path}.to`, "countdown \u5FC5\u987B\u63D0\u4F9B to\uFF08\u7ED3\u675F\u65F6\u95F4\uFF09");
1525
- if (node.precision !== void 0 && ["s", "cs"].indexOf(node.precision) === -1) {
1526
- add(`${path}.precision`, "precision \u53EA\u80FD\u662F 's'\uFF08\u6BCF\u79D2\uFF09\u6216 'cs'\uFF08\u5398\u79D2\uFF0C\u9010\u5E27\u5237\u65B0\uFF09");
1527
- }
1528
- if (node.children !== void 0 && !Array.isArray(node.children)) {
1529
- add(`${path}.children`, "children \u5FC5\u987B\u662F\u6570\u7EC4");
1530
- } else {
1531
- (node.children || []).forEach((child, index) => {
1532
- validateNode(child, `${path}.children[${index}]`, "flow", ctx);
1533
- });
1534
- }
1535
- }
1536
- if (node.type === "repeat") {
1537
- if (typeof node.bind !== "string") add(`${path}.bind`, "repeat \u5FC5\u987B\u63D0\u4F9B bind");
1538
- if (!node.template) {
1539
- add(`${path}.template`, "repeat \u5FC5\u987B\u63D0\u4F9B template");
1540
- } else {
1541
- validateNode(node.template, `${path}.template`, "flow", ctx);
1542
- }
1543
- }
1544
- if (CONTAINER_TYPES.indexOf(node.type) > -1) {
1545
- const childLayout = node.type === "box" ? "absolute" : "flow";
1546
- if (node.children !== void 0 && !Array.isArray(node.children)) {
1547
- add(`${path}.children`, "children \u5FC5\u987B\u662F\u6570\u7EC4");
1548
- } else {
1549
- (node.children || []).forEach((child, index) => {
1550
- validateNode(child, `${path}.children[${index}]`, childLayout, ctx);
1551
- });
1552
- }
1553
- }
1554
- }
1555
- function validateAction(action, path, ctx) {
1556
- const { add } = ctx;
1557
- if (!action || typeof action !== "object" || Array.isArray(action)) {
1558
- add(path, "action \u5FC5\u987B\u662F\u5BF9\u8C61");
1559
- return;
1560
- }
1561
- if (ACTION_TYPES.indexOf(action.type) === -1) {
1562
- add(`${path}.type`, `\u672A\u77E5\u52A8\u4F5C "${action.type}"\uFF0C\u53EF\u7528\uFF1A${ACTION_TYPES.join(" / ")}`);
1563
- return;
1564
- }
1565
- if (action.type === "navigate" && !action.url) {
1566
- add(`${path}.url`, "navigate \u5FC5\u987B\u63D0\u4F9B url");
1567
- }
1568
- if (action.type === "setState" && typeof action.key !== "string") {
1569
- add(`${path}.key`, "setState \u5FC5\u987B\u63D0\u4F9B key");
1570
- }
1571
- if (action.type === "track" && !action.event) {
1572
- add(`${path}.event`, "track \u5FC5\u987B\u63D0\u4F9B event");
1573
- }
1574
- if (action.type === "call" && !action.name) {
1575
- add(`${path}.name`, "call \u5FC5\u987B\u63D0\u4F9B name\uFF08\u5BBF\u4E3B\u6CE8\u518C\u7684\u65B9\u6CD5\u540D\uFF09");
1576
- }
1577
- if (action.type === "open") {
1578
- if (!action.view) {
1579
- add(`${path}.view`, "open \u5FC5\u987B\u63D0\u4F9B view\uFF08\u8981\u6253\u5F00\u7684\u89C6\u56FE\u540D\uFF09");
1580
- } else if (ctx.viewNames && ctx.viewNames.indexOf(action.view) === -1) {
1581
- add(`${path}.view`, `views \u91CC\u4E0D\u5B58\u5728\u89C6\u56FE "${action.view}"`);
1582
- }
1583
- if (action.mode !== void 0 && ["stack", "replace"].indexOf(action.mode) === -1) {
1584
- add(
1585
- `${path}.mode`,
1586
- "mode \u53EA\u80FD\u662F 'stack'\uFF08\u9ED8\u8BA4\uFF0C\u53E0\u4E00\u5C42\uFF09\u6216 'replace'\uFF08\u6362\u6389\u5F53\u524D\u5C42\uFF09"
1587
- );
1588
- }
1589
- }
1590
- if (action.type === "sequence") {
1591
- if (!Array.isArray(action.actions)) {
1592
- add(`${path}.actions`, "sequence \u5FC5\u987B\u63D0\u4F9B actions \u6570\u7EC4");
1593
- } else {
1594
- action.actions.forEach(
1595
- (item, index) => validateAction(item, `${path}.actions[${index}]`, ctx)
1596
- );
1597
- }
1598
- }
1599
- }
1600
- function formatIssues(issues) {
1601
- return issues.map((item) => item.path ? `${item.path}: ${item.message}` : item.message).join("\n");
1602
- }
1603
-
1604
- export { ACTION_TYPES, ALLOWED_STYLE_KEYS, CLOSE_POSITIONS, DSL_VERSION, NODE_TYPES, SINGLE_VIEW_NAME, check, computeParts, createRuntime, evaluate, formatIssues, formatParts, interpolate, interpolateDeep, isLength, normalizeViews, parseEndTime, safeImageUrl, safeUrl, toCssStyle, toLength, validate };
741
+ export { createRuntime, safeImageUrl, safeUrl };