@mmapp/react 0.1.0-alpha.18 → 0.1.0-alpha.20
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/actions-HOXZPBTT.mjs +116 -0
- package/dist/actions-MFI2V4DX.mjs +116 -0
- package/dist/atoms/index.d.mts +2 -2
- package/dist/atoms/index.d.ts +2 -2
- package/dist/atoms/index.js +1 -1
- package/dist/atoms/index.mjs +1 -1
- package/dist/builtin-atoms-C-sNyYJl.d.mts +647 -0
- package/dist/builtin-atoms-C-sNyYJl.d.ts +647 -0
- package/dist/builtin-atoms-DCKrjG7i.d.mts +96 -0
- package/dist/builtin-atoms-DCKrjG7i.d.ts +96 -0
- package/dist/builtin-atoms-DRD3EwG6.d.mts +648 -0
- package/dist/builtin-atoms-DRD3EwG6.d.ts +648 -0
- package/dist/builtin-atoms-jt04b7Rw.d.mts +643 -0
- package/dist/builtin-atoms-jt04b7Rw.d.ts +643 -0
- package/dist/chunk-247T4GDJ.mjs +677 -0
- package/dist/chunk-3H6CR7E7.mjs +1924 -0
- package/dist/chunk-3PL6FL6I.mjs +96 -0
- package/dist/chunk-3SJSW3C4.mjs +2039 -0
- package/dist/chunk-5OI2VI57.mjs +1964 -0
- package/dist/chunk-CL6FYZ43.mjs +105 -0
- package/dist/chunk-ENQOCZI5.mjs +1938 -0
- package/dist/chunk-FB3WCZAU.mjs +512 -0
- package/dist/chunk-FBKUGKQI.mjs +1938 -0
- package/dist/chunk-GLJ7VC7Z.mjs +684 -0
- package/dist/chunk-HHMWR6NA.mjs +504 -0
- package/dist/chunk-HULEMSN2.mjs +120 -0
- package/dist/chunk-J5MW6CRU.mjs +1938 -0
- package/dist/chunk-PNTTKNYU.mjs +677 -0
- package/dist/chunk-TY5OTJP4.mjs +684 -0
- package/dist/chunk-WV7DVCP6.mjs +513 -0
- package/dist/chunk-YFMPTGUF.mjs +677 -0
- package/dist/chunk-ZAHMWAER.mjs +1960 -0
- package/dist/{chunk-2VJQJM7S.mjs → chunk-ZDWACXZN.mjs} +1 -1
- package/dist/composition-BJ6QQTWT.mjs +12 -0
- package/dist/composition-XBGKKCI7.mjs +57 -0
- package/dist/content-QVPFUG4P.mjs +246 -0
- package/dist/control-flow-CBREHWJW.mjs +35 -0
- package/dist/control-flow-FWBOI6SM.mjs +35 -0
- package/dist/control-flow-ZWUGCDSP.mjs +35 -0
- package/dist/data-WCMIZYKD.mjs +97 -0
- package/dist/grouping-E6F377VZ.mjs +204 -0
- package/dist/grouping-FRPOEXO3.mjs +233 -0
- package/dist/index.d.mts +4 -433
- package/dist/index.d.ts +4 -433
- package/dist/index.js +3671 -582
- package/dist/index.mjs +335 -1040
- package/dist/input-PUOZDNSI.mjs +222 -0
- package/dist/layout-RATDMCLP.mjs +106 -0
- package/dist/navigation-VCT7ZBMA.mjs +15 -0
- package/dist/navigation-WFV7YWOU.mjs +14 -0
- package/dist/player/index.d.mts +37 -11
- package/dist/player/index.d.ts +37 -11
- package/dist/player/index.js +3321 -193
- package/dist/player/index.mjs +55 -5
- package/package.json +4 -4
|
@@ -0,0 +1,1960 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ScopeContext,
|
|
3
|
+
buildScope,
|
|
4
|
+
builtinFunctions,
|
|
5
|
+
classifyExpression,
|
|
6
|
+
evaluateExpression,
|
|
7
|
+
resolvePath,
|
|
8
|
+
useScope
|
|
9
|
+
} from "./chunk-WV7DVCP6.mjs";
|
|
10
|
+
|
|
11
|
+
// src/hooks/useQuery.ts
|
|
12
|
+
import { useState, useEffect, useCallback, useRef } from "react";
|
|
13
|
+
var _globalResolver = null;
|
|
14
|
+
function setQueryResolver(resolver) {
|
|
15
|
+
_globalResolver = resolver;
|
|
16
|
+
}
|
|
17
|
+
function useQuery(slugOrDef, params = {}) {
|
|
18
|
+
const slug = typeof slugOrDef === "string" ? slugOrDef : slugOrDef.slug;
|
|
19
|
+
const [data, setData] = useState([]);
|
|
20
|
+
const [loading, setLoading] = useState(true);
|
|
21
|
+
const [error, setError] = useState(null);
|
|
22
|
+
const [total, setTotal] = useState(void 0);
|
|
23
|
+
const paramsRef = useRef(params);
|
|
24
|
+
paramsRef.current = params;
|
|
25
|
+
const fetchData = useCallback(async () => {
|
|
26
|
+
if (paramsRef.current.enabled === false) {
|
|
27
|
+
setLoading(false);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const resolver = _globalResolver;
|
|
31
|
+
if (!resolver) {
|
|
32
|
+
setError(new Error(`useQuery: No query resolver configured. Wrap your app in a WorkflowProvider.`));
|
|
33
|
+
setLoading(false);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
setLoading(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
const result2 = await resolver.query(slug, paramsRef.current);
|
|
40
|
+
setData(result2.data);
|
|
41
|
+
setTotal(result2.total);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
44
|
+
} finally {
|
|
45
|
+
setLoading(false);
|
|
46
|
+
}
|
|
47
|
+
}, [slug]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
fetchData();
|
|
50
|
+
}, [fetchData]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!params.refetchInterval || params.refetchInterval <= 0) return;
|
|
53
|
+
const interval = setInterval(fetchData, params.refetchInterval);
|
|
54
|
+
return () => clearInterval(interval);
|
|
55
|
+
}, [fetchData, params.refetchInterval]);
|
|
56
|
+
const hasMore = total !== void 0 && data.length < total;
|
|
57
|
+
const result = {
|
|
58
|
+
data,
|
|
59
|
+
loading,
|
|
60
|
+
error,
|
|
61
|
+
refetch: fetchData,
|
|
62
|
+
total,
|
|
63
|
+
hasMore
|
|
64
|
+
};
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/hooks/useMutation.ts
|
|
69
|
+
import { useState as useState2, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
70
|
+
var _globalMutationResolver = null;
|
|
71
|
+
function setMutationResolver(resolver) {
|
|
72
|
+
_globalMutationResolver = resolver;
|
|
73
|
+
}
|
|
74
|
+
function useMutation(slugOrDef) {
|
|
75
|
+
const slug = typeof slugOrDef === "string" ? slugOrDef : slugOrDef.slug;
|
|
76
|
+
const [isPending, setIsPending] = useState2(false);
|
|
77
|
+
const [error, setError] = useState2(null);
|
|
78
|
+
const slugRef = useRef2(slug);
|
|
79
|
+
slugRef.current = slug;
|
|
80
|
+
const getResolver = useCallback2(() => {
|
|
81
|
+
const resolver = _globalMutationResolver;
|
|
82
|
+
if (!resolver) {
|
|
83
|
+
throw new Error(`useMutation: No mutation resolver configured. Wrap your app in a WorkflowProvider.`);
|
|
84
|
+
}
|
|
85
|
+
return resolver;
|
|
86
|
+
}, []);
|
|
87
|
+
const create = useCallback2(async (input) => {
|
|
88
|
+
try {
|
|
89
|
+
setIsPending(true);
|
|
90
|
+
setError(null);
|
|
91
|
+
const resolver = getResolver();
|
|
92
|
+
return await resolver.create(slugRef.current, input);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
95
|
+
setError(e);
|
|
96
|
+
throw e;
|
|
97
|
+
} finally {
|
|
98
|
+
setIsPending(false);
|
|
99
|
+
}
|
|
100
|
+
}, [getResolver]);
|
|
101
|
+
const update = useCallback2(async (instanceId, fields) => {
|
|
102
|
+
try {
|
|
103
|
+
setIsPending(true);
|
|
104
|
+
setError(null);
|
|
105
|
+
const resolver = getResolver();
|
|
106
|
+
await resolver.update(slugRef.current, instanceId, fields);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
109
|
+
setError(e);
|
|
110
|
+
throw e;
|
|
111
|
+
} finally {
|
|
112
|
+
setIsPending(false);
|
|
113
|
+
}
|
|
114
|
+
}, [getResolver]);
|
|
115
|
+
const transitionFn = useCallback2(async (instanceId, transitionName, input) => {
|
|
116
|
+
try {
|
|
117
|
+
setIsPending(true);
|
|
118
|
+
setError(null);
|
|
119
|
+
const resolver = getResolver();
|
|
120
|
+
await resolver.transition(slugRef.current, instanceId, transitionName, input);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
123
|
+
setError(e);
|
|
124
|
+
throw e;
|
|
125
|
+
} finally {
|
|
126
|
+
setIsPending(false);
|
|
127
|
+
}
|
|
128
|
+
}, [getResolver]);
|
|
129
|
+
const next = useCallback2(async (instanceId, input) => {
|
|
130
|
+
try {
|
|
131
|
+
setIsPending(true);
|
|
132
|
+
setError(null);
|
|
133
|
+
const resolver = getResolver();
|
|
134
|
+
if (resolver.next) {
|
|
135
|
+
await resolver.next(slugRef.current, instanceId, input);
|
|
136
|
+
} else {
|
|
137
|
+
await resolver.transition(slugRef.current, instanceId, "__next__", input);
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
141
|
+
setError(e);
|
|
142
|
+
throw e;
|
|
143
|
+
} finally {
|
|
144
|
+
setIsPending(false);
|
|
145
|
+
}
|
|
146
|
+
}, [getResolver]);
|
|
147
|
+
const remove = useCallback2(async (instanceId) => {
|
|
148
|
+
try {
|
|
149
|
+
setIsPending(true);
|
|
150
|
+
setError(null);
|
|
151
|
+
const resolver = getResolver();
|
|
152
|
+
await resolver.remove(slugRef.current, instanceId);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
155
|
+
setError(e);
|
|
156
|
+
throw e;
|
|
157
|
+
} finally {
|
|
158
|
+
setIsPending(false);
|
|
159
|
+
}
|
|
160
|
+
}, [getResolver]);
|
|
161
|
+
const reset = useCallback2(() => setError(null), []);
|
|
162
|
+
const handle = {
|
|
163
|
+
create,
|
|
164
|
+
update,
|
|
165
|
+
transition: transitionFn,
|
|
166
|
+
trigger: transitionFn,
|
|
167
|
+
next,
|
|
168
|
+
remove,
|
|
169
|
+
isPending,
|
|
170
|
+
error,
|
|
171
|
+
reset
|
|
172
|
+
};
|
|
173
|
+
return handle;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/player/binding-resolver.ts
|
|
177
|
+
function classifyBinding(expr) {
|
|
178
|
+
const t = expr.trim();
|
|
179
|
+
const type = classifyExpression(t);
|
|
180
|
+
switch (type) {
|
|
181
|
+
case "literal":
|
|
182
|
+
return { type, value: parseLiteralValue(t) };
|
|
183
|
+
case "path": {
|
|
184
|
+
const dotIdx = t.indexOf(".");
|
|
185
|
+
if (dotIdx === -1) return { type, root: t, rest: "" };
|
|
186
|
+
return { type, root: t.slice(0, dotIdx), rest: t.slice(dotIdx + 1) };
|
|
187
|
+
}
|
|
188
|
+
case "function": {
|
|
189
|
+
const fnCall = parseFunctionCall(t);
|
|
190
|
+
if (fnCall) return { type, name: fnCall.name, rawArgs: fnCall.args };
|
|
191
|
+
return { type, expression: t };
|
|
192
|
+
}
|
|
193
|
+
case "action": {
|
|
194
|
+
const fnCall = parseFunctionCall(t);
|
|
195
|
+
if (fnCall) return { type, name: fnCall.name, rawArgs: fnCall.args };
|
|
196
|
+
if (t.startsWith("$action.")) return { type, name: t.slice("$action.".length), rawArgs: [] };
|
|
197
|
+
return { type, expression: t };
|
|
198
|
+
}
|
|
199
|
+
case "condition":
|
|
200
|
+
if (t.startsWith("$expr(") && t.endsWith(")")) return { type, expression: t.slice(6, -1) };
|
|
201
|
+
return { type, expression: t };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function parseLiteralValue(t) {
|
|
205
|
+
if (t === "[Expression]" || t.includes("[Expression]")) return void 0;
|
|
206
|
+
if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) return t.slice(1, -1);
|
|
207
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
|
|
208
|
+
if (t === "true") return true;
|
|
209
|
+
if (t === "false") return false;
|
|
210
|
+
if (t === "null") return null;
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
function parseFunctionCall(expr) {
|
|
214
|
+
const match = expr.match(/^\$(fn|action)\.(\w+)\((.*)\)$/s);
|
|
215
|
+
if (!match) return null;
|
|
216
|
+
const [, namespace, name, argsStr] = match;
|
|
217
|
+
const args = argsStr.trim() ? splitArgs(argsStr.trim()) : [];
|
|
218
|
+
return { namespace, name, args };
|
|
219
|
+
}
|
|
220
|
+
function splitArgs(argsStr) {
|
|
221
|
+
const args = [];
|
|
222
|
+
let current = "";
|
|
223
|
+
let depth = 0;
|
|
224
|
+
let inString = null;
|
|
225
|
+
for (let i = 0; i < argsStr.length; i++) {
|
|
226
|
+
const ch = argsStr[i];
|
|
227
|
+
if (inString) {
|
|
228
|
+
current += ch;
|
|
229
|
+
if (ch === inString && argsStr[i - 1] !== "\\") inString = null;
|
|
230
|
+
} else if (ch === '"' || ch === "'") {
|
|
231
|
+
current += ch;
|
|
232
|
+
inString = ch;
|
|
233
|
+
} else if (ch === "(" || ch === "[") {
|
|
234
|
+
current += ch;
|
|
235
|
+
depth++;
|
|
236
|
+
} else if (ch === ")" || ch === "]") {
|
|
237
|
+
current += ch;
|
|
238
|
+
depth--;
|
|
239
|
+
} else if (ch === "," && depth === 0) {
|
|
240
|
+
args.push(current.trim());
|
|
241
|
+
current = "";
|
|
242
|
+
} else {
|
|
243
|
+
current += ch;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (current.trim()) args.push(current.trim());
|
|
247
|
+
return args;
|
|
248
|
+
}
|
|
249
|
+
function resolveBinding(expr, scope) {
|
|
250
|
+
const binding = classifyBinding(expr);
|
|
251
|
+
switch (binding.type) {
|
|
252
|
+
case "literal":
|
|
253
|
+
return binding.value;
|
|
254
|
+
case "path":
|
|
255
|
+
return resolveScopedPath(binding.root, binding.rest, scope);
|
|
256
|
+
case "function":
|
|
257
|
+
return resolveFunction(binding.name, binding.rawArgs, scope);
|
|
258
|
+
case "action":
|
|
259
|
+
return resolveAction(binding.name, binding.rawArgs, scope);
|
|
260
|
+
case "condition":
|
|
261
|
+
return resolveCondition(binding.expression ?? expr, scope);
|
|
262
|
+
default:
|
|
263
|
+
return void 0;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function resolveAllBindings(bindings, scope) {
|
|
267
|
+
const resolved = {};
|
|
268
|
+
for (const [propName, expr] of Object.entries(bindings)) {
|
|
269
|
+
try {
|
|
270
|
+
resolved[propName] = resolveBinding(expr, scope);
|
|
271
|
+
} catch {
|
|
272
|
+
resolved[propName] = void 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return resolved;
|
|
276
|
+
}
|
|
277
|
+
function resolveScopedPath(root, rest, scope) {
|
|
278
|
+
let rootValue;
|
|
279
|
+
switch (root) {
|
|
280
|
+
case "$instance":
|
|
281
|
+
rootValue = scope.$instance;
|
|
282
|
+
break;
|
|
283
|
+
case "$instances":
|
|
284
|
+
rootValue = scope.$instances;
|
|
285
|
+
break;
|
|
286
|
+
case "$definition":
|
|
287
|
+
rootValue = scope.$definition;
|
|
288
|
+
break;
|
|
289
|
+
case "$pagination":
|
|
290
|
+
rootValue = scope.$pagination;
|
|
291
|
+
break;
|
|
292
|
+
case "$entity":
|
|
293
|
+
rootValue = scope.$entity;
|
|
294
|
+
break;
|
|
295
|
+
case "$local":
|
|
296
|
+
rootValue = scope.$local;
|
|
297
|
+
break;
|
|
298
|
+
case "$user":
|
|
299
|
+
rootValue = scope.$user;
|
|
300
|
+
break;
|
|
301
|
+
case "$action":
|
|
302
|
+
rootValue = scope.$action;
|
|
303
|
+
break;
|
|
304
|
+
case "$fn":
|
|
305
|
+
rootValue = scope.$fn;
|
|
306
|
+
break;
|
|
307
|
+
case "$item":
|
|
308
|
+
rootValue = scope.$item;
|
|
309
|
+
break;
|
|
310
|
+
case "$index":
|
|
311
|
+
rootValue = scope.$index;
|
|
312
|
+
break;
|
|
313
|
+
default:
|
|
314
|
+
rootValue = scope[root];
|
|
315
|
+
if (rootValue === void 0) {
|
|
316
|
+
rootValue = scope.$item;
|
|
317
|
+
if (rootValue != null && rest) {
|
|
318
|
+
return resolvePath(`${root}.${rest}`, { [root]: scope.$item });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (!rest) return rootValue;
|
|
323
|
+
if (rootValue == null) return void 0;
|
|
324
|
+
return resolvePath(rest, rootValue);
|
|
325
|
+
}
|
|
326
|
+
function resolveFunction(name, rawArgs, scope) {
|
|
327
|
+
const fn = scope.$fn?.[name] ?? builtinFunctions[name];
|
|
328
|
+
if (typeof fn !== "function") {
|
|
329
|
+
console.warn(`[binding-resolver] Unknown function: $fn.${name}`);
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
const resolvedArgs = rawArgs.map((arg) => resolveBinding(arg.trim(), scope));
|
|
333
|
+
try {
|
|
334
|
+
return fn(...resolvedArgs);
|
|
335
|
+
} catch (err) {
|
|
336
|
+
console.warn(`[binding-resolver] Error calling $fn.${name}:`, err);
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function resolveAction(name, rawArgs, scope) {
|
|
341
|
+
const actions = scope.$action;
|
|
342
|
+
if (!actions) return void 0;
|
|
343
|
+
if (rawArgs.length === 0) {
|
|
344
|
+
const actionFn2 = actions[name];
|
|
345
|
+
if (typeof actionFn2 === "function") return actionFn2;
|
|
346
|
+
return void 0;
|
|
347
|
+
}
|
|
348
|
+
const actionFn = actions[name];
|
|
349
|
+
if (typeof actionFn !== "function") return void 0;
|
|
350
|
+
return (...callArgs) => {
|
|
351
|
+
const resolvedArgs = rawArgs.map((arg) => resolveBinding(arg.trim(), scope));
|
|
352
|
+
return actionFn(...resolvedArgs, ...callArgs);
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function resolveCondition(expression, scope) {
|
|
356
|
+
const context = buildEvalContext(scope);
|
|
357
|
+
return evaluateExpression(expression, context);
|
|
358
|
+
}
|
|
359
|
+
function buildEvalContext(scope) {
|
|
360
|
+
const ctx = {};
|
|
361
|
+
ctx.$instance = scope.$instance;
|
|
362
|
+
ctx.$instances = scope.$instances;
|
|
363
|
+
ctx.$definition = scope.$definition;
|
|
364
|
+
ctx.$pagination = scope.$pagination;
|
|
365
|
+
ctx.$entity = scope.$entity;
|
|
366
|
+
ctx.$local = scope.$local;
|
|
367
|
+
ctx.$user = scope.$user;
|
|
368
|
+
ctx.$action = scope.$action;
|
|
369
|
+
ctx.$fn = scope.$fn;
|
|
370
|
+
ctx.$item = scope.$item;
|
|
371
|
+
ctx.$index = scope.$index;
|
|
372
|
+
if (scope.$instances) ctx.items = scope.$instances;
|
|
373
|
+
if (scope.$instance) {
|
|
374
|
+
ctx.instance = scope.$instance;
|
|
375
|
+
ctx.loading = false;
|
|
376
|
+
}
|
|
377
|
+
if (scope.$item) ctx.item = scope.$item;
|
|
378
|
+
if (scope.$index != null) ctx.index = scope.$index;
|
|
379
|
+
if (scope.state_data) ctx.state_data = scope.state_data;
|
|
380
|
+
if (scope.memory) ctx.memory = scope.memory;
|
|
381
|
+
if (scope.context) ctx.context = scope.context;
|
|
382
|
+
return ctx;
|
|
383
|
+
}
|
|
384
|
+
function buildActionScope(opts) {
|
|
385
|
+
const { resolver, instanceId, slug, setLocal, router, toast, refreshQuery, onEvent } = opts;
|
|
386
|
+
return {
|
|
387
|
+
transition: async (name, id, data) => {
|
|
388
|
+
const targetId = id ?? instanceId;
|
|
389
|
+
if (!targetId) {
|
|
390
|
+
console.warn("[action] transition called without instanceId");
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
await resolver.transition(targetId, name, data);
|
|
394
|
+
refreshQuery?.();
|
|
395
|
+
},
|
|
396
|
+
create: async (targetSlug, data) => {
|
|
397
|
+
const id = await resolver.create(targetSlug || slug || "", data);
|
|
398
|
+
refreshQuery?.();
|
|
399
|
+
return id;
|
|
400
|
+
},
|
|
401
|
+
update: async (id, fields) => {
|
|
402
|
+
await resolver.update(id, fields);
|
|
403
|
+
refreshQuery?.();
|
|
404
|
+
},
|
|
405
|
+
remove: async (id) => {
|
|
406
|
+
await resolver.remove(id);
|
|
407
|
+
refreshQuery?.();
|
|
408
|
+
},
|
|
409
|
+
setLocal: (key, value) => {
|
|
410
|
+
setLocal(key, value);
|
|
411
|
+
},
|
|
412
|
+
navigate: (path) => {
|
|
413
|
+
router?.push(path);
|
|
414
|
+
},
|
|
415
|
+
toast: (message, variant) => {
|
|
416
|
+
toast?.(message, variant);
|
|
417
|
+
},
|
|
418
|
+
refreshQuery: (dataSourceName) => {
|
|
419
|
+
refreshQuery?.(dataSourceName);
|
|
420
|
+
},
|
|
421
|
+
scrollTo: (elementId) => {
|
|
422
|
+
if (typeof document !== "undefined") {
|
|
423
|
+
const el = document.getElementById(elementId);
|
|
424
|
+
el?.scrollIntoView({ behavior: "smooth" });
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
openModal: (modalId) => {
|
|
428
|
+
if (typeof window !== "undefined") {
|
|
429
|
+
window.dispatchEvent(new CustomEvent("player:open_modal", { detail: { id: modalId } }));
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
closeModal: (modalId) => {
|
|
433
|
+
if (typeof window !== "undefined") {
|
|
434
|
+
window.dispatchEvent(new CustomEvent("player:close_modal", { detail: { id: modalId } }));
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
emit: (event, payload) => {
|
|
438
|
+
onEvent?.(event, payload);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/player/atom-registry.ts
|
|
444
|
+
import React from "react";
|
|
445
|
+
function namedExport(loader, exportName) {
|
|
446
|
+
return () => loader().then((mod) => ({ default: mod[exportName] }));
|
|
447
|
+
}
|
|
448
|
+
var AtomRegistryImpl = class _AtomRegistryImpl {
|
|
449
|
+
loaders = /* @__PURE__ */ new Map();
|
|
450
|
+
lazyCache = /* @__PURE__ */ new Map();
|
|
451
|
+
eagerCache = /* @__PURE__ */ new Map();
|
|
452
|
+
register(name, loader) {
|
|
453
|
+
this.loaders.set(name, loader);
|
|
454
|
+
this.lazyCache.delete(name);
|
|
455
|
+
}
|
|
456
|
+
registerEager(name, component) {
|
|
457
|
+
this.eagerCache.set(name, component);
|
|
458
|
+
}
|
|
459
|
+
resolve(name) {
|
|
460
|
+
const eager = this.eagerCache.get(name);
|
|
461
|
+
if (eager) {
|
|
462
|
+
const cached2 = this.lazyCache.get(name);
|
|
463
|
+
if (cached2) return cached2;
|
|
464
|
+
const lazy2 = React.lazy(() => Promise.resolve({ default: eager }));
|
|
465
|
+
this.lazyCache.set(name, lazy2);
|
|
466
|
+
return lazy2;
|
|
467
|
+
}
|
|
468
|
+
const cached = this.lazyCache.get(name);
|
|
469
|
+
if (cached) return cached;
|
|
470
|
+
const loader = this.loaders.get(name);
|
|
471
|
+
if (!loader) return null;
|
|
472
|
+
const lazy = React.lazy(loader);
|
|
473
|
+
this.lazyCache.set(name, lazy);
|
|
474
|
+
return lazy;
|
|
475
|
+
}
|
|
476
|
+
resolveSync(name) {
|
|
477
|
+
return this.eagerCache.get(name) ?? null;
|
|
478
|
+
}
|
|
479
|
+
has(name) {
|
|
480
|
+
return this.loaders.has(name) || this.eagerCache.has(name);
|
|
481
|
+
}
|
|
482
|
+
list() {
|
|
483
|
+
const names = /* @__PURE__ */ new Set();
|
|
484
|
+
for (const k of this.loaders.keys()) names.add(k);
|
|
485
|
+
for (const k of this.eagerCache.keys()) names.add(k);
|
|
486
|
+
return Array.from(names).sort();
|
|
487
|
+
}
|
|
488
|
+
merge(other) {
|
|
489
|
+
const merged = new _AtomRegistryImpl();
|
|
490
|
+
for (const [name, loader] of this.loaders) merged.loaders.set(name, loader);
|
|
491
|
+
for (const [name, comp] of this.eagerCache) merged.eagerCache.set(name, comp);
|
|
492
|
+
for (const name of other.list()) {
|
|
493
|
+
const otherImpl = other;
|
|
494
|
+
if (otherImpl.loaders?.has(name)) {
|
|
495
|
+
merged.loaders.set(name, otherImpl.loaders.get(name));
|
|
496
|
+
}
|
|
497
|
+
if (otherImpl.eagerCache?.has(name)) {
|
|
498
|
+
merged.eagerCache.set(name, otherImpl.eagerCache.get(name));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return merged;
|
|
502
|
+
}
|
|
503
|
+
toRecord() {
|
|
504
|
+
const record = {};
|
|
505
|
+
for (const name of this.eagerCache.keys()) {
|
|
506
|
+
record[name] = this.eagerCache.get(name);
|
|
507
|
+
}
|
|
508
|
+
return record;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
function createCoreAtomRegistry() {
|
|
512
|
+
const registry = new AtomRegistryImpl();
|
|
513
|
+
registry.register("Stack", namedExport(() => import("./layout-RATDMCLP.mjs"), "Stack"));
|
|
514
|
+
registry.register("Row", namedExport(() => import("./layout-RATDMCLP.mjs"), "Row"));
|
|
515
|
+
registry.register("Grid", namedExport(() => import("./layout-RATDMCLP.mjs"), "Grid"));
|
|
516
|
+
registry.register("Column", namedExport(() => import("./layout-RATDMCLP.mjs"), "Column"));
|
|
517
|
+
registry.register("Divider", namedExport(() => import("./layout-RATDMCLP.mjs"), "Divider"));
|
|
518
|
+
registry.register("Spacer", namedExport(() => import("./layout-RATDMCLP.mjs"), "Spacer"));
|
|
519
|
+
registry.register("Text", namedExport(() => import("./content-QVPFUG4P.mjs"), "Text"));
|
|
520
|
+
registry.register("Heading", namedExport(() => import("./content-QVPFUG4P.mjs"), "Heading"));
|
|
521
|
+
registry.register("Field", namedExport(() => import("./content-QVPFUG4P.mjs"), "Field"));
|
|
522
|
+
registry.register("Image", namedExport(() => import("./content-QVPFUG4P.mjs"), "Image"));
|
|
523
|
+
registry.register("Badge", namedExport(() => import("./content-QVPFUG4P.mjs"), "Badge"));
|
|
524
|
+
registry.register("Icon", namedExport(() => import("./content-QVPFUG4P.mjs"), "Icon"));
|
|
525
|
+
registry.register("Button", namedExport(() => import("./actions-HOXZPBTT.mjs"), "Button"));
|
|
526
|
+
registry.register("Link", namedExport(() => import("./actions-HOXZPBTT.mjs"), "Link"));
|
|
527
|
+
registry.register("Show", namedExport(() => import("./control-flow-ZWUGCDSP.mjs"), "Show"));
|
|
528
|
+
registry.register("Each", namedExport(() => import("./control-flow-ZWUGCDSP.mjs"), "Each"));
|
|
529
|
+
registry.register("TextInput", namedExport(() => import("./input-PUOZDNSI.mjs"), "TextInput"));
|
|
530
|
+
registry.register("Input", namedExport(() => import("./input-PUOZDNSI.mjs"), "TextInput"));
|
|
531
|
+
registry.register("Select", namedExport(() => import("./input-PUOZDNSI.mjs"), "Select"));
|
|
532
|
+
registry.register("Toggle", namedExport(() => import("./input-PUOZDNSI.mjs"), "Toggle"));
|
|
533
|
+
registry.register("Slider", namedExport(() => import("./input-PUOZDNSI.mjs"), "Slider"));
|
|
534
|
+
registry.register("Card", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "Card"));
|
|
535
|
+
registry.register("Section", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "Section"));
|
|
536
|
+
registry.register("Tabs", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "Tabs"));
|
|
537
|
+
registry.register("Accordion", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "Accordion"));
|
|
538
|
+
registry.register("Modal", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "Modal"));
|
|
539
|
+
registry.register("Router", namedExport(() => import("./navigation-VCT7ZBMA.mjs"), "Router"));
|
|
540
|
+
registry.register("Route", namedExport(() => import("./navigation-VCT7ZBMA.mjs"), "Route"));
|
|
541
|
+
registry.register("NavLink", namedExport(() => import("./navigation-VCT7ZBMA.mjs"), "NavLink"));
|
|
542
|
+
registry.register("RoleGuard", namedExport(() => import("./navigation-VCT7ZBMA.mjs"), "RoleGuard"));
|
|
543
|
+
registry.register("Slot", namedExport(() => import("./composition-BJ6QQTWT.mjs"), "Slot"));
|
|
544
|
+
registry.register("ModuleOutlet", namedExport(() => import("./composition-BJ6QQTWT.mjs"), "ModuleOutlet"));
|
|
545
|
+
registry.register("Markdown", namedExport(() => import("./content-QVPFUG4P.mjs"), "Markdown"));
|
|
546
|
+
registry.register("ScrollArea", namedExport(() => import("./grouping-FRPOEXO3.mjs"), "ScrollArea"));
|
|
547
|
+
registry.register("Table", namedExport(() => import("./data-WCMIZYKD.mjs"), "Table"));
|
|
548
|
+
registry.register("DataGrid", namedExport(() => import("./data-WCMIZYKD.mjs"), "DataGrid"));
|
|
549
|
+
registry.register("Alert", namedExport(() => import("./content-QVPFUG4P.mjs"), "Alert"));
|
|
550
|
+
registry.register("EmptyState", namedExport(() => import("./content-QVPFUG4P.mjs"), "EmptyState"));
|
|
551
|
+
registry.register("Progress", namedExport(() => import("./content-QVPFUG4P.mjs"), "Progress"));
|
|
552
|
+
registry.register("Separator", namedExport(() => import("./content-QVPFUG4P.mjs"), "Separator"));
|
|
553
|
+
return registry;
|
|
554
|
+
}
|
|
555
|
+
function mergeRegistries(...registries) {
|
|
556
|
+
let merged = new AtomRegistryImpl();
|
|
557
|
+
for (const r of registries) {
|
|
558
|
+
merged = merged.merge(r);
|
|
559
|
+
}
|
|
560
|
+
return merged;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/player/theme.ts
|
|
564
|
+
var DEFAULT_DESIGN_TOKENS = {
|
|
565
|
+
// Colors
|
|
566
|
+
"token:primary": "#3182ce",
|
|
567
|
+
"token:primary-hover": "#2b6cb0",
|
|
568
|
+
"token:primary-light": "#ebf8ff",
|
|
569
|
+
"token:secondary": "#718096",
|
|
570
|
+
"token:secondary-hover": "#4a5568",
|
|
571
|
+
"token:accent": "#805ad5",
|
|
572
|
+
"token:success": "#38a169",
|
|
573
|
+
"token:warning": "#dd6b20",
|
|
574
|
+
"token:error": "#e53e3e",
|
|
575
|
+
"token:info": "#3182ce",
|
|
576
|
+
// Surfaces
|
|
577
|
+
"token:surface": "#ffffff",
|
|
578
|
+
"token:surface-hover": "#f7fafc",
|
|
579
|
+
"token:surface-secondary": "#edf2f7",
|
|
580
|
+
"token:background": "#f7fafc",
|
|
581
|
+
"token:card": "#ffffff",
|
|
582
|
+
"token:overlay": "rgba(0, 0, 0, 0.4)",
|
|
583
|
+
// Borders
|
|
584
|
+
"token:border": "#e2e8f0",
|
|
585
|
+
"token:border-light": "#edf2f7",
|
|
586
|
+
"token:border-focus": "#3182ce",
|
|
587
|
+
// Text
|
|
588
|
+
"token:text": "#1a202c",
|
|
589
|
+
"token:text-secondary": "#718096",
|
|
590
|
+
"token:text-muted": "#a0aec0",
|
|
591
|
+
"token:text-inverse": "#ffffff",
|
|
592
|
+
"token:text-link": "#3182ce",
|
|
593
|
+
// Shadows
|
|
594
|
+
"token:shadow-sm": "0 1px 2px rgba(0, 0, 0, 0.05)",
|
|
595
|
+
"token:shadow": "0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06)",
|
|
596
|
+
"token:shadow-md": "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)",
|
|
597
|
+
"token:shadow-lg": "0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05)",
|
|
598
|
+
// Spacing
|
|
599
|
+
"token:spacing-xs": "4px",
|
|
600
|
+
"token:spacing-sm": "8px",
|
|
601
|
+
"token:spacing-md": "16px",
|
|
602
|
+
"token:spacing-lg": "24px",
|
|
603
|
+
"token:spacing-xl": "32px",
|
|
604
|
+
// Radius
|
|
605
|
+
"token:radius-sm": "4px",
|
|
606
|
+
"token:radius": "6px",
|
|
607
|
+
"token:radius-md": "8px",
|
|
608
|
+
"token:radius-lg": "12px",
|
|
609
|
+
"token:radius-full": "9999px"
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// src/player/PlayerProvider.tsx
|
|
613
|
+
import { createContext, useContext, useEffect as useEffect2, useMemo, useRef as useRef3 } from "react";
|
|
614
|
+
import { jsx } from "react/jsx-runtime";
|
|
615
|
+
var PlayerContext = createContext(null);
|
|
616
|
+
function usePlayerContext() {
|
|
617
|
+
return useContext(PlayerContext);
|
|
618
|
+
}
|
|
619
|
+
var ThemeContext = createContext(DEFAULT_DESIGN_TOKENS);
|
|
620
|
+
function useTheme() {
|
|
621
|
+
return useContext(ThemeContext);
|
|
622
|
+
}
|
|
623
|
+
var PlayerProvider = ({
|
|
624
|
+
resolver,
|
|
625
|
+
atomRegistry: userAtomRegistry,
|
|
626
|
+
auth,
|
|
627
|
+
router,
|
|
628
|
+
toast,
|
|
629
|
+
onEvent,
|
|
630
|
+
theme: userTheme,
|
|
631
|
+
children
|
|
632
|
+
}) => {
|
|
633
|
+
const atomRegistry = useMemo(
|
|
634
|
+
() => userAtomRegistry ?? createCoreAtomRegistry(),
|
|
635
|
+
[userAtomRegistry]
|
|
636
|
+
);
|
|
637
|
+
const mergedTheme = useMemo(
|
|
638
|
+
() => userTheme ? { ...DEFAULT_DESIGN_TOKENS, ...userTheme } : DEFAULT_DESIGN_TOKENS,
|
|
639
|
+
[userTheme]
|
|
640
|
+
);
|
|
641
|
+
const resolverRef = useRef3(resolver);
|
|
642
|
+
resolverRef.current = resolver;
|
|
643
|
+
useEffect2(() => {
|
|
644
|
+
if (!resolver) return;
|
|
645
|
+
setQueryResolver({
|
|
646
|
+
query: async (_slug, params) => {
|
|
647
|
+
const r = resolverRef.current;
|
|
648
|
+
if (!r) return { data: [] };
|
|
649
|
+
const result = await r.query(_slug, {
|
|
650
|
+
state: params.state,
|
|
651
|
+
filter: params.filter,
|
|
652
|
+
limit: params.limit,
|
|
653
|
+
offset: params.offset,
|
|
654
|
+
search: params.search,
|
|
655
|
+
searchFields: params.searchFields,
|
|
656
|
+
sort: params.sort
|
|
657
|
+
});
|
|
658
|
+
return { data: result.data, total: result.total };
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
setMutationResolver({
|
|
662
|
+
create: async (_slug, input) => {
|
|
663
|
+
const r = resolverRef.current;
|
|
664
|
+
if (!r) throw new Error("No resolver");
|
|
665
|
+
return r.create(_slug, input);
|
|
666
|
+
},
|
|
667
|
+
update: async (_slug, instanceId, fields) => {
|
|
668
|
+
const r = resolverRef.current;
|
|
669
|
+
if (!r) throw new Error("No resolver");
|
|
670
|
+
await r.update(instanceId, fields);
|
|
671
|
+
},
|
|
672
|
+
transition: async (_slug, instanceId, transitionName, input) => {
|
|
673
|
+
const r = resolverRef.current;
|
|
674
|
+
if (!r) throw new Error("No resolver");
|
|
675
|
+
await r.transition(instanceId, transitionName, input);
|
|
676
|
+
},
|
|
677
|
+
remove: async (_slug, instanceId) => {
|
|
678
|
+
const r = resolverRef.current;
|
|
679
|
+
if (!r) throw new Error("No resolver");
|
|
680
|
+
await r.remove(instanceId);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
return () => {
|
|
684
|
+
setQueryResolver(null);
|
|
685
|
+
setMutationResolver(null);
|
|
686
|
+
};
|
|
687
|
+
}, [resolver]);
|
|
688
|
+
const value = useMemo(() => ({
|
|
689
|
+
resolver: resolver ?? null,
|
|
690
|
+
atomRegistry,
|
|
691
|
+
auth,
|
|
692
|
+
router,
|
|
693
|
+
toast,
|
|
694
|
+
onEvent
|
|
695
|
+
}), [resolver, atomRegistry, auth, router, toast, onEvent]);
|
|
696
|
+
return /* @__PURE__ */ jsx(PlayerContext.Provider, { value, children: /* @__PURE__ */ jsx(ThemeContext.Provider, { value: mergedTheme, children }) });
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/player/ExperienceRenderer.tsx
|
|
700
|
+
import React3, {
|
|
701
|
+
useState as useState3,
|
|
702
|
+
useEffect as useEffect3,
|
|
703
|
+
useCallback as useCallback3,
|
|
704
|
+
useMemo as useMemo2,
|
|
705
|
+
useRef as useRef4,
|
|
706
|
+
Suspense
|
|
707
|
+
} from "react";
|
|
708
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
709
|
+
var SharedLocalContext = React3.createContext({
|
|
710
|
+
state: {},
|
|
711
|
+
setLocal: () => {
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
function useSharedLocal() {
|
|
715
|
+
return React3.useContext(SharedLocalContext);
|
|
716
|
+
}
|
|
717
|
+
var NodeErrorBoundary = class extends React3.Component {
|
|
718
|
+
constructor(props) {
|
|
719
|
+
super(props);
|
|
720
|
+
this.state = { hasError: false };
|
|
721
|
+
}
|
|
722
|
+
static getDerivedStateFromError(error) {
|
|
723
|
+
return { hasError: true, error };
|
|
724
|
+
}
|
|
725
|
+
componentDidCatch(error, info) {
|
|
726
|
+
console.error(`[ExperienceRenderer] Error in node ${this.props.nodeId}:`, error, info);
|
|
727
|
+
}
|
|
728
|
+
render() {
|
|
729
|
+
if (this.state.hasError) {
|
|
730
|
+
return this.props.fallback ?? /* @__PURE__ */ jsxs("div", { style: { padding: 8, background: "#fff5f5", border: "1px solid #fed7d7", borderRadius: 6, fontSize: 12, color: "#c53030" }, children: [
|
|
731
|
+
"Error: ",
|
|
732
|
+
this.state.error?.message ?? "Render failed"
|
|
733
|
+
] });
|
|
734
|
+
}
|
|
735
|
+
return this.props.children;
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
function useDataSources(dataSources, resolver, scope) {
|
|
739
|
+
const [results, setResults] = useState3({});
|
|
740
|
+
const mountedRef = useRef4(true);
|
|
741
|
+
useEffect3(() => {
|
|
742
|
+
mountedRef.current = true;
|
|
743
|
+
return () => {
|
|
744
|
+
mountedRef.current = false;
|
|
745
|
+
};
|
|
746
|
+
}, []);
|
|
747
|
+
useEffect3(() => {
|
|
748
|
+
if (!dataSources?.length || !resolver) return;
|
|
749
|
+
const loading = {};
|
|
750
|
+
for (const ds of dataSources) {
|
|
751
|
+
loading[ds.name] = { loading: true, error: null };
|
|
752
|
+
}
|
|
753
|
+
setResults(loading);
|
|
754
|
+
const fetchAll = async () => {
|
|
755
|
+
const entries = await Promise.all(
|
|
756
|
+
dataSources.map(async (ds) => {
|
|
757
|
+
try {
|
|
758
|
+
const result = await fetchDataSource(ds, resolver, scope);
|
|
759
|
+
return [ds.name, result];
|
|
760
|
+
} catch (err) {
|
|
761
|
+
return [ds.name, { loading: false, error: err instanceof Error ? err : new Error(String(err)) }];
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
);
|
|
765
|
+
if (mountedRef.current) {
|
|
766
|
+
setResults(Object.fromEntries(entries));
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
fetchAll();
|
|
770
|
+
}, [dataSources, resolver]);
|
|
771
|
+
return results;
|
|
772
|
+
}
|
|
773
|
+
async function fetchDataSource(ds, resolver, scope) {
|
|
774
|
+
switch (ds.type) {
|
|
775
|
+
case "workflow": {
|
|
776
|
+
const slug = ds.slug ?? "";
|
|
777
|
+
if (!slug) return { loading: false, error: new Error("workflow dataSource missing slug") };
|
|
778
|
+
const queryResult = await resolver.query(slug, {
|
|
779
|
+
state: ds.filter?.current_state,
|
|
780
|
+
filter: ds.filter,
|
|
781
|
+
limit: ds.pageSize ?? 20,
|
|
782
|
+
page: scope.$local?.[ds.pageKey ?? "page"] ?? 1,
|
|
783
|
+
search: ds.search ? interpolateTemplate(ds.search, scope) : void 0,
|
|
784
|
+
searchFields: ds.searchFields,
|
|
785
|
+
sort: ds.sort,
|
|
786
|
+
facets: ds.facets,
|
|
787
|
+
entity: ds.entity,
|
|
788
|
+
includeDefinition: ds.includeDefinition,
|
|
789
|
+
parentInstanceId: ds.parentInstanceId ? interpolateTemplate(ds.parentInstanceId, scope) : void 0
|
|
790
|
+
});
|
|
791
|
+
const result = {
|
|
792
|
+
instances: queryResult.data,
|
|
793
|
+
loading: false,
|
|
794
|
+
error: null
|
|
795
|
+
};
|
|
796
|
+
if (queryResult.data.length === 1 && ds.query !== "list") {
|
|
797
|
+
result.instance = queryResult.data[0];
|
|
798
|
+
}
|
|
799
|
+
if (queryResult.definition) result.definition = queryResult.definition;
|
|
800
|
+
if (queryResult.total != null) {
|
|
801
|
+
result.pagination = {
|
|
802
|
+
total: queryResult.total,
|
|
803
|
+
page: queryResult.page,
|
|
804
|
+
pageSize: queryResult.pageSize
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (queryResult.facets) result.facets = queryResult.facets;
|
|
808
|
+
if (queryResult.aggregates) result.aggregates = queryResult.aggregates;
|
|
809
|
+
if (ds.autoStart && queryResult.data.length === 0) {
|
|
810
|
+
try {
|
|
811
|
+
const id = await resolver.create(slug, ds.initialData);
|
|
812
|
+
const instance = await resolver.getInstance(id);
|
|
813
|
+
result.instance = instance;
|
|
814
|
+
result.instances = [instance];
|
|
815
|
+
} catch {
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
case "api": {
|
|
821
|
+
if (!resolver.fetch) return { loading: false, error: new Error("API datasource: resolver.fetch not implemented") };
|
|
822
|
+
const endpoint = interpolateTemplate(ds.endpoint, scope);
|
|
823
|
+
const raw = await resolver.fetch(endpoint, {
|
|
824
|
+
method: ds.method,
|
|
825
|
+
body: ds.body,
|
|
826
|
+
headers: ds.headers
|
|
827
|
+
});
|
|
828
|
+
const mapped = mapApiResponse(raw, ds.mapping);
|
|
829
|
+
return { instances: mapped.items, loading: false, error: null, pagination: mapped.pagination };
|
|
830
|
+
}
|
|
831
|
+
case "static": {
|
|
832
|
+
const data = ds.data;
|
|
833
|
+
if (Array.isArray(data)) {
|
|
834
|
+
return {
|
|
835
|
+
instances: data.map((d, i) => ({
|
|
836
|
+
id: String(d.id ?? i),
|
|
837
|
+
state: "static",
|
|
838
|
+
fields: d
|
|
839
|
+
})),
|
|
840
|
+
loading: false,
|
|
841
|
+
error: null
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
return {
|
|
845
|
+
instance: { id: "static", state: "static", fields: data },
|
|
846
|
+
loading: false,
|
|
847
|
+
error: null
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
case "ref": {
|
|
851
|
+
const ctx = buildEvalContext(scope);
|
|
852
|
+
const resolved = evaluateExpression(ds.expression, ctx);
|
|
853
|
+
if (Array.isArray(resolved)) {
|
|
854
|
+
return {
|
|
855
|
+
instances: resolved.map((item, i) => ({
|
|
856
|
+
id: String(item?.id ?? i),
|
|
857
|
+
state: String(item?.state ?? "ref"),
|
|
858
|
+
fields: item && typeof item === "object" ? item : { value: item }
|
|
859
|
+
})),
|
|
860
|
+
loading: false,
|
|
861
|
+
error: null
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
if (resolved != null && typeof resolved === "object") {
|
|
865
|
+
const obj = resolved;
|
|
866
|
+
return {
|
|
867
|
+
instance: {
|
|
868
|
+
id: String(obj.id ?? "ref"),
|
|
869
|
+
state: String(obj.state ?? "ref"),
|
|
870
|
+
fields: obj
|
|
871
|
+
},
|
|
872
|
+
loading: false,
|
|
873
|
+
error: null
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
return { loading: false, error: null };
|
|
877
|
+
}
|
|
878
|
+
default:
|
|
879
|
+
return { loading: false, error: new Error(`Unknown dataSource type: ${ds.type}`) };
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function interpolateTemplate(template, scope) {
|
|
883
|
+
return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, path) => {
|
|
884
|
+
const val = resolveBinding(`$${path}`, scope) ?? resolveBinding(path, scope);
|
|
885
|
+
return val != null ? String(val) : "";
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
function mapApiResponse(raw, mapping) {
|
|
889
|
+
if (!raw || typeof raw !== "object") return { items: [] };
|
|
890
|
+
const obj = raw;
|
|
891
|
+
let items;
|
|
892
|
+
if (mapping?.items || mapping?.data) {
|
|
893
|
+
items = obj[mapping.items ?? mapping.data ?? "data"] ?? [];
|
|
894
|
+
} else if (Array.isArray(raw)) {
|
|
895
|
+
items = raw;
|
|
896
|
+
} else if (Array.isArray(obj.data)) {
|
|
897
|
+
items = obj.data;
|
|
898
|
+
} else if (Array.isArray(obj.items)) {
|
|
899
|
+
items = obj.items;
|
|
900
|
+
} else {
|
|
901
|
+
items = [raw];
|
|
902
|
+
}
|
|
903
|
+
const result = items.map((item, i) => ({
|
|
904
|
+
id: String(item?.id ?? i),
|
|
905
|
+
state: String(item?.state ?? "unknown"),
|
|
906
|
+
fields: item ?? {}
|
|
907
|
+
}));
|
|
908
|
+
let pagination;
|
|
909
|
+
const total = mapping?.total ? obj[mapping.total] : obj.total;
|
|
910
|
+
if (total != null) {
|
|
911
|
+
pagination = { total: Number(total), page: Number(obj.page ?? 1), pageSize: Number(obj.pageSize ?? items.length) };
|
|
912
|
+
}
|
|
913
|
+
return { items: result, pagination };
|
|
914
|
+
}
|
|
915
|
+
function resolveTokens(value, tokens) {
|
|
916
|
+
if (typeof value === "string" && value.startsWith("token:")) {
|
|
917
|
+
return tokens[value] ?? value;
|
|
918
|
+
}
|
|
919
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
920
|
+
const resolved = {};
|
|
921
|
+
for (const [k, v] of Object.entries(value)) {
|
|
922
|
+
resolved[k] = resolveTokens(v, tokens);
|
|
923
|
+
}
|
|
924
|
+
return resolved;
|
|
925
|
+
}
|
|
926
|
+
return value;
|
|
927
|
+
}
|
|
928
|
+
var NodeRenderer = ({ node, fallback }) => {
|
|
929
|
+
const parentScope = useScope();
|
|
930
|
+
const playerCtx = usePlayerContext();
|
|
931
|
+
const resolver = playerCtx?.resolver ?? null;
|
|
932
|
+
const atomRegistry = playerCtx?.atomRegistry;
|
|
933
|
+
const themeTokens = useTheme();
|
|
934
|
+
const sharedLocal = useSharedLocal();
|
|
935
|
+
const localDefaults = node.config?.localDefaults ?? {};
|
|
936
|
+
useEffect3(() => {
|
|
937
|
+
if (Object.keys(localDefaults).length > 0) {
|
|
938
|
+
for (const [key, value] of Object.entries(localDefaults)) {
|
|
939
|
+
if (sharedLocal.state[key] === void 0) {
|
|
940
|
+
sharedLocal.setLocal(key, value);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}, []);
|
|
945
|
+
const localState = sharedLocal.state;
|
|
946
|
+
const handleSetLocal = sharedLocal.setLocal;
|
|
947
|
+
const [, setFetchVersion] = useState3(0);
|
|
948
|
+
const handleRefreshQuery = useCallback3(() => {
|
|
949
|
+
setFetchVersion((v) => v + 1);
|
|
950
|
+
}, []);
|
|
951
|
+
const dsResults = useDataSources(node.dataSources, resolver, parentScope);
|
|
952
|
+
const primaryInstance = Object.values(dsResults).find((r) => r.instance)?.instance;
|
|
953
|
+
const primarySlug = node.dataSources?.find((ds) => ds.type === "workflow")?.slug;
|
|
954
|
+
const actionScope = useMemo2(() => {
|
|
955
|
+
if (!resolver) {
|
|
956
|
+
return {
|
|
957
|
+
transition: async () => {
|
|
958
|
+
},
|
|
959
|
+
create: async () => "",
|
|
960
|
+
update: async () => {
|
|
961
|
+
},
|
|
962
|
+
remove: async () => {
|
|
963
|
+
},
|
|
964
|
+
setLocal: handleSetLocal,
|
|
965
|
+
navigate: () => {
|
|
966
|
+
},
|
|
967
|
+
toast: () => {
|
|
968
|
+
},
|
|
969
|
+
refreshQuery: handleRefreshQuery,
|
|
970
|
+
scrollTo: () => {
|
|
971
|
+
},
|
|
972
|
+
openModal: () => {
|
|
973
|
+
},
|
|
974
|
+
closeModal: () => {
|
|
975
|
+
},
|
|
976
|
+
emit: () => {
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
return buildActionScope({
|
|
981
|
+
resolver,
|
|
982
|
+
instanceId: primaryInstance?.id,
|
|
983
|
+
slug: primarySlug,
|
|
984
|
+
setLocal: handleSetLocal,
|
|
985
|
+
router: playerCtx?.router,
|
|
986
|
+
toast: playerCtx?.toast,
|
|
987
|
+
refreshQuery: handleRefreshQuery,
|
|
988
|
+
onEvent: playerCtx?.onEvent
|
|
989
|
+
});
|
|
990
|
+
}, [resolver, primaryInstance?.id, primarySlug, handleSetLocal, handleRefreshQuery, playerCtx]);
|
|
991
|
+
const scope = useMemo2(() => buildScope({
|
|
992
|
+
dataSources: dsResults,
|
|
993
|
+
localState,
|
|
994
|
+
auth: playerCtx?.auth,
|
|
995
|
+
entity: node.dataSources?.find((ds) => ds.type === "workflow")?.entity,
|
|
996
|
+
parentScope,
|
|
997
|
+
actionScope
|
|
998
|
+
}), [dsResults, localState, parentScope, actionScope, playerCtx?.auth]);
|
|
999
|
+
const enrichedScope = useMemo2(() => {
|
|
1000
|
+
const handleCreate = () => {
|
|
1001
|
+
if (primarySlug && resolver) {
|
|
1002
|
+
resolver.create(primarySlug).then(() => handleRefreshQuery());
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
const setSearch = (e) => {
|
|
1006
|
+
const val = typeof e === "object" && e !== null && "target" in e ? e.target.value : String(e ?? "");
|
|
1007
|
+
handleSetLocal("search", val);
|
|
1008
|
+
};
|
|
1009
|
+
const enrichedAction = {
|
|
1010
|
+
...scope.$action,
|
|
1011
|
+
handleCreate,
|
|
1012
|
+
setSearch
|
|
1013
|
+
};
|
|
1014
|
+
const enrichedInstance = scope.$instance ? { ...scope.$instance, handleCreate, setSearch } : void 0;
|
|
1015
|
+
return {
|
|
1016
|
+
...scope,
|
|
1017
|
+
$action: enrichedAction,
|
|
1018
|
+
$instance: enrichedInstance,
|
|
1019
|
+
// Also add at top-level for direct access
|
|
1020
|
+
handleCreate,
|
|
1021
|
+
setSearch
|
|
1022
|
+
};
|
|
1023
|
+
}, [scope, primarySlug, resolver, handleRefreshQuery, handleSetLocal]);
|
|
1024
|
+
if (node.visible_when) {
|
|
1025
|
+
const ctx = buildEvalContext(enrichedScope);
|
|
1026
|
+
const visible = evaluateExpression(node.visible_when, ctx);
|
|
1027
|
+
if (!visible) return null;
|
|
1028
|
+
}
|
|
1029
|
+
if (node.$if) {
|
|
1030
|
+
const ctx = buildEvalContext(enrichedScope);
|
|
1031
|
+
const visible = evaluateExpression(node.$if, ctx);
|
|
1032
|
+
if (!visible) return null;
|
|
1033
|
+
}
|
|
1034
|
+
const resolvedBindings = node.bindings ? resolveAllBindings(node.bindings, enrichedScope) : {};
|
|
1035
|
+
const config = node.config ?? node.props ?? {};
|
|
1036
|
+
const rawMerged = { ...config, ...resolvedBindings };
|
|
1037
|
+
const mergedProps = {};
|
|
1038
|
+
for (const [k, v] of Object.entries(rawMerged)) {
|
|
1039
|
+
mergedProps[k] = resolveTokens(v, themeTokens);
|
|
1040
|
+
}
|
|
1041
|
+
if (node.className) mergedProps.className = node.className;
|
|
1042
|
+
if (node.id) mergedProps["data-node-id"] = node.id;
|
|
1043
|
+
const loadingAny = Object.values(dsResults).some((r) => r.loading);
|
|
1044
|
+
if (Object.keys(dsResults).length > 0) {
|
|
1045
|
+
mergedProps.loading = loadingAny;
|
|
1046
|
+
const firstResult = Object.values(dsResults)[0];
|
|
1047
|
+
if (firstResult?.instances) mergedProps.data = firstResult.instances;
|
|
1048
|
+
if (firstResult?.instance) mergedProps.instance = firstResult.instance;
|
|
1049
|
+
if (firstResult?.pagination) mergedProps.pagination = firstResult.pagination;
|
|
1050
|
+
if (firstResult?.definition) mergedProps.definition = firstResult.definition;
|
|
1051
|
+
}
|
|
1052
|
+
const componentName = node.component ?? node.type ?? "";
|
|
1053
|
+
let AtomComponent = null;
|
|
1054
|
+
if (atomRegistry) {
|
|
1055
|
+
const lazy = atomRegistry.resolve(componentName);
|
|
1056
|
+
if (lazy) AtomComponent = lazy;
|
|
1057
|
+
}
|
|
1058
|
+
if (!AtomComponent) {
|
|
1059
|
+
if (!componentName && node.children?.length) {
|
|
1060
|
+
return /* @__PURE__ */ jsx2(ScopeContext.Provider, { value: enrichedScope, children: /* @__PURE__ */ jsx2("div", { className: node.className, style: node.style, children: node.children.map((child, i) => /* @__PURE__ */ jsx2(NodeErrorBoundary, { nodeId: child.id, children: /* @__PURE__ */ jsx2(NodeRenderer, { node: child, fallback }) }, child.id ?? i)) }) });
|
|
1061
|
+
}
|
|
1062
|
+
if (componentName) {
|
|
1063
|
+
return /* @__PURE__ */ jsxs("div", { style: { border: "1px dashed #e53e3e", borderRadius: 6, padding: "8px 12px", margin: 4, fontSize: 13, color: "#e53e3e", background: "#fff5f5" }, children: [
|
|
1064
|
+
"Unknown: ",
|
|
1065
|
+
componentName
|
|
1066
|
+
] });
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
1070
|
+
let children = null;
|
|
1071
|
+
if (node.children?.length) {
|
|
1072
|
+
children = node.children.map((child, i) => /* @__PURE__ */ jsx2(NodeErrorBoundary, { nodeId: child.id, children: /* @__PURE__ */ jsx2(NodeRenderer, { node: child, fallback }) }, child.id ?? i));
|
|
1073
|
+
}
|
|
1074
|
+
if (!children && mergedProps.children != null) {
|
|
1075
|
+
children = mergedProps.children;
|
|
1076
|
+
delete mergedProps.children;
|
|
1077
|
+
}
|
|
1078
|
+
delete mergedProps.localDefaults;
|
|
1079
|
+
delete mergedProps.dataSources;
|
|
1080
|
+
delete mergedProps.bindings;
|
|
1081
|
+
delete mergedProps.visible_when;
|
|
1082
|
+
return /* @__PURE__ */ jsx2(ScopeContext.Provider, { value: enrichedScope, children: /* @__PURE__ */ jsx2(NodeErrorBoundary, { nodeId: node.id, fallback, children: /* @__PURE__ */ jsx2(Suspense, { fallback: /* @__PURE__ */ jsxs("div", { style: { padding: 8, color: "#a0aec0", fontSize: 13 }, children: [
|
|
1083
|
+
"Loading ",
|
|
1084
|
+
componentName,
|
|
1085
|
+
"..."
|
|
1086
|
+
] }), children: /* @__PURE__ */ jsx2(AtomComponent, { ...mergedProps, children }) }) }) });
|
|
1087
|
+
};
|
|
1088
|
+
var ExperienceRenderer = ({
|
|
1089
|
+
tree,
|
|
1090
|
+
initialScope,
|
|
1091
|
+
entity,
|
|
1092
|
+
fallback,
|
|
1093
|
+
className
|
|
1094
|
+
}) => {
|
|
1095
|
+
const playerCtx = usePlayerContext();
|
|
1096
|
+
const nodes = Array.isArray(tree) ? tree : [tree];
|
|
1097
|
+
const [sharedLocalState, setSharedLocalState] = useState3({});
|
|
1098
|
+
const handleSharedSetLocal = useCallback3((key, value) => {
|
|
1099
|
+
setSharedLocalState((prev) => ({ ...prev, [key]: value }));
|
|
1100
|
+
}, []);
|
|
1101
|
+
const sharedLocal = useMemo2(() => ({
|
|
1102
|
+
state: sharedLocalState,
|
|
1103
|
+
setLocal: handleSharedSetLocal
|
|
1104
|
+
}), [sharedLocalState, handleSharedSetLocal]);
|
|
1105
|
+
const rootScope = useMemo2(() => buildScope({
|
|
1106
|
+
auth: playerCtx?.auth ?? initialScope?.auth,
|
|
1107
|
+
entity,
|
|
1108
|
+
localState: initialScope?.$local ?? {},
|
|
1109
|
+
actionScope: initialScope?.$action ?? {
|
|
1110
|
+
transition: async () => {
|
|
1111
|
+
},
|
|
1112
|
+
create: async () => "",
|
|
1113
|
+
update: async () => {
|
|
1114
|
+
},
|
|
1115
|
+
remove: async () => {
|
|
1116
|
+
},
|
|
1117
|
+
setLocal: () => {
|
|
1118
|
+
},
|
|
1119
|
+
navigate: () => {
|
|
1120
|
+
},
|
|
1121
|
+
toast: () => {
|
|
1122
|
+
},
|
|
1123
|
+
refreshQuery: () => {
|
|
1124
|
+
},
|
|
1125
|
+
scrollTo: () => {
|
|
1126
|
+
},
|
|
1127
|
+
openModal: () => {
|
|
1128
|
+
},
|
|
1129
|
+
closeModal: () => {
|
|
1130
|
+
},
|
|
1131
|
+
emit: () => {
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}), [playerCtx?.auth, entity, initialScope]);
|
|
1135
|
+
return /* @__PURE__ */ jsx2(SharedLocalContext.Provider, { value: sharedLocal, children: /* @__PURE__ */ jsx2(ScopeContext.Provider, { value: rootScope, children: /* @__PURE__ */ jsx2("div", { className, children: nodes.map((node, i) => /* @__PURE__ */ jsx2(NodeErrorBoundary, { nodeId: node.id, children: /* @__PURE__ */ jsx2(NodeRenderer, { node, fallback }) }, node.id ?? i)) }) }) });
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// src/player/resolver.ts
|
|
1139
|
+
function getToken(token) {
|
|
1140
|
+
if (!token) return null;
|
|
1141
|
+
if (typeof token === "function") return token();
|
|
1142
|
+
return token;
|
|
1143
|
+
}
|
|
1144
|
+
function normalizeInstance(raw) {
|
|
1145
|
+
return {
|
|
1146
|
+
id: String(raw.id ?? ""),
|
|
1147
|
+
state: String(raw.current_state ?? raw.state ?? "unknown"),
|
|
1148
|
+
fields: raw.state_data ?? raw.fields ?? {},
|
|
1149
|
+
slug: raw.definition_slug,
|
|
1150
|
+
created_at: raw.created_at,
|
|
1151
|
+
updated_at: raw.updated_at,
|
|
1152
|
+
...raw
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function createApiResolver(config) {
|
|
1156
|
+
const { baseUrl, defaults } = config;
|
|
1157
|
+
const fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
1158
|
+
const defCache = /* @__PURE__ */ new Map();
|
|
1159
|
+
async function apiRequest(path, options = {}) {
|
|
1160
|
+
const tok = getToken(config.token);
|
|
1161
|
+
const headers = {
|
|
1162
|
+
"Content-Type": "application/json",
|
|
1163
|
+
...options.headers ?? {}
|
|
1164
|
+
};
|
|
1165
|
+
if (tok) headers["Authorization"] = `Bearer ${tok}`;
|
|
1166
|
+
const response = await fetchFn(`${baseUrl}${path}`, {
|
|
1167
|
+
...options,
|
|
1168
|
+
headers
|
|
1169
|
+
});
|
|
1170
|
+
if (!response.ok) {
|
|
1171
|
+
const body = await response.text().catch(() => "");
|
|
1172
|
+
let message = `API error ${response.status}: ${response.statusText}`;
|
|
1173
|
+
try {
|
|
1174
|
+
const parsed = JSON.parse(body);
|
|
1175
|
+
if (parsed.message) message = parsed.message;
|
|
1176
|
+
if (parsed.error) message = `${parsed.error}: ${parsed.message || ""}`;
|
|
1177
|
+
} catch {
|
|
1178
|
+
}
|
|
1179
|
+
throw new Error(message);
|
|
1180
|
+
}
|
|
1181
|
+
return response.json();
|
|
1182
|
+
}
|
|
1183
|
+
const resolver = {
|
|
1184
|
+
async query(slug, params) {
|
|
1185
|
+
const searchParams = new URLSearchParams();
|
|
1186
|
+
searchParams.set("definition", slug);
|
|
1187
|
+
if (params.state) {
|
|
1188
|
+
const states = Array.isArray(params.state) ? params.state : [params.state];
|
|
1189
|
+
states.forEach((s) => searchParams.append("state", s));
|
|
1190
|
+
}
|
|
1191
|
+
if (params.status) searchParams.set("status", params.status);
|
|
1192
|
+
if (params.limit ?? defaults?.limit) searchParams.set("limit", String(params.limit ?? defaults?.limit ?? 20));
|
|
1193
|
+
if (params.page) searchParams.set("page", String(params.page));
|
|
1194
|
+
if (params.offset) searchParams.set("offset", String(params.offset));
|
|
1195
|
+
if (params.search) searchParams.set("search", params.search);
|
|
1196
|
+
if (params.searchFields?.length) searchParams.set("search_fields", params.searchFields.join(","));
|
|
1197
|
+
if (params.sort) {
|
|
1198
|
+
if (typeof params.sort === "string") {
|
|
1199
|
+
searchParams.set("sort", params.sort);
|
|
1200
|
+
} else if (Array.isArray(params.sort)) {
|
|
1201
|
+
const sortStr = params.sort.map((s) => `${s.direction === "desc" ? "-" : ""}${s.field}`).join(",");
|
|
1202
|
+
searchParams.set("sort", sortStr);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
if (params.filter) {
|
|
1206
|
+
searchParams.set("state_filter", JSON.stringify(params.filter));
|
|
1207
|
+
}
|
|
1208
|
+
if (params.facets?.length) {
|
|
1209
|
+
searchParams.set("facets", params.facets.join(","));
|
|
1210
|
+
}
|
|
1211
|
+
if (params.entity) {
|
|
1212
|
+
searchParams.set("entity_type", params.entity.type);
|
|
1213
|
+
searchParams.set("entity_id", params.entity.id);
|
|
1214
|
+
}
|
|
1215
|
+
if (params.parentInstanceId) {
|
|
1216
|
+
searchParams.set("spawned_by_instance_id", params.parentInstanceId);
|
|
1217
|
+
}
|
|
1218
|
+
if (params.includeDefinition) {
|
|
1219
|
+
searchParams.set("include_definition", "true");
|
|
1220
|
+
}
|
|
1221
|
+
const raw = await apiRequest(
|
|
1222
|
+
`/workflow/instances?${searchParams.toString()}`
|
|
1223
|
+
);
|
|
1224
|
+
const items = raw.items ?? raw.data ?? [];
|
|
1225
|
+
const total = Number(raw.total ?? raw.count ?? items.length);
|
|
1226
|
+
const page = Number(raw.page ?? params.page ?? 1);
|
|
1227
|
+
const pageSize = Number(raw.page_size ?? raw.pageSize ?? params.limit ?? defaults?.limit ?? 20);
|
|
1228
|
+
const result = {
|
|
1229
|
+
data: items.map(normalizeInstance),
|
|
1230
|
+
total,
|
|
1231
|
+
page,
|
|
1232
|
+
pageSize
|
|
1233
|
+
};
|
|
1234
|
+
if (raw.definition) result.definition = raw.definition;
|
|
1235
|
+
if (raw.facets) result.facets = raw.facets;
|
|
1236
|
+
if (raw.aggregates) result.aggregates = raw.aggregates;
|
|
1237
|
+
if (raw.grouped) result.grouped = raw.grouped;
|
|
1238
|
+
return result;
|
|
1239
|
+
},
|
|
1240
|
+
async getInstance(id) {
|
|
1241
|
+
const raw = await apiRequest(`/workflow/instances/${id}`);
|
|
1242
|
+
return normalizeInstance(raw);
|
|
1243
|
+
},
|
|
1244
|
+
async getDefinition(slug) {
|
|
1245
|
+
const cached = defCache.get(slug);
|
|
1246
|
+
if (cached && cached.expires > Date.now()) return cached.data;
|
|
1247
|
+
const raw = await apiRequest(`/workflow/definitions/slug/${slug}`);
|
|
1248
|
+
defCache.set(slug, { data: raw, expires: Date.now() + 5 * 60 * 1e3 });
|
|
1249
|
+
return raw;
|
|
1250
|
+
},
|
|
1251
|
+
async create(slug, data) {
|
|
1252
|
+
const raw = await apiRequest("/workflow/instances", {
|
|
1253
|
+
method: "POST",
|
|
1254
|
+
body: JSON.stringify({
|
|
1255
|
+
definition_slug: slug,
|
|
1256
|
+
state_data: data ?? {}
|
|
1257
|
+
})
|
|
1258
|
+
});
|
|
1259
|
+
return String(raw.id);
|
|
1260
|
+
},
|
|
1261
|
+
async update(id, fields) {
|
|
1262
|
+
await apiRequest(`/workflow/instances/${id}/state-data`, {
|
|
1263
|
+
method: "PATCH",
|
|
1264
|
+
body: JSON.stringify(fields)
|
|
1265
|
+
});
|
|
1266
|
+
},
|
|
1267
|
+
async transition(id, name, data) {
|
|
1268
|
+
const raw = await apiRequest(
|
|
1269
|
+
`/workflow/instances/${id}/transitions/${name}`,
|
|
1270
|
+
{
|
|
1271
|
+
method: "POST",
|
|
1272
|
+
body: JSON.stringify({ data: data ?? {} })
|
|
1273
|
+
}
|
|
1274
|
+
);
|
|
1275
|
+
return raw;
|
|
1276
|
+
},
|
|
1277
|
+
async remove(id) {
|
|
1278
|
+
await apiRequest(`/workflow/instances/${id}`, { method: "DELETE" });
|
|
1279
|
+
},
|
|
1280
|
+
async availableTransitions(id) {
|
|
1281
|
+
const raw = await apiRequest(`/workflow/instances/${id}/transitions`);
|
|
1282
|
+
return Array.isArray(raw) ? raw : [];
|
|
1283
|
+
},
|
|
1284
|
+
async fetch(endpoint, fetchConfig) {
|
|
1285
|
+
return apiRequest(endpoint, {
|
|
1286
|
+
method: fetchConfig.method ?? "GET",
|
|
1287
|
+
body: fetchConfig.body ? JSON.stringify(fetchConfig.body) : void 0,
|
|
1288
|
+
headers: fetchConfig.headers
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
return resolver;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/player/ComponentTreeRenderer.tsx
|
|
1296
|
+
import React4, { useMemo as useMemo3, useCallback as useCallback4 } from "react";
|
|
1297
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1298
|
+
var EXPR_PATTERN = /\{\{(.+?)\}\}/g;
|
|
1299
|
+
function containsExpression(value) {
|
|
1300
|
+
return typeof value === "string" && EXPR_PATTERN.test(value);
|
|
1301
|
+
}
|
|
1302
|
+
function evaluateProp(value, scopes) {
|
|
1303
|
+
if (typeof value !== "string") return value;
|
|
1304
|
+
const fullMatch = value.match(/^\{\{(.+)\}\}$/s);
|
|
1305
|
+
if (fullMatch) return evaluateExpression(fullMatch[1], scopes);
|
|
1306
|
+
if (containsExpression(value)) {
|
|
1307
|
+
return value.replace(EXPR_PATTERN, (_, expr) => {
|
|
1308
|
+
const result = evaluateExpression(expr, scopes);
|
|
1309
|
+
return result == null ? "" : String(result);
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
return value;
|
|
1313
|
+
}
|
|
1314
|
+
var UnknownAtom = ({ type, children }) => /* @__PURE__ */ jsxs2("div", { style: { border: "1px dashed #e53e3e", borderRadius: 6, padding: "8px 12px", margin: 4, fontSize: 13, color: "#e53e3e", background: "#fff5f5" }, children: [
|
|
1315
|
+
/* @__PURE__ */ jsxs2("span", { style: { fontWeight: 600 }, children: [
|
|
1316
|
+
"Unknown: ",
|
|
1317
|
+
type
|
|
1318
|
+
] }),
|
|
1319
|
+
children && /* @__PURE__ */ jsx3("div", { style: { marginTop: 4 }, children })
|
|
1320
|
+
] });
|
|
1321
|
+
var RenderNode = ({ node, scopes, atoms, onEvent }) => {
|
|
1322
|
+
const nodeType = node.type || node.component || "";
|
|
1323
|
+
const nodeProps = node.props || node.config || {};
|
|
1324
|
+
if (node.$if) {
|
|
1325
|
+
const condition = evaluateProp(node.$if, scopes);
|
|
1326
|
+
if (!condition) return null;
|
|
1327
|
+
}
|
|
1328
|
+
if (node.$for) {
|
|
1329
|
+
const { each, as, key: keyField } = node.$for;
|
|
1330
|
+
const items = evaluateProp(each, scopes);
|
|
1331
|
+
if (!Array.isArray(items)) return null;
|
|
1332
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: items.map((item, index) => {
|
|
1333
|
+
const loopScopes = {
|
|
1334
|
+
...scopes,
|
|
1335
|
+
state_data: {
|
|
1336
|
+
...scopes.state_data ?? {},
|
|
1337
|
+
[as]: item,
|
|
1338
|
+
[`${as}Index`]: index
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
const nodeWithoutFor = { ...node, $for: void 0 };
|
|
1342
|
+
const itemKey = keyField ? String(item[keyField] ?? index) : String(index);
|
|
1343
|
+
return /* @__PURE__ */ jsx3(RenderNode, { node: nodeWithoutFor, scopes: loopScopes, atoms, onEvent }, itemKey);
|
|
1344
|
+
}) });
|
|
1345
|
+
}
|
|
1346
|
+
const Component = atoms[nodeType];
|
|
1347
|
+
if (!Component) {
|
|
1348
|
+
return /* @__PURE__ */ jsx3(UnknownAtom, { type: nodeType });
|
|
1349
|
+
}
|
|
1350
|
+
const evaluatedProps = {};
|
|
1351
|
+
if (node.className) evaluatedProps.className = node.className;
|
|
1352
|
+
if (node.id) evaluatedProps["data-node-id"] = node.id;
|
|
1353
|
+
for (const [key, value] of Object.entries(nodeProps)) {
|
|
1354
|
+
if (key.startsWith("on") && typeof value === "string") {
|
|
1355
|
+
evaluatedProps[key] = (...args) => {
|
|
1356
|
+
if (containsExpression(value)) {
|
|
1357
|
+
evaluateExpression(value.replace(/^\{\{|\}\}$/g, ""), scopes);
|
|
1358
|
+
} else {
|
|
1359
|
+
onEvent(value, args[0]);
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
} else {
|
|
1363
|
+
evaluatedProps[key] = evaluateProp(value, scopes);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
let children = null;
|
|
1367
|
+
if (node.children) {
|
|
1368
|
+
if (typeof node.children === "string") {
|
|
1369
|
+
children = evaluateProp(node.children, scopes);
|
|
1370
|
+
} else if (Array.isArray(node.children)) {
|
|
1371
|
+
children = node.children.map((child, index) => /* @__PURE__ */ jsx3(RenderNode, { node: child, scopes, atoms, onEvent }, child.id || index));
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return /* @__PURE__ */ jsx3(Component, { ...evaluatedProps, children });
|
|
1375
|
+
};
|
|
1376
|
+
var CTRErrorBoundary = class extends React4.Component {
|
|
1377
|
+
constructor(props) {
|
|
1378
|
+
super(props);
|
|
1379
|
+
this.state = { hasError: false };
|
|
1380
|
+
}
|
|
1381
|
+
static getDerivedStateFromError(error) {
|
|
1382
|
+
return { hasError: true, error };
|
|
1383
|
+
}
|
|
1384
|
+
componentDidCatch(error, errorInfo) {
|
|
1385
|
+
console.error("CTR rendering error:", error, errorInfo);
|
|
1386
|
+
}
|
|
1387
|
+
render() {
|
|
1388
|
+
if (this.state.hasError) {
|
|
1389
|
+
return this.props.fallback || /* @__PURE__ */ jsxs2("div", { style: { padding: 16, background: "#fff5f5", border: "1px solid #fed7d7", borderRadius: 8 }, children: [
|
|
1390
|
+
/* @__PURE__ */ jsx3("strong", { style: { color: "#c53030" }, children: "Rendering Error" }),
|
|
1391
|
+
/* @__PURE__ */ jsx3("p", { style: { color: "#e53e3e", fontSize: 14, marginTop: 4 }, children: this.state.error?.message || "An error occurred while rendering" })
|
|
1392
|
+
] });
|
|
1393
|
+
}
|
|
1394
|
+
return this.props.children;
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
var ComponentTreeRenderer = ({
|
|
1398
|
+
tree,
|
|
1399
|
+
scopes,
|
|
1400
|
+
atoms = {},
|
|
1401
|
+
onEvent = () => {
|
|
1402
|
+
},
|
|
1403
|
+
fallback
|
|
1404
|
+
}) => {
|
|
1405
|
+
const allAtoms = useMemo3(() => ({ ...atoms }), [atoms]);
|
|
1406
|
+
const handleEvent = useCallback4(
|
|
1407
|
+
(eventName, payload) => onEvent(eventName, payload),
|
|
1408
|
+
[onEvent]
|
|
1409
|
+
);
|
|
1410
|
+
const nodes = Array.isArray(tree) ? tree : [tree];
|
|
1411
|
+
return /* @__PURE__ */ jsx3(CTRErrorBoundary, { fallback, children: nodes.map((node, index) => /* @__PURE__ */ jsx3(RenderNode, { node, scopes, atoms: allAtoms, onEvent: handleEvent }, index)) });
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
// src/player/builtin-atoms.tsx
|
|
1415
|
+
import React5 from "react";
|
|
1416
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1417
|
+
var Stack = ({ children, gap = 8, style, ...rest }) => /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap, ...style }, ...rest, children });
|
|
1418
|
+
var Row = ({ children, gap = 8, align, justify, style, ...rest }) => /* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "row", gap, alignItems: align, justifyContent: justify, ...style }, ...rest, children });
|
|
1419
|
+
var Column = ({ children, span, style, ...rest }) => /* @__PURE__ */ jsx4("div", { style: { flex: span ? `0 0 ${Number(span) / 12 * 100}%` : 1, ...style }, ...rest, children });
|
|
1420
|
+
var Grid = ({ children, columns = 2, gap = 8, style, ...rest }) => /* @__PURE__ */ jsx4("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap, ...style }, ...rest, children });
|
|
1421
|
+
var Divider = ({ style }) => /* @__PURE__ */ jsx4("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: "8px 0", ...style } });
|
|
1422
|
+
var Spacer = ({ size = 16 }) => /* @__PURE__ */ jsx4("div", { style: { height: size, flexShrink: 0 } });
|
|
1423
|
+
var Text = ({ children, size, weight, color, style, ...rest }) => /* @__PURE__ */ jsx4("span", { style: { fontSize: size, fontWeight: weight, color, ...style }, ...rest, children });
|
|
1424
|
+
var Heading = ({ children, level = 2, style, ...rest }) => {
|
|
1425
|
+
const lvl = Math.min(Math.max(Number(level), 1), 6);
|
|
1426
|
+
const s = { margin: "0 0 8px", ...style };
|
|
1427
|
+
const c = children;
|
|
1428
|
+
if (lvl === 1) return /* @__PURE__ */ jsx4("h1", { style: s, ...rest, children: c });
|
|
1429
|
+
if (lvl === 2) return /* @__PURE__ */ jsx4("h2", { style: s, ...rest, children: c });
|
|
1430
|
+
if (lvl === 3) return /* @__PURE__ */ jsx4("h3", { style: s, ...rest, children: c });
|
|
1431
|
+
if (lvl === 4) return /* @__PURE__ */ jsx4("h4", { style: s, ...rest, children: c });
|
|
1432
|
+
if (lvl === 5) return /* @__PURE__ */ jsx4("h5", { style: s, ...rest, children: c });
|
|
1433
|
+
return /* @__PURE__ */ jsx4("h6", { style: s, ...rest, children: c });
|
|
1434
|
+
};
|
|
1435
|
+
var Field = ({ label, value, children, style }) => /* @__PURE__ */ jsxs3("div", { style: { marginBottom: 8, ...style }, children: [
|
|
1436
|
+
label ? /* @__PURE__ */ jsx4("div", { style: { fontSize: 12, color: "#718096", marginBottom: 2 }, children: label }) : null,
|
|
1437
|
+
/* @__PURE__ */ jsx4("div", { style: { fontSize: 14 }, children: children ?? value ?? "\u2014" })
|
|
1438
|
+
] });
|
|
1439
|
+
var Badge = ({ children, variant = "default", style }) => {
|
|
1440
|
+
const colors = {
|
|
1441
|
+
default: { bg: "#edf2f7", fg: "#4a5568" },
|
|
1442
|
+
success: { bg: "#c6f6d5", fg: "#276749" },
|
|
1443
|
+
warning: { bg: "#fefcbf", fg: "#975a16" },
|
|
1444
|
+
error: { bg: "#fed7d7", fg: "#9b2c2c" },
|
|
1445
|
+
info: { bg: "#bee3f8", fg: "#2a4365" }
|
|
1446
|
+
};
|
|
1447
|
+
const c = colors[variant] ?? colors.default;
|
|
1448
|
+
return /* @__PURE__ */ jsx4("span", { style: { display: "inline-block", padding: "2px 8px", borderRadius: 9999, fontSize: 12, fontWeight: 500, background: c.bg, color: c.fg, ...style }, children });
|
|
1449
|
+
};
|
|
1450
|
+
var ImageAtom = ({ src, alt, width, height, style, ...rest }) => /* @__PURE__ */ jsx4("img", { src, alt: alt ?? "", width, height, style: { maxWidth: "100%", ...style }, ...rest });
|
|
1451
|
+
var Button = ({ children, onClick, variant = "primary", disabled, style, ...rest }) => {
|
|
1452
|
+
const styles = {
|
|
1453
|
+
primary: { background: "#3182ce", color: "#fff", border: "none" },
|
|
1454
|
+
secondary: { background: "#edf2f7", color: "#4a5568", border: "1px solid #e2e8f0" },
|
|
1455
|
+
danger: { background: "#e53e3e", color: "#fff", border: "none" },
|
|
1456
|
+
ghost: { background: "transparent", color: "#4a5568", border: "none" }
|
|
1457
|
+
};
|
|
1458
|
+
const base = styles[variant] ?? styles.primary;
|
|
1459
|
+
return /* @__PURE__ */ jsx4(
|
|
1460
|
+
"button",
|
|
1461
|
+
{
|
|
1462
|
+
onClick,
|
|
1463
|
+
disabled,
|
|
1464
|
+
style: { padding: "6px 16px", borderRadius: 6, fontSize: 14, fontWeight: 500, cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.5 : 1, ...base, ...style },
|
|
1465
|
+
...rest,
|
|
1466
|
+
children
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
};
|
|
1470
|
+
var LinkAtom = ({ children, href, style, ...rest }) => /* @__PURE__ */ jsx4("a", { href, style: { color: "#3182ce", textDecoration: "underline", ...style }, ...rest, children });
|
|
1471
|
+
var TextInput = ({ value, onChange, placeholder, label, bind: _bind, style }) => {
|
|
1472
|
+
const isControlled = typeof onChange === "function";
|
|
1473
|
+
const [localValue, setLocalValue] = React5.useState(value ?? "");
|
|
1474
|
+
const handleChange = (e) => {
|
|
1475
|
+
setLocalValue(e.target.value);
|
|
1476
|
+
if (typeof onChange === "function") onChange(e);
|
|
1477
|
+
};
|
|
1478
|
+
return /* @__PURE__ */ jsxs3("div", { style: { marginBottom: 8 }, children: [
|
|
1479
|
+
label ? /* @__PURE__ */ jsx4("label", { style: { display: "block", fontSize: 12, color: "#718096", marginBottom: 2 }, children: label }) : null,
|
|
1480
|
+
/* @__PURE__ */ jsx4(
|
|
1481
|
+
"input",
|
|
1482
|
+
{
|
|
1483
|
+
type: "text",
|
|
1484
|
+
value: isControlled ? value ?? "" : localValue,
|
|
1485
|
+
onChange: handleChange,
|
|
1486
|
+
placeholder,
|
|
1487
|
+
style: { width: "100%", padding: "6px 10px", border: "1px solid #e2e8f0", borderRadius: 6, fontSize: 14, ...style }
|
|
1488
|
+
}
|
|
1489
|
+
)
|
|
1490
|
+
] });
|
|
1491
|
+
};
|
|
1492
|
+
var SelectAtom = ({ value, onChange, options, label, placeholder, style }) => /* @__PURE__ */ jsxs3("div", { style: { marginBottom: 8 }, children: [
|
|
1493
|
+
label ? /* @__PURE__ */ jsx4("label", { style: { display: "block", fontSize: 12, color: "#718096", marginBottom: 2 }, children: label }) : null,
|
|
1494
|
+
/* @__PURE__ */ jsxs3(
|
|
1495
|
+
"select",
|
|
1496
|
+
{
|
|
1497
|
+
value: value ?? "",
|
|
1498
|
+
onChange,
|
|
1499
|
+
style: { width: "100%", padding: "6px 10px", border: "1px solid #e2e8f0", borderRadius: 6, fontSize: 14, ...style },
|
|
1500
|
+
children: [
|
|
1501
|
+
placeholder ? /* @__PURE__ */ jsx4("option", { value: "", children: placeholder }) : null,
|
|
1502
|
+
Array.isArray(options) && options.map((opt) => {
|
|
1503
|
+
const v = typeof opt === "string" ? opt : opt.value;
|
|
1504
|
+
const l = typeof opt === "string" ? opt : opt.label;
|
|
1505
|
+
return /* @__PURE__ */ jsx4("option", { value: v, children: l }, v);
|
|
1506
|
+
})
|
|
1507
|
+
]
|
|
1508
|
+
}
|
|
1509
|
+
)
|
|
1510
|
+
] });
|
|
1511
|
+
var Card = ({ children, title, style, ...rest }) => /* @__PURE__ */ jsxs3("div", { style: { border: "1px solid #e2e8f0", borderRadius: 8, padding: 16, background: "#fff", ...style }, ...rest, children: [
|
|
1512
|
+
title ? /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, fontSize: 16, marginBottom: 12 }, children: title }) : null,
|
|
1513
|
+
children
|
|
1514
|
+
] });
|
|
1515
|
+
var Section = ({ children, title, style, ...rest }) => /* @__PURE__ */ jsxs3("section", { style: { marginBottom: 24, ...style }, ...rest, children: [
|
|
1516
|
+
title ? /* @__PURE__ */ jsx4("h3", { style: { fontSize: 18, fontWeight: 600, marginBottom: 8 }, children: title }) : null,
|
|
1517
|
+
children
|
|
1518
|
+
] });
|
|
1519
|
+
var Show = ({ when, children, fallback }) => when ? /* @__PURE__ */ jsx4(Fragment2, { children }) : /* @__PURE__ */ jsx4(Fragment2, { children: fallback ?? null });
|
|
1520
|
+
var Each = ({ items, children, renderItem }) => {
|
|
1521
|
+
if (!Array.isArray(items)) return null;
|
|
1522
|
+
if (typeof renderItem === "function") return /* @__PURE__ */ jsx4(Fragment2, { children: items.map((item, i) => renderItem(item, i)) });
|
|
1523
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children });
|
|
1524
|
+
};
|
|
1525
|
+
var RouterContext = React5.createContext({ path: "/", navigate: () => {
|
|
1526
|
+
} });
|
|
1527
|
+
var Router = ({ children, basePath, className, style, ...rest }) => {
|
|
1528
|
+
const [path, setPath] = React5.useState("/");
|
|
1529
|
+
const navigate = React5.useCallback((to) => setPath(to), []);
|
|
1530
|
+
return /* @__PURE__ */ jsx4(RouterContext.Provider, { value: { path, navigate }, children: /* @__PURE__ */ jsx4("div", { className, style, ...rest, children }) });
|
|
1531
|
+
};
|
|
1532
|
+
var Route = ({ children, path, exact, fallback: _fallback, className, style }) => {
|
|
1533
|
+
const { path: currentPath } = React5.useContext(RouterContext);
|
|
1534
|
+
const routePath = path || "/";
|
|
1535
|
+
const isExact = exact !== false;
|
|
1536
|
+
const matches = isExact ? currentPath === routePath || routePath === "/" && currentPath === "/" : currentPath.startsWith(routePath);
|
|
1537
|
+
if (!matches) return null;
|
|
1538
|
+
return /* @__PURE__ */ jsx4("div", { className, style, children });
|
|
1539
|
+
};
|
|
1540
|
+
var NavLink = ({ children, to, label, icon, className, style, ...rest }) => {
|
|
1541
|
+
const { path, navigate } = React5.useContext(RouterContext);
|
|
1542
|
+
const target = to || "/";
|
|
1543
|
+
const isActive = path === target;
|
|
1544
|
+
return /* @__PURE__ */ jsx4(
|
|
1545
|
+
"button",
|
|
1546
|
+
{
|
|
1547
|
+
onClick: () => navigate(target),
|
|
1548
|
+
className,
|
|
1549
|
+
style: {
|
|
1550
|
+
background: isActive ? "#edf2f7" : "transparent",
|
|
1551
|
+
border: "none",
|
|
1552
|
+
borderRadius: 4,
|
|
1553
|
+
padding: "4px 12px",
|
|
1554
|
+
fontSize: 13,
|
|
1555
|
+
cursor: "pointer",
|
|
1556
|
+
fontWeight: isActive ? 600 : 400,
|
|
1557
|
+
color: isActive ? "#2d3748" : "#718096",
|
|
1558
|
+
...style
|
|
1559
|
+
},
|
|
1560
|
+
...rest,
|
|
1561
|
+
children: children || label || target
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
};
|
|
1565
|
+
var RoleGuard = ({ children, role: _role, fallback: _fallback }) => {
|
|
1566
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children });
|
|
1567
|
+
};
|
|
1568
|
+
var Icon = ({ name, size = 16, color, style }) => /* @__PURE__ */ jsx4("span", { style: { display: "inline-flex", alignItems: "center", justifyContent: "center", width: size, height: size, fontSize: size, color, ...style }, title: name, children: iconGlyphs[name] || "\u25A1" });
|
|
1569
|
+
var iconGlyphs = {
|
|
1570
|
+
home: "\u2302",
|
|
1571
|
+
settings: "\u2699",
|
|
1572
|
+
plus: "+",
|
|
1573
|
+
search: "\u{1F50D}",
|
|
1574
|
+
box: "\u25A1",
|
|
1575
|
+
inbox: "\u2709",
|
|
1576
|
+
chevronRight: "\u203A",
|
|
1577
|
+
chevronLeft: "\u2039",
|
|
1578
|
+
x: "\u2715",
|
|
1579
|
+
check: "\u2713",
|
|
1580
|
+
edit: "\u270E",
|
|
1581
|
+
trash: "\u{1F5D1}",
|
|
1582
|
+
star: "\u2605",
|
|
1583
|
+
heart: "\u2665",
|
|
1584
|
+
user: "\u{1F464}",
|
|
1585
|
+
menu: "\u2630",
|
|
1586
|
+
close: "\u2715",
|
|
1587
|
+
arrow_right: "\u2192",
|
|
1588
|
+
arrow_left: "\u2190"
|
|
1589
|
+
};
|
|
1590
|
+
var Tabs = ({ children, tabs, defaultTab, style }) => {
|
|
1591
|
+
const tabList = tabs || [];
|
|
1592
|
+
const [active, setActive] = React5.useState(defaultTab || tabList[0]?.id || "");
|
|
1593
|
+
return /* @__PURE__ */ jsxs3("div", { style, children: [
|
|
1594
|
+
/* @__PURE__ */ jsx4("div", { style: { display: "flex", borderBottom: "1px solid #e2e8f0", marginBottom: 12 }, children: tabList.map((t) => /* @__PURE__ */ jsx4(
|
|
1595
|
+
"button",
|
|
1596
|
+
{
|
|
1597
|
+
onClick: () => setActive(t.id),
|
|
1598
|
+
style: { padding: "6px 16px", border: "none", borderBottom: active === t.id ? "2px solid #3182ce" : "2px solid transparent", background: "none", cursor: "pointer", fontWeight: active === t.id ? 600 : 400, color: active === t.id ? "#3182ce" : "#718096" },
|
|
1599
|
+
children: t.label
|
|
1600
|
+
},
|
|
1601
|
+
t.id
|
|
1602
|
+
)) }),
|
|
1603
|
+
children
|
|
1604
|
+
] });
|
|
1605
|
+
};
|
|
1606
|
+
var Accordion = ({ children, style }) => /* @__PURE__ */ jsx4("div", { style, children });
|
|
1607
|
+
var Modal = ({ children, open, title, onClose: _onClose, style }) => {
|
|
1608
|
+
if (!open) return null;
|
|
1609
|
+
return /* @__PURE__ */ jsx4("div", { style: { position: "fixed", inset: 0, background: "rgba(0,0,0,0.5)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1e3 }, children: /* @__PURE__ */ jsxs3("div", { style: { background: "#fff", borderRadius: 12, padding: 24, maxWidth: 480, width: "100%", ...style }, children: [
|
|
1610
|
+
title ? /* @__PURE__ */ jsx4("div", { style: { fontWeight: 600, fontSize: 18, marginBottom: 16 }, children: String(title) }) : null,
|
|
1611
|
+
children
|
|
1612
|
+
] }) });
|
|
1613
|
+
};
|
|
1614
|
+
var Markdown = ({ content, children, style }) => /* @__PURE__ */ jsx4("div", { style: { lineHeight: 1.6, ...style }, children: content || children });
|
|
1615
|
+
var ScrollArea = ({ children, maxHeight = 400, style }) => /* @__PURE__ */ jsx4("div", { style: { overflow: "auto", maxHeight, ...style }, children });
|
|
1616
|
+
var Slot = ({ children, fallback }) => /* @__PURE__ */ jsx4(Fragment2, { children: children || fallback || null });
|
|
1617
|
+
var ModuleOutlet = ({ children, module: moduleName, fallback }) => /* @__PURE__ */ jsx4("div", { "data-module": moduleName, children: children || fallback || /* @__PURE__ */ jsxs3("span", { style: { color: "#a0aec0", fontSize: 13 }, children: [
|
|
1618
|
+
"Module: ",
|
|
1619
|
+
moduleName
|
|
1620
|
+
] }) });
|
|
1621
|
+
var builtinAtoms = {
|
|
1622
|
+
// Layout
|
|
1623
|
+
Stack,
|
|
1624
|
+
Row,
|
|
1625
|
+
Column,
|
|
1626
|
+
Grid,
|
|
1627
|
+
Divider,
|
|
1628
|
+
Spacer,
|
|
1629
|
+
// Typography
|
|
1630
|
+
Text,
|
|
1631
|
+
Heading,
|
|
1632
|
+
Field,
|
|
1633
|
+
Badge,
|
|
1634
|
+
Image: ImageAtom,
|
|
1635
|
+
Icon,
|
|
1636
|
+
// Interactive
|
|
1637
|
+
Button,
|
|
1638
|
+
Link: LinkAtom,
|
|
1639
|
+
// Form
|
|
1640
|
+
TextInput,
|
|
1641
|
+
Select: SelectAtom,
|
|
1642
|
+
// Containers
|
|
1643
|
+
Card,
|
|
1644
|
+
Section,
|
|
1645
|
+
Tabs,
|
|
1646
|
+
Accordion,
|
|
1647
|
+
Modal,
|
|
1648
|
+
// Content
|
|
1649
|
+
Markdown,
|
|
1650
|
+
ScrollArea,
|
|
1651
|
+
// Control Flow
|
|
1652
|
+
Show,
|
|
1653
|
+
Each,
|
|
1654
|
+
// Routing
|
|
1655
|
+
Router,
|
|
1656
|
+
Route,
|
|
1657
|
+
NavLink,
|
|
1658
|
+
RoleGuard,
|
|
1659
|
+
// Composition
|
|
1660
|
+
Slot,
|
|
1661
|
+
ModuleOutlet
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
// src/player/DevPlayer.tsx
|
|
1665
|
+
import { useState as useState4, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect4, useMemo as useMemo4 } from "react";
|
|
1666
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1667
|
+
var S = {
|
|
1668
|
+
shell: {
|
|
1669
|
+
display: "flex",
|
|
1670
|
+
flexDirection: "column",
|
|
1671
|
+
height: "100%",
|
|
1672
|
+
minHeight: "100vh",
|
|
1673
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
1674
|
+
background: "#f7fafc",
|
|
1675
|
+
color: "#1a202c"
|
|
1676
|
+
},
|
|
1677
|
+
toolbar: {
|
|
1678
|
+
display: "flex",
|
|
1679
|
+
alignItems: "center",
|
|
1680
|
+
gap: 12,
|
|
1681
|
+
padding: "8px 16px",
|
|
1682
|
+
background: "#1a202c",
|
|
1683
|
+
color: "#e2e8f0",
|
|
1684
|
+
fontSize: 13,
|
|
1685
|
+
flexShrink: 0
|
|
1686
|
+
},
|
|
1687
|
+
toolbarTitle: { fontWeight: 600, fontSize: 14 },
|
|
1688
|
+
toolbarBadge: {
|
|
1689
|
+
display: "inline-block",
|
|
1690
|
+
padding: "1px 8px",
|
|
1691
|
+
borderRadius: 4,
|
|
1692
|
+
background: "#2d3748",
|
|
1693
|
+
color: "#a0aec0",
|
|
1694
|
+
fontSize: 11,
|
|
1695
|
+
fontFamily: "monospace"
|
|
1696
|
+
},
|
|
1697
|
+
toolbarBtn: {
|
|
1698
|
+
background: "none",
|
|
1699
|
+
border: "1px solid #4a5568",
|
|
1700
|
+
borderRadius: 4,
|
|
1701
|
+
color: "#a0aec0",
|
|
1702
|
+
padding: "2px 10px",
|
|
1703
|
+
fontSize: 12,
|
|
1704
|
+
cursor: "pointer"
|
|
1705
|
+
},
|
|
1706
|
+
toolbarBtnActive: {
|
|
1707
|
+
background: "#2b6cb0",
|
|
1708
|
+
borderColor: "#2b6cb0",
|
|
1709
|
+
color: "#fff"
|
|
1710
|
+
},
|
|
1711
|
+
body: { display: "flex", flex: 1, overflow: "hidden" },
|
|
1712
|
+
preview: { flex: 1, overflow: "auto", padding: 24 },
|
|
1713
|
+
sidebar: {
|
|
1714
|
+
width: 340,
|
|
1715
|
+
flexShrink: 0,
|
|
1716
|
+
borderLeft: "1px solid #e2e8f0",
|
|
1717
|
+
background: "#fff",
|
|
1718
|
+
overflow: "auto",
|
|
1719
|
+
fontSize: 13
|
|
1720
|
+
},
|
|
1721
|
+
sidebarSection: { padding: "12px 16px", borderBottom: "1px solid #f0f0f0" },
|
|
1722
|
+
sidebarHeading: {
|
|
1723
|
+
fontSize: 11,
|
|
1724
|
+
fontWeight: 700,
|
|
1725
|
+
textTransform: "uppercase",
|
|
1726
|
+
color: "#a0aec0",
|
|
1727
|
+
letterSpacing: "0.05em",
|
|
1728
|
+
marginBottom: 8
|
|
1729
|
+
},
|
|
1730
|
+
pre: {
|
|
1731
|
+
background: "#f7fafc",
|
|
1732
|
+
border: "1px solid #e2e8f0",
|
|
1733
|
+
borderRadius: 6,
|
|
1734
|
+
padding: "8px 12px",
|
|
1735
|
+
fontSize: 12,
|
|
1736
|
+
fontFamily: "monospace",
|
|
1737
|
+
whiteSpace: "pre-wrap",
|
|
1738
|
+
wordBreak: "break-word",
|
|
1739
|
+
maxHeight: 260,
|
|
1740
|
+
overflow: "auto"
|
|
1741
|
+
},
|
|
1742
|
+
eventRow: {
|
|
1743
|
+
display: "flex",
|
|
1744
|
+
gap: 8,
|
|
1745
|
+
padding: "4px 0",
|
|
1746
|
+
borderBottom: "1px solid #f7fafc",
|
|
1747
|
+
fontSize: 12,
|
|
1748
|
+
fontFamily: "monospace"
|
|
1749
|
+
},
|
|
1750
|
+
eventTime: { color: "#a0aec0", flexShrink: 0 },
|
|
1751
|
+
eventName: { color: "#3182ce", fontWeight: 500 },
|
|
1752
|
+
dot: (connected) => ({
|
|
1753
|
+
width: 8,
|
|
1754
|
+
height: 8,
|
|
1755
|
+
borderRadius: "50%",
|
|
1756
|
+
background: connected ? "#48bb78" : "#e53e3e",
|
|
1757
|
+
flexShrink: 0
|
|
1758
|
+
})
|
|
1759
|
+
};
|
|
1760
|
+
function useDevSocket(wsUrl, onReload) {
|
|
1761
|
+
const [connected, setConnected] = useState4(false);
|
|
1762
|
+
const wsRef = useRef5(null);
|
|
1763
|
+
useEffect4(() => {
|
|
1764
|
+
if (typeof window === "undefined") return;
|
|
1765
|
+
const url = wsUrl ?? `ws://${window.location.host}/__mm_dev`;
|
|
1766
|
+
let ws;
|
|
1767
|
+
let reconnectTimer;
|
|
1768
|
+
function connect() {
|
|
1769
|
+
try {
|
|
1770
|
+
ws = new WebSocket(url);
|
|
1771
|
+
wsRef.current = ws;
|
|
1772
|
+
ws.onopen = () => setConnected(true);
|
|
1773
|
+
ws.onclose = () => {
|
|
1774
|
+
setConnected(false);
|
|
1775
|
+
reconnectTimer = setTimeout(connect, 3e3);
|
|
1776
|
+
};
|
|
1777
|
+
ws.onmessage = (ev) => {
|
|
1778
|
+
try {
|
|
1779
|
+
const msg = JSON.parse(ev.data);
|
|
1780
|
+
if (msg.type === "workflow:compiled" || msg.type === "workflow:rebuild") {
|
|
1781
|
+
onReload?.();
|
|
1782
|
+
}
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
} catch {
|
|
1787
|
+
reconnectTimer = setTimeout(connect, 3e3);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
connect();
|
|
1791
|
+
return () => {
|
|
1792
|
+
clearTimeout(reconnectTimer);
|
|
1793
|
+
wsRef.current?.close();
|
|
1794
|
+
wsRef.current = null;
|
|
1795
|
+
};
|
|
1796
|
+
}, [wsUrl, onReload]);
|
|
1797
|
+
return connected;
|
|
1798
|
+
}
|
|
1799
|
+
var DevPlayer = ({
|
|
1800
|
+
tree,
|
|
1801
|
+
scopes = {},
|
|
1802
|
+
atoms: userAtoms,
|
|
1803
|
+
title = "DevPlayer",
|
|
1804
|
+
bare = false,
|
|
1805
|
+
wsUrl,
|
|
1806
|
+
onReload,
|
|
1807
|
+
onEvent: externalOnEvent
|
|
1808
|
+
}) => {
|
|
1809
|
+
const [showSidebar, setShowSidebar] = useState4(true);
|
|
1810
|
+
const [events, setEvents] = useState4([]);
|
|
1811
|
+
const nextId = useRef5(0);
|
|
1812
|
+
const connected = useDevSocket(wsUrl, onReload);
|
|
1813
|
+
const mergedAtoms = useMemo4(
|
|
1814
|
+
() => ({ ...builtinAtoms, ...userAtoms }),
|
|
1815
|
+
[userAtoms]
|
|
1816
|
+
);
|
|
1817
|
+
const handleEvent = useCallback5(
|
|
1818
|
+
(name, payload) => {
|
|
1819
|
+
setEvents((prev) => {
|
|
1820
|
+
const entry = {
|
|
1821
|
+
id: nextId.current++,
|
|
1822
|
+
time: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }),
|
|
1823
|
+
name,
|
|
1824
|
+
payload
|
|
1825
|
+
};
|
|
1826
|
+
const next = [entry, ...prev];
|
|
1827
|
+
return next.length > 100 ? next.slice(0, 100) : next;
|
|
1828
|
+
});
|
|
1829
|
+
externalOnEvent?.(name, payload);
|
|
1830
|
+
},
|
|
1831
|
+
[externalOnEvent]
|
|
1832
|
+
);
|
|
1833
|
+
if (bare) {
|
|
1834
|
+
return /* @__PURE__ */ jsx5(
|
|
1835
|
+
ComponentTreeRenderer,
|
|
1836
|
+
{
|
|
1837
|
+
tree,
|
|
1838
|
+
scopes,
|
|
1839
|
+
atoms: mergedAtoms,
|
|
1840
|
+
onEvent: handleEvent
|
|
1841
|
+
}
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
return /* @__PURE__ */ jsxs4("div", { style: S.shell, children: [
|
|
1845
|
+
/* @__PURE__ */ jsxs4("div", { style: S.toolbar, children: [
|
|
1846
|
+
/* @__PURE__ */ jsx5("div", { style: S.dot(connected), title: connected ? "HMR connected" : "HMR disconnected" }),
|
|
1847
|
+
/* @__PURE__ */ jsx5("span", { style: S.toolbarTitle, children: title }),
|
|
1848
|
+
/* @__PURE__ */ jsxs4("span", { style: S.toolbarBadge, children: [
|
|
1849
|
+
Array.isArray(tree) ? tree.length : 1,
|
|
1850
|
+
" node",
|
|
1851
|
+
Array.isArray(tree) && tree.length !== 1 ? "s" : ""
|
|
1852
|
+
] }),
|
|
1853
|
+
/* @__PURE__ */ jsxs4("span", { style: S.toolbarBadge, children: [
|
|
1854
|
+
Object.keys(mergedAtoms).length,
|
|
1855
|
+
" atoms"
|
|
1856
|
+
] }),
|
|
1857
|
+
/* @__PURE__ */ jsx5("div", { style: { flex: 1 } }),
|
|
1858
|
+
/* @__PURE__ */ jsx5(
|
|
1859
|
+
"button",
|
|
1860
|
+
{
|
|
1861
|
+
style: { ...S.toolbarBtn, ...showSidebar ? S.toolbarBtnActive : {} },
|
|
1862
|
+
onClick: () => setShowSidebar((v) => !v),
|
|
1863
|
+
children: "Inspector"
|
|
1864
|
+
}
|
|
1865
|
+
)
|
|
1866
|
+
] }),
|
|
1867
|
+
/* @__PURE__ */ jsxs4("div", { style: S.body, children: [
|
|
1868
|
+
/* @__PURE__ */ jsx5("div", { style: S.preview, children: /* @__PURE__ */ jsx5(
|
|
1869
|
+
ComponentTreeRenderer,
|
|
1870
|
+
{
|
|
1871
|
+
tree,
|
|
1872
|
+
scopes,
|
|
1873
|
+
atoms: mergedAtoms,
|
|
1874
|
+
onEvent: handleEvent
|
|
1875
|
+
}
|
|
1876
|
+
) }),
|
|
1877
|
+
showSidebar && /* @__PURE__ */ jsxs4("div", { style: S.sidebar, children: [
|
|
1878
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarSection, children: [
|
|
1879
|
+
/* @__PURE__ */ jsx5("div", { style: S.sidebarHeading, children: "Scopes" }),
|
|
1880
|
+
/* @__PURE__ */ jsx5("pre", { style: S.pre, children: JSON.stringify(scopes, null, 2) })
|
|
1881
|
+
] }),
|
|
1882
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarSection, children: [
|
|
1883
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarHeading, children: [
|
|
1884
|
+
"Atoms (",
|
|
1885
|
+
Object.keys(mergedAtoms).length,
|
|
1886
|
+
")"
|
|
1887
|
+
] }),
|
|
1888
|
+
/* @__PURE__ */ jsx5("div", { style: { display: "flex", flexWrap: "wrap", gap: 4 }, children: Object.keys(mergedAtoms).sort().map((name) => /* @__PURE__ */ jsx5(
|
|
1889
|
+
"span",
|
|
1890
|
+
{
|
|
1891
|
+
style: {
|
|
1892
|
+
display: "inline-block",
|
|
1893
|
+
padding: "1px 6px",
|
|
1894
|
+
borderRadius: 3,
|
|
1895
|
+
background: userAtoms?.[name] ? "#ebf8ff" : "#f7fafc",
|
|
1896
|
+
border: `1px solid ${userAtoms?.[name] ? "#90cdf4" : "#e2e8f0"}`,
|
|
1897
|
+
fontSize: 11,
|
|
1898
|
+
fontFamily: "monospace",
|
|
1899
|
+
color: userAtoms?.[name] ? "#2b6cb0" : "#718096"
|
|
1900
|
+
},
|
|
1901
|
+
children: name
|
|
1902
|
+
},
|
|
1903
|
+
name
|
|
1904
|
+
)) })
|
|
1905
|
+
] }),
|
|
1906
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarSection, children: [
|
|
1907
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }, children: [
|
|
1908
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarHeading, children: [
|
|
1909
|
+
"Events (",
|
|
1910
|
+
events.length,
|
|
1911
|
+
")"
|
|
1912
|
+
] }),
|
|
1913
|
+
events.length > 0 && /* @__PURE__ */ jsx5(
|
|
1914
|
+
"button",
|
|
1915
|
+
{
|
|
1916
|
+
style: { background: "none", border: "none", color: "#a0aec0", fontSize: 11, cursor: "pointer" },
|
|
1917
|
+
onClick: () => setEvents([]),
|
|
1918
|
+
children: "Clear"
|
|
1919
|
+
}
|
|
1920
|
+
)
|
|
1921
|
+
] }),
|
|
1922
|
+
events.length === 0 && /* @__PURE__ */ jsx5("div", { style: { color: "#a0aec0", fontSize: 12, fontStyle: "italic" }, children: "No events yet" }),
|
|
1923
|
+
events.slice(0, 50).map((e) => /* @__PURE__ */ jsxs4("div", { style: S.eventRow, children: [
|
|
1924
|
+
/* @__PURE__ */ jsx5("span", { style: S.eventTime, children: e.time }),
|
|
1925
|
+
/* @__PURE__ */ jsx5("span", { style: S.eventName, children: e.name }),
|
|
1926
|
+
e.payload !== void 0 && /* @__PURE__ */ jsx5("span", { style: { color: "#718096", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: JSON.stringify(e.payload) })
|
|
1927
|
+
] }, e.id))
|
|
1928
|
+
] }),
|
|
1929
|
+
/* @__PURE__ */ jsxs4("div", { style: S.sidebarSection, children: [
|
|
1930
|
+
/* @__PURE__ */ jsx5("div", { style: S.sidebarHeading, children: "Tree (JSON)" }),
|
|
1931
|
+
/* @__PURE__ */ jsx5("pre", { style: { ...S.pre, maxHeight: 400 }, children: JSON.stringify(tree, null, 2) })
|
|
1932
|
+
] })
|
|
1933
|
+
] })
|
|
1934
|
+
] })
|
|
1935
|
+
] });
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
export {
|
|
1939
|
+
setQueryResolver,
|
|
1940
|
+
useQuery,
|
|
1941
|
+
setMutationResolver,
|
|
1942
|
+
useMutation,
|
|
1943
|
+
classifyBinding,
|
|
1944
|
+
resolveBinding,
|
|
1945
|
+
resolveAllBindings,
|
|
1946
|
+
buildEvalContext,
|
|
1947
|
+
buildActionScope,
|
|
1948
|
+
AtomRegistryImpl,
|
|
1949
|
+
createCoreAtomRegistry,
|
|
1950
|
+
mergeRegistries,
|
|
1951
|
+
DEFAULT_DESIGN_TOKENS,
|
|
1952
|
+
usePlayerContext,
|
|
1953
|
+
useTheme,
|
|
1954
|
+
PlayerProvider,
|
|
1955
|
+
ExperienceRenderer,
|
|
1956
|
+
createApiResolver,
|
|
1957
|
+
ComponentTreeRenderer,
|
|
1958
|
+
builtinAtoms,
|
|
1959
|
+
DevPlayer
|
|
1960
|
+
};
|