@mmapp/react 0.1.0-alpha.22 → 0.1.0-alpha.24

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.
Files changed (3) hide show
  1. package/dist/index.js +1124 -39
  2. package/dist/index.mjs +1098 -25
  3. package/package.json +6 -3
package/dist/index.mjs CHANGED
@@ -66,14 +66,1094 @@ import {
66
66
  useScope
67
67
  } from "./chunk-WV7DVCP6.mjs";
68
68
 
69
+ // ../player-core/dist/index.mjs
70
+ var add = {
71
+ name: "add",
72
+ fn: (a, b) => Number(a) + Number(b),
73
+ arity: 2
74
+ };
75
+ var subtract = {
76
+ name: "subtract",
77
+ fn: (a, b) => Number(a) - Number(b),
78
+ arity: 2
79
+ };
80
+ var multiply = {
81
+ name: "multiply",
82
+ fn: (a, b) => Number(a) * Number(b),
83
+ arity: 2
84
+ };
85
+ var divide = {
86
+ name: "divide",
87
+ fn: (a, b) => {
88
+ const d = Number(b);
89
+ return d === 0 ? 0 : Number(a) / d;
90
+ },
91
+ arity: 2
92
+ };
93
+ var abs = {
94
+ name: "abs",
95
+ fn: (a) => Math.abs(Number(a)),
96
+ arity: 1
97
+ };
98
+ var round = {
99
+ name: "round",
100
+ fn: (a, decimals) => {
101
+ const d = decimals != null ? Number(decimals) : 0;
102
+ const factor = Math.pow(10, d);
103
+ return Math.round(Number(a) * factor) / factor;
104
+ },
105
+ arity: -1
106
+ };
107
+ var min = {
108
+ name: "min",
109
+ fn: (...args) => {
110
+ const nums = args.flat().map(Number).filter((n) => !isNaN(n));
111
+ return nums.length === 0 ? 0 : Math.min(...nums);
112
+ },
113
+ arity: -1
114
+ };
115
+ var max = {
116
+ name: "max",
117
+ fn: (...args) => {
118
+ const nums = args.flat().map(Number).filter((n) => !isNaN(n));
119
+ return nums.length === 0 ? 0 : Math.max(...nums);
120
+ },
121
+ arity: -1
122
+ };
123
+ var eq = {
124
+ name: "eq",
125
+ fn: (a, b) => a === b || String(a) === String(b),
126
+ arity: 2
127
+ };
128
+ var neq = {
129
+ name: "neq",
130
+ fn: (a, b) => a !== b && String(a) !== String(b),
131
+ arity: 2
132
+ };
133
+ var gt = {
134
+ name: "gt",
135
+ fn: (a, b) => Number(a) > Number(b),
136
+ arity: 2
137
+ };
138
+ var gte = {
139
+ name: "gte",
140
+ fn: (a, b) => Number(a) >= Number(b),
141
+ arity: 2
142
+ };
143
+ var lt = {
144
+ name: "lt",
145
+ fn: (a, b) => Number(a) < Number(b),
146
+ arity: 2
147
+ };
148
+ var lte = {
149
+ name: "lte",
150
+ fn: (a, b) => Number(a) <= Number(b),
151
+ arity: 2
152
+ };
153
+ var if_fn = {
154
+ name: "if",
155
+ fn: (cond, then, else_) => cond ? then : else_,
156
+ arity: 3
157
+ };
158
+ var and = {
159
+ name: "and",
160
+ fn: (...args) => args.every(Boolean),
161
+ arity: -1
162
+ };
163
+ var or = {
164
+ name: "or",
165
+ fn: (...args) => args.some(Boolean),
166
+ arity: -1
167
+ };
168
+ var not = {
169
+ name: "not",
170
+ fn: (a) => !a,
171
+ arity: 1
172
+ };
173
+ var coalesce = {
174
+ name: "coalesce",
175
+ fn: (...args) => {
176
+ for (const arg of args) {
177
+ if (arg != null) return arg;
178
+ }
179
+ return null;
180
+ },
181
+ arity: -1
182
+ };
183
+ var concat = {
184
+ name: "concat",
185
+ fn: (...args) => args.map(String).join(""),
186
+ arity: -1
187
+ };
188
+ var upper = {
189
+ name: "upper",
190
+ fn: (s) => String(s ?? "").toUpperCase(),
191
+ arity: 1
192
+ };
193
+ var lower = {
194
+ name: "lower",
195
+ fn: (s) => String(s ?? "").toLowerCase(),
196
+ arity: 1
197
+ };
198
+ var trim = {
199
+ name: "trim",
200
+ fn: (s) => String(s ?? "").trim(),
201
+ arity: 1
202
+ };
203
+ var format = {
204
+ name: "format",
205
+ fn: (template, ...args) => {
206
+ let result = String(template ?? "");
207
+ args.forEach((arg, i) => {
208
+ result = result.replace(`{${i}}`, String(arg ?? ""));
209
+ });
210
+ return result;
211
+ },
212
+ arity: -1
213
+ };
214
+ var length = {
215
+ name: "length",
216
+ fn: (v) => {
217
+ if (Array.isArray(v)) return v.length;
218
+ if (typeof v === "string") return v.length;
219
+ if (v && typeof v === "object") return Object.keys(v).length;
220
+ return 0;
221
+ },
222
+ arity: 1
223
+ };
224
+ var get = {
225
+ name: "get",
226
+ fn: (obj, path) => {
227
+ if (obj == null || typeof path !== "string") return void 0;
228
+ const parts = path.split(".");
229
+ let current = obj;
230
+ for (const part of parts) {
231
+ if (current == null || typeof current !== "object") return void 0;
232
+ current = current[part];
233
+ }
234
+ return current;
235
+ },
236
+ arity: 2
237
+ };
238
+ var includes = {
239
+ name: "includes",
240
+ fn: (collection, value) => {
241
+ if (Array.isArray(collection)) return collection.includes(value);
242
+ if (typeof collection === "string") return collection.includes(String(value));
243
+ return false;
244
+ },
245
+ arity: 2
246
+ };
247
+ var is_defined = {
248
+ name: "is_defined",
249
+ fn: (v) => v !== void 0 && v !== null,
250
+ arity: 1
251
+ };
252
+ var is_empty = {
253
+ name: "is_empty",
254
+ fn: (v) => {
255
+ if (v == null) return true;
256
+ if (typeof v === "string") return v.length === 0;
257
+ if (Array.isArray(v)) return v.length === 0;
258
+ if (typeof v === "object") return Object.keys(v).length === 0;
259
+ return false;
260
+ },
261
+ arity: 1
262
+ };
263
+ var is_null = {
264
+ name: "is_null",
265
+ fn: (v) => v === null || v === void 0,
266
+ arity: 1
267
+ };
268
+ var to_string = {
269
+ name: "to_string",
270
+ fn: (v) => {
271
+ if (v == null) return "";
272
+ if (typeof v === "object") return JSON.stringify(v);
273
+ return String(v);
274
+ },
275
+ arity: 1
276
+ };
277
+ var CORE_FUNCTIONS = [
278
+ // Math (8)
279
+ add,
280
+ subtract,
281
+ multiply,
282
+ divide,
283
+ abs,
284
+ round,
285
+ min,
286
+ max,
287
+ // Comparison (6)
288
+ eq,
289
+ neq,
290
+ gt,
291
+ gte,
292
+ lt,
293
+ lte,
294
+ // Logic (5)
295
+ if_fn,
296
+ and,
297
+ or,
298
+ not,
299
+ coalesce,
300
+ // String (6)
301
+ concat,
302
+ upper,
303
+ lower,
304
+ trim,
305
+ format,
306
+ length,
307
+ // Path (3)
308
+ get,
309
+ includes,
310
+ is_defined,
311
+ // Type (3)
312
+ is_empty,
313
+ is_null,
314
+ to_string
315
+ ];
316
+ function buildFunctionMap(functions) {
317
+ const map = /* @__PURE__ */ new Map();
318
+ for (const fn of functions) {
319
+ map.set(fn.name, fn.fn);
320
+ }
321
+ return map;
322
+ }
323
+ var MAX_DEPTH = 50;
324
+ var Parser = class {
325
+ pos = 0;
326
+ depth = 0;
327
+ input;
328
+ constructor(input) {
329
+ this.input = input;
330
+ }
331
+ parse() {
332
+ this.skipWhitespace();
333
+ const node = this.parseExpression();
334
+ this.skipWhitespace();
335
+ if (this.pos < this.input.length) {
336
+ throw new Error(`Unexpected character at position ${this.pos}: '${this.input[this.pos]}'`);
337
+ }
338
+ return node;
339
+ }
340
+ guardDepth() {
341
+ if (++this.depth > MAX_DEPTH) {
342
+ throw new Error("Expression too deeply nested");
343
+ }
344
+ }
345
+ parseExpression() {
346
+ this.guardDepth();
347
+ try {
348
+ return this.parseTernary();
349
+ } finally {
350
+ this.depth--;
351
+ }
352
+ }
353
+ parseTernary() {
354
+ let node = this.parseLogicalOr();
355
+ this.skipWhitespace();
356
+ if (this.peek() === "?") {
357
+ this.advance();
358
+ const consequent = this.parseExpression();
359
+ this.skipWhitespace();
360
+ this.expect(":");
361
+ const alternate = this.parseExpression();
362
+ node = { type: "ternary", condition: node, consequent, alternate };
363
+ }
364
+ return node;
365
+ }
366
+ parseLogicalOr() {
367
+ let left = this.parseLogicalAnd();
368
+ this.skipWhitespace();
369
+ while (this.match("||")) {
370
+ const right = this.parseLogicalAnd();
371
+ left = { type: "binary", operator: "||", left, right };
372
+ this.skipWhitespace();
373
+ }
374
+ return left;
375
+ }
376
+ parseLogicalAnd() {
377
+ let left = this.parseEquality();
378
+ this.skipWhitespace();
379
+ while (this.match("&&")) {
380
+ const right = this.parseEquality();
381
+ left = { type: "binary", operator: "&&", left, right };
382
+ this.skipWhitespace();
383
+ }
384
+ return left;
385
+ }
386
+ parseEquality() {
387
+ let left = this.parseComparison();
388
+ this.skipWhitespace();
389
+ while (true) {
390
+ if (this.match("==")) {
391
+ const right = this.parseComparison();
392
+ left = { type: "binary", operator: "==", left, right };
393
+ } else if (this.match("!=")) {
394
+ const right = this.parseComparison();
395
+ left = { type: "binary", operator: "!=", left, right };
396
+ } else {
397
+ break;
398
+ }
399
+ this.skipWhitespace();
400
+ }
401
+ return left;
402
+ }
403
+ parseComparison() {
404
+ let left = this.parseUnary();
405
+ this.skipWhitespace();
406
+ while (true) {
407
+ if (this.match(">=")) {
408
+ const right = this.parseUnary();
409
+ left = { type: "binary", operator: ">=", left, right };
410
+ } else if (this.match("<=")) {
411
+ const right = this.parseUnary();
412
+ left = { type: "binary", operator: "<=", left, right };
413
+ } else if (this.peek() === ">" && !this.lookAhead(">=")) {
414
+ this.advance();
415
+ const right = this.parseUnary();
416
+ left = { type: "binary", operator: ">", left, right };
417
+ } else if (this.peek() === "<" && !this.lookAhead("<=")) {
418
+ this.advance();
419
+ const right = this.parseUnary();
420
+ left = { type: "binary", operator: "<", left, right };
421
+ } else {
422
+ break;
423
+ }
424
+ this.skipWhitespace();
425
+ }
426
+ return left;
427
+ }
428
+ parseUnary() {
429
+ this.skipWhitespace();
430
+ if (this.peek() === "!") {
431
+ this.advance();
432
+ const operand = this.parseUnary();
433
+ return { type: "unary", operator: "!", operand };
434
+ }
435
+ if (this.peek() === "-") {
436
+ const nextChar = this.input[this.pos + 1];
437
+ if (nextChar !== void 0 && (nextChar >= "0" && nextChar <= "9" || nextChar === ".")) {
438
+ return this.parseCallChain();
439
+ }
440
+ }
441
+ return this.parseCallChain();
442
+ }
443
+ parseCallChain() {
444
+ let node = this.parsePrimary();
445
+ while (true) {
446
+ this.skipWhitespace();
447
+ if (this.peek() === "(") {
448
+ this.advance();
449
+ const args = this.parseArgList();
450
+ this.expect(")");
451
+ if (node.type === "identifier") {
452
+ node = { type: "call", name: node.name, args };
453
+ } else if (node.type === "path") {
454
+ const name = node.segments.join(".");
455
+ node = { type: "call", name, args };
456
+ } else if (node.type === "member") {
457
+ node = { type: "method_call", object: node.object, method: node.property, args };
458
+ } else {
459
+ throw new Error("Cannot call non-function");
460
+ }
461
+ } else if (this.peek() === ".") {
462
+ this.advance();
463
+ const prop = this.parseIdentifierName();
464
+ node = { type: "member", object: node, property: prop };
465
+ } else {
466
+ break;
467
+ }
468
+ }
469
+ return node;
470
+ }
471
+ parsePrimary() {
472
+ this.skipWhitespace();
473
+ const ch = this.peek();
474
+ if (ch === "(") {
475
+ this.advance();
476
+ const expr2 = this.parseExpression();
477
+ this.skipWhitespace();
478
+ this.expect(")");
479
+ return expr2;
480
+ }
481
+ if (ch === "'" || ch === '"') {
482
+ return this.parseString();
483
+ }
484
+ if (ch === "-" || ch >= "0" && ch <= "9") {
485
+ return this.parseNumber();
486
+ }
487
+ if (this.isIdentStart(ch)) {
488
+ return this.parseIdentifierOrPath();
489
+ }
490
+ throw new Error(
491
+ `Unexpected character at position ${this.pos}: '${ch || "EOF"}'`
492
+ );
493
+ }
494
+ parseString() {
495
+ const quote = this.advance();
496
+ let value = "";
497
+ while (this.pos < this.input.length && this.peek() !== quote) {
498
+ if (this.peek() === "\\") {
499
+ this.advance();
500
+ const esc = this.advance();
501
+ if (esc === "n") value += "\n";
502
+ else if (esc === "t") value += " ";
503
+ else if (esc === "r") value += "\r";
504
+ else value += esc;
505
+ } else {
506
+ value += this.advance();
507
+ }
508
+ }
509
+ if (this.pos >= this.input.length) {
510
+ throw new Error("Unterminated string literal");
511
+ }
512
+ this.advance();
513
+ return { type: "string", value };
514
+ }
515
+ parseNumber() {
516
+ let numStr = "";
517
+ if (this.peek() === "-") {
518
+ numStr += this.advance();
519
+ }
520
+ while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
521
+ numStr += this.advance();
522
+ }
523
+ if (this.peek() === "." && this.pos + 1 < this.input.length && this.input[this.pos + 1] >= "0" && this.input[this.pos + 1] <= "9") {
524
+ numStr += this.advance();
525
+ while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
526
+ numStr += this.advance();
527
+ }
528
+ }
529
+ return { type: "number", value: Number(numStr) };
530
+ }
531
+ parseIdentifierOrPath() {
532
+ const name = this.parseIdentifierName();
533
+ if (name === "true") return { type: "boolean", value: true };
534
+ if (name === "false") return { type: "boolean", value: false };
535
+ if (name === "null") return { type: "null" };
536
+ if (name === "undefined") return { type: "null" };
537
+ return { type: "identifier", name };
538
+ }
539
+ parseIdentifierName() {
540
+ let name = "";
541
+ if (this.peek() === "$") name += this.advance();
542
+ while (this.pos < this.input.length && this.isIdentPart(this.input[this.pos])) {
543
+ name += this.advance();
544
+ }
545
+ if (!name) {
546
+ throw new Error(`Expected identifier at position ${this.pos}`);
547
+ }
548
+ return name;
549
+ }
550
+ parseArgList() {
551
+ this.skipWhitespace();
552
+ if (this.peek() === ")") return [];
553
+ const args = [];
554
+ args.push(this.parseExpression());
555
+ this.skipWhitespace();
556
+ while (this.peek() === ",") {
557
+ this.advance();
558
+ args.push(this.parseExpression());
559
+ this.skipWhitespace();
560
+ }
561
+ return args;
562
+ }
563
+ // Character utilities
564
+ peek() {
565
+ return this.input[this.pos] ?? "";
566
+ }
567
+ advance() {
568
+ return this.input[this.pos++] ?? "";
569
+ }
570
+ match(str) {
571
+ if (this.input.startsWith(str, this.pos)) {
572
+ this.pos += str.length;
573
+ return true;
574
+ }
575
+ return false;
576
+ }
577
+ lookAhead(str) {
578
+ return this.input.startsWith(str, this.pos);
579
+ }
580
+ expect(ch) {
581
+ this.skipWhitespace();
582
+ if (this.peek() !== ch) {
583
+ throw new Error(`Expected '${ch}' at position ${this.pos}, got '${this.peek() || "EOF"}'`);
584
+ }
585
+ this.advance();
586
+ }
587
+ skipWhitespace() {
588
+ while (this.pos < this.input.length && " \n\r".includes(this.input[this.pos])) {
589
+ this.pos++;
590
+ }
591
+ }
592
+ isIdentStart(ch) {
593
+ return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_" || ch === "$";
594
+ }
595
+ isIdentPart(ch) {
596
+ return this.isIdentStart(ch) || ch >= "0" && ch <= "9";
597
+ }
598
+ };
599
+ function evaluateAST(node, context, fnMap) {
600
+ switch (node.type) {
601
+ case "number":
602
+ return node.value;
603
+ case "string":
604
+ return node.value;
605
+ case "boolean":
606
+ return node.value;
607
+ case "null":
608
+ return null;
609
+ case "identifier":
610
+ return resolvePath(node.name, context);
611
+ case "path":
612
+ return resolvePath(node.segments.join("."), context);
613
+ case "member": {
614
+ const obj = evaluateAST(node.object, context, fnMap);
615
+ if (obj == null || typeof obj !== "object") return void 0;
616
+ return obj[node.property];
617
+ }
618
+ case "call": {
619
+ const fn = fnMap.get(node.name);
620
+ if (!fn) return void 0;
621
+ const args = node.args.map((a) => evaluateAST(a, context, fnMap));
622
+ return fn(...args);
623
+ }
624
+ case "method_call": {
625
+ const obj = evaluateAST(node.object, context, fnMap);
626
+ if (obj != null && typeof obj === "object") {
627
+ const method = obj[node.method];
628
+ if (typeof method === "function") {
629
+ const args = node.args.map((a) => evaluateAST(a, context, fnMap));
630
+ return method.apply(obj, args);
631
+ }
632
+ }
633
+ return void 0;
634
+ }
635
+ case "unary": {
636
+ const operand = evaluateAST(node.operand, context, fnMap);
637
+ return !operand;
638
+ }
639
+ case "binary": {
640
+ if (node.operator === "&&") {
641
+ const left2 = evaluateAST(node.left, context, fnMap);
642
+ if (!left2) return left2;
643
+ return evaluateAST(node.right, context, fnMap);
644
+ }
645
+ if (node.operator === "||") {
646
+ const left2 = evaluateAST(node.left, context, fnMap);
647
+ if (left2) return left2;
648
+ return evaluateAST(node.right, context, fnMap);
649
+ }
650
+ const left = evaluateAST(node.left, context, fnMap);
651
+ const right = evaluateAST(node.right, context, fnMap);
652
+ switch (node.operator) {
653
+ // eslint-disable-next-line eqeqeq
654
+ case "==":
655
+ return left == right;
656
+ // eslint-disable-next-line eqeqeq
657
+ case "!=":
658
+ return left != right;
659
+ case ">":
660
+ return Number(left) > Number(right);
661
+ case "<":
662
+ return Number(left) < Number(right);
663
+ case ">=":
664
+ return Number(left) >= Number(right);
665
+ case "<=":
666
+ return Number(left) <= Number(right);
667
+ default:
668
+ return void 0;
669
+ }
670
+ }
671
+ case "ternary": {
672
+ const condition = evaluateAST(node.condition, context, fnMap);
673
+ return condition ? evaluateAST(node.consequent, context, fnMap) : evaluateAST(node.alternate, context, fnMap);
674
+ }
675
+ }
676
+ }
677
+ var MAX_CACHE = 500;
678
+ var astCache = /* @__PURE__ */ new Map();
679
+ function evictIfNeeded() {
680
+ if (astCache.size > MAX_CACHE) {
681
+ const keys = Array.from(astCache.keys());
682
+ const evictCount = Math.floor(MAX_CACHE * 0.25);
683
+ for (let i = 0; i < evictCount; i++) {
684
+ astCache.delete(keys[i]);
685
+ }
686
+ }
687
+ }
688
+ function parseAndCache(expr2) {
689
+ const cached = astCache.get(expr2);
690
+ if (cached) return cached;
691
+ const parser = new Parser(expr2);
692
+ const ast = parser.parse();
693
+ astCache.set(expr2, ast);
694
+ evictIfNeeded();
695
+ return ast;
696
+ }
697
+ var TEMPLATE_RE = /\{\{(.+?)\}\}/g;
698
+ function resolvePath(path, context) {
699
+ const parts = path.split(".");
700
+ let current = context;
701
+ for (const part of parts) {
702
+ if (current == null || typeof current !== "object") return void 0;
703
+ current = current[part];
704
+ }
705
+ return current;
706
+ }
707
+ function evaluateExpression2(expr2, context, fnMap) {
708
+ const trimmed = expr2.trim();
709
+ if (trimmed === "true") return true;
710
+ if (trimmed === "false") return false;
711
+ if (trimmed === "null") return null;
712
+ if (trimmed === "undefined") return void 0;
713
+ const num = Number(trimmed);
714
+ if (!isNaN(num) && trimmed !== "") return num;
715
+ if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"')) {
716
+ return trimmed.slice(1, -1);
717
+ }
718
+ if (/^[a-zA-Z_$][\w$.]*$/.test(trimmed)) {
719
+ return resolvePath(trimmed, context);
720
+ }
721
+ const ast = parseAndCache(trimmed);
722
+ return evaluateAST(ast, context, fnMap);
723
+ }
724
+ var WEB_FAILURE_POLICIES = {
725
+ VIEW_BINDING: {
726
+ on_error: "return_fallback",
727
+ fallback_value: "",
728
+ log_level: "warn"
729
+ },
730
+ EVENT_REACTION: {
731
+ on_error: "log_and_skip",
732
+ fallback_value: void 0,
733
+ log_level: "error"
734
+ },
735
+ DURING_ACTION: {
736
+ on_error: "log_and_skip",
737
+ fallback_value: void 0,
738
+ log_level: "error"
739
+ },
740
+ CONDITIONAL_VISIBILITY: {
741
+ on_error: "return_fallback",
742
+ fallback_value: true,
743
+ // Show by default if condition fails
744
+ log_level: "warn"
745
+ }
746
+ };
747
+ function createEvaluator(config) {
748
+ const allFunctions = [...CORE_FUNCTIONS, ...config.functions];
749
+ const fnMap = buildFunctionMap(allFunctions);
750
+ const policy = config.failurePolicy;
751
+ function handleError(expr2, error) {
752
+ const message = error instanceof Error ? error.message : String(error);
753
+ if (policy.log_level === "error") {
754
+ console.error(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
755
+ } else if (policy.log_level === "warn") {
756
+ console.warn(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
757
+ }
758
+ switch (policy.on_error) {
759
+ case "throw":
760
+ throw error;
761
+ case "return_fallback":
762
+ return { value: policy.fallback_value, status: "fallback", error: message };
763
+ case "log_and_skip":
764
+ default:
765
+ return { value: policy.fallback_value, status: "error", error: message };
766
+ }
767
+ }
768
+ return {
769
+ evaluate(expression, context) {
770
+ try {
771
+ const value = evaluateExpression2(expression, context, fnMap);
772
+ return { value, status: "ok" };
773
+ } catch (error) {
774
+ return handleError(expression, error);
775
+ }
776
+ },
777
+ evaluateTemplate(template, context) {
778
+ try {
779
+ if (!template.includes("{{")) {
780
+ return { value: template, status: "ok" };
781
+ }
782
+ const result = template.replace(TEMPLATE_RE, (_match, expr2) => {
783
+ const value = evaluateExpression2(expr2, context, fnMap);
784
+ return value != null ? String(value) : "";
785
+ });
786
+ return { value: result, status: "ok" };
787
+ } catch (error) {
788
+ return handleError(template, error);
789
+ }
790
+ },
791
+ validate(expression) {
792
+ const errors = [];
793
+ try {
794
+ parseAndCache(expression);
795
+ } catch (e) {
796
+ errors.push(e instanceof Error ? e.message : String(e));
797
+ }
798
+ return { valid: errors.length === 0, errors };
799
+ }
800
+ };
801
+ }
802
+ var MAX_AUTO_CHAIN = 10;
803
+ var StateMachine = class {
804
+ evaluator;
805
+ actionHandlers;
806
+ listeners = /* @__PURE__ */ new Set();
807
+ instance;
808
+ constructor(definition, initialData = {}, config) {
809
+ this.evaluator = config.evaluator;
810
+ this.actionHandlers = config.actionHandlers ?? /* @__PURE__ */ new Map();
811
+ const startState = definition.states.find((s) => s.type === "START");
812
+ if (!startState) {
813
+ throw new Error(`No START state found in definition ${definition.slug}`);
814
+ }
815
+ this.instance = {
816
+ definition,
817
+ current_state: startState.name,
818
+ state_data: { ...initialData },
819
+ memory: {},
820
+ status: "ACTIVE"
821
+ };
822
+ }
823
+ /** Get the current instance snapshot (immutable copy) */
824
+ getSnapshot() {
825
+ return { ...this.instance, state_data: { ...this.instance.state_data }, memory: { ...this.instance.memory } };
826
+ }
827
+ /** Get current state name */
828
+ get currentState() {
829
+ return this.instance.current_state;
830
+ }
831
+ /** Get current state_data */
832
+ get stateData() {
833
+ return this.instance.state_data;
834
+ }
835
+ /** Get current status */
836
+ get status() {
837
+ return this.instance.status;
838
+ }
839
+ /** Subscribe to state machine events */
840
+ on(listener) {
841
+ this.listeners.add(listener);
842
+ return () => this.listeners.delete(listener);
843
+ }
844
+ /** Register an action handler */
845
+ registerAction(type, handler) {
846
+ this.actionHandlers.set(type, handler);
847
+ }
848
+ /** Execute a named transition */
849
+ async transition(transitionName, data) {
850
+ if (this.instance.status !== "ACTIVE") {
851
+ return {
852
+ success: false,
853
+ from_state: this.instance.current_state,
854
+ to_state: this.instance.current_state,
855
+ actions_executed: [],
856
+ error: `Cannot transition: instance status is ${this.instance.status}`
857
+ };
858
+ }
859
+ const transition2 = this.instance.definition.transitions.find(
860
+ (t) => t.name === transitionName && t.from.includes(this.instance.current_state)
861
+ );
862
+ if (!transition2) {
863
+ return {
864
+ success: false,
865
+ from_state: this.instance.current_state,
866
+ to_state: this.instance.current_state,
867
+ actions_executed: [],
868
+ error: `Transition "${transitionName}" not valid from state "${this.instance.current_state}"`
869
+ };
870
+ }
871
+ if (data) {
872
+ this.instance.state_data = { ...this.instance.state_data, ...data };
873
+ }
874
+ if (transition2.conditions && transition2.conditions.length > 0) {
875
+ const ctx = this.buildContext();
876
+ for (const condition of transition2.conditions) {
877
+ const result2 = this.evaluator.evaluate(condition, ctx);
878
+ if (!result2.value) {
879
+ return {
880
+ success: false,
881
+ from_state: this.instance.current_state,
882
+ to_state: this.instance.current_state,
883
+ actions_executed: [],
884
+ error: `Transition condition not met: ${condition}`
885
+ };
886
+ }
887
+ }
888
+ }
889
+ const result = await this.executeTransition(transition2);
890
+ if (result.success) {
891
+ await this.drainAutoTransitions();
892
+ }
893
+ return result;
894
+ }
895
+ /** Update state_data directly (for on_event set_field actions) */
896
+ setField(field2, value) {
897
+ this.instance.state_data = { ...this.instance.state_data, [field2]: value };
898
+ }
899
+ /** Update memory */
900
+ setMemory(key, value) {
901
+ this.instance.memory = { ...this.instance.memory, [key]: value };
902
+ }
903
+ /** Get available transitions from the current state */
904
+ getAvailableTransitions() {
905
+ return this.instance.definition.transitions.filter(
906
+ (t) => t.from.includes(this.instance.current_state) && !t.auto
907
+ );
908
+ }
909
+ /** Get the current state definition */
910
+ getCurrentStateDefinition() {
911
+ return this.instance.definition.states.find((s) => s.name === this.instance.current_state);
912
+ }
913
+ // ===========================================================================
914
+ // Private implementation
915
+ // ===========================================================================
916
+ async executeTransition(transition2) {
917
+ const fromState = this.instance.current_state;
918
+ const allActionsExecuted = [];
919
+ const fromStateDef = this.getCurrentStateDefinition();
920
+ if (fromStateDef?.on_exit) {
921
+ await this.executeActions(fromStateDef.on_exit, allActionsExecuted);
922
+ }
923
+ this.emit({
924
+ type: "state_exit",
925
+ instance_id: this.instance.definition.id,
926
+ from_state: fromState
927
+ });
928
+ if (transition2.actions) {
929
+ await this.executeActions(transition2.actions, allActionsExecuted);
930
+ }
931
+ this.instance.current_state = transition2.to;
932
+ const toStateDef = this.instance.definition.states.find((s) => s.name === transition2.to);
933
+ if (toStateDef?.type === "END") {
934
+ this.instance.status = "COMPLETED";
935
+ } else if (toStateDef?.type === "CANCELLED") {
936
+ this.instance.status = "CANCELLED";
937
+ }
938
+ this.emit({
939
+ type: "state_enter",
940
+ instance_id: this.instance.definition.id,
941
+ to_state: transition2.to
942
+ });
943
+ if (toStateDef?.on_enter) {
944
+ await this.executeActions(toStateDef.on_enter, allActionsExecuted);
945
+ }
946
+ this.emit({
947
+ type: "transition",
948
+ instance_id: this.instance.definition.id,
949
+ from_state: fromState,
950
+ to_state: transition2.to
951
+ });
952
+ return {
953
+ success: true,
954
+ from_state: fromState,
955
+ to_state: transition2.to,
956
+ actions_executed: allActionsExecuted
957
+ };
958
+ }
959
+ async drainAutoTransitions() {
960
+ for (let depth = 0; depth < MAX_AUTO_CHAIN; depth++) {
961
+ if (this.instance.status !== "ACTIVE") break;
962
+ const autoTransition = this.findMatchingAutoTransition();
963
+ if (!autoTransition) break;
964
+ const result = await this.executeTransition(autoTransition);
965
+ if (!result.success) break;
966
+ }
967
+ }
968
+ findMatchingAutoTransition() {
969
+ const candidates = this.instance.definition.transitions.filter(
970
+ (t) => t.auto && t.from.includes(this.instance.current_state)
971
+ );
972
+ const ctx = this.buildContext();
973
+ for (const candidate of candidates) {
974
+ if (!candidate.conditions || candidate.conditions.length === 0) {
975
+ return candidate;
976
+ }
977
+ const allMet = candidate.conditions.every((condition) => {
978
+ const result = this.evaluator.evaluate(condition, ctx);
979
+ return result.value === true;
980
+ });
981
+ if (allMet) return candidate;
982
+ }
983
+ return null;
984
+ }
985
+ async executeActions(actions, collector) {
986
+ const ctx = this.buildContext();
987
+ for (const action2 of actions) {
988
+ if (action2.condition) {
989
+ const condResult = this.evaluator.evaluate(action2.condition, ctx);
990
+ if (!condResult.value) continue;
991
+ }
992
+ const handler = this.actionHandlers.get(action2.type);
993
+ if (handler) {
994
+ try {
995
+ await handler(action2, ctx);
996
+ collector.push(action2);
997
+ this.emit({
998
+ type: "action_executed",
999
+ instance_id: this.instance.definition.id,
1000
+ action: action2
1001
+ });
1002
+ } catch (error) {
1003
+ this.emit({
1004
+ type: "error",
1005
+ instance_id: this.instance.definition.id,
1006
+ action: action2,
1007
+ error: error instanceof Error ? error.message : String(error)
1008
+ });
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+ buildContext() {
1014
+ return {
1015
+ state_data: this.instance.state_data,
1016
+ memory: this.instance.memory,
1017
+ current_state: this.instance.current_state,
1018
+ status: this.instance.status,
1019
+ // Spread state_data for direct field access (e.g., "title" instead of "state_data.title")
1020
+ ...this.instance.state_data
1021
+ };
1022
+ }
1023
+ emit(event) {
1024
+ for (const listener of this.listeners) {
1025
+ try {
1026
+ listener(event);
1027
+ } catch {
1028
+ }
1029
+ }
1030
+ }
1031
+ };
1032
+ var patternCache = /* @__PURE__ */ new Map();
1033
+ var MAX_CACHE2 = 200;
1034
+ function compilePattern(pattern) {
1035
+ const cached = patternCache.get(pattern);
1036
+ if (cached) return cached;
1037
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<DOUBLESTAR>>").replace(/\*/g, "[^:.]+").replace(/<<DOUBLESTAR>>/g, ".*");
1038
+ const regex = new RegExp(`^${escaped}$`);
1039
+ if (patternCache.size >= MAX_CACHE2) {
1040
+ const firstKey = patternCache.keys().next().value;
1041
+ if (firstKey) patternCache.delete(firstKey);
1042
+ }
1043
+ patternCache.set(pattern, regex);
1044
+ return regex;
1045
+ }
1046
+ function matchTopic(pattern, topic) {
1047
+ return pattern.test(topic);
1048
+ }
1049
+ var EventBus = class {
1050
+ subscriptions = [];
1051
+ /**
1052
+ * Subscribe to events matching a glob pattern.
1053
+ * Returns an unsubscribe function.
1054
+ */
1055
+ subscribe(pattern, handler) {
1056
+ const regex = compilePattern(pattern);
1057
+ const subscription = { pattern, regex, handler };
1058
+ this.subscriptions.push(subscription);
1059
+ return () => {
1060
+ const idx = this.subscriptions.indexOf(subscription);
1061
+ if (idx !== -1) this.subscriptions.splice(idx, 1);
1062
+ };
1063
+ }
1064
+ /**
1065
+ * Publish an event. All matching subscriptions fire (async).
1066
+ * Errors in handlers are caught and logged, never propagated.
1067
+ */
1068
+ async publish(topic, payload = {}) {
1069
+ const event = { topic, payload };
1070
+ for (const sub of this.subscriptions) {
1071
+ if (matchTopic(sub.regex, topic)) {
1072
+ try {
1073
+ await sub.handler(event);
1074
+ } catch (error) {
1075
+ console.warn(
1076
+ `[player-core] Event handler error for pattern "${sub.pattern}" on topic "${topic}":`,
1077
+ error
1078
+ );
1079
+ }
1080
+ }
1081
+ }
1082
+ }
1083
+ /**
1084
+ * Publish synchronously (fire-and-forget).
1085
+ * Useful when you don't need to await handler completion.
1086
+ */
1087
+ emit(topic, payload = {}) {
1088
+ void this.publish(topic, payload);
1089
+ }
1090
+ /** Get count of active subscriptions */
1091
+ get size() {
1092
+ return this.subscriptions.length;
1093
+ }
1094
+ /** Remove all subscriptions */
1095
+ clear() {
1096
+ this.subscriptions.length = 0;
1097
+ }
1098
+ };
1099
+ var ActionDispatcher = class {
1100
+ handlers = /* @__PURE__ */ new Map();
1101
+ /** Register a handler for an action type */
1102
+ register(type, handler) {
1103
+ this.handlers.set(type, handler);
1104
+ }
1105
+ /** Unregister a handler */
1106
+ unregister(type) {
1107
+ this.handlers.delete(type);
1108
+ }
1109
+ /** Check if a handler is registered for the given type */
1110
+ has(type) {
1111
+ return this.handlers.has(type);
1112
+ }
1113
+ /**
1114
+ * Execute a list of actions sequentially.
1115
+ * Each action's condition is evaluated first (if present).
1116
+ * Missing handlers are skipped with a warning.
1117
+ */
1118
+ async execute(actions, context, evaluator) {
1119
+ const results = [];
1120
+ for (const action2 of actions) {
1121
+ if (action2.condition && evaluator) {
1122
+ const condResult = evaluator.evaluate(action2.condition, context);
1123
+ if (!condResult.value) continue;
1124
+ }
1125
+ const handler = this.handlers.get(action2.type);
1126
+ if (!handler) {
1127
+ console.warn(`[player-core] No handler registered for action type "${action2.type}" \u2014 unsupported action`);
1128
+ results.push({ type: action2.type, success: false, error: `unsupported action: "${action2.type}"` });
1129
+ continue;
1130
+ }
1131
+ try {
1132
+ await handler(action2.config, context);
1133
+ results.push({ type: action2.type, success: true });
1134
+ } catch (error) {
1135
+ const message = error instanceof Error ? error.message : String(error);
1136
+ console.warn(`[player-core] Action "${action2.type}" failed: ${message}`);
1137
+ results.push({ type: action2.type, success: false, error: message });
1138
+ }
1139
+ }
1140
+ return results;
1141
+ }
1142
+ /** Get count of registered handlers */
1143
+ get size() {
1144
+ return this.handlers.size;
1145
+ }
1146
+ /** Get all registered action type names */
1147
+ getRegisteredTypes() {
1148
+ return Array.from(this.handlers.keys());
1149
+ }
1150
+ /** Remove all handlers */
1151
+ clear() {
1152
+ this.handlers.clear();
1153
+ }
1154
+ };
1155
+
69
1156
  // src/core/WorkflowRuntime.ts
70
- import {
71
- StateMachine,
72
- EventBus,
73
- ActionDispatcher,
74
- createEvaluator,
75
- WEB_FAILURE_POLICIES
76
- } from "@mmapp/player-core";
77
1157
  var WorkflowRuntime = class {
78
1158
  sm;
79
1159
  eventBus;
@@ -3303,13 +4383,13 @@ function inState(state2) {
3303
4383
  function notInState(state2) {
3304
4384
  return { type: "expression", expression: `current_state != "${state2}"` };
3305
4385
  }
3306
- function and(...conditions) {
4386
+ function and2(...conditions) {
3307
4387
  return { AND: conditions.map(normalize) };
3308
4388
  }
3309
- function or(...conditions) {
4389
+ function or2(...conditions) {
3310
4390
  return { OR: conditions.map(normalize) };
3311
4391
  }
3312
- function not(condition) {
4392
+ function not2(condition) {
3313
4393
  const c = normalize(condition);
3314
4394
  if (c.type === "expression" && c.expression) {
3315
4395
  return { type: "expression", expression: `NOT(${c.expression})` };
@@ -5860,13 +6940,6 @@ function describeModel(def) {
5860
6940
 
5861
6941
  // src/hooks/usePlayer.ts
5862
6942
  import { useCallback as useCallback20, useEffect as useEffect21, useMemo as useMemo19, useRef as useRef23, useState as useState21 } from "react";
5863
- import {
5864
- StateMachine as StateMachine2,
5865
- EventBus as EventBus2,
5866
- ActionDispatcher as ActionDispatcher2,
5867
- createEvaluator as createEvaluator2,
5868
- WEB_FAILURE_POLICIES as WEB_FAILURE_POLICIES2
5869
- } from "@mmapp/player-core";
5870
6943
 
5871
6944
  // src/logger.ts
5872
6945
  var debugEnabled = false;
@@ -5912,9 +6985,9 @@ function usePlayer(config) {
5912
6985
  if (config.debug) setPlayerDebug(true);
5913
6986
  }, [config.debug]);
5914
6987
  const evaluator = useMemo19(() => {
5915
- return createEvaluator2({
6988
+ return createEvaluator({
5916
6989
  functions: config.functions ?? [],
5917
- failurePolicy: WEB_FAILURE_POLICIES2.EVENT_REACTION
6990
+ failurePolicy: WEB_FAILURE_POLICIES.EVENT_REACTION
5918
6991
  });
5919
6992
  }, [config.definition.id]);
5920
6993
  const engine = useMemo19(() => {
@@ -5945,14 +7018,14 @@ function usePlayer(config) {
5945
7018
  });
5946
7019
  }
5947
7020
  });
5948
- const sm2 = new StateMachine2(
7021
+ const sm2 = new StateMachine(
5949
7022
  config.definition,
5950
7023
  config.initialData ?? {},
5951
7024
  { evaluator, actionHandlers }
5952
7025
  );
5953
7026
  smRef = sm2;
5954
- const eventBus2 = new EventBus2();
5955
- const dispatcher = new ActionDispatcher2();
7027
+ const eventBus2 = new EventBus();
7028
+ const dispatcher = new ActionDispatcher();
5956
7029
  dispatcher.register("set_field", (cfg) => {
5957
7030
  if (smRef && typeof cfg.field === "string") {
5958
7031
  smRef.setField(cfg.field, cfg.value);
@@ -7398,7 +8471,7 @@ export {
7398
8471
  actor,
7399
8472
  after,
7400
8473
  allowTransition,
7401
- and,
8474
+ and2 as and,
7402
8475
  applyMixins,
7403
8476
  approval,
7404
8477
  assertModelValid,
@@ -7486,11 +8559,11 @@ export {
7486
8559
  model,
7487
8560
  named,
7488
8561
  normalizeDefinition,
7489
- not,
8562
+ not2 as not,
7490
8563
  notInState,
7491
8564
  notify,
7492
8565
  on,
7493
- or,
8566
+ or2 as or,
7494
8567
  orchestration,
7495
8568
  patch,
7496
8569
  pipe,