@mmapp/react 0.1.0-alpha.22 → 0.1.0-alpha.23
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 +1124 -39
- package/dist/index.mjs +1098 -25
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ function classifyUncached(expr2) {
|
|
|
54
54
|
if (/^[a-zA-Z_]\w*$/.test(t)) return "path";
|
|
55
55
|
return "condition";
|
|
56
56
|
}
|
|
57
|
-
function
|
|
57
|
+
function resolvePath2(path, scope) {
|
|
58
58
|
const parts = path.split(".");
|
|
59
59
|
let current = scope;
|
|
60
60
|
for (const part of parts) {
|
|
@@ -101,7 +101,7 @@ function compileToFunction(expr2, paramNames) {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
function
|
|
104
|
+
function evaluateExpression2(expr2, context) {
|
|
105
105
|
const trimmed = expr2.trim();
|
|
106
106
|
if (!trimmed) return void 0;
|
|
107
107
|
const cls = classifyExpression(trimmed);
|
|
@@ -147,9 +147,9 @@ function resolveScopePath(expr2, context) {
|
|
|
147
147
|
const rest = trimmed.slice(dotIdx + 1);
|
|
148
148
|
const rootVal = context[root];
|
|
149
149
|
if (rootVal == null) return void 0;
|
|
150
|
-
return
|
|
150
|
+
return resolvePath2(rest, rootVal);
|
|
151
151
|
}
|
|
152
|
-
return
|
|
152
|
+
return resolvePath2(trimmed, context);
|
|
153
153
|
}
|
|
154
154
|
function translateForWasm(expr2) {
|
|
155
155
|
return expr2.replace(/\$fn\./g, "").replace(/\$instance\./g, "state_data.").replace(/\$data\./g, "state_data.").replace(/\$row\./g, "state_data.");
|
|
@@ -884,8 +884,8 @@ var init_content = __esm({
|
|
|
884
884
|
}
|
|
885
885
|
);
|
|
886
886
|
};
|
|
887
|
-
Progress = ({ value, max, label, variant, className, style }) => {
|
|
888
|
-
const pct = Math.min(100, Math.max(0, Number(value) / (Number(
|
|
887
|
+
Progress = ({ value, max: max2, label, variant, className, style }) => {
|
|
888
|
+
const pct = Math.min(100, Math.max(0, Number(value) / (Number(max2) || 100) * 100));
|
|
889
889
|
const colorMap = {
|
|
890
890
|
default: "#3182ce",
|
|
891
891
|
success: "#38a169",
|
|
@@ -1274,8 +1274,8 @@ var init_input = __esm({
|
|
|
1274
1274
|
}
|
|
1275
1275
|
);
|
|
1276
1276
|
};
|
|
1277
|
-
Slider = ({ value, onChange, min, max, step, label, disabled, className, style }) => {
|
|
1278
|
-
const numVal = Number(value) || Number(
|
|
1277
|
+
Slider = ({ value, onChange, min: min2, max: max2, step, label, disabled, className, style }) => {
|
|
1278
|
+
const numVal = Number(value) || Number(min2) || 0;
|
|
1279
1279
|
const handleChange = (0, import_react37.useCallback)((e) => {
|
|
1280
1280
|
if (typeof onChange === "function") onChange(Number(e.target.value));
|
|
1281
1281
|
}, [onChange]);
|
|
@@ -1290,8 +1290,8 @@ var init_input = __esm({
|
|
|
1290
1290
|
type: "range",
|
|
1291
1291
|
value: numVal,
|
|
1292
1292
|
onChange: handleChange,
|
|
1293
|
-
min: Number(
|
|
1294
|
-
max: Number(
|
|
1293
|
+
min: Number(min2) ?? 0,
|
|
1294
|
+
max: Number(max2) ?? 100,
|
|
1295
1295
|
step: Number(step) ?? 1,
|
|
1296
1296
|
disabled: Boolean(disabled),
|
|
1297
1297
|
style: { width: "100%" }
|
|
@@ -1939,7 +1939,7 @@ __export(index_exports, {
|
|
|
1939
1939
|
actor: () => actor,
|
|
1940
1940
|
after: () => after,
|
|
1941
1941
|
allowTransition: () => allowTransition,
|
|
1942
|
-
and: () =>
|
|
1942
|
+
and: () => and2,
|
|
1943
1943
|
applyMixins: () => applyMixins,
|
|
1944
1944
|
approval: () => approval,
|
|
1945
1945
|
assertModelValid: () => assertModelValid,
|
|
@@ -2027,15 +2027,15 @@ __export(index_exports, {
|
|
|
2027
2027
|
model: () => model,
|
|
2028
2028
|
named: () => named,
|
|
2029
2029
|
normalizeDefinition: () => normalizeDefinition,
|
|
2030
|
-
not: () =>
|
|
2030
|
+
not: () => not2,
|
|
2031
2031
|
notInState: () => notInState,
|
|
2032
2032
|
notify: () => notify,
|
|
2033
2033
|
on: () => on,
|
|
2034
|
-
or: () =>
|
|
2034
|
+
or: () => or2,
|
|
2035
2035
|
orchestration: () => orchestration,
|
|
2036
2036
|
patch: () => patch,
|
|
2037
2037
|
pipe: () => pipe,
|
|
2038
|
-
playerEvaluateExpression: () =>
|
|
2038
|
+
playerEvaluateExpression: () => evaluateExpression2,
|
|
2039
2039
|
playerLog: () => playerLog,
|
|
2040
2040
|
prefetchData: () => prefetchData,
|
|
2041
2041
|
refHasAnyRole: () => refHasAnyRole,
|
|
@@ -2150,8 +2150,1094 @@ __export(index_exports, {
|
|
|
2150
2150
|
});
|
|
2151
2151
|
module.exports = __toCommonJS(index_exports);
|
|
2152
2152
|
|
|
2153
|
+
// ../player-core/dist/index.mjs
|
|
2154
|
+
var add = {
|
|
2155
|
+
name: "add",
|
|
2156
|
+
fn: (a, b) => Number(a) + Number(b),
|
|
2157
|
+
arity: 2
|
|
2158
|
+
};
|
|
2159
|
+
var subtract = {
|
|
2160
|
+
name: "subtract",
|
|
2161
|
+
fn: (a, b) => Number(a) - Number(b),
|
|
2162
|
+
arity: 2
|
|
2163
|
+
};
|
|
2164
|
+
var multiply = {
|
|
2165
|
+
name: "multiply",
|
|
2166
|
+
fn: (a, b) => Number(a) * Number(b),
|
|
2167
|
+
arity: 2
|
|
2168
|
+
};
|
|
2169
|
+
var divide = {
|
|
2170
|
+
name: "divide",
|
|
2171
|
+
fn: (a, b) => {
|
|
2172
|
+
const d = Number(b);
|
|
2173
|
+
return d === 0 ? 0 : Number(a) / d;
|
|
2174
|
+
},
|
|
2175
|
+
arity: 2
|
|
2176
|
+
};
|
|
2177
|
+
var abs = {
|
|
2178
|
+
name: "abs",
|
|
2179
|
+
fn: (a) => Math.abs(Number(a)),
|
|
2180
|
+
arity: 1
|
|
2181
|
+
};
|
|
2182
|
+
var round = {
|
|
2183
|
+
name: "round",
|
|
2184
|
+
fn: (a, decimals) => {
|
|
2185
|
+
const d = decimals != null ? Number(decimals) : 0;
|
|
2186
|
+
const factor = Math.pow(10, d);
|
|
2187
|
+
return Math.round(Number(a) * factor) / factor;
|
|
2188
|
+
},
|
|
2189
|
+
arity: -1
|
|
2190
|
+
};
|
|
2191
|
+
var min = {
|
|
2192
|
+
name: "min",
|
|
2193
|
+
fn: (...args) => {
|
|
2194
|
+
const nums = args.flat().map(Number).filter((n) => !isNaN(n));
|
|
2195
|
+
return nums.length === 0 ? 0 : Math.min(...nums);
|
|
2196
|
+
},
|
|
2197
|
+
arity: -1
|
|
2198
|
+
};
|
|
2199
|
+
var max = {
|
|
2200
|
+
name: "max",
|
|
2201
|
+
fn: (...args) => {
|
|
2202
|
+
const nums = args.flat().map(Number).filter((n) => !isNaN(n));
|
|
2203
|
+
return nums.length === 0 ? 0 : Math.max(...nums);
|
|
2204
|
+
},
|
|
2205
|
+
arity: -1
|
|
2206
|
+
};
|
|
2207
|
+
var eq = {
|
|
2208
|
+
name: "eq",
|
|
2209
|
+
fn: (a, b) => a === b || String(a) === String(b),
|
|
2210
|
+
arity: 2
|
|
2211
|
+
};
|
|
2212
|
+
var neq = {
|
|
2213
|
+
name: "neq",
|
|
2214
|
+
fn: (a, b) => a !== b && String(a) !== String(b),
|
|
2215
|
+
arity: 2
|
|
2216
|
+
};
|
|
2217
|
+
var gt = {
|
|
2218
|
+
name: "gt",
|
|
2219
|
+
fn: (a, b) => Number(a) > Number(b),
|
|
2220
|
+
arity: 2
|
|
2221
|
+
};
|
|
2222
|
+
var gte = {
|
|
2223
|
+
name: "gte",
|
|
2224
|
+
fn: (a, b) => Number(a) >= Number(b),
|
|
2225
|
+
arity: 2
|
|
2226
|
+
};
|
|
2227
|
+
var lt = {
|
|
2228
|
+
name: "lt",
|
|
2229
|
+
fn: (a, b) => Number(a) < Number(b),
|
|
2230
|
+
arity: 2
|
|
2231
|
+
};
|
|
2232
|
+
var lte = {
|
|
2233
|
+
name: "lte",
|
|
2234
|
+
fn: (a, b) => Number(a) <= Number(b),
|
|
2235
|
+
arity: 2
|
|
2236
|
+
};
|
|
2237
|
+
var if_fn = {
|
|
2238
|
+
name: "if",
|
|
2239
|
+
fn: (cond, then, else_) => cond ? then : else_,
|
|
2240
|
+
arity: 3
|
|
2241
|
+
};
|
|
2242
|
+
var and = {
|
|
2243
|
+
name: "and",
|
|
2244
|
+
fn: (...args) => args.every(Boolean),
|
|
2245
|
+
arity: -1
|
|
2246
|
+
};
|
|
2247
|
+
var or = {
|
|
2248
|
+
name: "or",
|
|
2249
|
+
fn: (...args) => args.some(Boolean),
|
|
2250
|
+
arity: -1
|
|
2251
|
+
};
|
|
2252
|
+
var not = {
|
|
2253
|
+
name: "not",
|
|
2254
|
+
fn: (a) => !a,
|
|
2255
|
+
arity: 1
|
|
2256
|
+
};
|
|
2257
|
+
var coalesce = {
|
|
2258
|
+
name: "coalesce",
|
|
2259
|
+
fn: (...args) => {
|
|
2260
|
+
for (const arg of args) {
|
|
2261
|
+
if (arg != null) return arg;
|
|
2262
|
+
}
|
|
2263
|
+
return null;
|
|
2264
|
+
},
|
|
2265
|
+
arity: -1
|
|
2266
|
+
};
|
|
2267
|
+
var concat = {
|
|
2268
|
+
name: "concat",
|
|
2269
|
+
fn: (...args) => args.map(String).join(""),
|
|
2270
|
+
arity: -1
|
|
2271
|
+
};
|
|
2272
|
+
var upper = {
|
|
2273
|
+
name: "upper",
|
|
2274
|
+
fn: (s) => String(s ?? "").toUpperCase(),
|
|
2275
|
+
arity: 1
|
|
2276
|
+
};
|
|
2277
|
+
var lower = {
|
|
2278
|
+
name: "lower",
|
|
2279
|
+
fn: (s) => String(s ?? "").toLowerCase(),
|
|
2280
|
+
arity: 1
|
|
2281
|
+
};
|
|
2282
|
+
var trim = {
|
|
2283
|
+
name: "trim",
|
|
2284
|
+
fn: (s) => String(s ?? "").trim(),
|
|
2285
|
+
arity: 1
|
|
2286
|
+
};
|
|
2287
|
+
var format = {
|
|
2288
|
+
name: "format",
|
|
2289
|
+
fn: (template, ...args) => {
|
|
2290
|
+
let result = String(template ?? "");
|
|
2291
|
+
args.forEach((arg, i) => {
|
|
2292
|
+
result = result.replace(`{${i}}`, String(arg ?? ""));
|
|
2293
|
+
});
|
|
2294
|
+
return result;
|
|
2295
|
+
},
|
|
2296
|
+
arity: -1
|
|
2297
|
+
};
|
|
2298
|
+
var length = {
|
|
2299
|
+
name: "length",
|
|
2300
|
+
fn: (v) => {
|
|
2301
|
+
if (Array.isArray(v)) return v.length;
|
|
2302
|
+
if (typeof v === "string") return v.length;
|
|
2303
|
+
if (v && typeof v === "object") return Object.keys(v).length;
|
|
2304
|
+
return 0;
|
|
2305
|
+
},
|
|
2306
|
+
arity: 1
|
|
2307
|
+
};
|
|
2308
|
+
var get = {
|
|
2309
|
+
name: "get",
|
|
2310
|
+
fn: (obj, path) => {
|
|
2311
|
+
if (obj == null || typeof path !== "string") return void 0;
|
|
2312
|
+
const parts = path.split(".");
|
|
2313
|
+
let current = obj;
|
|
2314
|
+
for (const part of parts) {
|
|
2315
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
2316
|
+
current = current[part];
|
|
2317
|
+
}
|
|
2318
|
+
return current;
|
|
2319
|
+
},
|
|
2320
|
+
arity: 2
|
|
2321
|
+
};
|
|
2322
|
+
var includes = {
|
|
2323
|
+
name: "includes",
|
|
2324
|
+
fn: (collection, value) => {
|
|
2325
|
+
if (Array.isArray(collection)) return collection.includes(value);
|
|
2326
|
+
if (typeof collection === "string") return collection.includes(String(value));
|
|
2327
|
+
return false;
|
|
2328
|
+
},
|
|
2329
|
+
arity: 2
|
|
2330
|
+
};
|
|
2331
|
+
var is_defined = {
|
|
2332
|
+
name: "is_defined",
|
|
2333
|
+
fn: (v) => v !== void 0 && v !== null,
|
|
2334
|
+
arity: 1
|
|
2335
|
+
};
|
|
2336
|
+
var is_empty = {
|
|
2337
|
+
name: "is_empty",
|
|
2338
|
+
fn: (v) => {
|
|
2339
|
+
if (v == null) return true;
|
|
2340
|
+
if (typeof v === "string") return v.length === 0;
|
|
2341
|
+
if (Array.isArray(v)) return v.length === 0;
|
|
2342
|
+
if (typeof v === "object") return Object.keys(v).length === 0;
|
|
2343
|
+
return false;
|
|
2344
|
+
},
|
|
2345
|
+
arity: 1
|
|
2346
|
+
};
|
|
2347
|
+
var is_null = {
|
|
2348
|
+
name: "is_null",
|
|
2349
|
+
fn: (v) => v === null || v === void 0,
|
|
2350
|
+
arity: 1
|
|
2351
|
+
};
|
|
2352
|
+
var to_string = {
|
|
2353
|
+
name: "to_string",
|
|
2354
|
+
fn: (v) => {
|
|
2355
|
+
if (v == null) return "";
|
|
2356
|
+
if (typeof v === "object") return JSON.stringify(v);
|
|
2357
|
+
return String(v);
|
|
2358
|
+
},
|
|
2359
|
+
arity: 1
|
|
2360
|
+
};
|
|
2361
|
+
var CORE_FUNCTIONS = [
|
|
2362
|
+
// Math (8)
|
|
2363
|
+
add,
|
|
2364
|
+
subtract,
|
|
2365
|
+
multiply,
|
|
2366
|
+
divide,
|
|
2367
|
+
abs,
|
|
2368
|
+
round,
|
|
2369
|
+
min,
|
|
2370
|
+
max,
|
|
2371
|
+
// Comparison (6)
|
|
2372
|
+
eq,
|
|
2373
|
+
neq,
|
|
2374
|
+
gt,
|
|
2375
|
+
gte,
|
|
2376
|
+
lt,
|
|
2377
|
+
lte,
|
|
2378
|
+
// Logic (5)
|
|
2379
|
+
if_fn,
|
|
2380
|
+
and,
|
|
2381
|
+
or,
|
|
2382
|
+
not,
|
|
2383
|
+
coalesce,
|
|
2384
|
+
// String (6)
|
|
2385
|
+
concat,
|
|
2386
|
+
upper,
|
|
2387
|
+
lower,
|
|
2388
|
+
trim,
|
|
2389
|
+
format,
|
|
2390
|
+
length,
|
|
2391
|
+
// Path (3)
|
|
2392
|
+
get,
|
|
2393
|
+
includes,
|
|
2394
|
+
is_defined,
|
|
2395
|
+
// Type (3)
|
|
2396
|
+
is_empty,
|
|
2397
|
+
is_null,
|
|
2398
|
+
to_string
|
|
2399
|
+
];
|
|
2400
|
+
function buildFunctionMap(functions) {
|
|
2401
|
+
const map = /* @__PURE__ */ new Map();
|
|
2402
|
+
for (const fn of functions) {
|
|
2403
|
+
map.set(fn.name, fn.fn);
|
|
2404
|
+
}
|
|
2405
|
+
return map;
|
|
2406
|
+
}
|
|
2407
|
+
var MAX_DEPTH = 50;
|
|
2408
|
+
var Parser = class {
|
|
2409
|
+
pos = 0;
|
|
2410
|
+
depth = 0;
|
|
2411
|
+
input;
|
|
2412
|
+
constructor(input) {
|
|
2413
|
+
this.input = input;
|
|
2414
|
+
}
|
|
2415
|
+
parse() {
|
|
2416
|
+
this.skipWhitespace();
|
|
2417
|
+
const node = this.parseExpression();
|
|
2418
|
+
this.skipWhitespace();
|
|
2419
|
+
if (this.pos < this.input.length) {
|
|
2420
|
+
throw new Error(`Unexpected character at position ${this.pos}: '${this.input[this.pos]}'`);
|
|
2421
|
+
}
|
|
2422
|
+
return node;
|
|
2423
|
+
}
|
|
2424
|
+
guardDepth() {
|
|
2425
|
+
if (++this.depth > MAX_DEPTH) {
|
|
2426
|
+
throw new Error("Expression too deeply nested");
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
parseExpression() {
|
|
2430
|
+
this.guardDepth();
|
|
2431
|
+
try {
|
|
2432
|
+
return this.parseTernary();
|
|
2433
|
+
} finally {
|
|
2434
|
+
this.depth--;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
parseTernary() {
|
|
2438
|
+
let node = this.parseLogicalOr();
|
|
2439
|
+
this.skipWhitespace();
|
|
2440
|
+
if (this.peek() === "?") {
|
|
2441
|
+
this.advance();
|
|
2442
|
+
const consequent = this.parseExpression();
|
|
2443
|
+
this.skipWhitespace();
|
|
2444
|
+
this.expect(":");
|
|
2445
|
+
const alternate = this.parseExpression();
|
|
2446
|
+
node = { type: "ternary", condition: node, consequent, alternate };
|
|
2447
|
+
}
|
|
2448
|
+
return node;
|
|
2449
|
+
}
|
|
2450
|
+
parseLogicalOr() {
|
|
2451
|
+
let left = this.parseLogicalAnd();
|
|
2452
|
+
this.skipWhitespace();
|
|
2453
|
+
while (this.match("||")) {
|
|
2454
|
+
const right = this.parseLogicalAnd();
|
|
2455
|
+
left = { type: "binary", operator: "||", left, right };
|
|
2456
|
+
this.skipWhitespace();
|
|
2457
|
+
}
|
|
2458
|
+
return left;
|
|
2459
|
+
}
|
|
2460
|
+
parseLogicalAnd() {
|
|
2461
|
+
let left = this.parseEquality();
|
|
2462
|
+
this.skipWhitespace();
|
|
2463
|
+
while (this.match("&&")) {
|
|
2464
|
+
const right = this.parseEquality();
|
|
2465
|
+
left = { type: "binary", operator: "&&", left, right };
|
|
2466
|
+
this.skipWhitespace();
|
|
2467
|
+
}
|
|
2468
|
+
return left;
|
|
2469
|
+
}
|
|
2470
|
+
parseEquality() {
|
|
2471
|
+
let left = this.parseComparison();
|
|
2472
|
+
this.skipWhitespace();
|
|
2473
|
+
while (true) {
|
|
2474
|
+
if (this.match("==")) {
|
|
2475
|
+
const right = this.parseComparison();
|
|
2476
|
+
left = { type: "binary", operator: "==", left, right };
|
|
2477
|
+
} else if (this.match("!=")) {
|
|
2478
|
+
const right = this.parseComparison();
|
|
2479
|
+
left = { type: "binary", operator: "!=", left, right };
|
|
2480
|
+
} else {
|
|
2481
|
+
break;
|
|
2482
|
+
}
|
|
2483
|
+
this.skipWhitespace();
|
|
2484
|
+
}
|
|
2485
|
+
return left;
|
|
2486
|
+
}
|
|
2487
|
+
parseComparison() {
|
|
2488
|
+
let left = this.parseUnary();
|
|
2489
|
+
this.skipWhitespace();
|
|
2490
|
+
while (true) {
|
|
2491
|
+
if (this.match(">=")) {
|
|
2492
|
+
const right = this.parseUnary();
|
|
2493
|
+
left = { type: "binary", operator: ">=", left, right };
|
|
2494
|
+
} else if (this.match("<=")) {
|
|
2495
|
+
const right = this.parseUnary();
|
|
2496
|
+
left = { type: "binary", operator: "<=", left, right };
|
|
2497
|
+
} else if (this.peek() === ">" && !this.lookAhead(">=")) {
|
|
2498
|
+
this.advance();
|
|
2499
|
+
const right = this.parseUnary();
|
|
2500
|
+
left = { type: "binary", operator: ">", left, right };
|
|
2501
|
+
} else if (this.peek() === "<" && !this.lookAhead("<=")) {
|
|
2502
|
+
this.advance();
|
|
2503
|
+
const right = this.parseUnary();
|
|
2504
|
+
left = { type: "binary", operator: "<", left, right };
|
|
2505
|
+
} else {
|
|
2506
|
+
break;
|
|
2507
|
+
}
|
|
2508
|
+
this.skipWhitespace();
|
|
2509
|
+
}
|
|
2510
|
+
return left;
|
|
2511
|
+
}
|
|
2512
|
+
parseUnary() {
|
|
2513
|
+
this.skipWhitespace();
|
|
2514
|
+
if (this.peek() === "!") {
|
|
2515
|
+
this.advance();
|
|
2516
|
+
const operand = this.parseUnary();
|
|
2517
|
+
return { type: "unary", operator: "!", operand };
|
|
2518
|
+
}
|
|
2519
|
+
if (this.peek() === "-") {
|
|
2520
|
+
const nextChar = this.input[this.pos + 1];
|
|
2521
|
+
if (nextChar !== void 0 && (nextChar >= "0" && nextChar <= "9" || nextChar === ".")) {
|
|
2522
|
+
return this.parseCallChain();
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return this.parseCallChain();
|
|
2526
|
+
}
|
|
2527
|
+
parseCallChain() {
|
|
2528
|
+
let node = this.parsePrimary();
|
|
2529
|
+
while (true) {
|
|
2530
|
+
this.skipWhitespace();
|
|
2531
|
+
if (this.peek() === "(") {
|
|
2532
|
+
this.advance();
|
|
2533
|
+
const args = this.parseArgList();
|
|
2534
|
+
this.expect(")");
|
|
2535
|
+
if (node.type === "identifier") {
|
|
2536
|
+
node = { type: "call", name: node.name, args };
|
|
2537
|
+
} else if (node.type === "path") {
|
|
2538
|
+
const name = node.segments.join(".");
|
|
2539
|
+
node = { type: "call", name, args };
|
|
2540
|
+
} else if (node.type === "member") {
|
|
2541
|
+
node = { type: "method_call", object: node.object, method: node.property, args };
|
|
2542
|
+
} else {
|
|
2543
|
+
throw new Error("Cannot call non-function");
|
|
2544
|
+
}
|
|
2545
|
+
} else if (this.peek() === ".") {
|
|
2546
|
+
this.advance();
|
|
2547
|
+
const prop = this.parseIdentifierName();
|
|
2548
|
+
node = { type: "member", object: node, property: prop };
|
|
2549
|
+
} else {
|
|
2550
|
+
break;
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
return node;
|
|
2554
|
+
}
|
|
2555
|
+
parsePrimary() {
|
|
2556
|
+
this.skipWhitespace();
|
|
2557
|
+
const ch = this.peek();
|
|
2558
|
+
if (ch === "(") {
|
|
2559
|
+
this.advance();
|
|
2560
|
+
const expr2 = this.parseExpression();
|
|
2561
|
+
this.skipWhitespace();
|
|
2562
|
+
this.expect(")");
|
|
2563
|
+
return expr2;
|
|
2564
|
+
}
|
|
2565
|
+
if (ch === "'" || ch === '"') {
|
|
2566
|
+
return this.parseString();
|
|
2567
|
+
}
|
|
2568
|
+
if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
2569
|
+
return this.parseNumber();
|
|
2570
|
+
}
|
|
2571
|
+
if (this.isIdentStart(ch)) {
|
|
2572
|
+
return this.parseIdentifierOrPath();
|
|
2573
|
+
}
|
|
2574
|
+
throw new Error(
|
|
2575
|
+
`Unexpected character at position ${this.pos}: '${ch || "EOF"}'`
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
parseString() {
|
|
2579
|
+
const quote = this.advance();
|
|
2580
|
+
let value = "";
|
|
2581
|
+
while (this.pos < this.input.length && this.peek() !== quote) {
|
|
2582
|
+
if (this.peek() === "\\") {
|
|
2583
|
+
this.advance();
|
|
2584
|
+
const esc = this.advance();
|
|
2585
|
+
if (esc === "n") value += "\n";
|
|
2586
|
+
else if (esc === "t") value += " ";
|
|
2587
|
+
else if (esc === "r") value += "\r";
|
|
2588
|
+
else value += esc;
|
|
2589
|
+
} else {
|
|
2590
|
+
value += this.advance();
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (this.pos >= this.input.length) {
|
|
2594
|
+
throw new Error("Unterminated string literal");
|
|
2595
|
+
}
|
|
2596
|
+
this.advance();
|
|
2597
|
+
return { type: "string", value };
|
|
2598
|
+
}
|
|
2599
|
+
parseNumber() {
|
|
2600
|
+
let numStr = "";
|
|
2601
|
+
if (this.peek() === "-") {
|
|
2602
|
+
numStr += this.advance();
|
|
2603
|
+
}
|
|
2604
|
+
while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
|
|
2605
|
+
numStr += this.advance();
|
|
2606
|
+
}
|
|
2607
|
+
if (this.peek() === "." && this.pos + 1 < this.input.length && this.input[this.pos + 1] >= "0" && this.input[this.pos + 1] <= "9") {
|
|
2608
|
+
numStr += this.advance();
|
|
2609
|
+
while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
|
|
2610
|
+
numStr += this.advance();
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
return { type: "number", value: Number(numStr) };
|
|
2614
|
+
}
|
|
2615
|
+
parseIdentifierOrPath() {
|
|
2616
|
+
const name = this.parseIdentifierName();
|
|
2617
|
+
if (name === "true") return { type: "boolean", value: true };
|
|
2618
|
+
if (name === "false") return { type: "boolean", value: false };
|
|
2619
|
+
if (name === "null") return { type: "null" };
|
|
2620
|
+
if (name === "undefined") return { type: "null" };
|
|
2621
|
+
return { type: "identifier", name };
|
|
2622
|
+
}
|
|
2623
|
+
parseIdentifierName() {
|
|
2624
|
+
let name = "";
|
|
2625
|
+
if (this.peek() === "$") name += this.advance();
|
|
2626
|
+
while (this.pos < this.input.length && this.isIdentPart(this.input[this.pos])) {
|
|
2627
|
+
name += this.advance();
|
|
2628
|
+
}
|
|
2629
|
+
if (!name) {
|
|
2630
|
+
throw new Error(`Expected identifier at position ${this.pos}`);
|
|
2631
|
+
}
|
|
2632
|
+
return name;
|
|
2633
|
+
}
|
|
2634
|
+
parseArgList() {
|
|
2635
|
+
this.skipWhitespace();
|
|
2636
|
+
if (this.peek() === ")") return [];
|
|
2637
|
+
const args = [];
|
|
2638
|
+
args.push(this.parseExpression());
|
|
2639
|
+
this.skipWhitespace();
|
|
2640
|
+
while (this.peek() === ",") {
|
|
2641
|
+
this.advance();
|
|
2642
|
+
args.push(this.parseExpression());
|
|
2643
|
+
this.skipWhitespace();
|
|
2644
|
+
}
|
|
2645
|
+
return args;
|
|
2646
|
+
}
|
|
2647
|
+
// Character utilities
|
|
2648
|
+
peek() {
|
|
2649
|
+
return this.input[this.pos] ?? "";
|
|
2650
|
+
}
|
|
2651
|
+
advance() {
|
|
2652
|
+
return this.input[this.pos++] ?? "";
|
|
2653
|
+
}
|
|
2654
|
+
match(str) {
|
|
2655
|
+
if (this.input.startsWith(str, this.pos)) {
|
|
2656
|
+
this.pos += str.length;
|
|
2657
|
+
return true;
|
|
2658
|
+
}
|
|
2659
|
+
return false;
|
|
2660
|
+
}
|
|
2661
|
+
lookAhead(str) {
|
|
2662
|
+
return this.input.startsWith(str, this.pos);
|
|
2663
|
+
}
|
|
2664
|
+
expect(ch) {
|
|
2665
|
+
this.skipWhitespace();
|
|
2666
|
+
if (this.peek() !== ch) {
|
|
2667
|
+
throw new Error(`Expected '${ch}' at position ${this.pos}, got '${this.peek() || "EOF"}'`);
|
|
2668
|
+
}
|
|
2669
|
+
this.advance();
|
|
2670
|
+
}
|
|
2671
|
+
skipWhitespace() {
|
|
2672
|
+
while (this.pos < this.input.length && " \n\r".includes(this.input[this.pos])) {
|
|
2673
|
+
this.pos++;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
isIdentStart(ch) {
|
|
2677
|
+
return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_" || ch === "$";
|
|
2678
|
+
}
|
|
2679
|
+
isIdentPart(ch) {
|
|
2680
|
+
return this.isIdentStart(ch) || ch >= "0" && ch <= "9";
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
function evaluateAST(node, context, fnMap) {
|
|
2684
|
+
switch (node.type) {
|
|
2685
|
+
case "number":
|
|
2686
|
+
return node.value;
|
|
2687
|
+
case "string":
|
|
2688
|
+
return node.value;
|
|
2689
|
+
case "boolean":
|
|
2690
|
+
return node.value;
|
|
2691
|
+
case "null":
|
|
2692
|
+
return null;
|
|
2693
|
+
case "identifier":
|
|
2694
|
+
return resolvePath(node.name, context);
|
|
2695
|
+
case "path":
|
|
2696
|
+
return resolvePath(node.segments.join("."), context);
|
|
2697
|
+
case "member": {
|
|
2698
|
+
const obj = evaluateAST(node.object, context, fnMap);
|
|
2699
|
+
if (obj == null || typeof obj !== "object") return void 0;
|
|
2700
|
+
return obj[node.property];
|
|
2701
|
+
}
|
|
2702
|
+
case "call": {
|
|
2703
|
+
const fn = fnMap.get(node.name);
|
|
2704
|
+
if (!fn) return void 0;
|
|
2705
|
+
const args = node.args.map((a) => evaluateAST(a, context, fnMap));
|
|
2706
|
+
return fn(...args);
|
|
2707
|
+
}
|
|
2708
|
+
case "method_call": {
|
|
2709
|
+
const obj = evaluateAST(node.object, context, fnMap);
|
|
2710
|
+
if (obj != null && typeof obj === "object") {
|
|
2711
|
+
const method = obj[node.method];
|
|
2712
|
+
if (typeof method === "function") {
|
|
2713
|
+
const args = node.args.map((a) => evaluateAST(a, context, fnMap));
|
|
2714
|
+
return method.apply(obj, args);
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
return void 0;
|
|
2718
|
+
}
|
|
2719
|
+
case "unary": {
|
|
2720
|
+
const operand = evaluateAST(node.operand, context, fnMap);
|
|
2721
|
+
return !operand;
|
|
2722
|
+
}
|
|
2723
|
+
case "binary": {
|
|
2724
|
+
if (node.operator === "&&") {
|
|
2725
|
+
const left2 = evaluateAST(node.left, context, fnMap);
|
|
2726
|
+
if (!left2) return left2;
|
|
2727
|
+
return evaluateAST(node.right, context, fnMap);
|
|
2728
|
+
}
|
|
2729
|
+
if (node.operator === "||") {
|
|
2730
|
+
const left2 = evaluateAST(node.left, context, fnMap);
|
|
2731
|
+
if (left2) return left2;
|
|
2732
|
+
return evaluateAST(node.right, context, fnMap);
|
|
2733
|
+
}
|
|
2734
|
+
const left = evaluateAST(node.left, context, fnMap);
|
|
2735
|
+
const right = evaluateAST(node.right, context, fnMap);
|
|
2736
|
+
switch (node.operator) {
|
|
2737
|
+
// eslint-disable-next-line eqeqeq
|
|
2738
|
+
case "==":
|
|
2739
|
+
return left == right;
|
|
2740
|
+
// eslint-disable-next-line eqeqeq
|
|
2741
|
+
case "!=":
|
|
2742
|
+
return left != right;
|
|
2743
|
+
case ">":
|
|
2744
|
+
return Number(left) > Number(right);
|
|
2745
|
+
case "<":
|
|
2746
|
+
return Number(left) < Number(right);
|
|
2747
|
+
case ">=":
|
|
2748
|
+
return Number(left) >= Number(right);
|
|
2749
|
+
case "<=":
|
|
2750
|
+
return Number(left) <= Number(right);
|
|
2751
|
+
default:
|
|
2752
|
+
return void 0;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
case "ternary": {
|
|
2756
|
+
const condition = evaluateAST(node.condition, context, fnMap);
|
|
2757
|
+
return condition ? evaluateAST(node.consequent, context, fnMap) : evaluateAST(node.alternate, context, fnMap);
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
var MAX_CACHE = 500;
|
|
2762
|
+
var astCache = /* @__PURE__ */ new Map();
|
|
2763
|
+
function evictIfNeeded() {
|
|
2764
|
+
if (astCache.size > MAX_CACHE) {
|
|
2765
|
+
const keys = Array.from(astCache.keys());
|
|
2766
|
+
const evictCount = Math.floor(MAX_CACHE * 0.25);
|
|
2767
|
+
for (let i = 0; i < evictCount; i++) {
|
|
2768
|
+
astCache.delete(keys[i]);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
function parseAndCache(expr2) {
|
|
2773
|
+
const cached = astCache.get(expr2);
|
|
2774
|
+
if (cached) return cached;
|
|
2775
|
+
const parser = new Parser(expr2);
|
|
2776
|
+
const ast = parser.parse();
|
|
2777
|
+
astCache.set(expr2, ast);
|
|
2778
|
+
evictIfNeeded();
|
|
2779
|
+
return ast;
|
|
2780
|
+
}
|
|
2781
|
+
var TEMPLATE_RE = /\{\{(.+?)\}\}/g;
|
|
2782
|
+
function resolvePath(path, context) {
|
|
2783
|
+
const parts = path.split(".");
|
|
2784
|
+
let current = context;
|
|
2785
|
+
for (const part of parts) {
|
|
2786
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
2787
|
+
current = current[part];
|
|
2788
|
+
}
|
|
2789
|
+
return current;
|
|
2790
|
+
}
|
|
2791
|
+
function evaluateExpression(expr2, context, fnMap) {
|
|
2792
|
+
const trimmed = expr2.trim();
|
|
2793
|
+
if (trimmed === "true") return true;
|
|
2794
|
+
if (trimmed === "false") return false;
|
|
2795
|
+
if (trimmed === "null") return null;
|
|
2796
|
+
if (trimmed === "undefined") return void 0;
|
|
2797
|
+
const num = Number(trimmed);
|
|
2798
|
+
if (!isNaN(num) && trimmed !== "") return num;
|
|
2799
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
2800
|
+
return trimmed.slice(1, -1);
|
|
2801
|
+
}
|
|
2802
|
+
if (/^[a-zA-Z_$][\w$.]*$/.test(trimmed)) {
|
|
2803
|
+
return resolvePath(trimmed, context);
|
|
2804
|
+
}
|
|
2805
|
+
const ast = parseAndCache(trimmed);
|
|
2806
|
+
return evaluateAST(ast, context, fnMap);
|
|
2807
|
+
}
|
|
2808
|
+
var WEB_FAILURE_POLICIES = {
|
|
2809
|
+
VIEW_BINDING: {
|
|
2810
|
+
on_error: "return_fallback",
|
|
2811
|
+
fallback_value: "",
|
|
2812
|
+
log_level: "warn"
|
|
2813
|
+
},
|
|
2814
|
+
EVENT_REACTION: {
|
|
2815
|
+
on_error: "log_and_skip",
|
|
2816
|
+
fallback_value: void 0,
|
|
2817
|
+
log_level: "error"
|
|
2818
|
+
},
|
|
2819
|
+
DURING_ACTION: {
|
|
2820
|
+
on_error: "log_and_skip",
|
|
2821
|
+
fallback_value: void 0,
|
|
2822
|
+
log_level: "error"
|
|
2823
|
+
},
|
|
2824
|
+
CONDITIONAL_VISIBILITY: {
|
|
2825
|
+
on_error: "return_fallback",
|
|
2826
|
+
fallback_value: true,
|
|
2827
|
+
// Show by default if condition fails
|
|
2828
|
+
log_level: "warn"
|
|
2829
|
+
}
|
|
2830
|
+
};
|
|
2831
|
+
function createEvaluator(config) {
|
|
2832
|
+
const allFunctions = [...CORE_FUNCTIONS, ...config.functions];
|
|
2833
|
+
const fnMap = buildFunctionMap(allFunctions);
|
|
2834
|
+
const policy = config.failurePolicy;
|
|
2835
|
+
function handleError(expr2, error) {
|
|
2836
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2837
|
+
if (policy.log_level === "error") {
|
|
2838
|
+
console.error(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
|
|
2839
|
+
} else if (policy.log_level === "warn") {
|
|
2840
|
+
console.warn(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
|
|
2841
|
+
}
|
|
2842
|
+
switch (policy.on_error) {
|
|
2843
|
+
case "throw":
|
|
2844
|
+
throw error;
|
|
2845
|
+
case "return_fallback":
|
|
2846
|
+
return { value: policy.fallback_value, status: "fallback", error: message };
|
|
2847
|
+
case "log_and_skip":
|
|
2848
|
+
default:
|
|
2849
|
+
return { value: policy.fallback_value, status: "error", error: message };
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
return {
|
|
2853
|
+
evaluate(expression, context) {
|
|
2854
|
+
try {
|
|
2855
|
+
const value = evaluateExpression(expression, context, fnMap);
|
|
2856
|
+
return { value, status: "ok" };
|
|
2857
|
+
} catch (error) {
|
|
2858
|
+
return handleError(expression, error);
|
|
2859
|
+
}
|
|
2860
|
+
},
|
|
2861
|
+
evaluateTemplate(template, context) {
|
|
2862
|
+
try {
|
|
2863
|
+
if (!template.includes("{{")) {
|
|
2864
|
+
return { value: template, status: "ok" };
|
|
2865
|
+
}
|
|
2866
|
+
const result = template.replace(TEMPLATE_RE, (_match, expr2) => {
|
|
2867
|
+
const value = evaluateExpression(expr2, context, fnMap);
|
|
2868
|
+
return value != null ? String(value) : "";
|
|
2869
|
+
});
|
|
2870
|
+
return { value: result, status: "ok" };
|
|
2871
|
+
} catch (error) {
|
|
2872
|
+
return handleError(template, error);
|
|
2873
|
+
}
|
|
2874
|
+
},
|
|
2875
|
+
validate(expression) {
|
|
2876
|
+
const errors = [];
|
|
2877
|
+
try {
|
|
2878
|
+
parseAndCache(expression);
|
|
2879
|
+
} catch (e) {
|
|
2880
|
+
errors.push(e instanceof Error ? e.message : String(e));
|
|
2881
|
+
}
|
|
2882
|
+
return { valid: errors.length === 0, errors };
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
var MAX_AUTO_CHAIN = 10;
|
|
2887
|
+
var StateMachine = class {
|
|
2888
|
+
evaluator;
|
|
2889
|
+
actionHandlers;
|
|
2890
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2891
|
+
instance;
|
|
2892
|
+
constructor(definition, initialData = {}, config) {
|
|
2893
|
+
this.evaluator = config.evaluator;
|
|
2894
|
+
this.actionHandlers = config.actionHandlers ?? /* @__PURE__ */ new Map();
|
|
2895
|
+
const startState = definition.states.find((s) => s.type === "START");
|
|
2896
|
+
if (!startState) {
|
|
2897
|
+
throw new Error(`No START state found in definition ${definition.slug}`);
|
|
2898
|
+
}
|
|
2899
|
+
this.instance = {
|
|
2900
|
+
definition,
|
|
2901
|
+
current_state: startState.name,
|
|
2902
|
+
state_data: { ...initialData },
|
|
2903
|
+
memory: {},
|
|
2904
|
+
status: "ACTIVE"
|
|
2905
|
+
};
|
|
2906
|
+
}
|
|
2907
|
+
/** Get the current instance snapshot (immutable copy) */
|
|
2908
|
+
getSnapshot() {
|
|
2909
|
+
return { ...this.instance, state_data: { ...this.instance.state_data }, memory: { ...this.instance.memory } };
|
|
2910
|
+
}
|
|
2911
|
+
/** Get current state name */
|
|
2912
|
+
get currentState() {
|
|
2913
|
+
return this.instance.current_state;
|
|
2914
|
+
}
|
|
2915
|
+
/** Get current state_data */
|
|
2916
|
+
get stateData() {
|
|
2917
|
+
return this.instance.state_data;
|
|
2918
|
+
}
|
|
2919
|
+
/** Get current status */
|
|
2920
|
+
get status() {
|
|
2921
|
+
return this.instance.status;
|
|
2922
|
+
}
|
|
2923
|
+
/** Subscribe to state machine events */
|
|
2924
|
+
on(listener) {
|
|
2925
|
+
this.listeners.add(listener);
|
|
2926
|
+
return () => this.listeners.delete(listener);
|
|
2927
|
+
}
|
|
2928
|
+
/** Register an action handler */
|
|
2929
|
+
registerAction(type, handler) {
|
|
2930
|
+
this.actionHandlers.set(type, handler);
|
|
2931
|
+
}
|
|
2932
|
+
/** Execute a named transition */
|
|
2933
|
+
async transition(transitionName, data) {
|
|
2934
|
+
if (this.instance.status !== "ACTIVE") {
|
|
2935
|
+
return {
|
|
2936
|
+
success: false,
|
|
2937
|
+
from_state: this.instance.current_state,
|
|
2938
|
+
to_state: this.instance.current_state,
|
|
2939
|
+
actions_executed: [],
|
|
2940
|
+
error: `Cannot transition: instance status is ${this.instance.status}`
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
2943
|
+
const transition2 = this.instance.definition.transitions.find(
|
|
2944
|
+
(t) => t.name === transitionName && t.from.includes(this.instance.current_state)
|
|
2945
|
+
);
|
|
2946
|
+
if (!transition2) {
|
|
2947
|
+
return {
|
|
2948
|
+
success: false,
|
|
2949
|
+
from_state: this.instance.current_state,
|
|
2950
|
+
to_state: this.instance.current_state,
|
|
2951
|
+
actions_executed: [],
|
|
2952
|
+
error: `Transition "${transitionName}" not valid from state "${this.instance.current_state}"`
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
if (data) {
|
|
2956
|
+
this.instance.state_data = { ...this.instance.state_data, ...data };
|
|
2957
|
+
}
|
|
2958
|
+
if (transition2.conditions && transition2.conditions.length > 0) {
|
|
2959
|
+
const ctx = this.buildContext();
|
|
2960
|
+
for (const condition of transition2.conditions) {
|
|
2961
|
+
const result2 = this.evaluator.evaluate(condition, ctx);
|
|
2962
|
+
if (!result2.value) {
|
|
2963
|
+
return {
|
|
2964
|
+
success: false,
|
|
2965
|
+
from_state: this.instance.current_state,
|
|
2966
|
+
to_state: this.instance.current_state,
|
|
2967
|
+
actions_executed: [],
|
|
2968
|
+
error: `Transition condition not met: ${condition}`
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
const result = await this.executeTransition(transition2);
|
|
2974
|
+
if (result.success) {
|
|
2975
|
+
await this.drainAutoTransitions();
|
|
2976
|
+
}
|
|
2977
|
+
return result;
|
|
2978
|
+
}
|
|
2979
|
+
/** Update state_data directly (for on_event set_field actions) */
|
|
2980
|
+
setField(field2, value) {
|
|
2981
|
+
this.instance.state_data = { ...this.instance.state_data, [field2]: value };
|
|
2982
|
+
}
|
|
2983
|
+
/** Update memory */
|
|
2984
|
+
setMemory(key, value) {
|
|
2985
|
+
this.instance.memory = { ...this.instance.memory, [key]: value };
|
|
2986
|
+
}
|
|
2987
|
+
/** Get available transitions from the current state */
|
|
2988
|
+
getAvailableTransitions() {
|
|
2989
|
+
return this.instance.definition.transitions.filter(
|
|
2990
|
+
(t) => t.from.includes(this.instance.current_state) && !t.auto
|
|
2991
|
+
);
|
|
2992
|
+
}
|
|
2993
|
+
/** Get the current state definition */
|
|
2994
|
+
getCurrentStateDefinition() {
|
|
2995
|
+
return this.instance.definition.states.find((s) => s.name === this.instance.current_state);
|
|
2996
|
+
}
|
|
2997
|
+
// ===========================================================================
|
|
2998
|
+
// Private implementation
|
|
2999
|
+
// ===========================================================================
|
|
3000
|
+
async executeTransition(transition2) {
|
|
3001
|
+
const fromState = this.instance.current_state;
|
|
3002
|
+
const allActionsExecuted = [];
|
|
3003
|
+
const fromStateDef = this.getCurrentStateDefinition();
|
|
3004
|
+
if (fromStateDef?.on_exit) {
|
|
3005
|
+
await this.executeActions(fromStateDef.on_exit, allActionsExecuted);
|
|
3006
|
+
}
|
|
3007
|
+
this.emit({
|
|
3008
|
+
type: "state_exit",
|
|
3009
|
+
instance_id: this.instance.definition.id,
|
|
3010
|
+
from_state: fromState
|
|
3011
|
+
});
|
|
3012
|
+
if (transition2.actions) {
|
|
3013
|
+
await this.executeActions(transition2.actions, allActionsExecuted);
|
|
3014
|
+
}
|
|
3015
|
+
this.instance.current_state = transition2.to;
|
|
3016
|
+
const toStateDef = this.instance.definition.states.find((s) => s.name === transition2.to);
|
|
3017
|
+
if (toStateDef?.type === "END") {
|
|
3018
|
+
this.instance.status = "COMPLETED";
|
|
3019
|
+
} else if (toStateDef?.type === "CANCELLED") {
|
|
3020
|
+
this.instance.status = "CANCELLED";
|
|
3021
|
+
}
|
|
3022
|
+
this.emit({
|
|
3023
|
+
type: "state_enter",
|
|
3024
|
+
instance_id: this.instance.definition.id,
|
|
3025
|
+
to_state: transition2.to
|
|
3026
|
+
});
|
|
3027
|
+
if (toStateDef?.on_enter) {
|
|
3028
|
+
await this.executeActions(toStateDef.on_enter, allActionsExecuted);
|
|
3029
|
+
}
|
|
3030
|
+
this.emit({
|
|
3031
|
+
type: "transition",
|
|
3032
|
+
instance_id: this.instance.definition.id,
|
|
3033
|
+
from_state: fromState,
|
|
3034
|
+
to_state: transition2.to
|
|
3035
|
+
});
|
|
3036
|
+
return {
|
|
3037
|
+
success: true,
|
|
3038
|
+
from_state: fromState,
|
|
3039
|
+
to_state: transition2.to,
|
|
3040
|
+
actions_executed: allActionsExecuted
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
async drainAutoTransitions() {
|
|
3044
|
+
for (let depth = 0; depth < MAX_AUTO_CHAIN; depth++) {
|
|
3045
|
+
if (this.instance.status !== "ACTIVE") break;
|
|
3046
|
+
const autoTransition = this.findMatchingAutoTransition();
|
|
3047
|
+
if (!autoTransition) break;
|
|
3048
|
+
const result = await this.executeTransition(autoTransition);
|
|
3049
|
+
if (!result.success) break;
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
findMatchingAutoTransition() {
|
|
3053
|
+
const candidates = this.instance.definition.transitions.filter(
|
|
3054
|
+
(t) => t.auto && t.from.includes(this.instance.current_state)
|
|
3055
|
+
);
|
|
3056
|
+
const ctx = this.buildContext();
|
|
3057
|
+
for (const candidate of candidates) {
|
|
3058
|
+
if (!candidate.conditions || candidate.conditions.length === 0) {
|
|
3059
|
+
return candidate;
|
|
3060
|
+
}
|
|
3061
|
+
const allMet = candidate.conditions.every((condition) => {
|
|
3062
|
+
const result = this.evaluator.evaluate(condition, ctx);
|
|
3063
|
+
return result.value === true;
|
|
3064
|
+
});
|
|
3065
|
+
if (allMet) return candidate;
|
|
3066
|
+
}
|
|
3067
|
+
return null;
|
|
3068
|
+
}
|
|
3069
|
+
async executeActions(actions, collector) {
|
|
3070
|
+
const ctx = this.buildContext();
|
|
3071
|
+
for (const action2 of actions) {
|
|
3072
|
+
if (action2.condition) {
|
|
3073
|
+
const condResult = this.evaluator.evaluate(action2.condition, ctx);
|
|
3074
|
+
if (!condResult.value) continue;
|
|
3075
|
+
}
|
|
3076
|
+
const handler = this.actionHandlers.get(action2.type);
|
|
3077
|
+
if (handler) {
|
|
3078
|
+
try {
|
|
3079
|
+
await handler(action2, ctx);
|
|
3080
|
+
collector.push(action2);
|
|
3081
|
+
this.emit({
|
|
3082
|
+
type: "action_executed",
|
|
3083
|
+
instance_id: this.instance.definition.id,
|
|
3084
|
+
action: action2
|
|
3085
|
+
});
|
|
3086
|
+
} catch (error) {
|
|
3087
|
+
this.emit({
|
|
3088
|
+
type: "error",
|
|
3089
|
+
instance_id: this.instance.definition.id,
|
|
3090
|
+
action: action2,
|
|
3091
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3092
|
+
});
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
buildContext() {
|
|
3098
|
+
return {
|
|
3099
|
+
state_data: this.instance.state_data,
|
|
3100
|
+
memory: this.instance.memory,
|
|
3101
|
+
current_state: this.instance.current_state,
|
|
3102
|
+
status: this.instance.status,
|
|
3103
|
+
// Spread state_data for direct field access (e.g., "title" instead of "state_data.title")
|
|
3104
|
+
...this.instance.state_data
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
emit(event) {
|
|
3108
|
+
for (const listener of this.listeners) {
|
|
3109
|
+
try {
|
|
3110
|
+
listener(event);
|
|
3111
|
+
} catch {
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
var patternCache = /* @__PURE__ */ new Map();
|
|
3117
|
+
var MAX_CACHE2 = 200;
|
|
3118
|
+
function compilePattern(pattern) {
|
|
3119
|
+
const cached = patternCache.get(pattern);
|
|
3120
|
+
if (cached) return cached;
|
|
3121
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<DOUBLESTAR>>").replace(/\*/g, "[^:.]+").replace(/<<DOUBLESTAR>>/g, ".*");
|
|
3122
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
3123
|
+
if (patternCache.size >= MAX_CACHE2) {
|
|
3124
|
+
const firstKey = patternCache.keys().next().value;
|
|
3125
|
+
if (firstKey) patternCache.delete(firstKey);
|
|
3126
|
+
}
|
|
3127
|
+
patternCache.set(pattern, regex);
|
|
3128
|
+
return regex;
|
|
3129
|
+
}
|
|
3130
|
+
function matchTopic(pattern, topic) {
|
|
3131
|
+
return pattern.test(topic);
|
|
3132
|
+
}
|
|
3133
|
+
var EventBus = class {
|
|
3134
|
+
subscriptions = [];
|
|
3135
|
+
/**
|
|
3136
|
+
* Subscribe to events matching a glob pattern.
|
|
3137
|
+
* Returns an unsubscribe function.
|
|
3138
|
+
*/
|
|
3139
|
+
subscribe(pattern, handler) {
|
|
3140
|
+
const regex = compilePattern(pattern);
|
|
3141
|
+
const subscription = { pattern, regex, handler };
|
|
3142
|
+
this.subscriptions.push(subscription);
|
|
3143
|
+
return () => {
|
|
3144
|
+
const idx = this.subscriptions.indexOf(subscription);
|
|
3145
|
+
if (idx !== -1) this.subscriptions.splice(idx, 1);
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Publish an event. All matching subscriptions fire (async).
|
|
3150
|
+
* Errors in handlers are caught and logged, never propagated.
|
|
3151
|
+
*/
|
|
3152
|
+
async publish(topic, payload = {}) {
|
|
3153
|
+
const event = { topic, payload };
|
|
3154
|
+
for (const sub of this.subscriptions) {
|
|
3155
|
+
if (matchTopic(sub.regex, topic)) {
|
|
3156
|
+
try {
|
|
3157
|
+
await sub.handler(event);
|
|
3158
|
+
} catch (error) {
|
|
3159
|
+
console.warn(
|
|
3160
|
+
`[player-core] Event handler error for pattern "${sub.pattern}" on topic "${topic}":`,
|
|
3161
|
+
error
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Publish synchronously (fire-and-forget).
|
|
3169
|
+
* Useful when you don't need to await handler completion.
|
|
3170
|
+
*/
|
|
3171
|
+
emit(topic, payload = {}) {
|
|
3172
|
+
void this.publish(topic, payload);
|
|
3173
|
+
}
|
|
3174
|
+
/** Get count of active subscriptions */
|
|
3175
|
+
get size() {
|
|
3176
|
+
return this.subscriptions.length;
|
|
3177
|
+
}
|
|
3178
|
+
/** Remove all subscriptions */
|
|
3179
|
+
clear() {
|
|
3180
|
+
this.subscriptions.length = 0;
|
|
3181
|
+
}
|
|
3182
|
+
};
|
|
3183
|
+
var ActionDispatcher = class {
|
|
3184
|
+
handlers = /* @__PURE__ */ new Map();
|
|
3185
|
+
/** Register a handler for an action type */
|
|
3186
|
+
register(type, handler) {
|
|
3187
|
+
this.handlers.set(type, handler);
|
|
3188
|
+
}
|
|
3189
|
+
/** Unregister a handler */
|
|
3190
|
+
unregister(type) {
|
|
3191
|
+
this.handlers.delete(type);
|
|
3192
|
+
}
|
|
3193
|
+
/** Check if a handler is registered for the given type */
|
|
3194
|
+
has(type) {
|
|
3195
|
+
return this.handlers.has(type);
|
|
3196
|
+
}
|
|
3197
|
+
/**
|
|
3198
|
+
* Execute a list of actions sequentially.
|
|
3199
|
+
* Each action's condition is evaluated first (if present).
|
|
3200
|
+
* Missing handlers are skipped with a warning.
|
|
3201
|
+
*/
|
|
3202
|
+
async execute(actions, context, evaluator) {
|
|
3203
|
+
const results = [];
|
|
3204
|
+
for (const action2 of actions) {
|
|
3205
|
+
if (action2.condition && evaluator) {
|
|
3206
|
+
const condResult = evaluator.evaluate(action2.condition, context);
|
|
3207
|
+
if (!condResult.value) continue;
|
|
3208
|
+
}
|
|
3209
|
+
const handler = this.handlers.get(action2.type);
|
|
3210
|
+
if (!handler) {
|
|
3211
|
+
console.warn(`[player-core] No handler registered for action type "${action2.type}" \u2014 unsupported action`);
|
|
3212
|
+
results.push({ type: action2.type, success: false, error: `unsupported action: "${action2.type}"` });
|
|
3213
|
+
continue;
|
|
3214
|
+
}
|
|
3215
|
+
try {
|
|
3216
|
+
await handler(action2.config, context);
|
|
3217
|
+
results.push({ type: action2.type, success: true });
|
|
3218
|
+
} catch (error) {
|
|
3219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3220
|
+
console.warn(`[player-core] Action "${action2.type}" failed: ${message}`);
|
|
3221
|
+
results.push({ type: action2.type, success: false, error: message });
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
return results;
|
|
3225
|
+
}
|
|
3226
|
+
/** Get count of registered handlers */
|
|
3227
|
+
get size() {
|
|
3228
|
+
return this.handlers.size;
|
|
3229
|
+
}
|
|
3230
|
+
/** Get all registered action type names */
|
|
3231
|
+
getRegisteredTypes() {
|
|
3232
|
+
return Array.from(this.handlers.keys());
|
|
3233
|
+
}
|
|
3234
|
+
/** Remove all handlers */
|
|
3235
|
+
clear() {
|
|
3236
|
+
this.handlers.clear();
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
|
|
2153
3240
|
// src/core/WorkflowRuntime.ts
|
|
2154
|
-
var import_player_core = require("@mmapp/player-core");
|
|
2155
3241
|
var WorkflowRuntime = class {
|
|
2156
3242
|
sm;
|
|
2157
3243
|
eventBus;
|
|
@@ -2159,9 +3245,9 @@ var WorkflowRuntime = class {
|
|
|
2159
3245
|
evaluator;
|
|
2160
3246
|
listeners = /* @__PURE__ */ new Set();
|
|
2161
3247
|
constructor(config) {
|
|
2162
|
-
this.evaluator =
|
|
3248
|
+
this.evaluator = createEvaluator({
|
|
2163
3249
|
functions: [],
|
|
2164
|
-
failurePolicy:
|
|
3250
|
+
failurePolicy: WEB_FAILURE_POLICIES.EVENT_REACTION
|
|
2165
3251
|
});
|
|
2166
3252
|
const actionHandlers = /* @__PURE__ */ new Map();
|
|
2167
3253
|
if (config.actionHandlers) {
|
|
@@ -2183,14 +3269,14 @@ var WorkflowRuntime = class {
|
|
|
2183
3269
|
smRef.setMemory(action2.config.key, action2.config.value);
|
|
2184
3270
|
}
|
|
2185
3271
|
});
|
|
2186
|
-
this.sm = new
|
|
3272
|
+
this.sm = new StateMachine(
|
|
2187
3273
|
config.definition,
|
|
2188
3274
|
config.initialData ?? {},
|
|
2189
3275
|
{ evaluator: this.evaluator, actionHandlers }
|
|
2190
3276
|
);
|
|
2191
3277
|
smRef = this.sm;
|
|
2192
|
-
this.eventBus = new
|
|
2193
|
-
this.dispatcher = new
|
|
3278
|
+
this.eventBus = new EventBus();
|
|
3279
|
+
this.dispatcher = new ActionDispatcher();
|
|
2194
3280
|
this.dispatcher.register("set_field", (cfg) => {
|
|
2195
3281
|
if (smRef && typeof cfg.field === "string") {
|
|
2196
3282
|
smRef.setField(cfg.field, cfg.value);
|
|
@@ -5546,13 +6632,13 @@ function inState(state2) {
|
|
|
5546
6632
|
function notInState(state2) {
|
|
5547
6633
|
return { type: "expression", expression: `current_state != "${state2}"` };
|
|
5548
6634
|
}
|
|
5549
|
-
function
|
|
6635
|
+
function and2(...conditions) {
|
|
5550
6636
|
return { AND: conditions.map(normalize) };
|
|
5551
6637
|
}
|
|
5552
|
-
function
|
|
6638
|
+
function or2(...conditions) {
|
|
5553
6639
|
return { OR: conditions.map(normalize) };
|
|
5554
6640
|
}
|
|
5555
|
-
function
|
|
6641
|
+
function not2(condition) {
|
|
5556
6642
|
const c = normalize(condition);
|
|
5557
6643
|
if (c.type === "expression" && c.expression) {
|
|
5558
6644
|
return { type: "expression", expression: `NOT(${c.expression})` };
|
|
@@ -5892,13 +6978,13 @@ function resolveScopedPath(root, rest, scope) {
|
|
|
5892
6978
|
if (rootValue === void 0) {
|
|
5893
6979
|
rootValue = scope.$item;
|
|
5894
6980
|
if (rootValue != null && rest) {
|
|
5895
|
-
return
|
|
6981
|
+
return resolvePath2(`${root}.${rest}`, { [root]: scope.$item });
|
|
5896
6982
|
}
|
|
5897
6983
|
}
|
|
5898
6984
|
}
|
|
5899
6985
|
if (!rest) return rootValue;
|
|
5900
6986
|
if (rootValue == null) return void 0;
|
|
5901
|
-
return
|
|
6987
|
+
return resolvePath2(rest, rootValue);
|
|
5902
6988
|
}
|
|
5903
6989
|
function resolveFunction(name, rawArgs, scope) {
|
|
5904
6990
|
const fn = scope.$fn?.[name] ?? builtinFunctions[name];
|
|
@@ -5953,11 +7039,11 @@ function resolveCondition(expression, scope) {
|
|
|
5953
7039
|
}
|
|
5954
7040
|
return () => {
|
|
5955
7041
|
const context2 = buildEvalContext(scope);
|
|
5956
|
-
return
|
|
7042
|
+
return evaluateExpression2(body, context2);
|
|
5957
7043
|
};
|
|
5958
7044
|
}
|
|
5959
7045
|
const context = buildEvalContext(scope);
|
|
5960
|
-
return
|
|
7046
|
+
return evaluateExpression2(expression, context);
|
|
5961
7047
|
}
|
|
5962
7048
|
function buildEvalContext(scope) {
|
|
5963
7049
|
const ctx = {};
|
|
@@ -6471,7 +7557,7 @@ async function fetchDataSource(ds, resolver, scope) {
|
|
|
6471
7557
|
}
|
|
6472
7558
|
case "ref": {
|
|
6473
7559
|
const ctx = buildEvalContext(scope);
|
|
6474
|
-
const resolved =
|
|
7560
|
+
const resolved = evaluateExpression2(ds.expression, ctx);
|
|
6475
7561
|
if (Array.isArray(resolved)) {
|
|
6476
7562
|
return {
|
|
6477
7563
|
instances: resolved.map((item, i) => ({
|
|
@@ -6676,12 +7762,12 @@ var NodeRenderer = ({ node, fallback }) => {
|
|
|
6676
7762
|
}, [scope, primarySlug, resolver, handleRefreshQuery, handleSetLocal]);
|
|
6677
7763
|
if (node.visible_when) {
|
|
6678
7764
|
const ctx = buildEvalContext(enrichedScope);
|
|
6679
|
-
const visible =
|
|
7765
|
+
const visible = evaluateExpression2(node.visible_when, ctx);
|
|
6680
7766
|
if (!visible) return null;
|
|
6681
7767
|
}
|
|
6682
7768
|
if (node.$if) {
|
|
6683
7769
|
const ctx = buildEvalContext(enrichedScope);
|
|
6684
|
-
const visible =
|
|
7770
|
+
const visible = evaluateExpression2(node.$if, ctx);
|
|
6685
7771
|
if (!visible) return null;
|
|
6686
7772
|
}
|
|
6687
7773
|
const resolvedBindings = node.bindings ? resolveAllBindings(node.bindings, enrichedScope) : {};
|
|
@@ -6960,10 +8046,10 @@ function containsExpression(value) {
|
|
|
6960
8046
|
function evaluateProp(value, scopes) {
|
|
6961
8047
|
if (typeof value !== "string") return value;
|
|
6962
8048
|
const fullMatch = value.match(/^\{\{(.+)\}\}$/s);
|
|
6963
|
-
if (fullMatch) return
|
|
8049
|
+
if (fullMatch) return evaluateExpression2(fullMatch[1], scopes);
|
|
6964
8050
|
if (containsExpression(value)) {
|
|
6965
8051
|
return value.replace(EXPR_PATTERN, (_, expr2) => {
|
|
6966
|
-
const result =
|
|
8052
|
+
const result = evaluateExpression2(expr2, scopes);
|
|
6967
8053
|
return result == null ? "" : String(result);
|
|
6968
8054
|
});
|
|
6969
8055
|
}
|
|
@@ -7012,7 +8098,7 @@ var RenderNode = ({ node, scopes, atoms, onEvent }) => {
|
|
|
7012
8098
|
if (key.startsWith("on") && typeof value === "string") {
|
|
7013
8099
|
evaluatedProps[key] = (...args) => {
|
|
7014
8100
|
if (containsExpression(value)) {
|
|
7015
|
-
|
|
8101
|
+
evaluateExpression2(value.replace(/^\{\{|\}\}$/g, ""), scopes);
|
|
7016
8102
|
} else {
|
|
7017
8103
|
onEvent(value, args[0]);
|
|
7018
8104
|
}
|
|
@@ -10017,7 +11103,6 @@ function describeModel(def) {
|
|
|
10017
11103
|
|
|
10018
11104
|
// src/hooks/usePlayer.ts
|
|
10019
11105
|
var import_react50 = require("react");
|
|
10020
|
-
var import_player_core2 = require("@mmapp/player-core");
|
|
10021
11106
|
|
|
10022
11107
|
// src/logger.ts
|
|
10023
11108
|
var debugEnabled = false;
|
|
@@ -10063,9 +11148,9 @@ function usePlayer(config) {
|
|
|
10063
11148
|
if (config.debug) setPlayerDebug(true);
|
|
10064
11149
|
}, [config.debug]);
|
|
10065
11150
|
const evaluator = (0, import_react50.useMemo)(() => {
|
|
10066
|
-
return
|
|
11151
|
+
return createEvaluator({
|
|
10067
11152
|
functions: config.functions ?? [],
|
|
10068
|
-
failurePolicy:
|
|
11153
|
+
failurePolicy: WEB_FAILURE_POLICIES.EVENT_REACTION
|
|
10069
11154
|
});
|
|
10070
11155
|
}, [config.definition.id]);
|
|
10071
11156
|
const engine = (0, import_react50.useMemo)(() => {
|
|
@@ -10096,14 +11181,14 @@ function usePlayer(config) {
|
|
|
10096
11181
|
});
|
|
10097
11182
|
}
|
|
10098
11183
|
});
|
|
10099
|
-
const sm2 = new
|
|
11184
|
+
const sm2 = new StateMachine(
|
|
10100
11185
|
config.definition,
|
|
10101
11186
|
config.initialData ?? {},
|
|
10102
11187
|
{ evaluator, actionHandlers }
|
|
10103
11188
|
);
|
|
10104
11189
|
smRef = sm2;
|
|
10105
|
-
const eventBus2 = new
|
|
10106
|
-
const dispatcher = new
|
|
11190
|
+
const eventBus2 = new EventBus();
|
|
11191
|
+
const dispatcher = new ActionDispatcher();
|
|
10107
11192
|
dispatcher.register("set_field", (cfg) => {
|
|
10108
11193
|
if (smRef && typeof cfg.field === "string") {
|
|
10109
11194
|
smRef.setField(cfg.field, cfg.value);
|