@mmapp/react 0.1.0-alpha.3 → 0.1.0-alpha.9
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.d.mts +1352 -93
- package/dist/index.d.ts +1352 -93
- package/dist/index.js +1032 -1314
- package/dist/index.mjs +981 -1301
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,1091 +1,11 @@
|
|
|
1
|
-
// ../player-core/dist/index.mjs
|
|
2
|
-
var add = {
|
|
3
|
-
name: "add",
|
|
4
|
-
fn: (a, b) => Number(a) + Number(b),
|
|
5
|
-
arity: 2
|
|
6
|
-
};
|
|
7
|
-
var subtract = {
|
|
8
|
-
name: "subtract",
|
|
9
|
-
fn: (a, b) => Number(a) - Number(b),
|
|
10
|
-
arity: 2
|
|
11
|
-
};
|
|
12
|
-
var multiply = {
|
|
13
|
-
name: "multiply",
|
|
14
|
-
fn: (a, b) => Number(a) * Number(b),
|
|
15
|
-
arity: 2
|
|
16
|
-
};
|
|
17
|
-
var divide = {
|
|
18
|
-
name: "divide",
|
|
19
|
-
fn: (a, b) => {
|
|
20
|
-
const d = Number(b);
|
|
21
|
-
return d === 0 ? 0 : Number(a) / d;
|
|
22
|
-
},
|
|
23
|
-
arity: 2
|
|
24
|
-
};
|
|
25
|
-
var abs = {
|
|
26
|
-
name: "abs",
|
|
27
|
-
fn: (a) => Math.abs(Number(a)),
|
|
28
|
-
arity: 1
|
|
29
|
-
};
|
|
30
|
-
var round = {
|
|
31
|
-
name: "round",
|
|
32
|
-
fn: (a, decimals) => {
|
|
33
|
-
const d = decimals != null ? Number(decimals) : 0;
|
|
34
|
-
const factor = Math.pow(10, d);
|
|
35
|
-
return Math.round(Number(a) * factor) / factor;
|
|
36
|
-
},
|
|
37
|
-
arity: -1
|
|
38
|
-
};
|
|
39
|
-
var min = {
|
|
40
|
-
name: "min",
|
|
41
|
-
fn: (...args) => {
|
|
42
|
-
const nums = args.flat().map(Number).filter((n) => !isNaN(n));
|
|
43
|
-
return nums.length === 0 ? 0 : Math.min(...nums);
|
|
44
|
-
},
|
|
45
|
-
arity: -1
|
|
46
|
-
};
|
|
47
|
-
var max = {
|
|
48
|
-
name: "max",
|
|
49
|
-
fn: (...args) => {
|
|
50
|
-
const nums = args.flat().map(Number).filter((n) => !isNaN(n));
|
|
51
|
-
return nums.length === 0 ? 0 : Math.max(...nums);
|
|
52
|
-
},
|
|
53
|
-
arity: -1
|
|
54
|
-
};
|
|
55
|
-
var eq = {
|
|
56
|
-
name: "eq",
|
|
57
|
-
fn: (a, b) => a === b || String(a) === String(b),
|
|
58
|
-
arity: 2
|
|
59
|
-
};
|
|
60
|
-
var neq = {
|
|
61
|
-
name: "neq",
|
|
62
|
-
fn: (a, b) => a !== b && String(a) !== String(b),
|
|
63
|
-
arity: 2
|
|
64
|
-
};
|
|
65
|
-
var gt = {
|
|
66
|
-
name: "gt",
|
|
67
|
-
fn: (a, b) => Number(a) > Number(b),
|
|
68
|
-
arity: 2
|
|
69
|
-
};
|
|
70
|
-
var gte = {
|
|
71
|
-
name: "gte",
|
|
72
|
-
fn: (a, b) => Number(a) >= Number(b),
|
|
73
|
-
arity: 2
|
|
74
|
-
};
|
|
75
|
-
var lt = {
|
|
76
|
-
name: "lt",
|
|
77
|
-
fn: (a, b) => Number(a) < Number(b),
|
|
78
|
-
arity: 2
|
|
79
|
-
};
|
|
80
|
-
var lte = {
|
|
81
|
-
name: "lte",
|
|
82
|
-
fn: (a, b) => Number(a) <= Number(b),
|
|
83
|
-
arity: 2
|
|
84
|
-
};
|
|
85
|
-
var if_fn = {
|
|
86
|
-
name: "if",
|
|
87
|
-
fn: (cond, then, else_) => cond ? then : else_,
|
|
88
|
-
arity: 3
|
|
89
|
-
};
|
|
90
|
-
var and = {
|
|
91
|
-
name: "and",
|
|
92
|
-
fn: (...args) => args.every(Boolean),
|
|
93
|
-
arity: -1
|
|
94
|
-
};
|
|
95
|
-
var or = {
|
|
96
|
-
name: "or",
|
|
97
|
-
fn: (...args) => args.some(Boolean),
|
|
98
|
-
arity: -1
|
|
99
|
-
};
|
|
100
|
-
var not = {
|
|
101
|
-
name: "not",
|
|
102
|
-
fn: (a) => !a,
|
|
103
|
-
arity: 1
|
|
104
|
-
};
|
|
105
|
-
var coalesce = {
|
|
106
|
-
name: "coalesce",
|
|
107
|
-
fn: (...args) => {
|
|
108
|
-
for (const arg of args) {
|
|
109
|
-
if (arg != null) return arg;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
},
|
|
113
|
-
arity: -1
|
|
114
|
-
};
|
|
115
|
-
var concat = {
|
|
116
|
-
name: "concat",
|
|
117
|
-
fn: (...args) => args.map(String).join(""),
|
|
118
|
-
arity: -1
|
|
119
|
-
};
|
|
120
|
-
var upper = {
|
|
121
|
-
name: "upper",
|
|
122
|
-
fn: (s) => String(s ?? "").toUpperCase(),
|
|
123
|
-
arity: 1
|
|
124
|
-
};
|
|
125
|
-
var lower = {
|
|
126
|
-
name: "lower",
|
|
127
|
-
fn: (s) => String(s ?? "").toLowerCase(),
|
|
128
|
-
arity: 1
|
|
129
|
-
};
|
|
130
|
-
var trim = {
|
|
131
|
-
name: "trim",
|
|
132
|
-
fn: (s) => String(s ?? "").trim(),
|
|
133
|
-
arity: 1
|
|
134
|
-
};
|
|
135
|
-
var format = {
|
|
136
|
-
name: "format",
|
|
137
|
-
fn: (template, ...args) => {
|
|
138
|
-
let result = String(template ?? "");
|
|
139
|
-
args.forEach((arg, i) => {
|
|
140
|
-
result = result.replace(`{${i}}`, String(arg ?? ""));
|
|
141
|
-
});
|
|
142
|
-
return result;
|
|
143
|
-
},
|
|
144
|
-
arity: -1
|
|
145
|
-
};
|
|
146
|
-
var length = {
|
|
147
|
-
name: "length",
|
|
148
|
-
fn: (v) => {
|
|
149
|
-
if (Array.isArray(v)) return v.length;
|
|
150
|
-
if (typeof v === "string") return v.length;
|
|
151
|
-
if (v && typeof v === "object") return Object.keys(v).length;
|
|
152
|
-
return 0;
|
|
153
|
-
},
|
|
154
|
-
arity: 1
|
|
155
|
-
};
|
|
156
|
-
var get = {
|
|
157
|
-
name: "get",
|
|
158
|
-
fn: (obj, path) => {
|
|
159
|
-
if (obj == null || typeof path !== "string") return void 0;
|
|
160
|
-
const parts = path.split(".");
|
|
161
|
-
let current = obj;
|
|
162
|
-
for (const part of parts) {
|
|
163
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
164
|
-
current = current[part];
|
|
165
|
-
}
|
|
166
|
-
return current;
|
|
167
|
-
},
|
|
168
|
-
arity: 2
|
|
169
|
-
};
|
|
170
|
-
var includes = {
|
|
171
|
-
name: "includes",
|
|
172
|
-
fn: (collection, value) => {
|
|
173
|
-
if (Array.isArray(collection)) return collection.includes(value);
|
|
174
|
-
if (typeof collection === "string") return collection.includes(String(value));
|
|
175
|
-
return false;
|
|
176
|
-
},
|
|
177
|
-
arity: 2
|
|
178
|
-
};
|
|
179
|
-
var is_defined = {
|
|
180
|
-
name: "is_defined",
|
|
181
|
-
fn: (v) => v !== void 0 && v !== null,
|
|
182
|
-
arity: 1
|
|
183
|
-
};
|
|
184
|
-
var is_empty = {
|
|
185
|
-
name: "is_empty",
|
|
186
|
-
fn: (v) => {
|
|
187
|
-
if (v == null) return true;
|
|
188
|
-
if (typeof v === "string") return v.length === 0;
|
|
189
|
-
if (Array.isArray(v)) return v.length === 0;
|
|
190
|
-
if (typeof v === "object") return Object.keys(v).length === 0;
|
|
191
|
-
return false;
|
|
192
|
-
},
|
|
193
|
-
arity: 1
|
|
194
|
-
};
|
|
195
|
-
var is_null = {
|
|
196
|
-
name: "is_null",
|
|
197
|
-
fn: (v) => v === null || v === void 0,
|
|
198
|
-
arity: 1
|
|
199
|
-
};
|
|
200
|
-
var to_string = {
|
|
201
|
-
name: "to_string",
|
|
202
|
-
fn: (v) => {
|
|
203
|
-
if (v == null) return "";
|
|
204
|
-
if (typeof v === "object") return JSON.stringify(v);
|
|
205
|
-
return String(v);
|
|
206
|
-
},
|
|
207
|
-
arity: 1
|
|
208
|
-
};
|
|
209
|
-
var CORE_FUNCTIONS = [
|
|
210
|
-
// Math (8)
|
|
211
|
-
add,
|
|
212
|
-
subtract,
|
|
213
|
-
multiply,
|
|
214
|
-
divide,
|
|
215
|
-
abs,
|
|
216
|
-
round,
|
|
217
|
-
min,
|
|
218
|
-
max,
|
|
219
|
-
// Comparison (6)
|
|
220
|
-
eq,
|
|
221
|
-
neq,
|
|
222
|
-
gt,
|
|
223
|
-
gte,
|
|
224
|
-
lt,
|
|
225
|
-
lte,
|
|
226
|
-
// Logic (5)
|
|
227
|
-
if_fn,
|
|
228
|
-
and,
|
|
229
|
-
or,
|
|
230
|
-
not,
|
|
231
|
-
coalesce,
|
|
232
|
-
// String (6)
|
|
233
|
-
concat,
|
|
234
|
-
upper,
|
|
235
|
-
lower,
|
|
236
|
-
trim,
|
|
237
|
-
format,
|
|
238
|
-
length,
|
|
239
|
-
// Path (3)
|
|
240
|
-
get,
|
|
241
|
-
includes,
|
|
242
|
-
is_defined,
|
|
243
|
-
// Type (3)
|
|
244
|
-
is_empty,
|
|
245
|
-
is_null,
|
|
246
|
-
to_string
|
|
247
|
-
];
|
|
248
|
-
function buildFunctionMap(functions) {
|
|
249
|
-
const map = /* @__PURE__ */ new Map();
|
|
250
|
-
for (const fn of functions) {
|
|
251
|
-
map.set(fn.name, fn.fn);
|
|
252
|
-
}
|
|
253
|
-
return map;
|
|
254
|
-
}
|
|
255
|
-
var MAX_DEPTH = 50;
|
|
256
|
-
var Parser = class {
|
|
257
|
-
pos = 0;
|
|
258
|
-
depth = 0;
|
|
259
|
-
input;
|
|
260
|
-
constructor(input) {
|
|
261
|
-
this.input = input;
|
|
262
|
-
}
|
|
263
|
-
parse() {
|
|
264
|
-
this.skipWhitespace();
|
|
265
|
-
const node = this.parseExpression();
|
|
266
|
-
this.skipWhitespace();
|
|
267
|
-
if (this.pos < this.input.length) {
|
|
268
|
-
throw new Error(`Unexpected character at position ${this.pos}: '${this.input[this.pos]}'`);
|
|
269
|
-
}
|
|
270
|
-
return node;
|
|
271
|
-
}
|
|
272
|
-
guardDepth() {
|
|
273
|
-
if (++this.depth > MAX_DEPTH) {
|
|
274
|
-
throw new Error("Expression too deeply nested");
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
parseExpression() {
|
|
278
|
-
this.guardDepth();
|
|
279
|
-
try {
|
|
280
|
-
return this.parseTernary();
|
|
281
|
-
} finally {
|
|
282
|
-
this.depth--;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
parseTernary() {
|
|
286
|
-
let node = this.parseLogicalOr();
|
|
287
|
-
this.skipWhitespace();
|
|
288
|
-
if (this.peek() === "?") {
|
|
289
|
-
this.advance();
|
|
290
|
-
const consequent = this.parseExpression();
|
|
291
|
-
this.skipWhitespace();
|
|
292
|
-
this.expect(":");
|
|
293
|
-
const alternate = this.parseExpression();
|
|
294
|
-
node = { type: "ternary", condition: node, consequent, alternate };
|
|
295
|
-
}
|
|
296
|
-
return node;
|
|
297
|
-
}
|
|
298
|
-
parseLogicalOr() {
|
|
299
|
-
let left = this.parseLogicalAnd();
|
|
300
|
-
this.skipWhitespace();
|
|
301
|
-
while (this.match("||")) {
|
|
302
|
-
const right = this.parseLogicalAnd();
|
|
303
|
-
left = { type: "binary", operator: "||", left, right };
|
|
304
|
-
this.skipWhitespace();
|
|
305
|
-
}
|
|
306
|
-
return left;
|
|
307
|
-
}
|
|
308
|
-
parseLogicalAnd() {
|
|
309
|
-
let left = this.parseEquality();
|
|
310
|
-
this.skipWhitespace();
|
|
311
|
-
while (this.match("&&")) {
|
|
312
|
-
const right = this.parseEquality();
|
|
313
|
-
left = { type: "binary", operator: "&&", left, right };
|
|
314
|
-
this.skipWhitespace();
|
|
315
|
-
}
|
|
316
|
-
return left;
|
|
317
|
-
}
|
|
318
|
-
parseEquality() {
|
|
319
|
-
let left = this.parseComparison();
|
|
320
|
-
this.skipWhitespace();
|
|
321
|
-
while (true) {
|
|
322
|
-
if (this.match("==")) {
|
|
323
|
-
const right = this.parseComparison();
|
|
324
|
-
left = { type: "binary", operator: "==", left, right };
|
|
325
|
-
} else if (this.match("!=")) {
|
|
326
|
-
const right = this.parseComparison();
|
|
327
|
-
left = { type: "binary", operator: "!=", left, right };
|
|
328
|
-
} else {
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
this.skipWhitespace();
|
|
332
|
-
}
|
|
333
|
-
return left;
|
|
334
|
-
}
|
|
335
|
-
parseComparison() {
|
|
336
|
-
let left = this.parseUnary();
|
|
337
|
-
this.skipWhitespace();
|
|
338
|
-
while (true) {
|
|
339
|
-
if (this.match(">=")) {
|
|
340
|
-
const right = this.parseUnary();
|
|
341
|
-
left = { type: "binary", operator: ">=", left, right };
|
|
342
|
-
} else if (this.match("<=")) {
|
|
343
|
-
const right = this.parseUnary();
|
|
344
|
-
left = { type: "binary", operator: "<=", left, right };
|
|
345
|
-
} else if (this.peek() === ">" && !this.lookAhead(">=")) {
|
|
346
|
-
this.advance();
|
|
347
|
-
const right = this.parseUnary();
|
|
348
|
-
left = { type: "binary", operator: ">", left, right };
|
|
349
|
-
} else if (this.peek() === "<" && !this.lookAhead("<=")) {
|
|
350
|
-
this.advance();
|
|
351
|
-
const right = this.parseUnary();
|
|
352
|
-
left = { type: "binary", operator: "<", left, right };
|
|
353
|
-
} else {
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
this.skipWhitespace();
|
|
357
|
-
}
|
|
358
|
-
return left;
|
|
359
|
-
}
|
|
360
|
-
parseUnary() {
|
|
361
|
-
this.skipWhitespace();
|
|
362
|
-
if (this.peek() === "!") {
|
|
363
|
-
this.advance();
|
|
364
|
-
const operand = this.parseUnary();
|
|
365
|
-
return { type: "unary", operator: "!", operand };
|
|
366
|
-
}
|
|
367
|
-
if (this.peek() === "-") {
|
|
368
|
-
const nextChar = this.input[this.pos + 1];
|
|
369
|
-
if (nextChar !== void 0 && (nextChar >= "0" && nextChar <= "9" || nextChar === ".")) {
|
|
370
|
-
return this.parseCallChain();
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return this.parseCallChain();
|
|
374
|
-
}
|
|
375
|
-
parseCallChain() {
|
|
376
|
-
let node = this.parsePrimary();
|
|
377
|
-
while (true) {
|
|
378
|
-
this.skipWhitespace();
|
|
379
|
-
if (this.peek() === "(") {
|
|
380
|
-
this.advance();
|
|
381
|
-
const args = this.parseArgList();
|
|
382
|
-
this.expect(")");
|
|
383
|
-
if (node.type === "identifier") {
|
|
384
|
-
node = { type: "call", name: node.name, args };
|
|
385
|
-
} else if (node.type === "path") {
|
|
386
|
-
const name = node.segments.join(".");
|
|
387
|
-
node = { type: "call", name, args };
|
|
388
|
-
} else if (node.type === "member") {
|
|
389
|
-
node = { type: "method_call", object: node.object, method: node.property, args };
|
|
390
|
-
} else {
|
|
391
|
-
throw new Error("Cannot call non-function");
|
|
392
|
-
}
|
|
393
|
-
} else if (this.peek() === ".") {
|
|
394
|
-
this.advance();
|
|
395
|
-
const prop = this.parseIdentifierName();
|
|
396
|
-
node = { type: "member", object: node, property: prop };
|
|
397
|
-
} else {
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return node;
|
|
402
|
-
}
|
|
403
|
-
parsePrimary() {
|
|
404
|
-
this.skipWhitespace();
|
|
405
|
-
const ch = this.peek();
|
|
406
|
-
if (ch === "(") {
|
|
407
|
-
this.advance();
|
|
408
|
-
const expr2 = this.parseExpression();
|
|
409
|
-
this.skipWhitespace();
|
|
410
|
-
this.expect(")");
|
|
411
|
-
return expr2;
|
|
412
|
-
}
|
|
413
|
-
if (ch === "'" || ch === '"') {
|
|
414
|
-
return this.parseString();
|
|
415
|
-
}
|
|
416
|
-
if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
417
|
-
return this.parseNumber();
|
|
418
|
-
}
|
|
419
|
-
if (this.isIdentStart(ch)) {
|
|
420
|
-
return this.parseIdentifierOrPath();
|
|
421
|
-
}
|
|
422
|
-
throw new Error(
|
|
423
|
-
`Unexpected character at position ${this.pos}: '${ch || "EOF"}'`
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
parseString() {
|
|
427
|
-
const quote = this.advance();
|
|
428
|
-
let value = "";
|
|
429
|
-
while (this.pos < this.input.length && this.peek() !== quote) {
|
|
430
|
-
if (this.peek() === "\\") {
|
|
431
|
-
this.advance();
|
|
432
|
-
const esc = this.advance();
|
|
433
|
-
if (esc === "n") value += "\n";
|
|
434
|
-
else if (esc === "t") value += " ";
|
|
435
|
-
else if (esc === "r") value += "\r";
|
|
436
|
-
else value += esc;
|
|
437
|
-
} else {
|
|
438
|
-
value += this.advance();
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
if (this.pos >= this.input.length) {
|
|
442
|
-
throw new Error("Unterminated string literal");
|
|
443
|
-
}
|
|
444
|
-
this.advance();
|
|
445
|
-
return { type: "string", value };
|
|
446
|
-
}
|
|
447
|
-
parseNumber() {
|
|
448
|
-
let numStr = "";
|
|
449
|
-
if (this.peek() === "-") {
|
|
450
|
-
numStr += this.advance();
|
|
451
|
-
}
|
|
452
|
-
while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
|
|
453
|
-
numStr += this.advance();
|
|
454
|
-
}
|
|
455
|
-
if (this.peek() === "." && this.pos + 1 < this.input.length && this.input[this.pos + 1] >= "0" && this.input[this.pos + 1] <= "9") {
|
|
456
|
-
numStr += this.advance();
|
|
457
|
-
while (this.pos < this.input.length && (this.input[this.pos] >= "0" && this.input[this.pos] <= "9")) {
|
|
458
|
-
numStr += this.advance();
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return { type: "number", value: Number(numStr) };
|
|
462
|
-
}
|
|
463
|
-
parseIdentifierOrPath() {
|
|
464
|
-
const name = this.parseIdentifierName();
|
|
465
|
-
if (name === "true") return { type: "boolean", value: true };
|
|
466
|
-
if (name === "false") return { type: "boolean", value: false };
|
|
467
|
-
if (name === "null") return { type: "null" };
|
|
468
|
-
if (name === "undefined") return { type: "null" };
|
|
469
|
-
return { type: "identifier", name };
|
|
470
|
-
}
|
|
471
|
-
parseIdentifierName() {
|
|
472
|
-
let name = "";
|
|
473
|
-
if (this.peek() === "$") name += this.advance();
|
|
474
|
-
while (this.pos < this.input.length && this.isIdentPart(this.input[this.pos])) {
|
|
475
|
-
name += this.advance();
|
|
476
|
-
}
|
|
477
|
-
if (!name) {
|
|
478
|
-
throw new Error(`Expected identifier at position ${this.pos}`);
|
|
479
|
-
}
|
|
480
|
-
return name;
|
|
481
|
-
}
|
|
482
|
-
parseArgList() {
|
|
483
|
-
this.skipWhitespace();
|
|
484
|
-
if (this.peek() === ")") return [];
|
|
485
|
-
const args = [];
|
|
486
|
-
args.push(this.parseExpression());
|
|
487
|
-
this.skipWhitespace();
|
|
488
|
-
while (this.peek() === ",") {
|
|
489
|
-
this.advance();
|
|
490
|
-
args.push(this.parseExpression());
|
|
491
|
-
this.skipWhitespace();
|
|
492
|
-
}
|
|
493
|
-
return args;
|
|
494
|
-
}
|
|
495
|
-
// Character utilities
|
|
496
|
-
peek() {
|
|
497
|
-
return this.input[this.pos] ?? "";
|
|
498
|
-
}
|
|
499
|
-
advance() {
|
|
500
|
-
return this.input[this.pos++] ?? "";
|
|
501
|
-
}
|
|
502
|
-
match(str) {
|
|
503
|
-
if (this.input.startsWith(str, this.pos)) {
|
|
504
|
-
this.pos += str.length;
|
|
505
|
-
return true;
|
|
506
|
-
}
|
|
507
|
-
return false;
|
|
508
|
-
}
|
|
509
|
-
lookAhead(str) {
|
|
510
|
-
return this.input.startsWith(str, this.pos);
|
|
511
|
-
}
|
|
512
|
-
expect(ch) {
|
|
513
|
-
this.skipWhitespace();
|
|
514
|
-
if (this.peek() !== ch) {
|
|
515
|
-
throw new Error(`Expected '${ch}' at position ${this.pos}, got '${this.peek() || "EOF"}'`);
|
|
516
|
-
}
|
|
517
|
-
this.advance();
|
|
518
|
-
}
|
|
519
|
-
skipWhitespace() {
|
|
520
|
-
while (this.pos < this.input.length && " \n\r".includes(this.input[this.pos])) {
|
|
521
|
-
this.pos++;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
isIdentStart(ch) {
|
|
525
|
-
return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_" || ch === "$";
|
|
526
|
-
}
|
|
527
|
-
isIdentPart(ch) {
|
|
528
|
-
return this.isIdentStart(ch) || ch >= "0" && ch <= "9";
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
function evaluateAST(node, context, fnMap) {
|
|
532
|
-
switch (node.type) {
|
|
533
|
-
case "number":
|
|
534
|
-
return node.value;
|
|
535
|
-
case "string":
|
|
536
|
-
return node.value;
|
|
537
|
-
case "boolean":
|
|
538
|
-
return node.value;
|
|
539
|
-
case "null":
|
|
540
|
-
return null;
|
|
541
|
-
case "identifier":
|
|
542
|
-
return resolvePath(node.name, context);
|
|
543
|
-
case "path":
|
|
544
|
-
return resolvePath(node.segments.join("."), context);
|
|
545
|
-
case "member": {
|
|
546
|
-
const obj = evaluateAST(node.object, context, fnMap);
|
|
547
|
-
if (obj == null || typeof obj !== "object") return void 0;
|
|
548
|
-
return obj[node.property];
|
|
549
|
-
}
|
|
550
|
-
case "call": {
|
|
551
|
-
const fn = fnMap.get(node.name);
|
|
552
|
-
if (!fn) return void 0;
|
|
553
|
-
const args = node.args.map((a) => evaluateAST(a, context, fnMap));
|
|
554
|
-
return fn(...args);
|
|
555
|
-
}
|
|
556
|
-
case "method_call": {
|
|
557
|
-
const obj = evaluateAST(node.object, context, fnMap);
|
|
558
|
-
if (obj != null && typeof obj === "object") {
|
|
559
|
-
const method = obj[node.method];
|
|
560
|
-
if (typeof method === "function") {
|
|
561
|
-
const args = node.args.map((a) => evaluateAST(a, context, fnMap));
|
|
562
|
-
return method.apply(obj, args);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return void 0;
|
|
566
|
-
}
|
|
567
|
-
case "unary": {
|
|
568
|
-
const operand = evaluateAST(node.operand, context, fnMap);
|
|
569
|
-
return !operand;
|
|
570
|
-
}
|
|
571
|
-
case "binary": {
|
|
572
|
-
if (node.operator === "&&") {
|
|
573
|
-
const left2 = evaluateAST(node.left, context, fnMap);
|
|
574
|
-
if (!left2) return left2;
|
|
575
|
-
return evaluateAST(node.right, context, fnMap);
|
|
576
|
-
}
|
|
577
|
-
if (node.operator === "||") {
|
|
578
|
-
const left2 = evaluateAST(node.left, context, fnMap);
|
|
579
|
-
if (left2) return left2;
|
|
580
|
-
return evaluateAST(node.right, context, fnMap);
|
|
581
|
-
}
|
|
582
|
-
const left = evaluateAST(node.left, context, fnMap);
|
|
583
|
-
const right = evaluateAST(node.right, context, fnMap);
|
|
584
|
-
switch (node.operator) {
|
|
585
|
-
// eslint-disable-next-line eqeqeq
|
|
586
|
-
case "==":
|
|
587
|
-
return left == right;
|
|
588
|
-
// eslint-disable-next-line eqeqeq
|
|
589
|
-
case "!=":
|
|
590
|
-
return left != right;
|
|
591
|
-
case ">":
|
|
592
|
-
return Number(left) > Number(right);
|
|
593
|
-
case "<":
|
|
594
|
-
return Number(left) < Number(right);
|
|
595
|
-
case ">=":
|
|
596
|
-
return Number(left) >= Number(right);
|
|
597
|
-
case "<=":
|
|
598
|
-
return Number(left) <= Number(right);
|
|
599
|
-
default:
|
|
600
|
-
return void 0;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
case "ternary": {
|
|
604
|
-
const condition = evaluateAST(node.condition, context, fnMap);
|
|
605
|
-
return condition ? evaluateAST(node.consequent, context, fnMap) : evaluateAST(node.alternate, context, fnMap);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
var MAX_CACHE = 500;
|
|
610
|
-
var astCache = /* @__PURE__ */ new Map();
|
|
611
|
-
function evictIfNeeded() {
|
|
612
|
-
if (astCache.size > MAX_CACHE) {
|
|
613
|
-
const keys = Array.from(astCache.keys());
|
|
614
|
-
const evictCount = Math.floor(MAX_CACHE * 0.25);
|
|
615
|
-
for (let i = 0; i < evictCount; i++) {
|
|
616
|
-
astCache.delete(keys[i]);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
function parseAndCache(expr2) {
|
|
621
|
-
const cached = astCache.get(expr2);
|
|
622
|
-
if (cached) return cached;
|
|
623
|
-
const parser = new Parser(expr2);
|
|
624
|
-
const ast = parser.parse();
|
|
625
|
-
astCache.set(expr2, ast);
|
|
626
|
-
evictIfNeeded();
|
|
627
|
-
return ast;
|
|
628
|
-
}
|
|
629
|
-
var TEMPLATE_RE = /\{\{(.+?)\}\}/g;
|
|
630
|
-
function resolvePath(path, context) {
|
|
631
|
-
const parts = path.split(".");
|
|
632
|
-
let current = context;
|
|
633
|
-
for (const part of parts) {
|
|
634
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
635
|
-
current = current[part];
|
|
636
|
-
}
|
|
637
|
-
return current;
|
|
638
|
-
}
|
|
639
|
-
function evaluateExpression(expr2, context, fnMap) {
|
|
640
|
-
const trimmed = expr2.trim();
|
|
641
|
-
if (trimmed === "true") return true;
|
|
642
|
-
if (trimmed === "false") return false;
|
|
643
|
-
if (trimmed === "null") return null;
|
|
644
|
-
if (trimmed === "undefined") return void 0;
|
|
645
|
-
const num = Number(trimmed);
|
|
646
|
-
if (!isNaN(num) && trimmed !== "") return num;
|
|
647
|
-
if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
648
|
-
return trimmed.slice(1, -1);
|
|
649
|
-
}
|
|
650
|
-
if (/^[a-zA-Z_$][\w$.]*$/.test(trimmed)) {
|
|
651
|
-
return resolvePath(trimmed, context);
|
|
652
|
-
}
|
|
653
|
-
const ast = parseAndCache(trimmed);
|
|
654
|
-
return evaluateAST(ast, context, fnMap);
|
|
655
|
-
}
|
|
656
|
-
var WEB_FAILURE_POLICIES = {
|
|
657
|
-
VIEW_BINDING: {
|
|
658
|
-
on_error: "return_fallback",
|
|
659
|
-
fallback_value: "",
|
|
660
|
-
log_level: "warn"
|
|
661
|
-
},
|
|
662
|
-
EVENT_REACTION: {
|
|
663
|
-
on_error: "log_and_skip",
|
|
664
|
-
fallback_value: void 0,
|
|
665
|
-
log_level: "error"
|
|
666
|
-
},
|
|
667
|
-
DURING_ACTION: {
|
|
668
|
-
on_error: "log_and_skip",
|
|
669
|
-
fallback_value: void 0,
|
|
670
|
-
log_level: "error"
|
|
671
|
-
},
|
|
672
|
-
CONDITIONAL_VISIBILITY: {
|
|
673
|
-
on_error: "return_fallback",
|
|
674
|
-
fallback_value: true,
|
|
675
|
-
// Show by default if condition fails
|
|
676
|
-
log_level: "warn"
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
function createEvaluator(config) {
|
|
680
|
-
const allFunctions = [...CORE_FUNCTIONS, ...config.functions];
|
|
681
|
-
const fnMap = buildFunctionMap(allFunctions);
|
|
682
|
-
const policy = config.failurePolicy;
|
|
683
|
-
function handleError(expr2, error) {
|
|
684
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
685
|
-
if (policy.log_level === "error") {
|
|
686
|
-
console.error(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
|
|
687
|
-
} else if (policy.log_level === "warn") {
|
|
688
|
-
console.warn(`[player-core] Expression error: "${expr2}" \u2014 ${message}`);
|
|
689
|
-
}
|
|
690
|
-
switch (policy.on_error) {
|
|
691
|
-
case "throw":
|
|
692
|
-
throw error;
|
|
693
|
-
case "return_fallback":
|
|
694
|
-
return { value: policy.fallback_value, status: "fallback", error: message };
|
|
695
|
-
case "log_and_skip":
|
|
696
|
-
default:
|
|
697
|
-
return { value: policy.fallback_value, status: "error", error: message };
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
return {
|
|
701
|
-
evaluate(expression, context) {
|
|
702
|
-
try {
|
|
703
|
-
const value = evaluateExpression(expression, context, fnMap);
|
|
704
|
-
return { value, status: "ok" };
|
|
705
|
-
} catch (error) {
|
|
706
|
-
return handleError(expression, error);
|
|
707
|
-
}
|
|
708
|
-
},
|
|
709
|
-
evaluateTemplate(template, context) {
|
|
710
|
-
try {
|
|
711
|
-
if (!template.includes("{{")) {
|
|
712
|
-
return { value: template, status: "ok" };
|
|
713
|
-
}
|
|
714
|
-
const result = template.replace(TEMPLATE_RE, (_match, expr2) => {
|
|
715
|
-
const value = evaluateExpression(expr2, context, fnMap);
|
|
716
|
-
return value != null ? String(value) : "";
|
|
717
|
-
});
|
|
718
|
-
return { value: result, status: "ok" };
|
|
719
|
-
} catch (error) {
|
|
720
|
-
return handleError(template, error);
|
|
721
|
-
}
|
|
722
|
-
},
|
|
723
|
-
validate(expression) {
|
|
724
|
-
const errors = [];
|
|
725
|
-
try {
|
|
726
|
-
parseAndCache(expression);
|
|
727
|
-
} catch (e) {
|
|
728
|
-
errors.push(e instanceof Error ? e.message : String(e));
|
|
729
|
-
}
|
|
730
|
-
return { valid: errors.length === 0, errors };
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
var MAX_AUTO_CHAIN = 10;
|
|
735
|
-
var StateMachine = class {
|
|
736
|
-
evaluator;
|
|
737
|
-
actionHandlers;
|
|
738
|
-
listeners = /* @__PURE__ */ new Set();
|
|
739
|
-
instance;
|
|
740
|
-
constructor(definition, initialData = {}, config) {
|
|
741
|
-
this.evaluator = config.evaluator;
|
|
742
|
-
this.actionHandlers = config.actionHandlers ?? /* @__PURE__ */ new Map();
|
|
743
|
-
const startState = definition.states.find((s) => s.type === "START");
|
|
744
|
-
if (!startState) {
|
|
745
|
-
throw new Error(`No START state found in definition ${definition.slug}`);
|
|
746
|
-
}
|
|
747
|
-
this.instance = {
|
|
748
|
-
definition,
|
|
749
|
-
current_state: startState.name,
|
|
750
|
-
state_data: { ...initialData },
|
|
751
|
-
memory: {},
|
|
752
|
-
status: "ACTIVE"
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
/** Get the current instance snapshot (immutable copy) */
|
|
756
|
-
getSnapshot() {
|
|
757
|
-
return { ...this.instance, state_data: { ...this.instance.state_data }, memory: { ...this.instance.memory } };
|
|
758
|
-
}
|
|
759
|
-
/** Get current state name */
|
|
760
|
-
get currentState() {
|
|
761
|
-
return this.instance.current_state;
|
|
762
|
-
}
|
|
763
|
-
/** Get current state_data */
|
|
764
|
-
get stateData() {
|
|
765
|
-
return this.instance.state_data;
|
|
766
|
-
}
|
|
767
|
-
/** Get current status */
|
|
768
|
-
get status() {
|
|
769
|
-
return this.instance.status;
|
|
770
|
-
}
|
|
771
|
-
/** Subscribe to state machine events */
|
|
772
|
-
on(listener) {
|
|
773
|
-
this.listeners.add(listener);
|
|
774
|
-
return () => this.listeners.delete(listener);
|
|
775
|
-
}
|
|
776
|
-
/** Register an action handler */
|
|
777
|
-
registerAction(type, handler) {
|
|
778
|
-
this.actionHandlers.set(type, handler);
|
|
779
|
-
}
|
|
780
|
-
/** Execute a named transition */
|
|
781
|
-
async transition(transitionName, data) {
|
|
782
|
-
if (this.instance.status !== "ACTIVE") {
|
|
783
|
-
return {
|
|
784
|
-
success: false,
|
|
785
|
-
from_state: this.instance.current_state,
|
|
786
|
-
to_state: this.instance.current_state,
|
|
787
|
-
actions_executed: [],
|
|
788
|
-
error: `Cannot transition: instance status is ${this.instance.status}`
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
const transition2 = this.instance.definition.transitions.find(
|
|
792
|
-
(t) => t.name === transitionName && t.from.includes(this.instance.current_state)
|
|
793
|
-
);
|
|
794
|
-
if (!transition2) {
|
|
795
|
-
return {
|
|
796
|
-
success: false,
|
|
797
|
-
from_state: this.instance.current_state,
|
|
798
|
-
to_state: this.instance.current_state,
|
|
799
|
-
actions_executed: [],
|
|
800
|
-
error: `Transition "${transitionName}" not valid from state "${this.instance.current_state}"`
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
|
-
if (data) {
|
|
804
|
-
this.instance.state_data = { ...this.instance.state_data, ...data };
|
|
805
|
-
}
|
|
806
|
-
if (transition2.conditions && transition2.conditions.length > 0) {
|
|
807
|
-
const ctx = this.buildContext();
|
|
808
|
-
for (const condition of transition2.conditions) {
|
|
809
|
-
const result2 = this.evaluator.evaluate(condition, ctx);
|
|
810
|
-
if (!result2.value) {
|
|
811
|
-
return {
|
|
812
|
-
success: false,
|
|
813
|
-
from_state: this.instance.current_state,
|
|
814
|
-
to_state: this.instance.current_state,
|
|
815
|
-
actions_executed: [],
|
|
816
|
-
error: `Transition condition not met: ${condition}`
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
const result = await this.executeTransition(transition2);
|
|
822
|
-
if (result.success) {
|
|
823
|
-
await this.drainAutoTransitions();
|
|
824
|
-
}
|
|
825
|
-
return result;
|
|
826
|
-
}
|
|
827
|
-
/** Update state_data directly (for on_event set_field actions) */
|
|
828
|
-
setField(field2, value) {
|
|
829
|
-
this.instance.state_data = { ...this.instance.state_data, [field2]: value };
|
|
830
|
-
}
|
|
831
|
-
/** Update memory */
|
|
832
|
-
setMemory(key, value) {
|
|
833
|
-
this.instance.memory = { ...this.instance.memory, [key]: value };
|
|
834
|
-
}
|
|
835
|
-
/** Get available transitions from the current state */
|
|
836
|
-
getAvailableTransitions() {
|
|
837
|
-
return this.instance.definition.transitions.filter(
|
|
838
|
-
(t) => t.from.includes(this.instance.current_state) && !t.auto
|
|
839
|
-
);
|
|
840
|
-
}
|
|
841
|
-
/** Get the current state definition */
|
|
842
|
-
getCurrentStateDefinition() {
|
|
843
|
-
return this.instance.definition.states.find((s) => s.name === this.instance.current_state);
|
|
844
|
-
}
|
|
845
|
-
// ===========================================================================
|
|
846
|
-
// Private implementation
|
|
847
|
-
// ===========================================================================
|
|
848
|
-
async executeTransition(transition2) {
|
|
849
|
-
const fromState = this.instance.current_state;
|
|
850
|
-
const allActionsExecuted = [];
|
|
851
|
-
const fromStateDef = this.getCurrentStateDefinition();
|
|
852
|
-
if (fromStateDef?.on_exit) {
|
|
853
|
-
await this.executeActions(fromStateDef.on_exit, allActionsExecuted);
|
|
854
|
-
}
|
|
855
|
-
this.emit({
|
|
856
|
-
type: "state_exit",
|
|
857
|
-
instance_id: this.instance.definition.id,
|
|
858
|
-
from_state: fromState
|
|
859
|
-
});
|
|
860
|
-
if (transition2.actions) {
|
|
861
|
-
await this.executeActions(transition2.actions, allActionsExecuted);
|
|
862
|
-
}
|
|
863
|
-
this.instance.current_state = transition2.to;
|
|
864
|
-
const toStateDef = this.instance.definition.states.find((s) => s.name === transition2.to);
|
|
865
|
-
if (toStateDef?.type === "END") {
|
|
866
|
-
this.instance.status = "COMPLETED";
|
|
867
|
-
} else if (toStateDef?.type === "CANCELLED") {
|
|
868
|
-
this.instance.status = "CANCELLED";
|
|
869
|
-
}
|
|
870
|
-
this.emit({
|
|
871
|
-
type: "state_enter",
|
|
872
|
-
instance_id: this.instance.definition.id,
|
|
873
|
-
to_state: transition2.to
|
|
874
|
-
});
|
|
875
|
-
if (toStateDef?.on_enter) {
|
|
876
|
-
await this.executeActions(toStateDef.on_enter, allActionsExecuted);
|
|
877
|
-
}
|
|
878
|
-
this.emit({
|
|
879
|
-
type: "transition",
|
|
880
|
-
instance_id: this.instance.definition.id,
|
|
881
|
-
from_state: fromState,
|
|
882
|
-
to_state: transition2.to
|
|
883
|
-
});
|
|
884
|
-
return {
|
|
885
|
-
success: true,
|
|
886
|
-
from_state: fromState,
|
|
887
|
-
to_state: transition2.to,
|
|
888
|
-
actions_executed: allActionsExecuted
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
async drainAutoTransitions() {
|
|
892
|
-
for (let depth = 0; depth < MAX_AUTO_CHAIN; depth++) {
|
|
893
|
-
if (this.instance.status !== "ACTIVE") break;
|
|
894
|
-
const autoTransition = this.findMatchingAutoTransition();
|
|
895
|
-
if (!autoTransition) break;
|
|
896
|
-
const result = await this.executeTransition(autoTransition);
|
|
897
|
-
if (!result.success) break;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
findMatchingAutoTransition() {
|
|
901
|
-
const candidates = this.instance.definition.transitions.filter(
|
|
902
|
-
(t) => t.auto && t.from.includes(this.instance.current_state)
|
|
903
|
-
);
|
|
904
|
-
const ctx = this.buildContext();
|
|
905
|
-
for (const candidate of candidates) {
|
|
906
|
-
if (!candidate.conditions || candidate.conditions.length === 0) {
|
|
907
|
-
return candidate;
|
|
908
|
-
}
|
|
909
|
-
const allMet = candidate.conditions.every((condition) => {
|
|
910
|
-
const result = this.evaluator.evaluate(condition, ctx);
|
|
911
|
-
return result.value === true;
|
|
912
|
-
});
|
|
913
|
-
if (allMet) return candidate;
|
|
914
|
-
}
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
async executeActions(actions, collector) {
|
|
918
|
-
const ctx = this.buildContext();
|
|
919
|
-
for (const action2 of actions) {
|
|
920
|
-
if (action2.condition) {
|
|
921
|
-
const condResult = this.evaluator.evaluate(action2.condition, ctx);
|
|
922
|
-
if (!condResult.value) continue;
|
|
923
|
-
}
|
|
924
|
-
const handler = this.actionHandlers.get(action2.type);
|
|
925
|
-
if (handler) {
|
|
926
|
-
try {
|
|
927
|
-
await handler(action2, ctx);
|
|
928
|
-
collector.push(action2);
|
|
929
|
-
this.emit({
|
|
930
|
-
type: "action_executed",
|
|
931
|
-
instance_id: this.instance.definition.id,
|
|
932
|
-
action: action2
|
|
933
|
-
});
|
|
934
|
-
} catch (error) {
|
|
935
|
-
this.emit({
|
|
936
|
-
type: "error",
|
|
937
|
-
instance_id: this.instance.definition.id,
|
|
938
|
-
action: action2,
|
|
939
|
-
error: error instanceof Error ? error.message : String(error)
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
buildContext() {
|
|
946
|
-
return {
|
|
947
|
-
state_data: this.instance.state_data,
|
|
948
|
-
memory: this.instance.memory,
|
|
949
|
-
current_state: this.instance.current_state,
|
|
950
|
-
status: this.instance.status,
|
|
951
|
-
// Spread state_data for direct field access (e.g., "title" instead of "state_data.title")
|
|
952
|
-
...this.instance.state_data
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
emit(event) {
|
|
956
|
-
for (const listener of this.listeners) {
|
|
957
|
-
try {
|
|
958
|
-
listener(event);
|
|
959
|
-
} catch {
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
var patternCache = /* @__PURE__ */ new Map();
|
|
965
|
-
var MAX_CACHE2 = 200;
|
|
966
|
-
function compilePattern(pattern) {
|
|
967
|
-
const cached = patternCache.get(pattern);
|
|
968
|
-
if (cached) return cached;
|
|
969
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<DOUBLESTAR>>").replace(/\*/g, "[^:.]+").replace(/<<DOUBLESTAR>>/g, ".*");
|
|
970
|
-
const regex = new RegExp(`^${escaped}$`);
|
|
971
|
-
if (patternCache.size >= MAX_CACHE2) {
|
|
972
|
-
const firstKey = patternCache.keys().next().value;
|
|
973
|
-
if (firstKey) patternCache.delete(firstKey);
|
|
974
|
-
}
|
|
975
|
-
patternCache.set(pattern, regex);
|
|
976
|
-
return regex;
|
|
977
|
-
}
|
|
978
|
-
function matchTopic(pattern, topic) {
|
|
979
|
-
return pattern.test(topic);
|
|
980
|
-
}
|
|
981
|
-
var EventBus = class {
|
|
982
|
-
subscriptions = [];
|
|
983
|
-
/**
|
|
984
|
-
* Subscribe to events matching a glob pattern.
|
|
985
|
-
* Returns an unsubscribe function.
|
|
986
|
-
*/
|
|
987
|
-
subscribe(pattern, handler) {
|
|
988
|
-
const regex = compilePattern(pattern);
|
|
989
|
-
const subscription = { pattern, regex, handler };
|
|
990
|
-
this.subscriptions.push(subscription);
|
|
991
|
-
return () => {
|
|
992
|
-
const idx = this.subscriptions.indexOf(subscription);
|
|
993
|
-
if (idx !== -1) this.subscriptions.splice(idx, 1);
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Publish an event. All matching subscriptions fire (async).
|
|
998
|
-
* Errors in handlers are caught and logged, never propagated.
|
|
999
|
-
*/
|
|
1000
|
-
async publish(topic, payload = {}) {
|
|
1001
|
-
const event = { topic, payload };
|
|
1002
|
-
for (const sub of this.subscriptions) {
|
|
1003
|
-
if (matchTopic(sub.regex, topic)) {
|
|
1004
|
-
try {
|
|
1005
|
-
await sub.handler(event);
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
console.warn(
|
|
1008
|
-
`[player-core] Event handler error for pattern "${sub.pattern}" on topic "${topic}":`,
|
|
1009
|
-
error
|
|
1010
|
-
);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
/**
|
|
1016
|
-
* Publish synchronously (fire-and-forget).
|
|
1017
|
-
* Useful when you don't need to await handler completion.
|
|
1018
|
-
*/
|
|
1019
|
-
emit(topic, payload = {}) {
|
|
1020
|
-
void this.publish(topic, payload);
|
|
1021
|
-
}
|
|
1022
|
-
/** Get count of active subscriptions */
|
|
1023
|
-
get size() {
|
|
1024
|
-
return this.subscriptions.length;
|
|
1025
|
-
}
|
|
1026
|
-
/** Remove all subscriptions */
|
|
1027
|
-
clear() {
|
|
1028
|
-
this.subscriptions.length = 0;
|
|
1029
|
-
}
|
|
1030
|
-
};
|
|
1031
|
-
var ActionDispatcher = class {
|
|
1032
|
-
handlers = /* @__PURE__ */ new Map();
|
|
1033
|
-
/** Register a handler for an action type */
|
|
1034
|
-
register(type, handler) {
|
|
1035
|
-
this.handlers.set(type, handler);
|
|
1036
|
-
}
|
|
1037
|
-
/** Unregister a handler */
|
|
1038
|
-
unregister(type) {
|
|
1039
|
-
this.handlers.delete(type);
|
|
1040
|
-
}
|
|
1041
|
-
/** Check if a handler is registered for the given type */
|
|
1042
|
-
has(type) {
|
|
1043
|
-
return this.handlers.has(type);
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Execute a list of actions sequentially.
|
|
1047
|
-
* Each action's condition is evaluated first (if present).
|
|
1048
|
-
* Missing handlers are skipped with a warning.
|
|
1049
|
-
*/
|
|
1050
|
-
async execute(actions, context, evaluator) {
|
|
1051
|
-
const results = [];
|
|
1052
|
-
for (const action2 of actions) {
|
|
1053
|
-
if (action2.condition && evaluator) {
|
|
1054
|
-
const condResult = evaluator.evaluate(action2.condition, context);
|
|
1055
|
-
if (!condResult.value) continue;
|
|
1056
|
-
}
|
|
1057
|
-
const handler = this.handlers.get(action2.type);
|
|
1058
|
-
if (!handler) {
|
|
1059
|
-
console.warn(`[player-core] No handler registered for action type "${action2.type}" \u2014 unsupported action`);
|
|
1060
|
-
results.push({ type: action2.type, success: false, error: `unsupported action: "${action2.type}"` });
|
|
1061
|
-
continue;
|
|
1062
|
-
}
|
|
1063
|
-
try {
|
|
1064
|
-
await handler(action2.config, context);
|
|
1065
|
-
results.push({ type: action2.type, success: true });
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1068
|
-
console.warn(`[player-core] Action "${action2.type}" failed: ${message}`);
|
|
1069
|
-
results.push({ type: action2.type, success: false, error: message });
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
return results;
|
|
1073
|
-
}
|
|
1074
|
-
/** Get count of registered handlers */
|
|
1075
|
-
get size() {
|
|
1076
|
-
return this.handlers.size;
|
|
1077
|
-
}
|
|
1078
|
-
/** Get all registered action type names */
|
|
1079
|
-
getRegisteredTypes() {
|
|
1080
|
-
return Array.from(this.handlers.keys());
|
|
1081
|
-
}
|
|
1082
|
-
/** Remove all handlers */
|
|
1083
|
-
clear() {
|
|
1084
|
-
this.handlers.clear();
|
|
1085
|
-
}
|
|
1086
|
-
};
|
|
1087
|
-
|
|
1088
1
|
// src/core/WorkflowRuntime.ts
|
|
2
|
+
import {
|
|
3
|
+
StateMachine,
|
|
4
|
+
EventBus,
|
|
5
|
+
ActionDispatcher,
|
|
6
|
+
createEvaluator,
|
|
7
|
+
WEB_FAILURE_POLICIES
|
|
8
|
+
} from "@mmapp/player-core";
|
|
1089
9
|
var WorkflowRuntime = class {
|
|
1090
10
|
sm;
|
|
1091
11
|
eventBus;
|
|
@@ -1184,24 +104,24 @@ import { useEffect, useRef } from "react";
|
|
|
1184
104
|
import { createContext, useContext } from "react";
|
|
1185
105
|
var RuntimeContext = createContext(null);
|
|
1186
106
|
function useRuntimeContext() {
|
|
1187
|
-
const
|
|
1188
|
-
if (!
|
|
107
|
+
const runtime2 = useContext(RuntimeContext);
|
|
108
|
+
if (!runtime2) {
|
|
1189
109
|
throw new Error(
|
|
1190
110
|
"useRuntimeContext must be used within a WorkflowProvider or RuntimeContext.Provider"
|
|
1191
111
|
);
|
|
1192
112
|
}
|
|
1193
|
-
return
|
|
113
|
+
return runtime2;
|
|
1194
114
|
}
|
|
1195
115
|
|
|
1196
116
|
// src/hooks/useOnEnter.ts
|
|
1197
117
|
function useOnEnter(stateName, effect) {
|
|
1198
|
-
const
|
|
118
|
+
const runtime2 = useRuntimeContext();
|
|
1199
119
|
const effectRef = useRef(effect);
|
|
1200
120
|
effectRef.current = effect;
|
|
1201
121
|
const cleanupRef = useRef(null);
|
|
1202
122
|
const states = Array.isArray(stateName) ? stateName : [stateName];
|
|
1203
123
|
useEffect(() => {
|
|
1204
|
-
if (states.includes(
|
|
124
|
+
if (states.includes(runtime2.sm.currentState)) {
|
|
1205
125
|
const result = effectRef.current();
|
|
1206
126
|
if (result instanceof Promise) {
|
|
1207
127
|
result.then((cleanup) => {
|
|
@@ -1211,7 +131,7 @@ function useOnEnter(stateName, effect) {
|
|
|
1211
131
|
cleanupRef.current = result;
|
|
1212
132
|
}
|
|
1213
133
|
}
|
|
1214
|
-
const unsub =
|
|
134
|
+
const unsub = runtime2.sm.on((event) => {
|
|
1215
135
|
if (event.type === "state_enter" && event.to_state && states.includes(event.to_state)) {
|
|
1216
136
|
cleanupRef.current?.();
|
|
1217
137
|
const result = effectRef.current();
|
|
@@ -1232,34 +152,34 @@ function useOnEnter(stateName, effect) {
|
|
|
1232
152
|
unsub();
|
|
1233
153
|
cleanupRef.current?.();
|
|
1234
154
|
};
|
|
1235
|
-
}, [
|
|
155
|
+
}, [runtime2, ...states]);
|
|
1236
156
|
}
|
|
1237
157
|
|
|
1238
158
|
// src/hooks/useOnExit.ts
|
|
1239
159
|
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1240
160
|
function useOnExit(stateName, effect) {
|
|
1241
|
-
const
|
|
161
|
+
const runtime2 = useRuntimeContext();
|
|
1242
162
|
const effectRef = useRef2(effect);
|
|
1243
163
|
effectRef.current = effect;
|
|
1244
164
|
const states = Array.isArray(stateName) ? stateName : [stateName];
|
|
1245
165
|
useEffect2(() => {
|
|
1246
|
-
const unsub =
|
|
166
|
+
const unsub = runtime2.sm.on((event) => {
|
|
1247
167
|
if (event.type === "state_exit" && event.from_state && states.includes(event.from_state)) {
|
|
1248
168
|
effectRef.current();
|
|
1249
169
|
}
|
|
1250
170
|
});
|
|
1251
171
|
return unsub;
|
|
1252
|
-
}, [
|
|
172
|
+
}, [runtime2, ...states]);
|
|
1253
173
|
}
|
|
1254
174
|
|
|
1255
175
|
// src/hooks/useOnTransition.ts
|
|
1256
176
|
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
1257
177
|
function useOnTransition(effect) {
|
|
1258
|
-
const
|
|
178
|
+
const runtime2 = useRuntimeContext();
|
|
1259
179
|
const effectRef = useRef3(effect);
|
|
1260
180
|
effectRef.current = effect;
|
|
1261
181
|
useEffect3(() => {
|
|
1262
|
-
const unsub =
|
|
182
|
+
const unsub = runtime2.sm.on((event) => {
|
|
1263
183
|
if (event.type === "transition") {
|
|
1264
184
|
effectRef.current({
|
|
1265
185
|
from: event.from_state,
|
|
@@ -1268,33 +188,33 @@ function useOnTransition(effect) {
|
|
|
1268
188
|
}
|
|
1269
189
|
});
|
|
1270
190
|
return unsub;
|
|
1271
|
-
}, [
|
|
191
|
+
}, [runtime2]);
|
|
1272
192
|
}
|
|
1273
193
|
|
|
1274
194
|
// src/hooks/useOnEvent.ts
|
|
1275
195
|
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
1276
196
|
function useOnEvent(pattern, handler, options) {
|
|
1277
|
-
const
|
|
197
|
+
const runtime2 = useRuntimeContext();
|
|
1278
198
|
const handlerRef = useRef4(handler);
|
|
1279
199
|
handlerRef.current = handler;
|
|
1280
200
|
useEffect4(() => {
|
|
1281
|
-
const unsub =
|
|
1282
|
-
if (options?.while &&
|
|
201
|
+
const unsub = runtime2.eventBus.subscribe(pattern, (event) => {
|
|
202
|
+
if (options?.while && runtime2.sm.currentState !== options.while) return;
|
|
1283
203
|
handlerRef.current(event);
|
|
1284
204
|
});
|
|
1285
205
|
return unsub;
|
|
1286
|
-
}, [
|
|
206
|
+
}, [runtime2, pattern, options?.while]);
|
|
1287
207
|
}
|
|
1288
208
|
|
|
1289
209
|
// src/hooks/useOnChange.ts
|
|
1290
210
|
import { useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
1291
211
|
function useOnChange(field2, handler) {
|
|
1292
|
-
const
|
|
212
|
+
const runtime2 = useRuntimeContext();
|
|
1293
213
|
const handlerRef = useRef5(handler);
|
|
1294
214
|
handlerRef.current = handler;
|
|
1295
|
-
const prevRef = useRef5(
|
|
215
|
+
const prevRef = useRef5(runtime2.sm.stateData[field2]);
|
|
1296
216
|
useEffect5(() => {
|
|
1297
|
-
const unsub =
|
|
217
|
+
const unsub = runtime2.subscribe((snap) => {
|
|
1298
218
|
const newVal = snap.stateData[field2];
|
|
1299
219
|
if (newVal !== prevRef.current) {
|
|
1300
220
|
handlerRef.current(newVal, prevRef.current);
|
|
@@ -1302,13 +222,13 @@ function useOnChange(field2, handler) {
|
|
|
1302
222
|
}
|
|
1303
223
|
});
|
|
1304
224
|
return unsub;
|
|
1305
|
-
}, [
|
|
225
|
+
}, [runtime2, field2]);
|
|
1306
226
|
}
|
|
1307
227
|
|
|
1308
228
|
// src/hooks/useWhileIn.ts
|
|
1309
229
|
import { useEffect as useEffect6, useRef as useRef6 } from "react";
|
|
1310
230
|
function useWhileIn(stateName, intervalMs, effect) {
|
|
1311
|
-
const
|
|
231
|
+
const runtime2 = useRuntimeContext();
|
|
1312
232
|
const effectRef = useRef6(effect);
|
|
1313
233
|
effectRef.current = effect;
|
|
1314
234
|
useEffect6(() => {
|
|
@@ -1323,8 +243,8 @@ function useWhileIn(stateName, intervalMs, effect) {
|
|
|
1323
243
|
timer = null;
|
|
1324
244
|
}
|
|
1325
245
|
}
|
|
1326
|
-
if (
|
|
1327
|
-
const unsub =
|
|
246
|
+
if (runtime2.sm.currentState === stateName) startInterval();
|
|
247
|
+
const unsub = runtime2.sm.on((event) => {
|
|
1328
248
|
if (event.type === "state_enter" && event.to_state === stateName) startInterval();
|
|
1329
249
|
if (event.type === "state_exit" && event.from_state === stateName) stopInterval();
|
|
1330
250
|
});
|
|
@@ -1332,13 +252,13 @@ function useWhileIn(stateName, intervalMs, effect) {
|
|
|
1332
252
|
unsub();
|
|
1333
253
|
stopInterval();
|
|
1334
254
|
};
|
|
1335
|
-
}, [
|
|
255
|
+
}, [runtime2, stateName, intervalMs]);
|
|
1336
256
|
}
|
|
1337
257
|
|
|
1338
258
|
// src/hooks/useDuringAction.ts
|
|
1339
259
|
import { useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
1340
260
|
function useDuringAction(config) {
|
|
1341
|
-
const
|
|
261
|
+
const runtime2 = useRuntimeContext();
|
|
1342
262
|
const actionRef = useRef7(config.action);
|
|
1343
263
|
actionRef.current = config.action;
|
|
1344
264
|
const states = Array.isArray(config.state) ? config.state : [config.state];
|
|
@@ -1376,10 +296,10 @@ function useDuringAction(config) {
|
|
|
1376
296
|
timer = null;
|
|
1377
297
|
}
|
|
1378
298
|
}
|
|
1379
|
-
if (states.includes(
|
|
299
|
+
if (states.includes(runtime2.sm.currentState)) {
|
|
1380
300
|
startAction();
|
|
1381
301
|
}
|
|
1382
|
-
const unsub =
|
|
302
|
+
const unsub = runtime2.sm.on((event) => {
|
|
1383
303
|
if (event.type === "state_enter" && event.to_state && states.includes(event.to_state)) {
|
|
1384
304
|
startAction();
|
|
1385
305
|
}
|
|
@@ -1391,7 +311,7 @@ function useDuringAction(config) {
|
|
|
1391
311
|
unsub();
|
|
1392
312
|
stopAction();
|
|
1393
313
|
};
|
|
1394
|
-
}, [
|
|
314
|
+
}, [runtime2, intervalMs, immediate, enabled, ...states]);
|
|
1395
315
|
}
|
|
1396
316
|
|
|
1397
317
|
// src/hooks/useQuery.ts
|
|
@@ -1884,29 +804,29 @@ function useServerState(instanceId, options = {}) {
|
|
|
1884
804
|
// src/hooks/useWorkflow.ts
|
|
1885
805
|
import { useState as useState7, useEffect as useEffect12, useMemo, useCallback as useCallback7 } from "react";
|
|
1886
806
|
function useWorkflow(definition, options) {
|
|
1887
|
-
const
|
|
807
|
+
const runtime2 = useMemo(() => new WorkflowRuntime({
|
|
1888
808
|
definition,
|
|
1889
809
|
initialData: options?.initialData,
|
|
1890
810
|
actionHandlers: options?.actionHandlers
|
|
1891
811
|
}), [definition.id]);
|
|
1892
|
-
const [snapshot, setSnapshot] = useState7(() =>
|
|
812
|
+
const [snapshot, setSnapshot] = useState7(() => runtime2.getSnapshot());
|
|
1893
813
|
useEffect12(() => {
|
|
1894
|
-
const unsub =
|
|
814
|
+
const unsub = runtime2.subscribe(setSnapshot);
|
|
1895
815
|
return unsub;
|
|
1896
|
-
}, [
|
|
816
|
+
}, [runtime2]);
|
|
1897
817
|
const transition2 = useCallback7(async (name, data) => {
|
|
1898
|
-
const result = await
|
|
818
|
+
const result = await runtime2.transition(name, data);
|
|
1899
819
|
options?.onTransition?.(result);
|
|
1900
820
|
return result;
|
|
1901
|
-
}, [
|
|
821
|
+
}, [runtime2, options?.onTransition]);
|
|
1902
822
|
const handle = {
|
|
1903
823
|
slug: definition.slug,
|
|
1904
824
|
...snapshot,
|
|
1905
825
|
transition: transition2,
|
|
1906
|
-
setField:
|
|
1907
|
-
setMemory:
|
|
1908
|
-
publishEvent:
|
|
1909
|
-
runtime
|
|
826
|
+
setField: runtime2.setField.bind(runtime2),
|
|
827
|
+
setMemory: runtime2.setMemory.bind(runtime2),
|
|
828
|
+
publishEvent: runtime2.publishEvent.bind(runtime2),
|
|
829
|
+
runtime: runtime2
|
|
1910
830
|
};
|
|
1911
831
|
return handle;
|
|
1912
832
|
}
|
|
@@ -1914,17 +834,17 @@ function useWorkflow(definition, options) {
|
|
|
1914
834
|
// src/hooks/useTransition.ts
|
|
1915
835
|
import { useCallback as useCallback8, useState as useState8 } from "react";
|
|
1916
836
|
function useTransition(transitionName, _config) {
|
|
1917
|
-
const
|
|
837
|
+
const runtime2 = useRuntimeContext();
|
|
1918
838
|
const [pending, setPending] = useState8(false);
|
|
1919
|
-
const available =
|
|
839
|
+
const available = runtime2.sm.getAvailableTransitions().some((t) => t.name === transitionName);
|
|
1920
840
|
const fire = useCallback8(async (data) => {
|
|
1921
841
|
setPending(true);
|
|
1922
842
|
try {
|
|
1923
|
-
return await
|
|
843
|
+
return await runtime2.transition(transitionName, data);
|
|
1924
844
|
} finally {
|
|
1925
845
|
setPending(false);
|
|
1926
846
|
}
|
|
1927
|
-
}, [
|
|
847
|
+
}, [runtime2, transitionName]);
|
|
1928
848
|
const handle = { fire, available, pending };
|
|
1929
849
|
return handle;
|
|
1930
850
|
}
|
|
@@ -1936,12 +856,12 @@ function setRoleHierarchy(hierarchy) {
|
|
|
1936
856
|
_globalHierarchy = hierarchy;
|
|
1937
857
|
}
|
|
1938
858
|
function useRole(roleName, options = {}) {
|
|
1939
|
-
const
|
|
859
|
+
const runtime2 = useRuntimeContext();
|
|
1940
860
|
const hierarchy = options.hierarchy ?? _globalHierarchy ?? [];
|
|
1941
861
|
const inheritPermissions = options.inheritPermissions ?? true;
|
|
1942
862
|
const result = useMemo2(() => {
|
|
1943
|
-
const snapshot =
|
|
1944
|
-
const definition =
|
|
863
|
+
const snapshot = runtime2.getSnapshot();
|
|
864
|
+
const definition = runtime2.config?.definition;
|
|
1945
865
|
const userRoles = snapshot.stateData?._userRoles ?? snapshot.stateData?.user_roles ?? [];
|
|
1946
866
|
let hasRole2 = userRoles.includes(roleName);
|
|
1947
867
|
if (!hasRole2 && hierarchy.length > 0) {
|
|
@@ -1989,7 +909,7 @@ function useRole(roleName, options = {}) {
|
|
|
1989
909
|
}
|
|
1990
910
|
}
|
|
1991
911
|
return { hasRole: hasRole2, permissions, roles: userRoles, highestRole };
|
|
1992
|
-
}, [
|
|
912
|
+
}, [runtime2, roleName, hierarchy, inheritPermissions]);
|
|
1993
913
|
const isAbove = useCallback9(
|
|
1994
914
|
(role) => {
|
|
1995
915
|
if (hierarchy.length === 0) return false;
|
|
@@ -2040,21 +960,21 @@ function useRole(roleName, options = {}) {
|
|
|
2040
960
|
// src/hooks/useParams.ts
|
|
2041
961
|
import { useMemo as useMemo3 } from "react";
|
|
2042
962
|
function useParams() {
|
|
2043
|
-
const
|
|
963
|
+
const runtime2 = useRuntimeContext();
|
|
2044
964
|
return useMemo3(() => {
|
|
2045
|
-
const snapshot =
|
|
965
|
+
const snapshot = runtime2.getSnapshot();
|
|
2046
966
|
const params = snapshot.stateData?._params ?? snapshot.memory?._callParams ?? {};
|
|
2047
967
|
return params;
|
|
2048
|
-
}, [
|
|
968
|
+
}, [runtime2]);
|
|
2049
969
|
}
|
|
2050
970
|
|
|
2051
971
|
// src/hooks/usePackage.ts
|
|
2052
972
|
import { useMemo as useMemo4 } from "react";
|
|
2053
973
|
function usePackage(packageSlug) {
|
|
2054
|
-
const
|
|
974
|
+
const runtime2 = useRuntimeContext();
|
|
2055
975
|
return useMemo4(() => {
|
|
2056
|
-
const snapshot =
|
|
2057
|
-
const definition =
|
|
976
|
+
const snapshot = runtime2.getSnapshot();
|
|
977
|
+
const definition = runtime2.config?.definition;
|
|
2058
978
|
const childDefs = definition?.child_definitions ?? [];
|
|
2059
979
|
const metadata = definition?.metadata ?? {};
|
|
2060
980
|
if (definition?.slug === packageSlug && definition?.category === "blueprint") {
|
|
@@ -2117,7 +1037,7 @@ function usePackage(packageSlug) {
|
|
|
2117
1037
|
isResolved: false,
|
|
2118
1038
|
dependencySlugs: []
|
|
2119
1039
|
};
|
|
2120
|
-
}, [
|
|
1040
|
+
}, [runtime2, packageSlug]);
|
|
2121
1041
|
}
|
|
2122
1042
|
|
|
2123
1043
|
// src/hooks/useRouter.ts
|
|
@@ -2243,16 +1163,16 @@ function useRouter(options = {}) {
|
|
|
2243
1163
|
};
|
|
2244
1164
|
setIsNavigating(true);
|
|
2245
1165
|
try {
|
|
2246
|
-
for (const
|
|
2247
|
-
const allowed = await
|
|
1166
|
+
for (const guard2 of guardsRef.current) {
|
|
1167
|
+
const allowed = await guard2(to, location);
|
|
2248
1168
|
if (!allowed) {
|
|
2249
1169
|
onRejectRef.current?.(to);
|
|
2250
1170
|
return false;
|
|
2251
1171
|
}
|
|
2252
1172
|
}
|
|
2253
1173
|
if (found?.route?.guards) {
|
|
2254
|
-
for (const
|
|
2255
|
-
const allowed = await
|
|
1174
|
+
for (const guard2 of found.route.guards) {
|
|
1175
|
+
const allowed = await guard2(to, location);
|
|
2256
1176
|
if (!allowed) {
|
|
2257
1177
|
if (found.route.redirectOnFail) {
|
|
2258
1178
|
const fullRedirect = basePathRef.current + found.route.redirectOnFail;
|
|
@@ -2678,7 +1598,7 @@ function useGeolocation(options = {}) {
|
|
|
2678
1598
|
const {
|
|
2679
1599
|
enableHighAccuracy = false,
|
|
2680
1600
|
maximumAge = 0,
|
|
2681
|
-
timeout = 1e4,
|
|
1601
|
+
timeout: timeout2 = 1e4,
|
|
2682
1602
|
watch = false,
|
|
2683
1603
|
enabled = true
|
|
2684
1604
|
} = options;
|
|
@@ -2689,8 +1609,8 @@ function useGeolocation(options = {}) {
|
|
|
2689
1609
|
const watchIdRef = useRef18(null);
|
|
2690
1610
|
const supported = typeof navigator !== "undefined" && "geolocation" in navigator;
|
|
2691
1611
|
const positionOptions = useMemo8(
|
|
2692
|
-
() => ({ enableHighAccuracy, maximumAge, timeout }),
|
|
2693
|
-
[enableHighAccuracy, maximumAge,
|
|
1612
|
+
() => ({ enableHighAccuracy, maximumAge, timeout: timeout2 }),
|
|
1613
|
+
[enableHighAccuracy, maximumAge, timeout2]
|
|
2694
1614
|
);
|
|
2695
1615
|
const handleSuccess = useCallback13((pos) => {
|
|
2696
1616
|
setPosition({
|
|
@@ -3050,7 +1970,7 @@ import { useState as useState16, useCallback as useCallback16, useMemo as useMem
|
|
|
3050
1970
|
function useForm(config) {
|
|
3051
1971
|
const {
|
|
3052
1972
|
initialValues,
|
|
3053
|
-
validate,
|
|
1973
|
+
validate: validate2,
|
|
3054
1974
|
fieldValidators,
|
|
3055
1975
|
onSubmit,
|
|
3056
1976
|
onError,
|
|
@@ -3063,8 +1983,8 @@ function useForm(config) {
|
|
|
3063
1983
|
const [isSubmitting, setIsSubmitting] = useState16(false);
|
|
3064
1984
|
const [submitCount, setSubmitCount] = useState16(0);
|
|
3065
1985
|
const initialRef = useRef20(initialValues);
|
|
3066
|
-
const validateRef = useRef20(
|
|
3067
|
-
validateRef.current =
|
|
1986
|
+
const validateRef = useRef20(validate2);
|
|
1987
|
+
validateRef.current = validate2;
|
|
3068
1988
|
const fieldValidatorsRef = useRef20(fieldValidators);
|
|
3069
1989
|
fieldValidatorsRef.current = fieldValidators;
|
|
3070
1990
|
const onSubmitRef = useRef20(onSubmit);
|
|
@@ -3281,7 +2201,7 @@ function useNotification() {
|
|
|
3281
2201
|
setPermission(result);
|
|
3282
2202
|
return result;
|
|
3283
2203
|
}, [supported]);
|
|
3284
|
-
const
|
|
2204
|
+
const notify3 = useCallback17(
|
|
3285
2205
|
(title, options = {}) => {
|
|
3286
2206
|
if (!supported || permission !== "granted") return null;
|
|
3287
2207
|
const {
|
|
@@ -3314,8 +2234,8 @@ function useNotification() {
|
|
|
3314
2234
|
[supported, permission]
|
|
3315
2235
|
);
|
|
3316
2236
|
return useMemo12(
|
|
3317
|
-
() => ({ permission, supported, requestPermission, notify:
|
|
3318
|
-
[permission, supported, requestPermission,
|
|
2237
|
+
() => ({ permission, supported, requestPermission, notify: notify3 }),
|
|
2238
|
+
[permission, supported, requestPermission, notify3]
|
|
3319
2239
|
);
|
|
3320
2240
|
}
|
|
3321
2241
|
|
|
@@ -3332,12 +2252,12 @@ function notifyListeners() {
|
|
|
3332
2252
|
}
|
|
3333
2253
|
function addToast(config) {
|
|
3334
2254
|
const id = `toast-${++_nextId}`;
|
|
3335
|
-
const
|
|
2255
|
+
const instance2 = {
|
|
3336
2256
|
...config,
|
|
3337
2257
|
id,
|
|
3338
2258
|
createdAt: Date.now()
|
|
3339
2259
|
};
|
|
3340
|
-
_toasts = [..._toasts,
|
|
2260
|
+
_toasts = [..._toasts, instance2];
|
|
3341
2261
|
const duration = config.duration ?? 5e3;
|
|
3342
2262
|
if (duration > 0) {
|
|
3343
2263
|
const timer = setTimeout(() => {
|
|
@@ -3388,8 +2308,191 @@ function useToast() {
|
|
|
3388
2308
|
);
|
|
3389
2309
|
}
|
|
3390
2310
|
|
|
2311
|
+
// src/hooks/useVisibility.ts
|
|
2312
|
+
import { useMemo as useMemo14 } from "react";
|
|
2313
|
+
function useVisibility(model2, role, playerType) {
|
|
2314
|
+
return useMemo14(() => {
|
|
2315
|
+
return computeVisibility(model2, role, playerType);
|
|
2316
|
+
}, [model2, role, playerType]);
|
|
2317
|
+
}
|
|
2318
|
+
function computeVisibility(model2, role, playerType) {
|
|
2319
|
+
const allFields = Object.keys(model2.fields || {});
|
|
2320
|
+
const allStates = Object.keys(model2.states || {});
|
|
2321
|
+
const vis = model2.orchestration?.visibility;
|
|
2322
|
+
if (!vis) {
|
|
2323
|
+
return makeResult(allFields, allStates);
|
|
2324
|
+
}
|
|
2325
|
+
let roleFields;
|
|
2326
|
+
let roleStates;
|
|
2327
|
+
let playerFields;
|
|
2328
|
+
if (role && vis.roles && vis.roles[role]) {
|
|
2329
|
+
const rv = vis.roles[role];
|
|
2330
|
+
roleFields = rv.fields === "*" ? "*" : [...rv.fields];
|
|
2331
|
+
roleStates = rv.states === "*" ? "*" : rv.states ? [...rv.states] : "*";
|
|
2332
|
+
}
|
|
2333
|
+
if (playerType && vis.players && vis.players[playerType]) {
|
|
2334
|
+
const pv = vis.players[playerType];
|
|
2335
|
+
playerFields = pv.fields === "*" ? "*" : [...pv.fields];
|
|
2336
|
+
}
|
|
2337
|
+
if (roleFields === void 0 && playerFields === void 0) {
|
|
2338
|
+
return makeResult(allFields, allStates);
|
|
2339
|
+
}
|
|
2340
|
+
let resolvedFields;
|
|
2341
|
+
if (roleFields === "*" && playerFields === "*") {
|
|
2342
|
+
resolvedFields = allFields;
|
|
2343
|
+
} else if (roleFields === "*") {
|
|
2344
|
+
resolvedFields = playerFields === void 0 ? allFields : playerFields;
|
|
2345
|
+
} else if (playerFields === "*") {
|
|
2346
|
+
resolvedFields = roleFields === void 0 ? allFields : roleFields;
|
|
2347
|
+
} else if (roleFields !== void 0 && playerFields !== void 0) {
|
|
2348
|
+
const roleSet = new Set(roleFields);
|
|
2349
|
+
resolvedFields = playerFields.filter((f) => roleSet.has(f));
|
|
2350
|
+
} else {
|
|
2351
|
+
resolvedFields = roleFields || playerFields || allFields;
|
|
2352
|
+
}
|
|
2353
|
+
const resolvedStates = roleStates === "*" || roleStates === void 0 ? allStates : roleStates;
|
|
2354
|
+
return makeResult(resolvedFields, resolvedStates);
|
|
2355
|
+
}
|
|
2356
|
+
function makeResult(fields, states) {
|
|
2357
|
+
const fieldSet = new Set(fields);
|
|
2358
|
+
const stateSet = new Set(states);
|
|
2359
|
+
return {
|
|
2360
|
+
visibleFields: fields,
|
|
2361
|
+
visibleStates: states,
|
|
2362
|
+
canSeeField: (f) => fieldSet.has(f),
|
|
2363
|
+
canSeeState: (s) => stateSet.has(s)
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
// src/hooks/usePresence.ts
|
|
2368
|
+
import { useState as useState19, useEffect as useEffect20, useCallback as useCallback19, useRef as useRef22 } from "react";
|
|
2369
|
+
var PRESENCE_EVENTS = {
|
|
2370
|
+
JOIN: "presence:join",
|
|
2371
|
+
LEAVE: "presence:leave",
|
|
2372
|
+
HEARTBEAT: "presence:heartbeat",
|
|
2373
|
+
EDIT_START: "presence:edit_start",
|
|
2374
|
+
EDIT_STOP: "presence:edit_stop"
|
|
2375
|
+
};
|
|
2376
|
+
function usePresence(instanceId, options = {}) {
|
|
2377
|
+
const {
|
|
2378
|
+
userId = "anonymous",
|
|
2379
|
+
userName = "Anonymous",
|
|
2380
|
+
wsUrl,
|
|
2381
|
+
enabled = true,
|
|
2382
|
+
heartbeatInterval = 15e3,
|
|
2383
|
+
staleTimeout = 45e3
|
|
2384
|
+
} = options;
|
|
2385
|
+
const channelName = `presence:${instanceId}`;
|
|
2386
|
+
const { publish, subscribe, connected } = useChannel(channelName, {
|
|
2387
|
+
wsUrl,
|
|
2388
|
+
enabled
|
|
2389
|
+
});
|
|
2390
|
+
const [viewers, setViewers] = useState19([]);
|
|
2391
|
+
const [editors, setEditors] = useState19([]);
|
|
2392
|
+
const viewersRef = useRef22(/* @__PURE__ */ new Map());
|
|
2393
|
+
const editorsRef = useRef22(/* @__PURE__ */ new Map());
|
|
2394
|
+
useEffect20(() => {
|
|
2395
|
+
if (!connected || !enabled) return;
|
|
2396
|
+
publish(PRESENCE_EVENTS.JOIN, { id: userId, name: userName, connectedAt: Date.now() });
|
|
2397
|
+
const hb = setInterval(() => {
|
|
2398
|
+
publish(PRESENCE_EVENTS.HEARTBEAT, { id: userId, name: userName });
|
|
2399
|
+
}, heartbeatInterval);
|
|
2400
|
+
return () => {
|
|
2401
|
+
clearInterval(hb);
|
|
2402
|
+
publish(PRESENCE_EVENTS.LEAVE, { id: userId });
|
|
2403
|
+
};
|
|
2404
|
+
}, [connected, enabled, userId, userName, heartbeatInterval, publish]);
|
|
2405
|
+
useEffect20(() => {
|
|
2406
|
+
if (!enabled) return;
|
|
2407
|
+
const cleanup = setInterval(() => {
|
|
2408
|
+
const now = Date.now();
|
|
2409
|
+
const stale = [];
|
|
2410
|
+
for (const [id, v] of viewersRef.current) {
|
|
2411
|
+
if (now - v.connectedAt > staleTimeout) stale.push(id);
|
|
2412
|
+
}
|
|
2413
|
+
if (stale.length > 0) {
|
|
2414
|
+
for (const id of stale) {
|
|
2415
|
+
viewersRef.current.delete(id);
|
|
2416
|
+
editorsRef.current.delete(id);
|
|
2417
|
+
}
|
|
2418
|
+
setViewers(Array.from(viewersRef.current.values()));
|
|
2419
|
+
setEditors(Array.from(editorsRef.current.values()));
|
|
2420
|
+
}
|
|
2421
|
+
}, staleTimeout / 3);
|
|
2422
|
+
return () => clearInterval(cleanup);
|
|
2423
|
+
}, [enabled, staleTimeout]);
|
|
2424
|
+
useEffect20(() => {
|
|
2425
|
+
if (!enabled) return;
|
|
2426
|
+
const unsubs = [];
|
|
2427
|
+
unsubs.push(subscribe(PRESENCE_EVENTS.JOIN, (data) => {
|
|
2428
|
+
if (data.id === userId) return;
|
|
2429
|
+
viewersRef.current.set(data.id, {
|
|
2430
|
+
id: data.id,
|
|
2431
|
+
name: data.name || "Anonymous",
|
|
2432
|
+
connectedAt: data.connectedAt || Date.now()
|
|
2433
|
+
});
|
|
2434
|
+
setViewers(Array.from(viewersRef.current.values()));
|
|
2435
|
+
}));
|
|
2436
|
+
unsubs.push(subscribe(PRESENCE_EVENTS.LEAVE, (data) => {
|
|
2437
|
+
viewersRef.current.delete(data.id);
|
|
2438
|
+
editorsRef.current.delete(data.id);
|
|
2439
|
+
setViewers(Array.from(viewersRef.current.values()));
|
|
2440
|
+
setEditors(Array.from(editorsRef.current.values()));
|
|
2441
|
+
}));
|
|
2442
|
+
unsubs.push(subscribe(PRESENCE_EVENTS.HEARTBEAT, (data) => {
|
|
2443
|
+
if (data.id === userId) return;
|
|
2444
|
+
const existing = viewersRef.current.get(data.id);
|
|
2445
|
+
if (existing) {
|
|
2446
|
+
existing.connectedAt = Date.now();
|
|
2447
|
+
} else {
|
|
2448
|
+
viewersRef.current.set(data.id, {
|
|
2449
|
+
id: data.id,
|
|
2450
|
+
name: data.name || "Anonymous",
|
|
2451
|
+
connectedAt: Date.now()
|
|
2452
|
+
});
|
|
2453
|
+
setViewers(Array.from(viewersRef.current.values()));
|
|
2454
|
+
}
|
|
2455
|
+
}));
|
|
2456
|
+
unsubs.push(subscribe(PRESENCE_EVENTS.EDIT_START, (data) => {
|
|
2457
|
+
if (data.id === userId) return;
|
|
2458
|
+
editorsRef.current.set(data.id, {
|
|
2459
|
+
id: data.id,
|
|
2460
|
+
name: data.name || "Anonymous",
|
|
2461
|
+
field: data.field,
|
|
2462
|
+
startedAt: Date.now()
|
|
2463
|
+
});
|
|
2464
|
+
setEditors(Array.from(editorsRef.current.values()));
|
|
2465
|
+
}));
|
|
2466
|
+
unsubs.push(subscribe(PRESENCE_EVENTS.EDIT_STOP, (data) => {
|
|
2467
|
+
editorsRef.current.delete(data.id);
|
|
2468
|
+
setEditors(Array.from(editorsRef.current.values()));
|
|
2469
|
+
}));
|
|
2470
|
+
return () => unsubs.forEach((fn) => fn());
|
|
2471
|
+
}, [enabled, userId, subscribe]);
|
|
2472
|
+
const startEditing = useCallback19((field2) => {
|
|
2473
|
+
publish(PRESENCE_EVENTS.EDIT_START, { id: userId, name: userName, field: field2 });
|
|
2474
|
+
}, [publish, userId, userName]);
|
|
2475
|
+
const stopEditing = useCallback19((_field) => {
|
|
2476
|
+
publish(PRESENCE_EVENTS.EDIT_STOP, { id: userId });
|
|
2477
|
+
}, [publish, userId]);
|
|
2478
|
+
const isEditing = useCallback19((field2) => {
|
|
2479
|
+
for (const editor of editorsRef.current.values()) {
|
|
2480
|
+
if (editor.field === field2 && editor.id !== userId) return true;
|
|
2481
|
+
}
|
|
2482
|
+
return false;
|
|
2483
|
+
}, [userId]);
|
|
2484
|
+
return {
|
|
2485
|
+
viewers,
|
|
2486
|
+
editors,
|
|
2487
|
+
viewerCount: viewers.length,
|
|
2488
|
+
isEditing,
|
|
2489
|
+
startEditing,
|
|
2490
|
+
stopEditing
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
|
|
3391
2494
|
// src/hooks/useMiddleware.ts
|
|
3392
|
-
import { useState as
|
|
2495
|
+
import { useState as useState20, useEffect as useEffect21, useRef as useRef23, useMemo as useMemo15 } from "react";
|
|
3393
2496
|
function requireAuth(loginPath = "/login") {
|
|
3394
2497
|
return (ctx) => {
|
|
3395
2498
|
if (!ctx.token) {
|
|
@@ -3461,24 +2564,24 @@ function useMiddleware(middlewares, options = {}) {
|
|
|
3461
2564
|
watchPathname = true,
|
|
3462
2565
|
onRedirect
|
|
3463
2566
|
} = options;
|
|
3464
|
-
const [ready, setReady] =
|
|
3465
|
-
const [loading, setLoading] =
|
|
3466
|
-
const [redirect, setRedirect] =
|
|
3467
|
-
const [error, setError] =
|
|
3468
|
-
const [data, setData] =
|
|
3469
|
-
const [runKey, setRunKey] =
|
|
3470
|
-
const middlewaresRef =
|
|
2567
|
+
const [ready, setReady] = useState20(false);
|
|
2568
|
+
const [loading, setLoading] = useState20(true);
|
|
2569
|
+
const [redirect, setRedirect] = useState20(null);
|
|
2570
|
+
const [error, setError] = useState20(null);
|
|
2571
|
+
const [data, setData] = useState20({});
|
|
2572
|
+
const [runKey, setRunKey] = useState20(0);
|
|
2573
|
+
const middlewaresRef = useRef23(middlewares);
|
|
3471
2574
|
middlewaresRef.current = middlewares;
|
|
3472
|
-
const onRedirectRef =
|
|
2575
|
+
const onRedirectRef = useRef23(onRedirect);
|
|
3473
2576
|
onRedirectRef.current = onRedirect;
|
|
3474
|
-
const [pathname, setPathname] =
|
|
3475
|
-
|
|
2577
|
+
const [pathname, setPathname] = useState20(getPathname);
|
|
2578
|
+
useEffect21(() => {
|
|
3476
2579
|
if (!watchPathname) return;
|
|
3477
2580
|
const handler = () => setPathname(getPathname());
|
|
3478
2581
|
window.addEventListener("popstate", handler);
|
|
3479
2582
|
return () => window.removeEventListener("popstate", handler);
|
|
3480
2583
|
}, [watchPathname]);
|
|
3481
|
-
|
|
2584
|
+
useEffect21(() => {
|
|
3482
2585
|
if (!enabled) {
|
|
3483
2586
|
setReady(true);
|
|
3484
2587
|
setLoading(false);
|
|
@@ -3525,11 +2628,11 @@ function useMiddleware(middlewares, options = {}) {
|
|
|
3525
2628
|
cancelled = true;
|
|
3526
2629
|
};
|
|
3527
2630
|
}, [enabled, pathname, runKey]);
|
|
3528
|
-
const rerun =
|
|
2631
|
+
const rerun = useMemo15(
|
|
3529
2632
|
() => () => setRunKey((k) => k + 1),
|
|
3530
2633
|
[]
|
|
3531
2634
|
);
|
|
3532
|
-
return
|
|
2635
|
+
return useMemo15(
|
|
3533
2636
|
() => ({ ready, loading, redirect, error, data, rerun }),
|
|
3534
2637
|
[ready, loading, redirect, error, data, rerun]
|
|
3535
2638
|
);
|
|
@@ -3539,8 +2642,8 @@ function useMiddleware(middlewares, options = {}) {
|
|
|
3539
2642
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3540
2643
|
import { jsx } from "react/jsx-runtime";
|
|
3541
2644
|
var defaultQueryClient = new QueryClient();
|
|
3542
|
-
function WorkflowProvider({ runtime, queryClient, children }) {
|
|
3543
|
-
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient ?? defaultQueryClient, children: /* @__PURE__ */ jsx(RuntimeContext.Provider, { value:
|
|
2645
|
+
function WorkflowProvider({ runtime: runtime2, queryClient, children }) {
|
|
2646
|
+
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient ?? defaultQueryClient, children: /* @__PURE__ */ jsx(RuntimeContext.Provider, { value: runtime2, children }) });
|
|
3544
2647
|
}
|
|
3545
2648
|
|
|
3546
2649
|
// src/local/LocalEngine.ts
|
|
@@ -3601,8 +2704,8 @@ var LocalWorkflowEngine = class {
|
|
|
3601
2704
|
* Process all pending events from a transition result.
|
|
3602
2705
|
* Emits locally and returns the events for P2P broadcast.
|
|
3603
2706
|
*/
|
|
3604
|
-
processPendingEvents(
|
|
3605
|
-
const events =
|
|
2707
|
+
processPendingEvents(instance2) {
|
|
2708
|
+
const events = instance2._pendingEvents;
|
|
3606
2709
|
if (!events?.length) return [];
|
|
3607
2710
|
for (const evt of events) {
|
|
3608
2711
|
this.emitEvent(evt);
|
|
@@ -3645,18 +2748,18 @@ var LocalWorkflowEngine = class {
|
|
|
3645
2748
|
};
|
|
3646
2749
|
}
|
|
3647
2750
|
/** Check if a transition can fire from the instance's current state */
|
|
3648
|
-
canTransition(
|
|
3649
|
-
const def = this.definitions.get(
|
|
2751
|
+
canTransition(instance2, transitionName) {
|
|
2752
|
+
const def = this.definitions.get(instance2.definitionSlug);
|
|
3650
2753
|
if (!def) return false;
|
|
3651
2754
|
const transition2 = def.transitions.find((t) => t.name === transitionName);
|
|
3652
2755
|
if (!transition2) return false;
|
|
3653
|
-
return transition2.from.includes(
|
|
2756
|
+
return transition2.from.includes(instance2.currentState);
|
|
3654
2757
|
}
|
|
3655
2758
|
/** Get all transitions available from the instance's current state */
|
|
3656
|
-
getAvailableTransitions(
|
|
3657
|
-
const def = this.definitions.get(
|
|
2759
|
+
getAvailableTransitions(instance2) {
|
|
2760
|
+
const def = this.definitions.get(instance2.definitionSlug);
|
|
3658
2761
|
if (!def) return [];
|
|
3659
|
-
return def.transitions.filter((t) => t.from.includes(
|
|
2762
|
+
return def.transitions.filter((t) => t.from.includes(instance2.currentState)).map((t) => t.name);
|
|
3660
2763
|
}
|
|
3661
2764
|
/**
|
|
3662
2765
|
* Apply a transition, returning a new WorkflowInstance (immutable).
|
|
@@ -3667,29 +2770,29 @@ var LocalWorkflowEngine = class {
|
|
|
3667
2770
|
* @returns New WorkflowInstance with updated state and version
|
|
3668
2771
|
* @throws If transition is not valid from current state
|
|
3669
2772
|
*/
|
|
3670
|
-
applyTransition(
|
|
3671
|
-
const def = this.definitions.get(
|
|
2773
|
+
applyTransition(instance2, transitionName, data) {
|
|
2774
|
+
const def = this.definitions.get(instance2.definitionSlug);
|
|
3672
2775
|
if (!def) {
|
|
3673
|
-
throw new Error(`Definition not found: ${
|
|
2776
|
+
throw new Error(`Definition not found: ${instance2.definitionSlug}`);
|
|
3674
2777
|
}
|
|
3675
2778
|
const transition2 = def.transitions.find((t) => t.name === transitionName);
|
|
3676
2779
|
if (!transition2) {
|
|
3677
2780
|
throw new Error(
|
|
3678
|
-
`Transition "${transitionName}" not found in ${
|
|
2781
|
+
`Transition "${transitionName}" not found in ${instance2.definitionSlug}`
|
|
3679
2782
|
);
|
|
3680
2783
|
}
|
|
3681
|
-
if (!transition2.from.includes(
|
|
2784
|
+
if (!transition2.from.includes(instance2.currentState)) {
|
|
3682
2785
|
throw new Error(
|
|
3683
|
-
`Transition "${transitionName}" cannot fire from state "${
|
|
2786
|
+
`Transition "${transitionName}" cannot fire from state "${instance2.currentState}" (allowed from: ${transition2.from.join(", ")})`
|
|
3684
2787
|
);
|
|
3685
2788
|
}
|
|
3686
|
-
const nextState = transition2.to ??
|
|
3687
|
-
const fields = data ? { ...
|
|
2789
|
+
const nextState = transition2.to ?? instance2.currentState;
|
|
2790
|
+
const fields = data ? { ...instance2.fields, ...data } : { ...instance2.fields };
|
|
3688
2791
|
let result = {
|
|
3689
|
-
...
|
|
2792
|
+
...instance2,
|
|
3690
2793
|
currentState: nextState,
|
|
3691
2794
|
fields,
|
|
3692
|
-
version:
|
|
2795
|
+
version: instance2.version + 1,
|
|
3693
2796
|
updatedAt: Date.now()
|
|
3694
2797
|
};
|
|
3695
2798
|
const targetStateDef = def.states.find((s) => s.name === nextState);
|
|
@@ -3762,14 +2865,14 @@ var LocalWorkflowEngine = class {
|
|
|
3762
2865
|
* Apply a transition only if the incoming version is newer.
|
|
3763
2866
|
* Used for P2P sync — skips stale transitions.
|
|
3764
2867
|
*/
|
|
3765
|
-
applyRemoteTransition(
|
|
3766
|
-
if (remoteVersion <=
|
|
2868
|
+
applyRemoteTransition(instance2, transitionName, data, remoteVersion) {
|
|
2869
|
+
if (remoteVersion <= instance2.version) {
|
|
3767
2870
|
return null;
|
|
3768
2871
|
}
|
|
3769
|
-
if (!this.canTransition(
|
|
2872
|
+
if (!this.canTransition(instance2, transitionName)) {
|
|
3770
2873
|
return null;
|
|
3771
2874
|
}
|
|
3772
|
-
const updated = this.applyTransition(
|
|
2875
|
+
const updated = this.applyTransition(instance2, transitionName, data);
|
|
3773
2876
|
return { ...updated, version: remoteVersion };
|
|
3774
2877
|
}
|
|
3775
2878
|
};
|
|
@@ -4035,6 +3138,155 @@ function defineModel(def) {
|
|
|
4035
3138
|
return def;
|
|
4036
3139
|
}
|
|
4037
3140
|
|
|
3141
|
+
// src/config/orchestration-presets.ts
|
|
3142
|
+
var SOLO = {
|
|
3143
|
+
strategy: "solo",
|
|
3144
|
+
evaluation: { pure: "local" },
|
|
3145
|
+
scheduling: { coordinator: "any" },
|
|
3146
|
+
events: { evaluator: "any-subscriber" },
|
|
3147
|
+
errorHandling: { strategy: "manual" },
|
|
3148
|
+
conflicts: { default: "last-writer-wins", fields: {} },
|
|
3149
|
+
visibility: { roles: {}, players: {} },
|
|
3150
|
+
presence: { enabled: false, showEditing: false, showViewing: false },
|
|
3151
|
+
services: {},
|
|
3152
|
+
actionRouting: {}
|
|
3153
|
+
};
|
|
3154
|
+
var CLIENT_SERVER = {
|
|
3155
|
+
strategy: "client-server",
|
|
3156
|
+
evaluation: { pure: "local" },
|
|
3157
|
+
scheduling: { coordinator: "server" },
|
|
3158
|
+
events: { evaluator: "server" },
|
|
3159
|
+
errorHandling: { strategy: "rollback" },
|
|
3160
|
+
conflicts: { default: "last-writer-wins", fields: {} },
|
|
3161
|
+
visibility: { roles: {}, players: {} },
|
|
3162
|
+
presence: { enabled: false, showEditing: false, showViewing: false },
|
|
3163
|
+
services: {},
|
|
3164
|
+
actionRouting: {}
|
|
3165
|
+
};
|
|
3166
|
+
var MULTI_CLIENT = {
|
|
3167
|
+
strategy: "multi-client",
|
|
3168
|
+
evaluation: { pure: "local" },
|
|
3169
|
+
scheduling: { coordinator: "server" },
|
|
3170
|
+
events: { evaluator: "server" },
|
|
3171
|
+
errorHandling: { strategy: "compensate" },
|
|
3172
|
+
conflicts: { default: "last-writer-wins", fields: {} },
|
|
3173
|
+
visibility: { roles: {}, players: {} },
|
|
3174
|
+
presence: { enabled: true, showEditing: true, showViewing: true, conflictHighlight: true },
|
|
3175
|
+
services: {},
|
|
3176
|
+
actionRouting: {}
|
|
3177
|
+
};
|
|
3178
|
+
var SERVICE_MESH = {
|
|
3179
|
+
strategy: "service-mesh",
|
|
3180
|
+
evaluation: { pure: "local" },
|
|
3181
|
+
scheduling: { coordinator: "server" },
|
|
3182
|
+
events: { evaluator: "all" },
|
|
3183
|
+
errorHandling: { strategy: "saga" },
|
|
3184
|
+
conflicts: { default: "first-write-wins", fields: {} },
|
|
3185
|
+
visibility: { roles: {}, players: {} },
|
|
3186
|
+
presence: { enabled: false, showEditing: false, showViewing: false },
|
|
3187
|
+
services: {},
|
|
3188
|
+
actionRouting: {}
|
|
3189
|
+
};
|
|
3190
|
+
var PEER_TO_PEER = {
|
|
3191
|
+
strategy: "peer-to-peer",
|
|
3192
|
+
evaluation: { pure: "local" },
|
|
3193
|
+
scheduling: { coordinator: "any" },
|
|
3194
|
+
events: { evaluator: "any-subscriber" },
|
|
3195
|
+
errorHandling: { strategy: "compensate" },
|
|
3196
|
+
conflicts: { default: "crdt", fields: {} },
|
|
3197
|
+
visibility: { roles: {}, players: {} },
|
|
3198
|
+
presence: { enabled: true, showEditing: true, showViewing: true, conflictHighlight: false },
|
|
3199
|
+
services: {},
|
|
3200
|
+
actionRouting: {}
|
|
3201
|
+
};
|
|
3202
|
+
var HYBRID = {
|
|
3203
|
+
strategy: "hybrid",
|
|
3204
|
+
evaluation: { pure: "local" },
|
|
3205
|
+
scheduling: { coordinator: "server" },
|
|
3206
|
+
events: { evaluator: "server" },
|
|
3207
|
+
errorHandling: { strategy: "saga" },
|
|
3208
|
+
conflicts: { default: "last-writer-wins", fields: {} },
|
|
3209
|
+
visibility: { roles: {}, players: {} },
|
|
3210
|
+
presence: { enabled: true, showEditing: true, showViewing: false, conflictHighlight: false },
|
|
3211
|
+
services: {},
|
|
3212
|
+
actionRouting: {}
|
|
3213
|
+
};
|
|
3214
|
+
var HEADLESS = {
|
|
3215
|
+
strategy: "headless",
|
|
3216
|
+
evaluation: { pure: "local" },
|
|
3217
|
+
// expression engine runs server-local (native Rust, fastest)
|
|
3218
|
+
scheduling: { coordinator: "server" },
|
|
3219
|
+
// server manages cron/interval/timeout
|
|
3220
|
+
events: { evaluator: "server" },
|
|
3221
|
+
// server matches events, no clients involved
|
|
3222
|
+
errorHandling: { strategy: "compensate" },
|
|
3223
|
+
conflicts: { default: "first-write-wins", fields: {} },
|
|
3224
|
+
// no concurrent clients
|
|
3225
|
+
visibility: { roles: {}, players: {} },
|
|
3226
|
+
presence: { enabled: false, showEditing: false, showViewing: false, conflictHighlight: false },
|
|
3227
|
+
services: {},
|
|
3228
|
+
actionRouting: {}
|
|
3229
|
+
};
|
|
3230
|
+
var ORCHESTRATION_PRESETS = {
|
|
3231
|
+
"solo": SOLO,
|
|
3232
|
+
"client-server": CLIENT_SERVER,
|
|
3233
|
+
"multi-client": MULTI_CLIENT,
|
|
3234
|
+
"service-mesh": SERVICE_MESH,
|
|
3235
|
+
"peer-to-peer": PEER_TO_PEER,
|
|
3236
|
+
"hybrid": HYBRID,
|
|
3237
|
+
"headless": HEADLESS,
|
|
3238
|
+
"custom": CLIENT_SERVER
|
|
3239
|
+
// custom uses client-server as base
|
|
3240
|
+
};
|
|
3241
|
+
var DEFAULT_RESOLVED = {
|
|
3242
|
+
strategy: "client-server",
|
|
3243
|
+
evaluation: { pure: "local" },
|
|
3244
|
+
scheduling: { coordinator: "server" },
|
|
3245
|
+
events: { evaluator: "server" },
|
|
3246
|
+
errorHandling: { strategy: "rollback" },
|
|
3247
|
+
conflicts: { default: "last-writer-wins", fields: {} },
|
|
3248
|
+
visibility: { roles: {}, players: {} },
|
|
3249
|
+
presence: { enabled: false, showEditing: false, showViewing: false, conflictHighlight: false },
|
|
3250
|
+
services: {},
|
|
3251
|
+
actionRouting: {}
|
|
3252
|
+
};
|
|
3253
|
+
function resolveOrchestration(config) {
|
|
3254
|
+
if (!config) return { ...DEFAULT_RESOLVED };
|
|
3255
|
+
const strategy = config.strategy || "client-server";
|
|
3256
|
+
const preset = ORCHESTRATION_PRESETS[strategy] || CLIENT_SERVER;
|
|
3257
|
+
return {
|
|
3258
|
+
strategy,
|
|
3259
|
+
evaluation: {
|
|
3260
|
+
pure: config.evaluation?.pure ?? preset.evaluation?.pure ?? "local"
|
|
3261
|
+
},
|
|
3262
|
+
scheduling: {
|
|
3263
|
+
coordinator: config.scheduling?.coordinator ?? preset.scheduling?.coordinator ?? "server"
|
|
3264
|
+
},
|
|
3265
|
+
events: {
|
|
3266
|
+
evaluator: config.events?.evaluator ?? preset.events?.evaluator ?? "server"
|
|
3267
|
+
},
|
|
3268
|
+
errorHandling: {
|
|
3269
|
+
strategy: config.errorHandling?.strategy ?? preset.errorHandling?.strategy ?? "rollback"
|
|
3270
|
+
},
|
|
3271
|
+
conflicts: {
|
|
3272
|
+
default: config.conflicts?.default ?? preset.conflicts?.default ?? "last-writer-wins",
|
|
3273
|
+
fields: { ...preset.conflicts?.fields || {}, ...config.conflicts?.fields || {} }
|
|
3274
|
+
},
|
|
3275
|
+
visibility: {
|
|
3276
|
+
roles: { ...preset.visibility?.roles || {}, ...config.visibility?.roles || {} },
|
|
3277
|
+
players: { ...preset.visibility?.players || {}, ...config.visibility?.players || {} }
|
|
3278
|
+
},
|
|
3279
|
+
presence: {
|
|
3280
|
+
enabled: config.presence?.enabled ?? preset.presence?.enabled ?? false,
|
|
3281
|
+
showEditing: config.presence?.showEditing ?? preset.presence?.showEditing ?? false,
|
|
3282
|
+
showViewing: config.presence?.showViewing ?? preset.presence?.showViewing ?? false,
|
|
3283
|
+
conflictHighlight: config.presence?.conflictHighlight ?? preset.presence?.conflictHighlight ?? false
|
|
3284
|
+
},
|
|
3285
|
+
services: { ...preset.services || {}, ...config.services || {} },
|
|
3286
|
+
actionRouting: { ...preset.actionRouting || {}, ...config.actionRouting || {} }
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
|
|
4038
3290
|
// src/config/defineBlueprint.ts
|
|
4039
3291
|
function defineBlueprint(config) {
|
|
4040
3292
|
if (!config.slug) {
|
|
@@ -4148,13 +3400,13 @@ function inState(state2) {
|
|
|
4148
3400
|
function notInState(state2) {
|
|
4149
3401
|
return { type: "expression", expression: `current_state != "${state2}"` };
|
|
4150
3402
|
}
|
|
4151
|
-
function
|
|
3403
|
+
function and(...conditions) {
|
|
4152
3404
|
return { AND: conditions.map(normalize) };
|
|
4153
3405
|
}
|
|
4154
|
-
function
|
|
3406
|
+
function or(...conditions) {
|
|
4155
3407
|
return { OR: conditions.map(normalize) };
|
|
4156
3408
|
}
|
|
4157
|
-
function
|
|
3409
|
+
function not(condition) {
|
|
4158
3410
|
const c = normalize(condition);
|
|
4159
3411
|
if (c.type === "expression" && c.expression) {
|
|
4160
3412
|
return { type: "expression", expression: `NOT(${c.expression})` };
|
|
@@ -4199,7 +3451,7 @@ function refHasAnyRole(slug, lookupField, lookupValue, roles) {
|
|
|
4199
3451
|
}
|
|
4200
3452
|
|
|
4201
3453
|
// src/hooks/useModel.ts
|
|
4202
|
-
import { useCallback as
|
|
3454
|
+
import { useCallback as useCallback20, useRef as useRef24, useMemo as useMemo16 } from "react";
|
|
4203
3455
|
function getDefaultFields(definition) {
|
|
4204
3456
|
const defaults = {};
|
|
4205
3457
|
for (const [key, field2] of Object.entries(definition.fields)) {
|
|
@@ -4254,18 +3506,18 @@ function useModel(definition, options = {}) {
|
|
|
4254
3506
|
if (options.state) queryParams.state = options.state;
|
|
4255
3507
|
const query = useQuery(slug, queryParams);
|
|
4256
3508
|
const mutation = useMutation(slug);
|
|
4257
|
-
const
|
|
4258
|
-
const instanceId =
|
|
4259
|
-
const currentState =
|
|
4260
|
-
const instanceFields =
|
|
4261
|
-
const defaultFields =
|
|
4262
|
-
const fields =
|
|
3509
|
+
const instance2 = query.data?.[0];
|
|
3510
|
+
const instanceId = instance2?.id ?? null;
|
|
3511
|
+
const currentState = instance2?.currentState ?? "";
|
|
3512
|
+
const instanceFields = instance2?.fields ?? null;
|
|
3513
|
+
const defaultFields = useMemo16(() => getDefaultFields(definition), [definition]);
|
|
3514
|
+
const fields = useMemo16(() => {
|
|
4263
3515
|
if (!instanceFields) return defaultFields;
|
|
4264
3516
|
return { ...defaultFields, ...instanceFields };
|
|
4265
3517
|
}, [instanceFields, defaultFields]);
|
|
4266
|
-
const instanceIdRef =
|
|
3518
|
+
const instanceIdRef = useRef24(instanceId);
|
|
4267
3519
|
instanceIdRef.current = instanceId;
|
|
4268
|
-
const trigger =
|
|
3520
|
+
const trigger = useCallback20(async (name, input) => {
|
|
4269
3521
|
const id = instanceIdRef.current;
|
|
4270
3522
|
if (!id) {
|
|
4271
3523
|
throw new Error(`useModel(${slug}): No instance loaded. Cannot trigger '${name}'.`);
|
|
@@ -4273,12 +3525,12 @@ function useModel(definition, options = {}) {
|
|
|
4273
3525
|
await mutation.transition(id, name, input);
|
|
4274
3526
|
await query.refetch();
|
|
4275
3527
|
}, [slug, mutation, query]);
|
|
4276
|
-
const create =
|
|
3528
|
+
const create = useCallback20(async (input) => {
|
|
4277
3529
|
const id = await mutation.create(input);
|
|
4278
3530
|
await query.refetch();
|
|
4279
3531
|
return id;
|
|
4280
3532
|
}, [mutation, query]);
|
|
4281
|
-
const update =
|
|
3533
|
+
const update = useCallback20(async (fieldUpdates) => {
|
|
4282
3534
|
const id = instanceIdRef.current;
|
|
4283
3535
|
if (!id) {
|
|
4284
3536
|
throw new Error(`useModel(${slug}): No instance loaded. Cannot update.`);
|
|
@@ -4307,27 +3559,27 @@ function useCollection(definition, options = {}) {
|
|
|
4307
3559
|
const slug = definition.slug;
|
|
4308
3560
|
const query = useQuery(slug, options);
|
|
4309
3561
|
const mutation = useMutation(slug);
|
|
4310
|
-
const items =
|
|
4311
|
-
return (query.data || []).map((
|
|
4312
|
-
id:
|
|
4313
|
-
fields:
|
|
4314
|
-
state:
|
|
3562
|
+
const items = useMemo16(() => {
|
|
3563
|
+
return (query.data || []).map((instance2) => ({
|
|
3564
|
+
id: instance2.id,
|
|
3565
|
+
fields: instance2.fields ?? {},
|
|
3566
|
+
state: instance2.currentState ?? ""
|
|
4315
3567
|
}));
|
|
4316
3568
|
}, [query.data]);
|
|
4317
|
-
const trigger =
|
|
3569
|
+
const trigger = useCallback20(async (instanceId, name, input) => {
|
|
4318
3570
|
await mutation.transition(instanceId, name, input);
|
|
4319
3571
|
await query.refetch();
|
|
4320
3572
|
}, [mutation, query]);
|
|
4321
|
-
const create =
|
|
3573
|
+
const create = useCallback20(async (input) => {
|
|
4322
3574
|
const id = await mutation.create(input);
|
|
4323
3575
|
await query.refetch();
|
|
4324
3576
|
return id;
|
|
4325
3577
|
}, [mutation, query]);
|
|
4326
|
-
const update =
|
|
3578
|
+
const update = useCallback20(async (instanceId, fieldUpdates) => {
|
|
4327
3579
|
await mutation.update(instanceId, fieldUpdates);
|
|
4328
3580
|
await query.refetch();
|
|
4329
3581
|
}, [mutation, query]);
|
|
4330
|
-
const remove =
|
|
3582
|
+
const remove = useCallback20(async (instanceId) => {
|
|
4331
3583
|
await mutation.remove(instanceId);
|
|
4332
3584
|
await query.refetch();
|
|
4333
3585
|
}, [mutation, query]);
|
|
@@ -4351,7 +3603,7 @@ function useCollection(definition, options = {}) {
|
|
|
4351
3603
|
function stub(displayName) {
|
|
4352
3604
|
const Component = () => {
|
|
4353
3605
|
throw new Error(
|
|
4354
|
-
`<${displayName}> is a compile-time stub from @
|
|
3606
|
+
`<${displayName}> is a compile-time stub from @mmapp/react/atoms. It should only appear in .workflow.tsx files processed by the compiler. At runtime, use the ComponentTreeRenderer which resolves "${displayName}" from the component registry.`
|
|
4355
3607
|
);
|
|
4356
3608
|
};
|
|
4357
3609
|
Component.displayName = displayName;
|
|
@@ -4478,7 +3730,7 @@ function djb2Hash(str) {
|
|
|
4478
3730
|
}
|
|
4479
3731
|
|
|
4480
3732
|
// src/authoring.ts
|
|
4481
|
-
function
|
|
3733
|
+
function useState21(_defaultOrKey) {
|
|
4482
3734
|
return [void 0, () => {
|
|
4483
3735
|
}];
|
|
4484
3736
|
}
|
|
@@ -4489,10 +3741,10 @@ function defineWorkspace(config) {
|
|
|
4489
3741
|
}
|
|
4490
3742
|
|
|
4491
3743
|
// src/hooks/useModule.ts
|
|
4492
|
-
import { useMemo as
|
|
3744
|
+
import { useMemo as useMemo17 } from "react";
|
|
4493
3745
|
function useModule(slug, config = {}, options = {}) {
|
|
4494
3746
|
const { enabled = true } = options;
|
|
4495
|
-
return
|
|
3747
|
+
return useMemo17(() => ({
|
|
4496
3748
|
slug,
|
|
4497
3749
|
config,
|
|
4498
3750
|
isLoaded: enabled
|
|
@@ -4500,7 +3752,7 @@ function useModule(slug, config = {}, options = {}) {
|
|
|
4500
3752
|
}
|
|
4501
3753
|
|
|
4502
3754
|
// src/hooks/useModuleConfig.ts
|
|
4503
|
-
import { useCallback as
|
|
3755
|
+
import { useCallback as useCallback21, useMemo as useMemo18, useState as useState22 } from "react";
|
|
4504
3756
|
var installedModulesStore = [];
|
|
4505
3757
|
var configDefaultsStore = /* @__PURE__ */ new Map();
|
|
4506
3758
|
function setInstalledModules(modules) {
|
|
@@ -4516,7 +3768,7 @@ function getInstalledModules() {
|
|
|
4516
3768
|
return installedModulesStore;
|
|
4517
3769
|
}
|
|
4518
3770
|
function useModuleConfig(moduleSlug) {
|
|
4519
|
-
return
|
|
3771
|
+
return useMemo18(() => {
|
|
4520
3772
|
const installed = getInstalledModule(moduleSlug);
|
|
4521
3773
|
const defaults = configDefaultsStore.get(moduleSlug) ?? {};
|
|
4522
3774
|
const persisted = persistedConfigStore.get(moduleSlug) ?? {};
|
|
@@ -4564,8 +3816,8 @@ async function updateDefinitionConfig(values, definitionId) {
|
|
|
4564
3816
|
}
|
|
4565
3817
|
function useModuleConfigWithMutation(moduleSlug) {
|
|
4566
3818
|
const config = useModuleConfig(moduleSlug);
|
|
4567
|
-
const [isSaving, setIsSaving] =
|
|
4568
|
-
const updateConfig =
|
|
3819
|
+
const [isSaving, setIsSaving] = useState22(false);
|
|
3820
|
+
const updateConfig = useCallback21(async (values) => {
|
|
4569
3821
|
setIsSaving(true);
|
|
4570
3822
|
try {
|
|
4571
3823
|
return await updateDefinitionConfig(values);
|
|
@@ -6078,7 +5330,7 @@ function approval(config = {}) {
|
|
|
6078
5330
|
}
|
|
6079
5331
|
function escalation(config) {
|
|
6080
5332
|
const {
|
|
6081
|
-
timeout,
|
|
5333
|
+
timeout: timeout2,
|
|
6082
5334
|
notifyRole = "admin",
|
|
6083
5335
|
fromState = "pending_approval",
|
|
6084
5336
|
escalatedState = "escalated"
|
|
@@ -6090,7 +5342,7 @@ function escalation(config) {
|
|
|
6090
5342
|
});
|
|
6091
5343
|
if (def.states[fromState]) {
|
|
6092
5344
|
def.states[fromState].timeout = {
|
|
6093
|
-
duration:
|
|
5345
|
+
duration: timeout2,
|
|
6094
5346
|
fallback: { transition: "escalate" }
|
|
6095
5347
|
};
|
|
6096
5348
|
}
|
|
@@ -6109,8 +5361,8 @@ function escalation(config) {
|
|
|
6109
5361
|
from: fromState,
|
|
6110
5362
|
to: escalatedState,
|
|
6111
5363
|
auto: true,
|
|
6112
|
-
description: `Auto-escalate after ${
|
|
6113
|
-
actions: [logEvent("escalated", { timeout })]
|
|
5364
|
+
description: `Auto-escalate after ${timeout2}`,
|
|
5365
|
+
actions: [logEvent("escalated", { timeout: timeout2 })]
|
|
6114
5366
|
}
|
|
6115
5367
|
});
|
|
6116
5368
|
return def;
|
|
@@ -6768,7 +6020,14 @@ function describeModel(def) {
|
|
|
6768
6020
|
}
|
|
6769
6021
|
|
|
6770
6022
|
// src/hooks/usePlayer.ts
|
|
6771
|
-
import { useCallback as
|
|
6023
|
+
import { useCallback as useCallback22, useEffect as useEffect22, useMemo as useMemo19, useRef as useRef25, useState as useState23 } from "react";
|
|
6024
|
+
import {
|
|
6025
|
+
StateMachine as StateMachine2,
|
|
6026
|
+
EventBus as EventBus2,
|
|
6027
|
+
ActionDispatcher as ActionDispatcher2,
|
|
6028
|
+
createEvaluator as createEvaluator2,
|
|
6029
|
+
WEB_FAILURE_POLICIES as WEB_FAILURE_POLICIES2
|
|
6030
|
+
} from "@mmapp/player-core";
|
|
6772
6031
|
|
|
6773
6032
|
// src/logger.ts
|
|
6774
6033
|
var debugEnabled = false;
|
|
@@ -6808,18 +6067,18 @@ function computePlayerState(sm) {
|
|
|
6808
6067
|
};
|
|
6809
6068
|
}
|
|
6810
6069
|
function usePlayer(config) {
|
|
6811
|
-
const configRef =
|
|
6070
|
+
const configRef = useRef25(config);
|
|
6812
6071
|
configRef.current = config;
|
|
6813
|
-
|
|
6072
|
+
useEffect22(() => {
|
|
6814
6073
|
if (config.debug) setPlayerDebug(true);
|
|
6815
6074
|
}, [config.debug]);
|
|
6816
|
-
const evaluator =
|
|
6817
|
-
return
|
|
6075
|
+
const evaluator = useMemo19(() => {
|
|
6076
|
+
return createEvaluator2({
|
|
6818
6077
|
functions: config.functions ?? [],
|
|
6819
|
-
failurePolicy:
|
|
6078
|
+
failurePolicy: WEB_FAILURE_POLICIES2.EVENT_REACTION
|
|
6820
6079
|
});
|
|
6821
6080
|
}, [config.definition.id]);
|
|
6822
|
-
const engine =
|
|
6081
|
+
const engine = useMemo19(() => {
|
|
6823
6082
|
const actionHandlers = /* @__PURE__ */ new Map();
|
|
6824
6083
|
if (config.actionHandlers) {
|
|
6825
6084
|
for (const [type, handler] of Object.entries(config.actionHandlers)) {
|
|
@@ -6847,14 +6106,14 @@ function usePlayer(config) {
|
|
|
6847
6106
|
});
|
|
6848
6107
|
}
|
|
6849
6108
|
});
|
|
6850
|
-
const sm2 = new
|
|
6109
|
+
const sm2 = new StateMachine2(
|
|
6851
6110
|
config.definition,
|
|
6852
6111
|
config.initialData ?? {},
|
|
6853
6112
|
{ evaluator, actionHandlers }
|
|
6854
6113
|
);
|
|
6855
6114
|
smRef = sm2;
|
|
6856
|
-
const eventBus2 = new
|
|
6857
|
-
const dispatcher = new
|
|
6115
|
+
const eventBus2 = new EventBus2();
|
|
6116
|
+
const dispatcher = new ActionDispatcher2();
|
|
6858
6117
|
dispatcher.register("set_field", (cfg) => {
|
|
6859
6118
|
if (smRef && typeof cfg.field === "string") {
|
|
6860
6119
|
smRef.setField(cfg.field, cfg.value);
|
|
@@ -6873,8 +6132,8 @@ function usePlayer(config) {
|
|
|
6873
6132
|
return { sm: sm2, eventBus: eventBus2, dispatcher };
|
|
6874
6133
|
}, [config.definition.id, evaluator]);
|
|
6875
6134
|
const { sm, eventBus } = engine;
|
|
6876
|
-
const [playerState, setPlayerState] =
|
|
6877
|
-
|
|
6135
|
+
const [playerState, setPlayerState] = useState23(() => computePlayerState(sm));
|
|
6136
|
+
useEffect22(() => {
|
|
6878
6137
|
const stateDef = sm.getCurrentStateDefinition();
|
|
6879
6138
|
if (!stateDef?.on_event?.length) return;
|
|
6880
6139
|
const unsubs = [];
|
|
@@ -6938,7 +6197,7 @@ function usePlayer(config) {
|
|
|
6938
6197
|
for (const unsub of unsubs) unsub();
|
|
6939
6198
|
};
|
|
6940
6199
|
}, [sm, eventBus, evaluator, engine.dispatcher, playerState.currentState]);
|
|
6941
|
-
|
|
6200
|
+
useEffect22(() => {
|
|
6942
6201
|
const unsub = sm.on((event) => {
|
|
6943
6202
|
if (event.type === "transition" || event.type === "state_enter") {
|
|
6944
6203
|
setPlayerState(computePlayerState(sm));
|
|
@@ -6954,7 +6213,7 @@ function usePlayer(config) {
|
|
|
6954
6213
|
});
|
|
6955
6214
|
return unsub;
|
|
6956
6215
|
}, [sm]);
|
|
6957
|
-
const transition2 =
|
|
6216
|
+
const transition2 = useCallback22(
|
|
6958
6217
|
async (name, data) => {
|
|
6959
6218
|
playerLog({
|
|
6960
6219
|
level: "info",
|
|
@@ -6981,20 +6240,20 @@ function usePlayer(config) {
|
|
|
6981
6240
|
},
|
|
6982
6241
|
[sm]
|
|
6983
6242
|
);
|
|
6984
|
-
const setField2 =
|
|
6243
|
+
const setField2 = useCallback22(
|
|
6985
6244
|
(field2, value) => {
|
|
6986
6245
|
sm.setField(field2, value);
|
|
6987
6246
|
setPlayerState(computePlayerState(sm));
|
|
6988
6247
|
},
|
|
6989
6248
|
[sm]
|
|
6990
6249
|
);
|
|
6991
|
-
const setMemory =
|
|
6250
|
+
const setMemory = useCallback22(
|
|
6992
6251
|
(key, value) => {
|
|
6993
6252
|
sm.setMemory(key, value);
|
|
6994
6253
|
},
|
|
6995
6254
|
[sm]
|
|
6996
6255
|
);
|
|
6997
|
-
const publishEvent =
|
|
6256
|
+
const publishEvent = useCallback22(
|
|
6998
6257
|
(topic, payload) => {
|
|
6999
6258
|
playerLog({
|
|
7000
6259
|
level: "debug",
|
|
@@ -7020,11 +6279,11 @@ function usePlayer(config) {
|
|
|
7020
6279
|
}
|
|
7021
6280
|
|
|
7022
6281
|
// src/hooks/useDomainSubscription.ts
|
|
7023
|
-
import { useEffect as
|
|
6282
|
+
import { useEffect as useEffect23, useRef as useRef26 } from "react";
|
|
7024
6283
|
function useDomainSubscription(eventBus, transport, config) {
|
|
7025
|
-
const configRef =
|
|
6284
|
+
const configRef = useRef26(config);
|
|
7026
6285
|
configRef.current = config;
|
|
7027
|
-
|
|
6286
|
+
useEffect23(() => {
|
|
7028
6287
|
if (!transport || config.enabled === false) return;
|
|
7029
6288
|
const unsub = transport.subscribe(
|
|
7030
6289
|
{
|
|
@@ -7077,11 +6336,11 @@ function useDomainSubscription(eventBus, transport, config) {
|
|
|
7077
6336
|
}
|
|
7078
6337
|
|
|
7079
6338
|
// src/hooks/useExperienceState.ts
|
|
7080
|
-
import { useCallback as
|
|
6339
|
+
import { useCallback as useCallback23, useRef as useRef27 } from "react";
|
|
7081
6340
|
function useExperienceState(player, selector) {
|
|
7082
|
-
const selectorRef =
|
|
6341
|
+
const selectorRef = useRef27(selector);
|
|
7083
6342
|
selectorRef.current = selector;
|
|
7084
|
-
const getSnapshot =
|
|
6343
|
+
const getSnapshot = useCallback23(() => {
|
|
7085
6344
|
return selectorRef.current({
|
|
7086
6345
|
currentState: player.currentState,
|
|
7087
6346
|
stateData: player.stateData,
|
|
@@ -7097,20 +6356,20 @@ function useStateField(player, field2, defaultValue) {
|
|
|
7097
6356
|
}
|
|
7098
6357
|
|
|
7099
6358
|
// src/hooks/useComputed.ts
|
|
7100
|
-
import { useMemo as
|
|
6359
|
+
import { useMemo as useMemo20, useRef as useRef28 } from "react";
|
|
7101
6360
|
function useComputed(_name, compute, options) {
|
|
7102
6361
|
const mode = options?.mode ?? "read-time";
|
|
7103
6362
|
const deps = options?.deps ?? [];
|
|
7104
|
-
const computeRef =
|
|
6363
|
+
const computeRef = useRef28(compute);
|
|
7105
6364
|
computeRef.current = compute;
|
|
7106
6365
|
if (mode === "read-time") {
|
|
7107
|
-
return
|
|
6366
|
+
return useMemo20(() => computeRef.current(), [
|
|
7108
6367
|
// We intentionally depend on deps.join to recompute when tracked fields change
|
|
7109
6368
|
// The actual dependency tracking happens at the compiler level
|
|
7110
6369
|
deps.join(",")
|
|
7111
6370
|
]);
|
|
7112
6371
|
}
|
|
7113
|
-
return
|
|
6372
|
+
return useMemo20(() => computeRef.current(), [deps.join(",")]);
|
|
7114
6373
|
}
|
|
7115
6374
|
function useComputedWithMeta(name, compute, options) {
|
|
7116
6375
|
const value = useComputed(name, compute, options);
|
|
@@ -7143,7 +6402,7 @@ function usePlayerContextSafe() {
|
|
|
7143
6402
|
}
|
|
7144
6403
|
|
|
7145
6404
|
// src/components/ExperienceWorkflowBridge.tsx
|
|
7146
|
-
import { useMemo as
|
|
6405
|
+
import { useMemo as useMemo21 } from "react";
|
|
7147
6406
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
7148
6407
|
function ExperienceWorkflowBridgeInner({
|
|
7149
6408
|
definition,
|
|
@@ -7161,7 +6420,7 @@ function ExperienceWorkflowBridgeInner({
|
|
|
7161
6420
|
actionHandlers,
|
|
7162
6421
|
debug
|
|
7163
6422
|
});
|
|
7164
|
-
const viewConfig =
|
|
6423
|
+
const viewConfig = useMemo21(() => {
|
|
7165
6424
|
if (!definition.state_views) return void 0;
|
|
7166
6425
|
return definition.state_views[player.currentState];
|
|
7167
6426
|
}, [definition.state_views, player.currentState]);
|
|
@@ -7517,7 +6776,7 @@ var BrowserPlayer = class {
|
|
|
7517
6776
|
this.ensureInitialized();
|
|
7518
6777
|
const id = crypto.randomUUID();
|
|
7519
6778
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7520
|
-
const
|
|
6779
|
+
const instance2 = {
|
|
7521
6780
|
id,
|
|
7522
6781
|
definitionId: options.definitionId ?? definitionSlug,
|
|
7523
6782
|
definitionSlug,
|
|
@@ -7529,15 +6788,15 @@ var BrowserPlayer = class {
|
|
|
7529
6788
|
createdAt: now,
|
|
7530
6789
|
updatedAt: now
|
|
7531
6790
|
};
|
|
7532
|
-
await this.putInstance(
|
|
6791
|
+
await this.putInstance(instance2);
|
|
7533
6792
|
this.log("info", `Instance created: ${id} (${definitionSlug})`);
|
|
7534
6793
|
this.emit({
|
|
7535
6794
|
type: "instance:created",
|
|
7536
6795
|
instanceId: id,
|
|
7537
|
-
data: { definitionSlug, currentState:
|
|
6796
|
+
data: { definitionSlug, currentState: instance2.currentState },
|
|
7538
6797
|
timestamp: Date.now()
|
|
7539
6798
|
});
|
|
7540
|
-
return
|
|
6799
|
+
return instance2;
|
|
7541
6800
|
}
|
|
7542
6801
|
/** Get an instance by ID. */
|
|
7543
6802
|
async getInstance(id) {
|
|
@@ -7546,13 +6805,13 @@ var BrowserPlayer = class {
|
|
|
7546
6805
|
}
|
|
7547
6806
|
/** Get the current state of an instance. */
|
|
7548
6807
|
async getState(id) {
|
|
7549
|
-
const
|
|
7550
|
-
if (!
|
|
6808
|
+
const instance2 = await this.getInstance(id);
|
|
6809
|
+
if (!instance2) return null;
|
|
7551
6810
|
return {
|
|
7552
|
-
currentState:
|
|
7553
|
-
status:
|
|
7554
|
-
stateData:
|
|
7555
|
-
lockVersion:
|
|
6811
|
+
currentState: instance2.currentState,
|
|
6812
|
+
status: instance2.status,
|
|
6813
|
+
stateData: instance2.stateData,
|
|
6814
|
+
lockVersion: instance2.lockVersion
|
|
7556
6815
|
};
|
|
7557
6816
|
}
|
|
7558
6817
|
/**
|
|
@@ -7563,34 +6822,34 @@ var BrowserPlayer = class {
|
|
|
7563
6822
|
*/
|
|
7564
6823
|
async transition(instanceId, transitionName, data = {}) {
|
|
7565
6824
|
this.ensureInitialized();
|
|
7566
|
-
const
|
|
7567
|
-
if (!
|
|
6825
|
+
const instance2 = await this.getInstanceFromDb(instanceId);
|
|
6826
|
+
if (!instance2) {
|
|
7568
6827
|
return {
|
|
7569
6828
|
success: false,
|
|
7570
6829
|
instance: null,
|
|
7571
6830
|
error: `Instance ${instanceId} not found`
|
|
7572
6831
|
};
|
|
7573
6832
|
}
|
|
7574
|
-
if (
|
|
6833
|
+
if (instance2.status !== "ACTIVE") {
|
|
7575
6834
|
return {
|
|
7576
6835
|
success: false,
|
|
7577
|
-
instance,
|
|
7578
|
-
error: `Instance is ${
|
|
6836
|
+
instance: instance2,
|
|
6837
|
+
error: `Instance is ${instance2.status}, cannot transition`
|
|
7579
6838
|
};
|
|
7580
6839
|
}
|
|
7581
6840
|
try {
|
|
7582
|
-
const newStateData = { ...
|
|
7583
|
-
const newLockVersion =
|
|
6841
|
+
const newStateData = { ...instance2.stateData, ...data };
|
|
6842
|
+
const newLockVersion = instance2.lockVersion + 1;
|
|
7584
6843
|
if (this.wasm.execute_transition) {
|
|
7585
6844
|
const request = {
|
|
7586
6845
|
instance: {
|
|
7587
|
-
id:
|
|
7588
|
-
definition_id:
|
|
7589
|
-
current_state:
|
|
7590
|
-
status:
|
|
6846
|
+
id: instance2.id,
|
|
6847
|
+
definition_id: instance2.definitionId,
|
|
6848
|
+
current_state: instance2.currentState,
|
|
6849
|
+
status: instance2.status,
|
|
7591
6850
|
state_data: newStateData,
|
|
7592
|
-
memory:
|
|
7593
|
-
execution_lock_version:
|
|
6851
|
+
memory: instance2.memory,
|
|
6852
|
+
execution_lock_version: instance2.lockVersion
|
|
7594
6853
|
},
|
|
7595
6854
|
transition_name: transitionName,
|
|
7596
6855
|
actor_id: this.peerId,
|
|
@@ -7599,26 +6858,26 @@ var BrowserPlayer = class {
|
|
|
7599
6858
|
const resultJson = this.wasm.execute_transition(JSON.stringify(request));
|
|
7600
6859
|
const result = JSON.parse(resultJson);
|
|
7601
6860
|
if (result.success === false || result.error) {
|
|
7602
|
-
return { success: false, instance, error: result.error ?? "Transition failed" };
|
|
6861
|
+
return { success: false, instance: instance2, error: result.error ?? "Transition failed" };
|
|
7603
6862
|
}
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
6863
|
+
instance2.currentState = result.to_state ?? instance2.currentState;
|
|
6864
|
+
instance2.status = result.status ?? instance2.status;
|
|
6865
|
+
instance2.stateData = result.state_data ?? newStateData;
|
|
6866
|
+
instance2.memory = result.memory ?? instance2.memory;
|
|
7608
6867
|
} else {
|
|
7609
|
-
|
|
6868
|
+
instance2.stateData = newStateData;
|
|
7610
6869
|
}
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
await this.putInstance(
|
|
6870
|
+
instance2.lockVersion = newLockVersion;
|
|
6871
|
+
instance2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6872
|
+
await this.putInstance(instance2);
|
|
7614
6873
|
this.log("info", `Transition: ${instanceId} ${transitionName}`);
|
|
7615
6874
|
this.emit({
|
|
7616
6875
|
type: "instance:transition",
|
|
7617
6876
|
instanceId,
|
|
7618
6877
|
data: {
|
|
7619
6878
|
transitionName,
|
|
7620
|
-
fromState:
|
|
7621
|
-
toState:
|
|
6879
|
+
fromState: instance2.currentState,
|
|
6880
|
+
toState: instance2.currentState,
|
|
7622
6881
|
lockVersion: newLockVersion
|
|
7623
6882
|
},
|
|
7624
6883
|
timestamp: Date.now()
|
|
@@ -7628,7 +6887,7 @@ var BrowserPlayer = class {
|
|
|
7628
6887
|
type: "TransitionReplication",
|
|
7629
6888
|
instance_id: instanceId,
|
|
7630
6889
|
transition_name: transitionName,
|
|
7631
|
-
state_data:
|
|
6890
|
+
state_data: instance2.stateData,
|
|
7632
6891
|
lock_version: newLockVersion
|
|
7633
6892
|
});
|
|
7634
6893
|
const envelope = this.wasm.sign_envelope(this.secretKeyHex, payload);
|
|
@@ -7638,11 +6897,11 @@ var BrowserPlayer = class {
|
|
|
7638
6897
|
envelope
|
|
7639
6898
|
}));
|
|
7640
6899
|
}
|
|
7641
|
-
return { success: true, instance };
|
|
6900
|
+
return { success: true, instance: instance2 };
|
|
7642
6901
|
} catch (err) {
|
|
7643
6902
|
const error = err instanceof Error ? err.message : String(err);
|
|
7644
6903
|
this.log("error", `Transition failed: ${error}`);
|
|
7645
|
-
return { success: false, instance, error };
|
|
6904
|
+
return { success: false, instance: instance2, error };
|
|
7646
6905
|
}
|
|
7647
6906
|
}
|
|
7648
6907
|
// ─── Subscriptions ──────────────────────────────────────────────────
|
|
@@ -7722,12 +6981,12 @@ var BrowserPlayer = class {
|
|
|
7722
6981
|
request.onerror = () => reject(request.error);
|
|
7723
6982
|
});
|
|
7724
6983
|
}
|
|
7725
|
-
async putInstance(
|
|
6984
|
+
async putInstance(instance2) {
|
|
7726
6985
|
if (!this.db) return;
|
|
7727
6986
|
return new Promise((resolve, reject) => {
|
|
7728
6987
|
const tx = this.db.transaction(INSTANCE_STORE_NAME, "readwrite");
|
|
7729
6988
|
const store = tx.objectStore(INSTANCE_STORE_NAME);
|
|
7730
|
-
store.put(
|
|
6989
|
+
store.put(instance2);
|
|
7731
6990
|
tx.oncomplete = () => resolve();
|
|
7732
6991
|
tx.onerror = () => reject(tx.error);
|
|
7733
6992
|
});
|
|
@@ -7874,9 +7133,381 @@ var BrowserPlayer = class {
|
|
|
7874
7133
|
}
|
|
7875
7134
|
}
|
|
7876
7135
|
};
|
|
7136
|
+
|
|
7137
|
+
// src/middleware-def.ts
|
|
7138
|
+
function defineMiddleware(def) {
|
|
7139
|
+
if (process.env.NODE_ENV !== "production") {
|
|
7140
|
+
if (!def.name || typeof def.name !== "string") {
|
|
7141
|
+
throw new Error("defineMiddleware: name is required and must be a string");
|
|
7142
|
+
}
|
|
7143
|
+
if (!def.match) {
|
|
7144
|
+
throw new Error("defineMiddleware: match pattern is required");
|
|
7145
|
+
}
|
|
7146
|
+
const patterns = Array.isArray(def.match) ? def.match : [def.match];
|
|
7147
|
+
for (const p of patterns) {
|
|
7148
|
+
if (typeof p !== "string") {
|
|
7149
|
+
throw new Error(`defineMiddleware: match pattern must be a string, got ${typeof p}`);
|
|
7150
|
+
}
|
|
7151
|
+
}
|
|
7152
|
+
if (def.priority !== void 0 && typeof def.priority !== "number") {
|
|
7153
|
+
throw new Error("defineMiddleware: priority must be a number");
|
|
7154
|
+
}
|
|
7155
|
+
if (!def.before && !def.after && !def.around) {
|
|
7156
|
+
throw new Error("defineMiddleware: at least one of before, after, or around must be provided");
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
return def;
|
|
7160
|
+
}
|
|
7161
|
+
function extendMiddleware(base, overrides) {
|
|
7162
|
+
let introduce;
|
|
7163
|
+
if (base.introduce || overrides.introduce) {
|
|
7164
|
+
introduce = {
|
|
7165
|
+
fields: { ...base.introduce?.fields, ...overrides.introduce?.fields },
|
|
7166
|
+
states: { ...base.introduce?.states, ...overrides.introduce?.states },
|
|
7167
|
+
transitions: { ...base.introduce?.transitions, ...overrides.introduce?.transitions }
|
|
7168
|
+
};
|
|
7169
|
+
if (introduce.fields && Object.keys(introduce.fields).length === 0) introduce.fields = void 0;
|
|
7170
|
+
if (introduce.states && Object.keys(introduce.states).length === 0) introduce.states = void 0;
|
|
7171
|
+
if (introduce.transitions && Object.keys(introduce.transitions).length === 0) introduce.transitions = void 0;
|
|
7172
|
+
}
|
|
7173
|
+
const merged = {
|
|
7174
|
+
...base,
|
|
7175
|
+
...overrides,
|
|
7176
|
+
// Config is merged (overrides win per-key)
|
|
7177
|
+
config: base.config || overrides.config ? { ...base.config, ...overrides.config } : void 0
|
|
7178
|
+
};
|
|
7179
|
+
if (introduce) {
|
|
7180
|
+
merged.introduce = introduce;
|
|
7181
|
+
}
|
|
7182
|
+
if (process.env.NODE_ENV !== "production") {
|
|
7183
|
+
if (!merged.name || typeof merged.name !== "string") {
|
|
7184
|
+
throw new Error("extendMiddleware: resulting middleware must have a name");
|
|
7185
|
+
}
|
|
7186
|
+
if (!merged.match) {
|
|
7187
|
+
throw new Error("extendMiddleware: resulting middleware must have a match pattern");
|
|
7188
|
+
}
|
|
7189
|
+
if (!merged.before && !merged.after && !merged.around) {
|
|
7190
|
+
throw new Error("extendMiddleware: resulting middleware must have at least one of before, after, or around");
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
return merged;
|
|
7194
|
+
}
|
|
7195
|
+
function withAuth(opts) {
|
|
7196
|
+
const _redirectTo = opts?.redirectTo;
|
|
7197
|
+
return {
|
|
7198
|
+
name: "mm:auth",
|
|
7199
|
+
match: "*:*:transition.execute",
|
|
7200
|
+
priority: 90,
|
|
7201
|
+
before(ctx) {
|
|
7202
|
+
if (!ctx.actor.id) {
|
|
7203
|
+
ctx.block(_redirectTo ? `redirect:${_redirectTo}` : "Authentication required");
|
|
7204
|
+
}
|
|
7205
|
+
}
|
|
7206
|
+
};
|
|
7207
|
+
}
|
|
7208
|
+
function withAuditLog(opts) {
|
|
7209
|
+
const _level = opts?.level ?? "info";
|
|
7210
|
+
return {
|
|
7211
|
+
name: "mm:audit-log",
|
|
7212
|
+
match: "*:*:transition.execute",
|
|
7213
|
+
priority: 0,
|
|
7214
|
+
after(ctx) {
|
|
7215
|
+
ctx.modify({
|
|
7216
|
+
__audit: {
|
|
7217
|
+
level: _level,
|
|
7218
|
+
actor: ctx.actor.id,
|
|
7219
|
+
transition: ctx.transition?.name,
|
|
7220
|
+
from: ctx.transition?.from,
|
|
7221
|
+
to: ctx.transition?.to,
|
|
7222
|
+
timestamp: ctx.timestamp
|
|
7223
|
+
}
|
|
7224
|
+
});
|
|
7225
|
+
}
|
|
7226
|
+
};
|
|
7227
|
+
}
|
|
7228
|
+
function withRateLimit(opts) {
|
|
7229
|
+
const _max = opts?.maxPerMinute ?? 60;
|
|
7230
|
+
return {
|
|
7231
|
+
name: "mm:rate-limit",
|
|
7232
|
+
match: "*:*:transition.execute",
|
|
7233
|
+
priority: 80,
|
|
7234
|
+
config: {
|
|
7235
|
+
maxPerMinute: { type: "number", default: _max }
|
|
7236
|
+
},
|
|
7237
|
+
before(ctx) {
|
|
7238
|
+
const fields = ctx.instance.fields;
|
|
7239
|
+
const counter = fields.__rateCounter || 0;
|
|
7240
|
+
if (counter >= _max) {
|
|
7241
|
+
ctx.block(`Rate limit exceeded: ${_max} per minute`);
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
};
|
|
7245
|
+
}
|
|
7246
|
+
function withValidation(opts) {
|
|
7247
|
+
const _rules = opts?.rules ?? [];
|
|
7248
|
+
return {
|
|
7249
|
+
name: "mm:validation",
|
|
7250
|
+
match: "*:*:field.change",
|
|
7251
|
+
priority: 70,
|
|
7252
|
+
config: {
|
|
7253
|
+
rules: { type: "json", default: _rules }
|
|
7254
|
+
},
|
|
7255
|
+
before(ctx) {
|
|
7256
|
+
if (!ctx.field) return;
|
|
7257
|
+
for (const rule of _rules) {
|
|
7258
|
+
if (rule.fields.includes(ctx.field.name)) {
|
|
7259
|
+
ctx.block(rule.message);
|
|
7260
|
+
}
|
|
7261
|
+
}
|
|
7262
|
+
}
|
|
7263
|
+
};
|
|
7264
|
+
}
|
|
7265
|
+
function withMetrics(opts) {
|
|
7266
|
+
const _endpoint = opts?.endpoint;
|
|
7267
|
+
return {
|
|
7268
|
+
name: "mm:metrics",
|
|
7269
|
+
match: "*:*:*",
|
|
7270
|
+
priority: 100,
|
|
7271
|
+
config: {
|
|
7272
|
+
endpoint: { type: "string", default: _endpoint }
|
|
7273
|
+
},
|
|
7274
|
+
async around(ctx, next) {
|
|
7275
|
+
const start = ctx.timestamp;
|
|
7276
|
+
await next();
|
|
7277
|
+
const duration = Date.now() - start;
|
|
7278
|
+
ctx.modify({
|
|
7279
|
+
__metrics: {
|
|
7280
|
+
duration,
|
|
7281
|
+
endpoint: _endpoint,
|
|
7282
|
+
actor: ctx.actor.id
|
|
7283
|
+
}
|
|
7284
|
+
});
|
|
7285
|
+
}
|
|
7286
|
+
};
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7289
|
+
// src/actor.ts
|
|
7290
|
+
function compileTimeOnly(name) {
|
|
7291
|
+
throw new Error(
|
|
7292
|
+
`${name}() is a compile-time function and cannot be called at runtime. Use \`mmrc build\` to compile your workflow.`
|
|
7293
|
+
);
|
|
7294
|
+
}
|
|
7295
|
+
var DEFAULT_SUPERVISION = {
|
|
7296
|
+
strategy: "escalate"
|
|
7297
|
+
};
|
|
7298
|
+
var DEFAULT_MAILBOX = {
|
|
7299
|
+
capacity: 1e3,
|
|
7300
|
+
overflow: "error",
|
|
7301
|
+
priority: "fifo"
|
|
7302
|
+
};
|
|
7303
|
+
function configureActor(config) {
|
|
7304
|
+
void config;
|
|
7305
|
+
return {
|
|
7306
|
+
__actor: true,
|
|
7307
|
+
supervision: config.supervision ?? DEFAULT_SUPERVISION,
|
|
7308
|
+
mailbox: config.mailbox ?? DEFAULT_MAILBOX,
|
|
7309
|
+
hierarchy: config.hierarchy ?? {}
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
function spawnActor(_slug, _options) {
|
|
7313
|
+
compileTimeOnly("spawnActor");
|
|
7314
|
+
}
|
|
7315
|
+
function sendMessage(_instanceId, _action, _payload) {
|
|
7316
|
+
compileTimeOnly("sendMessage");
|
|
7317
|
+
}
|
|
7318
|
+
function isActorConfig(value) {
|
|
7319
|
+
return typeof value === "object" && value !== null && value.__actor === true;
|
|
7320
|
+
}
|
|
7321
|
+
|
|
7322
|
+
// src/constraints.ts
|
|
7323
|
+
var BUILT_IN_CONSTRAINTS = /* @__PURE__ */ new Set([
|
|
7324
|
+
"every state is reachable",
|
|
7325
|
+
"no deadlocks",
|
|
7326
|
+
"no unreachable states",
|
|
7327
|
+
"deterministic guards",
|
|
7328
|
+
"terminates",
|
|
7329
|
+
"no guard overlaps",
|
|
7330
|
+
"all roles defined",
|
|
7331
|
+
"all fields validated"
|
|
7332
|
+
]);
|
|
7333
|
+
function constraints(...rules) {
|
|
7334
|
+
if (process.env.NODE_ENV !== "production") {
|
|
7335
|
+
if (rules.length === 0) {
|
|
7336
|
+
throw new Error("constraints(): at least one constraint rule is required");
|
|
7337
|
+
}
|
|
7338
|
+
for (let i = 0; i < rules.length; i++) {
|
|
7339
|
+
const rule = rules[i];
|
|
7340
|
+
if (typeof rule !== "string" || rule.trim() === "") {
|
|
7341
|
+
throw new Error(
|
|
7342
|
+
`constraints(): rule at index ${i} must be a non-empty string, got ${typeof rule}`
|
|
7343
|
+
);
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
7346
|
+
}
|
|
7347
|
+
return {
|
|
7348
|
+
__constraints: true,
|
|
7349
|
+
rules: Object.freeze([...rules])
|
|
7350
|
+
};
|
|
7351
|
+
}
|
|
7352
|
+
function isConstraintDeclaration(value) {
|
|
7353
|
+
return typeof value === "object" && value !== null && value.__constraints === true && Array.isArray(value.rules);
|
|
7354
|
+
}
|
|
7355
|
+
function isBuiltInConstraint(rule) {
|
|
7356
|
+
return BUILT_IN_CONSTRAINTS.has(rule);
|
|
7357
|
+
}
|
|
7358
|
+
|
|
7359
|
+
// src/extend.ts
|
|
7360
|
+
function extend(base, options) {
|
|
7361
|
+
const _metadata = {
|
|
7362
|
+
__extend: true,
|
|
7363
|
+
base,
|
|
7364
|
+
options
|
|
7365
|
+
};
|
|
7366
|
+
throw new Error(
|
|
7367
|
+
`extend() is a compile-time declaration and cannot be called at runtime. Ensure your build pipeline includes the @mmapp/react-compiler Babel plugin or compile your workflow files with \`mmrc build\` before execution.
|
|
7368
|
+
|
|
7369
|
+
Base workflow: ${base?.name ?? "(anonymous)"}
|
|
7370
|
+
Derived slug: ${options.slug ?? "(not specified)"}`
|
|
7371
|
+
);
|
|
7372
|
+
return _metadata;
|
|
7373
|
+
}
|
|
7374
|
+
|
|
7375
|
+
// src/compose.ts
|
|
7376
|
+
function compose(workflow, ...mixins) {
|
|
7377
|
+
for (let i = 0; i < mixins.length; i++) {
|
|
7378
|
+
const mixin = mixins[i];
|
|
7379
|
+
if (!mixin || mixin.__mixin !== true) {
|
|
7380
|
+
throw new Error(
|
|
7381
|
+
`compose() argument ${i + 2} is not a valid WorkflowMixin. Each mixin must be created by a mixin factory (e.g. withAuditTrail(), withSoftDelete()). Received: ${typeof mixin === "object" ? JSON.stringify(mixin) : String(mixin)}`
|
|
7382
|
+
);
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
const metadata = {
|
|
7386
|
+
__composed: true,
|
|
7387
|
+
workflow,
|
|
7388
|
+
mixins: Object.freeze([...mixins])
|
|
7389
|
+
};
|
|
7390
|
+
const composed = (async (...args) => {
|
|
7391
|
+
return workflow(...args);
|
|
7392
|
+
});
|
|
7393
|
+
Object.defineProperty(composed, "__composed", {
|
|
7394
|
+
value: metadata,
|
|
7395
|
+
writable: false,
|
|
7396
|
+
enumerable: false,
|
|
7397
|
+
configurable: false
|
|
7398
|
+
});
|
|
7399
|
+
Object.defineProperty(composed, "name", {
|
|
7400
|
+
value: `composed(${workflow.name || "anonymous"})`,
|
|
7401
|
+
writable: false,
|
|
7402
|
+
configurable: true
|
|
7403
|
+
});
|
|
7404
|
+
return composed;
|
|
7405
|
+
}
|
|
7406
|
+
|
|
7407
|
+
// src/imperative.ts
|
|
7408
|
+
function compileTimeOnly2(name) {
|
|
7409
|
+
throw new Error(
|
|
7410
|
+
`${name}() is a compile-time function and cannot be called at runtime. Use \`mmrc build\` to compile your workflow.`
|
|
7411
|
+
);
|
|
7412
|
+
}
|
|
7413
|
+
function compileTimeProxy(label) {
|
|
7414
|
+
return new Proxy(
|
|
7415
|
+
{},
|
|
7416
|
+
{
|
|
7417
|
+
get(_target, prop) {
|
|
7418
|
+
throw new Error(
|
|
7419
|
+
`Cannot access \`${label}.${String(prop)}\` at runtime. \`${label}\` is a compile-time constant. Use \`mmrc build\` to compile your workflow.`
|
|
7420
|
+
);
|
|
7421
|
+
}
|
|
7422
|
+
}
|
|
7423
|
+
);
|
|
7424
|
+
}
|
|
7425
|
+
function validate(_condition, _message, _severity) {
|
|
7426
|
+
compileTimeOnly2("validate");
|
|
7427
|
+
}
|
|
7428
|
+
function log(_event, _data) {
|
|
7429
|
+
compileTimeOnly2("log");
|
|
7430
|
+
}
|
|
7431
|
+
function notify2(_to, _message, _opts) {
|
|
7432
|
+
compileTimeOnly2("notify");
|
|
7433
|
+
}
|
|
7434
|
+
function emit(_event, _payload) {
|
|
7435
|
+
compileTimeOnly2("emit");
|
|
7436
|
+
}
|
|
7437
|
+
function requireRole2(..._roles) {
|
|
7438
|
+
compileTimeOnly2("requireRole");
|
|
7439
|
+
}
|
|
7440
|
+
function requireField(..._fields) {
|
|
7441
|
+
compileTimeOnly2("requireField");
|
|
7442
|
+
}
|
|
7443
|
+
function guard(_expression) {
|
|
7444
|
+
compileTimeOnly2("guard");
|
|
7445
|
+
}
|
|
7446
|
+
function every(_interval, _fn) {
|
|
7447
|
+
compileTimeOnly2("every");
|
|
7448
|
+
}
|
|
7449
|
+
function cron2(_expression, _fn) {
|
|
7450
|
+
compileTimeOnly2("cron");
|
|
7451
|
+
}
|
|
7452
|
+
function after(_delay, _fn) {
|
|
7453
|
+
compileTimeOnly2("after");
|
|
7454
|
+
}
|
|
7455
|
+
function on(_pattern, _handlerOrOpts) {
|
|
7456
|
+
compileTimeOnly2("on");
|
|
7457
|
+
}
|
|
7458
|
+
function timeout(_duration, _handler) {
|
|
7459
|
+
compileTimeOnly2("timeout");
|
|
7460
|
+
}
|
|
7461
|
+
function userAction(_name, _opts) {
|
|
7462
|
+
compileTimeOnly2("userAction");
|
|
7463
|
+
}
|
|
7464
|
+
function userChoice(_options) {
|
|
7465
|
+
compileTimeOnly2("userChoice");
|
|
7466
|
+
}
|
|
7467
|
+
function named(_name, _stateCall) {
|
|
7468
|
+
compileTimeOnly2("named");
|
|
7469
|
+
}
|
|
7470
|
+
function delay(_duration) {
|
|
7471
|
+
compileTimeOnly2("delay");
|
|
7472
|
+
}
|
|
7473
|
+
function allowTransition(_name, _opts) {
|
|
7474
|
+
compileTimeOnly2("allowTransition");
|
|
7475
|
+
}
|
|
7476
|
+
function restrict(_field, _opts) {
|
|
7477
|
+
compileTimeOnly2("restrict");
|
|
7478
|
+
}
|
|
7479
|
+
function visibleTo(..._roles) {
|
|
7480
|
+
compileTimeOnly2("visibleTo");
|
|
7481
|
+
}
|
|
7482
|
+
function editableBy(..._roles) {
|
|
7483
|
+
compileTimeOnly2("editableBy");
|
|
7484
|
+
}
|
|
7485
|
+
function editableIn(..._states) {
|
|
7486
|
+
compileTimeOnly2("editableIn");
|
|
7487
|
+
}
|
|
7488
|
+
function defineRoles(roles) {
|
|
7489
|
+
void roles;
|
|
7490
|
+
compileTimeOnly2("defineRoles");
|
|
7491
|
+
}
|
|
7492
|
+
function runtime(_target) {
|
|
7493
|
+
compileTimeOnly2("runtime");
|
|
7494
|
+
}
|
|
7495
|
+
function orchestration(config) {
|
|
7496
|
+
void config;
|
|
7497
|
+
compileTimeOnly2("orchestration");
|
|
7498
|
+
}
|
|
7499
|
+
function blueprint(_slug, _config) {
|
|
7500
|
+
compileTimeOnly2("blueprint");
|
|
7501
|
+
}
|
|
7502
|
+
function patch(_id, _overrides) {
|
|
7503
|
+
compileTimeOnly2("patch");
|
|
7504
|
+
}
|
|
7505
|
+
var actor = compileTimeProxy("actor");
|
|
7506
|
+
var instance = compileTimeProxy("instance");
|
|
7877
7507
|
export {
|
|
7878
7508
|
Accordion,
|
|
7879
7509
|
AnimatedBox,
|
|
7510
|
+
BUILT_IN_CONSTRAINTS,
|
|
7880
7511
|
Badge,
|
|
7881
7512
|
Blueprint,
|
|
7882
7513
|
BrowserPlayer,
|
|
@@ -7903,6 +7534,7 @@ export {
|
|
|
7903
7534
|
Modal,
|
|
7904
7535
|
ModelBuilder,
|
|
7905
7536
|
NavLink,
|
|
7537
|
+
ORCHESTRATION_PRESETS,
|
|
7906
7538
|
PlayerProvider,
|
|
7907
7539
|
RoleGuard,
|
|
7908
7540
|
Route,
|
|
@@ -7926,12 +7558,19 @@ export {
|
|
|
7926
7558
|
WorkflowProvider,
|
|
7927
7559
|
WorkflowRuntime,
|
|
7928
7560
|
action,
|
|
7929
|
-
|
|
7561
|
+
actor,
|
|
7562
|
+
after,
|
|
7563
|
+
allowTransition,
|
|
7564
|
+
and,
|
|
7930
7565
|
applyMixins,
|
|
7931
7566
|
approval,
|
|
7932
7567
|
assertModelValid,
|
|
7933
7568
|
cedar,
|
|
7569
|
+
compose,
|
|
7570
|
+
computeVisibility,
|
|
7571
|
+
configureActor,
|
|
7934
7572
|
connector,
|
|
7573
|
+
constraints,
|
|
7935
7574
|
createActions,
|
|
7936
7575
|
createCRUD,
|
|
7937
7576
|
createLocalDataResolver,
|
|
@@ -7940,16 +7579,26 @@ export {
|
|
|
7940
7579
|
cron,
|
|
7941
7580
|
crud,
|
|
7942
7581
|
defineBlueprint,
|
|
7582
|
+
blueprint as defineImperativeBlueprint,
|
|
7583
|
+
defineMiddleware,
|
|
7943
7584
|
defineModel,
|
|
7944
7585
|
defineModule,
|
|
7586
|
+
defineRoles,
|
|
7945
7587
|
defineWorkspace,
|
|
7588
|
+
delay,
|
|
7946
7589
|
deriveInstanceKey,
|
|
7947
7590
|
deriveInstanceKeySync,
|
|
7948
7591
|
describeModel,
|
|
7949
7592
|
deviceAction,
|
|
7950
7593
|
dmn,
|
|
7594
|
+
editableBy,
|
|
7595
|
+
editableIn,
|
|
7596
|
+
emit,
|
|
7951
7597
|
escalation,
|
|
7598
|
+
every,
|
|
7952
7599
|
expr,
|
|
7600
|
+
extend,
|
|
7601
|
+
extendMiddleware,
|
|
7953
7602
|
field,
|
|
7954
7603
|
fieldContains,
|
|
7955
7604
|
fieldEquals,
|
|
@@ -7964,12 +7613,21 @@ export {
|
|
|
7964
7613
|
getInstalledModule,
|
|
7965
7614
|
getInstalledModules,
|
|
7966
7615
|
graphql,
|
|
7616
|
+
guard,
|
|
7967
7617
|
hasAnyRole,
|
|
7968
7618
|
hasRole,
|
|
7619
|
+
cron2 as imperativeCron,
|
|
7620
|
+
log as imperativeLog,
|
|
7621
|
+
notify2 as imperativeNotify,
|
|
7622
|
+
requireRole2 as imperativeRequireRole,
|
|
7969
7623
|
inState,
|
|
7970
7624
|
inputEquals,
|
|
7971
7625
|
inputRequired,
|
|
7626
|
+
instance,
|
|
7972
7627
|
isActor,
|
|
7628
|
+
isActorConfig,
|
|
7629
|
+
isBuiltInConstraint,
|
|
7630
|
+
isConstraintDeclaration,
|
|
7973
7631
|
isCreator,
|
|
7974
7632
|
isOwner,
|
|
7975
7633
|
isPlayerDebug,
|
|
@@ -7979,19 +7637,28 @@ export {
|
|
|
7979
7637
|
loadExperienceWorkflow,
|
|
7980
7638
|
logEvent,
|
|
7981
7639
|
model,
|
|
7640
|
+
named,
|
|
7982
7641
|
normalizeDefinition,
|
|
7983
|
-
|
|
7642
|
+
not,
|
|
7984
7643
|
notInState,
|
|
7985
7644
|
notify,
|
|
7986
|
-
|
|
7645
|
+
on,
|
|
7646
|
+
or,
|
|
7647
|
+
orchestration,
|
|
7648
|
+
patch,
|
|
7987
7649
|
pipe,
|
|
7988
7650
|
playerLog,
|
|
7989
7651
|
prefetchData,
|
|
7990
7652
|
refHasAnyRole,
|
|
7991
7653
|
refHasRole,
|
|
7992
7654
|
requireAuth,
|
|
7655
|
+
requireField,
|
|
7993
7656
|
requireRole,
|
|
7657
|
+
resolveOrchestration,
|
|
7658
|
+
restrict,
|
|
7994
7659
|
review,
|
|
7660
|
+
runtime,
|
|
7661
|
+
sendMessage,
|
|
7995
7662
|
serverAction,
|
|
7996
7663
|
setAuthResolver,
|
|
7997
7664
|
setChannelTransport,
|
|
@@ -8011,10 +7678,12 @@ export {
|
|
|
8011
7678
|
setServerStateResolver,
|
|
8012
7679
|
setViewResolver,
|
|
8013
7680
|
spawn,
|
|
7681
|
+
spawnActor,
|
|
8014
7682
|
sql,
|
|
8015
7683
|
state,
|
|
8016
7684
|
syncConfigDefaults,
|
|
8017
7685
|
testModel,
|
|
7686
|
+
timeout,
|
|
8018
7687
|
transition,
|
|
8019
7688
|
updateDefinitionConfig,
|
|
8020
7689
|
useAuth,
|
|
@@ -8047,6 +7716,7 @@ export {
|
|
|
8047
7716
|
usePlayer,
|
|
8048
7717
|
usePlayerContext,
|
|
8049
7718
|
usePlayerContextSafe,
|
|
7719
|
+
usePresence,
|
|
8050
7720
|
useQuery,
|
|
8051
7721
|
useRealtimeQuery,
|
|
8052
7722
|
useRole,
|
|
@@ -8059,20 +7729,30 @@ export {
|
|
|
8059
7729
|
useToast,
|
|
8060
7730
|
useTransition,
|
|
8061
7731
|
useView,
|
|
7732
|
+
useVisibility,
|
|
8062
7733
|
useWhileIn,
|
|
8063
7734
|
useWorkflow,
|
|
8064
|
-
|
|
7735
|
+
useState21 as useWorkflowState,
|
|
7736
|
+
userAction,
|
|
7737
|
+
userChoice,
|
|
7738
|
+
validate,
|
|
8065
7739
|
validateExperienceWorkflow,
|
|
8066
7740
|
validateModel,
|
|
7741
|
+
visibleTo,
|
|
8067
7742
|
when,
|
|
7743
|
+
withAuditLog,
|
|
8068
7744
|
withAuditTrail,
|
|
7745
|
+
withAuth,
|
|
7746
|
+
withMetrics,
|
|
8069
7747
|
withOwnership,
|
|
8070
7748
|
withPagination,
|
|
8071
7749
|
withRBAC,
|
|
7750
|
+
withRateLimit,
|
|
8072
7751
|
withSearch,
|
|
8073
7752
|
withSlug,
|
|
8074
7753
|
withSoftDelete,
|
|
8075
7754
|
withTags,
|
|
8076
7755
|
withTimestamps,
|
|
7756
|
+
withValidation,
|
|
8077
7757
|
withVersioning
|
|
8078
7758
|
};
|