@01.software/sdk 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/auth.d.cts +1 -1
  2. package/dist/auth.d.ts +1 -1
  3. package/dist/{const-Cy_NaRCl.d.cts → const-C9I6r5Wa.d.cts} +1 -1
  4. package/dist/{const-C6vryj3j.d.ts → const-JbuUTzeh.d.ts} +1 -1
  5. package/dist/index.d.cts +6 -6
  6. package/dist/index.d.ts +6 -6
  7. package/dist/{payload-types-JCbsSVeL.d.cts → payload-types-D8fN_vZR.d.cts} +5 -1
  8. package/dist/{payload-types-JCbsSVeL.d.ts → payload-types-D8fN_vZR.d.ts} +5 -1
  9. package/dist/realtime.d.cts +2 -2
  10. package/dist/realtime.d.ts +2 -2
  11. package/dist/{server-B80o7igg.d.cts → server-StNHlSjW.d.cts} +5 -1
  12. package/dist/{server-B80o7igg.d.ts → server-StNHlSjW.d.ts} +5 -1
  13. package/dist/ui/code-block.cjs +5 -1
  14. package/dist/ui/code-block.cjs.map +1 -1
  15. package/dist/ui/code-block.js +5 -1
  16. package/dist/ui/code-block.js.map +1 -1
  17. package/dist/ui/flow/server.cjs +101 -34
  18. package/dist/ui/flow/server.cjs.map +1 -1
  19. package/dist/ui/flow/server.d.cts +1 -1
  20. package/dist/ui/flow/server.d.ts +1 -1
  21. package/dist/ui/flow/server.js +101 -34
  22. package/dist/ui/flow/server.js.map +1 -1
  23. package/dist/ui/flow.cjs +780 -180
  24. package/dist/ui/flow.cjs.map +1 -1
  25. package/dist/ui/flow.d.cts +77 -11
  26. package/dist/ui/flow.d.ts +77 -11
  27. package/dist/ui/flow.js +767 -167
  28. package/dist/ui/flow.js.map +1 -1
  29. package/dist/ui/form.d.cts +1 -1
  30. package/dist/ui/form.d.ts +1 -1
  31. package/dist/ui/video.d.cts +1 -1
  32. package/dist/ui/video.d.ts +1 -1
  33. package/dist/{webhook-Dd_7Qgaa.d.ts → webhook-BkwMrrL1.d.ts} +2 -2
  34. package/dist/{webhook-CDu_s44-.d.cts → webhook-Dbx-pRib.d.cts} +2 -2
  35. package/dist/webhook.d.cts +3 -3
  36. package/dist/webhook.d.ts +3 -3
  37. package/package.json +3 -1
package/dist/ui/flow.cjs CHANGED
@@ -23,6 +23,18 @@ var __spreadValues = (a, b) => {
23
23
  return a;
24
24
  };
25
25
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
26
+ var __objRest = (source, exclude) => {
27
+ var target = {};
28
+ for (var prop in source)
29
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
30
+ target[prop] = source[prop];
31
+ if (source != null && __getOwnPropSymbols)
32
+ for (var prop of __getOwnPropSymbols(source)) {
33
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
34
+ target[prop] = source[prop];
35
+ }
36
+ return target;
37
+ };
26
38
  var __export = (target, all) => {
27
39
  for (var name in all)
28
40
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -70,15 +82,22 @@ var Flow_exports = {};
70
82
  __export(Flow_exports, {
71
83
  BUILT_IN_EDGE_TYPES: () => BUILT_IN_EDGE_TYPES,
72
84
  BUILT_IN_NODE_TYPES: () => BUILT_IN_NODE_TYPES,
85
+ FlowFrame: () => FlowFrame,
73
86
  FlowRenderer: () => FlowRenderer,
74
87
  clearTemplateCache: () => clearTemplateCache,
75
88
  compileTemplate: () => compileTemplate,
89
+ getBooleanField: () => getBooleanField,
76
90
  getFrameData: () => getFrameData,
77
91
  getFrames: () => getFrames,
92
+ getImageField: () => getImageField,
78
93
  getNodeBounds: () => getNodeBounds,
94
+ getNumberField: () => getNumberField,
95
+ getTextField: () => getTextField,
79
96
  isDynamicNode: () => isDynamicNode,
80
97
  isFrameNode: () => isFrameNode,
81
98
  prefetchFlow: () => prefetchFlow,
99
+ renderFieldValue: () => renderFieldValue,
100
+ sanitizeCSS: () => sanitizeCSS,
82
101
  useFlow: () => useFlow,
83
102
  useFlowData: () => useFlowData
84
103
  });
@@ -147,6 +166,43 @@ function collectionKeys(collection) {
147
166
  };
148
167
  }
149
168
 
169
+ // src/ui/Flow/query-options.ts
170
+ function flowQueryOptions(client, slug, id) {
171
+ var _a;
172
+ const identifier = (_a = id != null ? id : slug) != null ? _a : "";
173
+ return {
174
+ queryKey: collectionKeys("flows").detail(identifier),
175
+ queryFn: () => __async(null, null, function* () {
176
+ if (id) return client.from("flows").findById(id);
177
+ const result = yield client.from("flows").find({
178
+ where: { slug: { equals: slug } },
179
+ limit: 1
180
+ });
181
+ const doc = result.docs[0];
182
+ if (!doc) throw new Error(`Flow not found: ${slug}`);
183
+ return doc;
184
+ })
185
+ };
186
+ }
187
+ function nodeTypesQueryOptions(client) {
188
+ return {
189
+ queryKey: collectionKeys("flow-node-types").lists(),
190
+ queryFn: () => __async(null, null, function* () {
191
+ const result = yield client.from("flow-node-types").find({ limit: 0 });
192
+ return result.docs;
193
+ })
194
+ };
195
+ }
196
+ function edgeTypesQueryOptions(client) {
197
+ return {
198
+ queryKey: collectionKeys("flow-edge-types").lists(),
199
+ queryFn: () => __async(null, null, function* () {
200
+ const result = yield client.from("flow-edge-types").find({ limit: 0 });
201
+ return result.docs;
202
+ })
203
+ };
204
+ }
205
+
150
206
  // src/ui/Flow/useFlow.ts
151
207
  function toNodeTypeDef(doc) {
152
208
  var _a, _b, _c, _d, _e, _f;
@@ -179,49 +235,25 @@ function toEdgeTypeDef(doc) {
179
235
  };
180
236
  }
181
237
  function useFlow(options) {
182
- var _a, _b, _c;
238
+ var _a, _b;
183
239
  const { client, slug, id, enabled = true } = options;
184
240
  const hasIdentifier = !!(slug || id);
185
- const identifier = (_a = id != null ? id : slug) != null ? _a : "";
186
241
  const flowQuery = (0, import_react_query.useQuery)(
187
- {
188
- queryKey: collectionKeys("flows").detail(identifier),
189
- queryFn: () => __async(null, null, function* () {
190
- if (id) {
191
- return client.from("flows").findById(id);
192
- }
193
- const result = yield client.from("flows").find({
194
- where: { slug: { equals: slug } },
195
- limit: 1
196
- });
197
- const doc = result.docs[0];
198
- if (!doc) throw new Error(`Flow not found: ${slug}`);
199
- return doc;
200
- }),
242
+ __spreadProps(__spreadValues({}, flowQueryOptions(client, slug, id)), {
201
243
  enabled: enabled && hasIdentifier
202
- },
244
+ }),
203
245
  client.queryClient
204
246
  );
205
247
  const nodeTypesQuery = (0, import_react_query.useQuery)(
206
- {
207
- queryKey: collectionKeys("flow-node-types").lists(),
208
- queryFn: () => __async(null, null, function* () {
209
- const result = yield client.from("flow-node-types").find({ limit: 100 });
210
- return result.docs;
211
- }),
248
+ __spreadProps(__spreadValues({}, nodeTypesQueryOptions(client)), {
212
249
  enabled
213
- },
250
+ }),
214
251
  client.queryClient
215
252
  );
216
253
  const edgeTypesQuery = (0, import_react_query.useQuery)(
217
- {
218
- queryKey: collectionKeys("flow-edge-types").lists(),
219
- queryFn: () => __async(null, null, function* () {
220
- const result = yield client.from("flow-edge-types").find({ limit: 100 });
221
- return result.docs;
222
- }),
254
+ __spreadProps(__spreadValues({}, edgeTypesQueryOptions(client)), {
223
255
  enabled
224
- },
256
+ }),
225
257
  client.queryClient
226
258
  );
227
259
  const nodeTypeDefs = (0, import_react.useMemo)(() => {
@@ -246,44 +278,21 @@ function useFlow(options) {
246
278
  edgeTypeDefs,
247
279
  flow,
248
280
  isLoading: flowQuery.isLoading || nodeTypesQuery.isLoading || edgeTypesQuery.isLoading,
249
- error: (_c = (_b = flowQuery.error) != null ? _b : nodeTypesQuery.error) != null ? _c : edgeTypesQuery.error
281
+ error: (_b = (_a = flowQuery.error) != null ? _a : nodeTypesQuery.error) != null ? _b : edgeTypesQuery.error
250
282
  };
251
283
  }
252
284
 
253
285
  // src/ui/Flow/prefetchFlow.ts
254
286
  function prefetchFlow(options) {
255
287
  return __async(this, null, function* () {
256
- var _a;
257
288
  const { client, slug, id } = options;
258
- const identifier = (_a = id != null ? id : slug) != null ? _a : "";
289
+ if (!slug && !id) {
290
+ throw new Error("prefetchFlow requires either slug or id");
291
+ }
259
292
  yield Promise.all([
260
- client.queryClient.prefetchQuery({
261
- queryKey: collectionKeys("flows").detail(identifier),
262
- queryFn: () => __async(null, null, function* () {
263
- if (id) return client.from("flows").findById(id);
264
- const result = yield client.from("flows").find({
265
- where: { slug: { equals: slug } },
266
- limit: 1
267
- });
268
- const doc = result.docs[0];
269
- if (!doc) throw new Error(`Flow not found: ${slug}`);
270
- return doc;
271
- })
272
- }),
273
- client.queryClient.prefetchQuery({
274
- queryKey: collectionKeys("flow-node-types").lists(),
275
- queryFn: () => __async(null, null, function* () {
276
- const result = yield client.from("flow-node-types").find({ limit: 100 });
277
- return result.docs;
278
- })
279
- }),
280
- client.queryClient.prefetchQuery({
281
- queryKey: collectionKeys("flow-edge-types").lists(),
282
- queryFn: () => __async(null, null, function* () {
283
- const result = yield client.from("flow-edge-types").find({ limit: 100 });
284
- return result.docs;
285
- })
286
- })
293
+ client.queryClient.prefetchQuery(flowQueryOptions(client, slug, id)),
294
+ client.queryClient.prefetchQuery(nodeTypesQueryOptions(client)),
295
+ client.queryClient.prefetchQuery(edgeTypesQueryOptions(client))
287
296
  ]);
288
297
  });
289
298
  }
@@ -342,15 +351,28 @@ function getAbsolutePosition(node, nodeMap) {
342
351
  return { x, y };
343
352
  }
344
353
  function collectDescendants(nodes, rootId) {
354
+ const childrenMap = /* @__PURE__ */ new Map();
355
+ for (const n of nodes) {
356
+ if (n.parentId) {
357
+ let siblings = childrenMap.get(n.parentId);
358
+ if (!siblings) {
359
+ siblings = [];
360
+ childrenMap.set(n.parentId, siblings);
361
+ }
362
+ siblings.push(n.id);
363
+ }
364
+ }
345
365
  const result = /* @__PURE__ */ new Set([rootId]);
346
366
  const queue = [rootId];
347
367
  let i = 0;
348
368
  while (i < queue.length) {
349
- const current = queue[i++];
350
- for (const n of nodes) {
351
- if (n.parentId === current && !result.has(n.id)) {
352
- result.add(n.id);
353
- queue.push(n.id);
369
+ const children = childrenMap.get(queue[i++]);
370
+ if (children) {
371
+ for (const childId of children) {
372
+ if (!result.has(childId)) {
373
+ result.add(childId);
374
+ queue.push(childId);
375
+ }
354
376
  }
355
377
  }
356
378
  }
@@ -423,18 +445,78 @@ function getFrameData(data, frameId) {
423
445
  }
424
446
 
425
447
  // src/ui/Flow/template-compiler.ts
426
- var import_react3 = __toESM(require("react"), 1);
448
+ var import_react4 = __toESM(require("react"), 1);
427
449
  var import_sucrase = require("sucrase");
428
- var MAX_CACHE_SIZE = 100;
429
- var componentCache = /* @__PURE__ */ new Map();
430
- function hashCode(str) {
431
- let hash = 0;
432
- for (let i = 0; i < str.length; i++) {
433
- const char = str.charCodeAt(i);
434
- hash = (hash << 5) - hash + char | 0;
435
- }
436
- return hash.toString(36);
450
+
451
+ // src/ui/Flow/quickjs-loader.ts
452
+ var import_react3 = require("react");
453
+ var _state = { status: "idle", module: null };
454
+ var _listeners = /* @__PURE__ */ new Set();
455
+ function _notify() {
456
+ _listeners.forEach((fn) => fn());
457
+ }
458
+ function subscribeQuickJS(cb) {
459
+ _listeners.add(cb);
460
+ return () => _listeners.delete(cb);
461
+ }
462
+ function getQuickJSSnapshot() {
463
+ return _state.module;
437
464
  }
465
+ function ensureQuickJSLoaded() {
466
+ if (_state.status !== "idle") return;
467
+ _state = { status: "loading", module: null };
468
+ import("quickjs-emscripten").then(
469
+ (mod) => mod.getQuickJS()
470
+ ).then((module2) => {
471
+ _state = { status: "ready", module: module2 };
472
+ _notify();
473
+ }).catch(() => {
474
+ _state = { status: "failed", module: null };
475
+ _notify();
476
+ });
477
+ }
478
+ function useQuickJS() {
479
+ return (0, import_react3.useSyncExternalStore)(subscribeQuickJS, getQuickJSSnapshot, () => null);
480
+ }
481
+
482
+ // src/ui/Flow/template-compiler.ts
483
+ var MAX_CACHE_SIZE = 100;
484
+ var MAX_MATERIALIZE_DEPTH = 20;
485
+ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
486
+ "div",
487
+ "span",
488
+ "p",
489
+ "h1",
490
+ "h2",
491
+ "h3",
492
+ "h4",
493
+ "h5",
494
+ "h6",
495
+ "ul",
496
+ "ol",
497
+ "li",
498
+ "strong",
499
+ "em",
500
+ "b",
501
+ "i",
502
+ "br",
503
+ "img",
504
+ "figure",
505
+ "section",
506
+ "table",
507
+ "thead",
508
+ "tbody",
509
+ "tr",
510
+ "th",
511
+ "td",
512
+ "pre",
513
+ "code",
514
+ "svg",
515
+ "path",
516
+ "g",
517
+ "circle",
518
+ "rect"
519
+ ]);
438
520
  var BLOCKED_PATTERNS = [
439
521
  /\bdocument\s*\./,
440
522
  /\bwindow\s*\./,
@@ -454,75 +536,219 @@ var BLOCKED_PATTERNS = [
454
536
  /\bsetTimeout\s*\(/,
455
537
  /\bsetInterval\s*\(/,
456
538
  /\bsetImmediate\s*\(/,
457
- /\brequire\s*\(/
539
+ /\brequire\s*\(/,
540
+ /\bself\b/,
541
+ /\bconstructor\s*\.\s*constructor/,
542
+ /\bReflect\b/,
543
+ /\bProxy\b/,
544
+ /\b__proto__\b/
458
545
  ];
546
+ var VM_SETUP = `
547
+ var React = {
548
+ createElement: function(type, props) {
549
+ var args = Array.prototype.slice.call(arguments, 2);
550
+ return { $$t: 'el', type: String(type), props: props || {}, ch: args };
551
+ },
552
+ Fragment: '__frag__'
553
+ };
554
+ var exports = {};
555
+ var module = { exports: exports };
556
+ var window = undefined;
557
+ var document = undefined;
558
+ var globalThis = undefined;
559
+ var self = undefined;
560
+ var setTimeout = undefined;
561
+ var setInterval = undefined;
562
+ var setImmediate = undefined;
563
+ var fetch = undefined;
564
+ var XMLHttpRequest = undefined;
565
+ var navigator = undefined;
566
+ var location = undefined;
567
+ var localStorage = undefined;
568
+ var sessionStorage = undefined;
569
+ var cookie = undefined;
570
+ var postMessage = undefined;
571
+ `;
572
+ var rendererCache = /* @__PURE__ */ new Map();
573
+ function hashCode(str) {
574
+ let hash = 0;
575
+ for (let i = 0; i < str.length; i++) {
576
+ const char = str.charCodeAt(i);
577
+ hash = (hash << 5) - hash + char | 0;
578
+ }
579
+ return hash.toString(36);
580
+ }
459
581
  function validateTemplateCode(code) {
460
582
  return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
461
583
  }
462
- function compileTemplate(code, slug) {
463
- const cacheKey = `${slug}:${hashCode(code)}`;
464
- if (componentCache.has(cacheKey)) {
465
- const cached = componentCache.get(cacheKey);
466
- componentCache.delete(cacheKey);
467
- componentCache.set(cacheKey, cached);
468
- return cached;
584
+ var SAFE_DATA_URI_PREFIXES = [
585
+ "data:image/png;",
586
+ "data:image/jpeg;",
587
+ "data:image/gif;",
588
+ "data:image/webp;",
589
+ "data:image/avif;"
590
+ ];
591
+ function isSafeUrl(value) {
592
+ if (typeof value !== "string") return false;
593
+ if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) return true;
594
+ if (SAFE_DATA_URI_PREFIXES.some((p) => value.startsWith(p))) return true;
595
+ try {
596
+ const url = new URL(value);
597
+ return url.protocol === "http:" || url.protocol === "https:";
598
+ } catch (e) {
599
+ return false;
469
600
  }
470
- if (!validateTemplateCode(code)) {
471
- console.warn(`[flow] Template "${slug}" contains blocked patterns`);
472
- return null;
601
+ }
602
+ var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
603
+ "backgroundImage",
604
+ "background",
605
+ "listStyleImage",
606
+ "content",
607
+ "borderImage",
608
+ "borderImageSource",
609
+ "maskImage",
610
+ "mask",
611
+ "filter",
612
+ "cursor"
613
+ ]);
614
+ function sanitizeStyle(style) {
615
+ if (!style || typeof style !== "object" || Array.isArray(style)) return {};
616
+ const safe = {};
617
+ for (const [k, v] of Object.entries(style)) {
618
+ if (BLOCKED_STYLE_PROPS.has(k)) continue;
619
+ if (typeof v === "string" && /url\s*\(/i.test(v)) continue;
620
+ if (k === "position" && typeof v === "string" && /fixed|sticky/i.test(v)) continue;
621
+ if (k === "zIndex") {
622
+ const n = typeof v === "number" ? v : parseInt(String(v), 10);
623
+ if (isNaN(n) || n > 9998) continue;
624
+ }
625
+ safe[k] = v;
626
+ }
627
+ return safe;
628
+ }
629
+ function materialize(node, depth = 0) {
630
+ var _a;
631
+ if (depth > MAX_MATERIALIZE_DEPTH) return null;
632
+ if (node == null) return null;
633
+ if (typeof node === "string" || typeof node === "number") return node;
634
+ if (Array.isArray(node)) {
635
+ return import_react4.default.createElement(
636
+ import_react4.default.Fragment,
637
+ null,
638
+ ...node.map((c) => materialize(c, depth + 1))
639
+ );
473
640
  }
641
+ if (typeof node !== "object") return null;
642
+ const d = node;
643
+ if (d.$$t === "frag") {
644
+ const ch = Array.isArray(d.ch) ? d.ch : [];
645
+ return import_react4.default.createElement(
646
+ import_react4.default.Fragment,
647
+ null,
648
+ ...ch.map((c) => materialize(c, depth + 1))
649
+ );
650
+ }
651
+ if (d.$$t === "el") {
652
+ const type = String((_a = d.type) != null ? _a : "");
653
+ const props = d.props && typeof d.props === "object" ? d.props : {};
654
+ const ch = Array.isArray(d.ch) ? d.ch : [];
655
+ if (!ALLOWED_ELEMENTS.has(type.toLowerCase())) return null;
656
+ const safeProps = {};
657
+ for (const [k, v] of Object.entries(props)) {
658
+ if (k.startsWith("on")) continue;
659
+ if (k === "dangerouslySetInnerHTML" || k === "ref") continue;
660
+ if ((k === "src" || k === "href") && !isSafeUrl(v)) continue;
661
+ if (k === "style") {
662
+ safeProps[k] = sanitizeStyle(v);
663
+ continue;
664
+ }
665
+ safeProps[k] = v;
666
+ }
667
+ const children = ch.map((c) => materialize(c, depth + 1));
668
+ return import_react4.default.createElement(type, safeProps, ...children);
669
+ }
670
+ return null;
671
+ }
672
+ function evalInContext(vm, code) {
673
+ const result = vm.evalCode(code);
674
+ if (result.error) {
675
+ result.error.dispose();
676
+ return { ok: false };
677
+ }
678
+ return { ok: true, handle: result.value };
679
+ }
680
+ var MAX_VM_CYCLES = 2e5;
681
+ function makeRenderer(jsCode) {
682
+ return function runInQuickJS(qjs, props) {
683
+ const vm = qjs.newContext();
684
+ let cycles = 0;
685
+ vm.runtime.setInterruptHandler(() => {
686
+ cycles++;
687
+ return cycles > MAX_VM_CYCLES;
688
+ });
689
+ try {
690
+ const setupResult = evalInContext(vm, VM_SETUP + jsCode);
691
+ if (!setupResult.ok) return null;
692
+ setupResult.handle.dispose();
693
+ const propsJson = JSON.stringify(props);
694
+ const callCode = `(function(){var __c=module.exports.default||module.exports;return JSON.stringify(__c(${propsJson}));})();`;
695
+ const callResult = evalInContext(vm, callCode);
696
+ if (!callResult.ok) return null;
697
+ const raw = vm.dump(callResult.handle);
698
+ callResult.handle.dispose();
699
+ return materialize(JSON.parse(String(raw)));
700
+ } catch (e) {
701
+ return null;
702
+ } finally {
703
+ vm.dispose();
704
+ }
705
+ };
706
+ }
707
+ function makeReactFC(renderer, qjs) {
708
+ return function QuickJSTemplateComponent(props) {
709
+ var _a;
710
+ return (_a = renderer(qjs, props)) != null ? _a : null;
711
+ };
712
+ }
713
+ function compileTemplate(code, slug) {
714
+ ensureQuickJSLoaded();
715
+ if (!validateTemplateCode(code)) return null;
716
+ const qjs = getQuickJSSnapshot();
717
+ if (!qjs) return null;
718
+ const cacheKey = `${slug}:${code.length}:${hashCode(code)}`;
719
+ if (rendererCache.has(cacheKey)) {
720
+ const entry = rendererCache.get(cacheKey);
721
+ rendererCache.delete(cacheKey);
722
+ rendererCache.set(cacheKey, entry);
723
+ return entry.fc;
724
+ }
725
+ let jsCode;
474
726
  try {
475
- const { code: jsCode } = (0, import_sucrase.transform)(code, {
727
+ const result = (0, import_sucrase.transform)(code, {
476
728
  transforms: ["typescript", "jsx", "imports"],
477
729
  jsxRuntime: "classic",
478
730
  jsxPragma: "React.createElement",
479
731
  jsxFragmentPragma: "React.Fragment"
480
732
  });
481
- const factory = new Function(
482
- "React",
483
- `
484
- var window = undefined;
485
- var document = undefined;
486
- var globalThis = undefined;
487
- var setTimeout = undefined;
488
- var setInterval = undefined;
489
- var setImmediate = undefined;
490
- var fetch = undefined;
491
- var XMLHttpRequest = undefined;
492
- var navigator = undefined;
493
- var location = undefined;
494
- var exports = {};
495
- var module = { exports: exports };
496
- ${jsCode}
497
- return module.exports.default || module.exports;
498
- `
499
- );
500
- const Component = factory(import_react3.default);
501
- if (typeof Component !== "function") return null;
502
- if (componentCache.size >= MAX_CACHE_SIZE) {
503
- const oldestKey = componentCache.keys().next().value;
504
- if (oldestKey) componentCache.delete(oldestKey);
505
- }
506
- componentCache.set(cacheKey, Component);
507
- return Component;
733
+ jsCode = result.code;
508
734
  } catch (e) {
509
- console.warn(`[flow] Failed to compile template for "${slug}":`, e);
510
735
  return null;
511
736
  }
737
+ const renderer = makeRenderer(jsCode);
738
+ const fc = makeReactFC(renderer, qjs);
739
+ if (rendererCache.size >= MAX_CACHE_SIZE) {
740
+ const oldest = rendererCache.keys().next().value;
741
+ if (oldest) rendererCache.delete(oldest);
742
+ }
743
+ rendererCache.set(cacheKey, { renderer, fc });
744
+ return fc;
512
745
  }
513
746
  function clearTemplateCache() {
514
- componentCache.clear();
747
+ rendererCache.clear();
515
748
  }
516
749
 
517
- // src/ui/Flow/FlowRenderer.tsx
518
- var import_react9 = __toESM(require("react"), 1);
519
- var import_react10 = require("@xyflow/react");
520
-
521
- // src/ui/Flow/node-types-factory.tsx
522
- var import_react5 = __toESM(require("react"), 1);
523
-
524
750
  // src/ui/Flow/node-renderers.tsx
525
- var import_react4 = __toESM(require("react"), 1);
751
+ var import_react5 = __toESM(require("react"), 1);
526
752
  function sanitizeUrl(url) {
527
753
  if (!url) return url;
528
754
  try {
@@ -540,7 +766,7 @@ function renderFieldValue(key, val, fieldDef) {
540
766
  const imgUrl = typeof val === "string" ? val : val == null ? void 0 : val.url;
541
767
  const safeUrl = sanitizeUrl(imgUrl);
542
768
  if (!safeUrl) return null;
543
- return /* @__PURE__ */ import_react4.default.createElement(
769
+ return /* @__PURE__ */ import_react5.default.createElement(
544
770
  "img",
545
771
  {
546
772
  key,
@@ -551,7 +777,7 @@ function renderFieldValue(key, val, fieldDef) {
551
777
  }
552
778
  );
553
779
  }
554
- return /* @__PURE__ */ import_react4.default.createElement(
780
+ return /* @__PURE__ */ import_react5.default.createElement(
555
781
  "div",
556
782
  {
557
783
  key,
@@ -568,7 +794,7 @@ function renderFieldValue(key, val, fieldDef) {
568
794
  }
569
795
  function DefaultDynamicNode({ data }) {
570
796
  const d = data;
571
- return /* @__PURE__ */ import_react4.default.createElement(
797
+ return /* @__PURE__ */ import_react5.default.createElement(
572
798
  "div",
573
799
  {
574
800
  style: {
@@ -581,7 +807,7 @@ function DefaultDynamicNode({ data }) {
581
807
  d.fields && Object.entries(d.fields).filter(([, v]) => v != null && v !== "").map(([key, val]) => renderFieldValue(key, val))
582
808
  );
583
809
  }
584
- var TemplateErrorBoundary = class extends import_react4.default.Component {
810
+ var TemplateErrorBoundary = class extends import_react5.default.Component {
585
811
  constructor() {
586
812
  super(...arguments);
587
813
  this.state = { error: null };
@@ -596,7 +822,7 @@ var TemplateErrorBoundary = class extends import_react4.default.Component {
596
822
  }
597
823
  render() {
598
824
  if (this.state.error) {
599
- return /* @__PURE__ */ import_react4.default.createElement("div", { style: { padding: 8, fontSize: 11, color: "#ef4444" } }, /* @__PURE__ */ import_react4.default.createElement("strong", null, "Render error"), /* @__PURE__ */ import_react4.default.createElement("pre", { style: { fontSize: 10, whiteSpace: "pre-wrap" } }, this.state.error.message));
825
+ return /* @__PURE__ */ import_react5.default.createElement("div", { style: { padding: 8, fontSize: 11, color: "#ef4444" } }, /* @__PURE__ */ import_react5.default.createElement("strong", null, "Render error"), /* @__PURE__ */ import_react5.default.createElement("pre", { style: { fontSize: 10, whiteSpace: "pre-wrap" } }, process.env.NODE_ENV === "development" ? this.state.error.message : "Template render failed"));
600
826
  }
601
827
  return this.props.children;
602
828
  }
@@ -607,16 +833,17 @@ function EnhancedDynamicNode({
607
833
  width,
608
834
  height
609
835
  }) {
836
+ useQuickJS();
610
837
  if (typeDef.template) {
611
838
  const Component = compileTemplate(typeDef.template, typeDef.slug);
612
839
  if (Component) {
613
- return /* @__PURE__ */ import_react4.default.createElement(
840
+ return /* @__PURE__ */ import_react5.default.createElement(
614
841
  "div",
615
842
  {
616
843
  className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? " flow-node--transparent-bg" : ""}`,
617
844
  style: { width: "100%", height: "100%" }
618
845
  },
619
- /* @__PURE__ */ import_react4.default.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ import_react4.default.createElement(
846
+ /* @__PURE__ */ import_react5.default.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ import_react5.default.createElement(
620
847
  Component,
621
848
  {
622
849
  fields: data.fields,
@@ -630,9 +857,10 @@ function EnhancedDynamicNode({
630
857
  );
631
858
  }
632
859
  }
633
- return /* @__PURE__ */ import_react4.default.createElement(
860
+ return /* @__PURE__ */ import_react5.default.createElement(
634
861
  "div",
635
862
  {
863
+ className: `flow-node flow-node--${typeDef.slug}`,
636
864
  style: {
637
865
  width: "100%",
638
866
  height: "100%",
@@ -659,7 +887,7 @@ function DefaultFrameNode({ data }) {
659
887
  if (m) return `rgba(${m[1]},${m[2]},${m[3]},${opacity})`;
660
888
  return baseColor;
661
889
  })();
662
- return /* @__PURE__ */ import_react4.default.createElement(
890
+ return /* @__PURE__ */ import_react5.default.createElement(
663
891
  "div",
664
892
  {
665
893
  style: {
@@ -670,7 +898,7 @@ function DefaultFrameNode({ data }) {
670
898
  border: borderStyle === "none" ? "none" : `2px ${borderStyle} rgba(128,128,128,0.3)`
671
899
  }
672
900
  },
673
- /* @__PURE__ */ import_react4.default.createElement(
901
+ /* @__PURE__ */ import_react5.default.createElement(
674
902
  "div",
675
903
  {
676
904
  style: {
@@ -685,14 +913,298 @@ function DefaultFrameNode({ data }) {
685
913
  );
686
914
  }
687
915
 
916
+ // src/ui/Flow/field-helpers.ts
917
+ function getImageField(fields, name) {
918
+ const val = fields == null ? void 0 : fields[name];
919
+ if (val && typeof val === "object" && val !== null && "url" in val)
920
+ return val;
921
+ return void 0;
922
+ }
923
+ function getTextField(fields, name) {
924
+ const val = fields == null ? void 0 : fields[name];
925
+ return typeof val === "string" ? val : void 0;
926
+ }
927
+ function getNumberField(fields, name) {
928
+ const val = fields == null ? void 0 : fields[name];
929
+ return typeof val === "number" ? val : void 0;
930
+ }
931
+ function getBooleanField(fields, name) {
932
+ const val = fields == null ? void 0 : fields[name];
933
+ return typeof val === "boolean" ? val : void 0;
934
+ }
935
+
936
+ // src/ui/Flow/css-sanitizer.ts
937
+ var import_postcss = __toESM(require("postcss"), 1);
938
+ var ALLOWED_AT_RULES = /* @__PURE__ */ new Set(["keyframes", "media", "supports", "container", "layer"]);
939
+ var BLOCKED_VALUE_PATTERNS = [/url\s*\(/i, /expression\s*\(/i, /paint\s*\(/i, /-moz-binding/i];
940
+ var DANGEROUS_SELECTOR = /(?:^|[\s,])(?::root|html|body)\b/;
941
+ var BLOCKED_PROPERTY_VALUES = {
942
+ // absolute is allowed — contained by nearest positioned ancestor (node wrapper)
943
+ position: /fixed|sticky/i
944
+ };
945
+ var Z_INDEX_MAX = 9998;
946
+ function sanitizeCSS(css, scopeClass) {
947
+ let root;
948
+ try {
949
+ root = import_postcss.default.parse(css);
950
+ } catch (e) {
951
+ return "";
952
+ }
953
+ const safeScopeClass = scopeClass ? scopeClass.replace(/[{}()[\];,'"\\<>\s]/g, "") : "";
954
+ const keyframeNameMap = /* @__PURE__ */ new Map();
955
+ if (safeScopeClass) {
956
+ root.walkAtRules(/^keyframes$/i, (node) => {
957
+ const rawName = node.params.trim().replace(/^['"]|['"]$/g, "");
958
+ const originalName = rawName;
959
+ const scopedName = `${safeScopeClass}__${rawName.replace(/\s+/g, "_")}`;
960
+ keyframeNameMap.set(originalName, scopedName);
961
+ node.params = scopedName;
962
+ });
963
+ }
964
+ if (safeScopeClass && keyframeNameMap.size > 0) {
965
+ const ANIM_KEYWORDS = /* @__PURE__ */ new Set(["none", "initial", "inherit", "unset", "revert"]);
966
+ const replaceAnimName = (token) => {
967
+ var _a;
968
+ const t = token.trim().replace(/^['"]|['"]$/g, "");
969
+ return ANIM_KEYWORDS.has(t) ? token : (_a = keyframeNameMap.get(t)) != null ? _a : token;
970
+ };
971
+ root.walkDecls(/^animation-name$/i, (node) => {
972
+ node.value = node.value.split(",").map(replaceAnimName).join(", ");
973
+ });
974
+ root.walkDecls(/^animation$/i, (node) => {
975
+ node.value = node.value.split(",").map(
976
+ (anim) => anim.split(/(\s+)/).map((token) => /\s/.test(token) ? token : replaceAnimName(token)).join("")
977
+ ).join(",");
978
+ });
979
+ }
980
+ root.walkAtRules((node) => {
981
+ if (!ALLOWED_AT_RULES.has(node.name.toLowerCase())) {
982
+ node.remove();
983
+ }
984
+ });
985
+ root.walkDecls((node) => {
986
+ const normalizedValue = node.value.replace(/\/\*[\s\S]*?\*\//g, "");
987
+ if (BLOCKED_VALUE_PATTERNS.some((p) => p.test(normalizedValue))) {
988
+ node.remove();
989
+ return;
990
+ }
991
+ const propLower = node.prop.toLowerCase();
992
+ const blockedValuePattern = BLOCKED_PROPERTY_VALUES[propLower];
993
+ if (blockedValuePattern) {
994
+ const deescaped = normalizedValue.replace(
995
+ /\\([0-9a-fA-F]{1,6})\s?/g,
996
+ (_, hex) => String.fromCodePoint(parseInt(hex, 16))
997
+ );
998
+ if (blockedValuePattern.test(deescaped)) {
999
+ node.remove();
1000
+ return;
1001
+ }
1002
+ }
1003
+ if (propLower === "z-index") {
1004
+ const zv = node.value.trim();
1005
+ if (zv === "auto" || zv === "initial" || zv === "inherit" || zv === "unset" || zv === "revert") {
1006
+ } else {
1007
+ const val = parseInt(zv, 10);
1008
+ if (isNaN(val) || String(val) !== zv || val > Z_INDEX_MAX) {
1009
+ node.remove();
1010
+ return;
1011
+ }
1012
+ }
1013
+ }
1014
+ });
1015
+ root.walkRules((rule) => {
1016
+ rule.selectors = rule.selectors.filter((s) => !DANGEROUS_SELECTOR.test(s));
1017
+ if (!rule.selectors.length) {
1018
+ rule.remove();
1019
+ return;
1020
+ }
1021
+ if (scopeClass) {
1022
+ if (!safeScopeClass) {
1023
+ rule.remove();
1024
+ return;
1025
+ }
1026
+ const parent = rule.parent;
1027
+ if ((parent == null ? void 0 : parent.type) === "atrule" && parent.name.toLowerCase() === "keyframes") {
1028
+ return;
1029
+ }
1030
+ rule.selectors = rule.selectors.map((sel) => `.${safeScopeClass} ${sel}`);
1031
+ }
1032
+ });
1033
+ return root.toString().replace(/<\/style/gi, "<\\/style");
1034
+ }
1035
+
1036
+ // src/ui/Flow/FlowRenderer.tsx
1037
+ var import_react11 = __toESM(require("react"), 1);
1038
+ var import_react12 = require("@xyflow/react");
1039
+
1040
+ // src/ui/Flow/node-types-factory.tsx
1041
+ var import_react7 = __toESM(require("react"), 1);
1042
+
1043
+ // src/ui/Image/index.tsx
1044
+ var import_react6 = __toESM(require("react"), 1);
1045
+
1046
+ // src/utils/image.ts
1047
+ var IMAGE_SIZES = [384, 768, 1536];
1048
+ function getImageSrcSet(image) {
1049
+ const parts = [];
1050
+ const sizes = image.sizes;
1051
+ if (sizes) {
1052
+ for (const size of IMAGE_SIZES) {
1053
+ const entry = sizes[String(size)];
1054
+ if ((entry == null ? void 0 : entry.url) && entry.width) {
1055
+ parts.push(`${entry.url} ${entry.width}w`);
1056
+ }
1057
+ }
1058
+ }
1059
+ if (image.url && image.width) {
1060
+ parts.push(`${image.url} ${image.width}w`);
1061
+ }
1062
+ return parts.join(", ");
1063
+ }
1064
+ function getImagePlaceholderStyle(image, options) {
1065
+ var _a, _b, _c;
1066
+ const type = (_a = options == null ? void 0 : options.type) != null ? _a : "blur";
1067
+ const paletteColor = (_b = options == null ? void 0 : options.paletteColor) != null ? _b : "muted";
1068
+ if (type === "none") return {};
1069
+ const color = (_c = image.palette) == null ? void 0 : _c[paletteColor];
1070
+ if (type === "blur") {
1071
+ const lqip = image.lqip;
1072
+ if (lqip) {
1073
+ return {
1074
+ backgroundImage: `url(${lqip})`,
1075
+ backgroundSize: "cover",
1076
+ backgroundPosition: "center"
1077
+ };
1078
+ }
1079
+ if (color) {
1080
+ return { backgroundColor: color };
1081
+ }
1082
+ return {};
1083
+ }
1084
+ if (color) {
1085
+ return { backgroundColor: color };
1086
+ }
1087
+ return {};
1088
+ }
1089
+
1090
+ // src/ui/Image/index.tsx
1091
+ function Image({
1092
+ image,
1093
+ width,
1094
+ dpr = 1,
1095
+ placeholder: placeholderProp,
1096
+ className,
1097
+ style,
1098
+ imgClassName,
1099
+ imgStyle,
1100
+ sizes,
1101
+ loading: loadingProp,
1102
+ onLoad,
1103
+ objectFit = "cover",
1104
+ priority = false,
1105
+ fill = false,
1106
+ imageRendering
1107
+ }) {
1108
+ var _a, _b;
1109
+ const [loaded, setLoaded] = (0, import_react6.useState)(false);
1110
+ const firedRef = (0, import_react6.useRef)(false);
1111
+ const isPixelRendering = imageRendering === "pixelated" || imageRendering === "crisp-edges";
1112
+ const placeholder = placeholderProp != null ? placeholderProp : isPixelRendering ? "none" : "blur";
1113
+ const loading = priority ? "eager" : loadingProp != null ? loadingProp : "lazy";
1114
+ const aspectRatio = !fill && image.width && image.height ? `${image.width} / ${image.height}` : void 0;
1115
+ const srcSet = getImageSrcSet(image);
1116
+ const src = (_a = image.url) != null ? _a : void 0;
1117
+ const hasLqip = placeholder === "blur" && !!image.lqip;
1118
+ const placeholderStyle = getImagePlaceholderStyle(image, {
1119
+ type: placeholder
1120
+ });
1121
+ const placeholderColor = !hasLqip && "backgroundColor" in placeholderStyle ? placeholderStyle.backgroundColor : void 0;
1122
+ const fireLoad = (0, import_react6.useCallback)(() => {
1123
+ if (firedRef.current) return;
1124
+ firedRef.current = true;
1125
+ setLoaded(true);
1126
+ onLoad == null ? void 0 : onLoad();
1127
+ }, [onLoad]);
1128
+ const imgRef = (0, import_react6.useCallback)(
1129
+ (node) => {
1130
+ if (node && node.complete && node.naturalWidth > 0) {
1131
+ fireLoad();
1132
+ }
1133
+ },
1134
+ [fireLoad]
1135
+ );
1136
+ const containerStyle = __spreadValues(__spreadValues(__spreadValues({
1137
+ position: "relative",
1138
+ overflow: "hidden"
1139
+ }, fill ? { width: "100%", height: "100%" } : {}), aspectRatio ? { aspectRatio } : {}), style);
1140
+ const overlayBase = {
1141
+ position: "absolute",
1142
+ top: 0,
1143
+ left: 0,
1144
+ width: "100%",
1145
+ height: "100%",
1146
+ opacity: loaded ? 0 : 1,
1147
+ transition: "opacity 0.3s ease",
1148
+ pointerEvents: "none"
1149
+ };
1150
+ const mainImgStyle = __spreadValues(__spreadProps(__spreadValues({
1151
+ display: "block",
1152
+ width: "100%",
1153
+ height: "100%",
1154
+ objectFit
1155
+ }, imageRendering ? { imageRendering } : {}), {
1156
+ opacity: loaded ? 1 : 0,
1157
+ transition: "opacity 0.3s ease"
1158
+ }), imgStyle);
1159
+ return /* @__PURE__ */ import_react6.default.createElement("div", { className, style: containerStyle }, hasLqip && /* @__PURE__ */ import_react6.default.createElement(
1160
+ "img",
1161
+ {
1162
+ "aria-hidden": true,
1163
+ alt: "",
1164
+ src: image.lqip,
1165
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1166
+ display: "block",
1167
+ objectFit,
1168
+ filter: "blur(20px)",
1169
+ transform: "scale(1.1)"
1170
+ })
1171
+ }
1172
+ ), placeholderColor && /* @__PURE__ */ import_react6.default.createElement(
1173
+ "div",
1174
+ {
1175
+ "aria-hidden": true,
1176
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1177
+ backgroundColor: placeholderColor
1178
+ })
1179
+ }
1180
+ ), /* @__PURE__ */ import_react6.default.createElement(
1181
+ "img",
1182
+ {
1183
+ ref: imgRef,
1184
+ alt: (_b = image.alt) != null ? _b : "",
1185
+ src,
1186
+ srcSet: srcSet || void 0,
1187
+ sizes,
1188
+ width: width ? width * dpr : void 0,
1189
+ loading,
1190
+ decoding: "async",
1191
+ fetchPriority: priority ? "high" : void 0,
1192
+ onLoad: fireLoad,
1193
+ className: imgClassName,
1194
+ style: mainImgStyle
1195
+ }
1196
+ ));
1197
+ }
1198
+
688
1199
  // src/ui/Flow/node-types-factory.tsx
689
1200
  function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
690
1201
  const types = {};
691
1202
  types.dynamic = ((props) => {
1203
+ var _a;
692
1204
  const d = props.data;
693
1205
  const typeDef = nodeTypeDefsMap == null ? void 0 : nodeTypeDefsMap.get(d.nodeTypeSlug);
694
1206
  const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
695
- const defaultRender = typeDef ? /* @__PURE__ */ import_react5.default.createElement(
1207
+ const defaultRender = typeDef ? /* @__PURE__ */ import_react7.default.createElement(
696
1208
  EnhancedDynamicNode,
697
1209
  {
698
1210
  data: d,
@@ -700,7 +1212,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
700
1212
  width: props.width,
701
1213
  height: props.height
702
1214
  }
703
- ) : /* @__PURE__ */ import_react5.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
1215
+ ) : /* @__PURE__ */ import_react7.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
704
1216
  const slotProps = {
705
1217
  id: props.id,
706
1218
  nodeTypeSlug: d.nodeTypeSlug,
@@ -712,14 +1224,35 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
712
1224
  height: props.height,
713
1225
  defaultRender
714
1226
  };
715
- let content = CustomRenderer ? /* @__PURE__ */ import_react5.default.createElement(CustomRenderer, __spreadValues({}, slotProps)) : defaultRender;
1227
+ let content;
1228
+ if (CustomRenderer) {
1229
+ content = /* @__PURE__ */ import_react7.default.createElement(CustomRenderer, __spreadValues({}, slotProps));
1230
+ } else if (d.nodeTypeSlug === "image") {
1231
+ const imageVal = (_a = d.fields) == null ? void 0 : _a.image;
1232
+ if (imageVal && typeof imageVal === "object" && "url" in imageVal) {
1233
+ content = /* @__PURE__ */ import_react7.default.createElement(
1234
+ Image,
1235
+ {
1236
+ image: imageVal,
1237
+ width: props.width,
1238
+ fill: true,
1239
+ priority: true,
1240
+ objectFit: "contain"
1241
+ }
1242
+ );
1243
+ } else {
1244
+ content = defaultRender;
1245
+ }
1246
+ } else {
1247
+ content = defaultRender;
1248
+ }
716
1249
  if (renderNode) {
717
1250
  const result = renderNode(slotProps, content);
718
1251
  if (result !== null) content = result;
719
1252
  }
720
1253
  if (nodeWrapper) {
721
1254
  const Wrapper = nodeWrapper;
722
- content = /* @__PURE__ */ import_react5.default.createElement(
1255
+ content = /* @__PURE__ */ import_react7.default.createElement(
723
1256
  Wrapper,
724
1257
  {
725
1258
  id: props.id,
@@ -736,7 +1269,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
736
1269
  types.frame = frameRenderer ? ((props) => {
737
1270
  const d = props.data;
738
1271
  const Renderer = frameRenderer;
739
- return /* @__PURE__ */ import_react5.default.createElement(
1272
+ return /* @__PURE__ */ import_react7.default.createElement(
740
1273
  Renderer,
741
1274
  {
742
1275
  id: props.id,
@@ -760,7 +1293,7 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
760
1293
  types[slug] = ((props) => {
761
1294
  var _a;
762
1295
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
763
- return /* @__PURE__ */ import_react5.default.createElement(
1296
+ return /* @__PURE__ */ import_react7.default.createElement(
764
1297
  Renderer,
765
1298
  {
766
1299
  id: props.id,
@@ -779,10 +1312,10 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
779
1312
  }
780
1313
 
781
1314
  // src/ui/Flow/edge-styles.ts
782
- var import_react6 = require("@xyflow/react");
1315
+ var import_react8 = require("@xyflow/react");
783
1316
  function toMarkerType(value) {
784
- if (value === "arrow") return import_react6.MarkerType.Arrow;
785
- if (value === "arrowclosed") return import_react6.MarkerType.ArrowClosed;
1317
+ if (value === "arrow") return import_react8.MarkerType.Arrow;
1318
+ if (value === "arrowclosed") return import_react8.MarkerType.ArrowClosed;
786
1319
  return void 0;
787
1320
  }
788
1321
  var EDGE_TYPE_MAP = {
@@ -826,8 +1359,8 @@ function applyEdgeStyles(edges, edgeTypeDefsMap) {
826
1359
  }
827
1360
 
828
1361
  // src/ui/Flow/focus-handler.tsx
829
- var import_react7 = __toESM(require("react"), 1);
830
- var import_react8 = require("@xyflow/react");
1362
+ var import_react9 = __toESM(require("react"), 1);
1363
+ var import_react10 = require("@xyflow/react");
831
1364
  function clampViewport(vp, cw, ch, extent) {
832
1365
  const left = -vp.x / vp.zoom;
833
1366
  const right = (cw - vp.x) / vp.zoom;
@@ -853,16 +1386,16 @@ function FocusHandler({
853
1386
  minZoomProp,
854
1387
  onInitialFit
855
1388
  }) {
856
- const { setViewport } = (0, import_react8.useReactFlow)();
857
- const store = (0, import_react8.useStoreApi)();
858
- const containerRef = import_react7.default.useRef(null);
1389
+ const { setViewport } = (0, import_react10.useReactFlow)();
1390
+ const store = (0, import_react10.useStoreApi)();
1391
+ const containerRef = import_react9.default.useRef(null);
859
1392
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
860
- const boundsRef = import_react7.default.useRef(bounds);
1393
+ const boundsRef = import_react9.default.useRef(bounds);
861
1394
  boundsRef.current = bounds;
862
- const [containerSize, setContainerSize] = import_react7.default.useState({ w: 0, h: 0 });
863
- const prevBoundsKeyRef = import_react7.default.useRef(null);
864
- const prevSizeRef = import_react7.default.useRef({ w: 0, h: 0 });
865
- import_react7.default.useEffect(() => {
1395
+ const [containerSize, setContainerSize] = import_react9.default.useState({ w: 0, h: 0 });
1396
+ const prevBoundsKeyRef = import_react9.default.useRef(null);
1397
+ const prevSizeRef = import_react9.default.useRef({ w: 0, h: 0 });
1398
+ import_react9.default.useEffect(() => {
866
1399
  const el = containerRef.current;
867
1400
  if (!el) return;
868
1401
  const observer = new ResizeObserver((entries) => {
@@ -874,7 +1407,7 @@ function FocusHandler({
874
1407
  observer.observe(el);
875
1408
  return () => observer.disconnect();
876
1409
  }, []);
877
- import_react7.default.useEffect(() => {
1410
+ import_react9.default.useEffect(() => {
878
1411
  if (containerSize.w === 0 || containerSize.h === 0) return;
879
1412
  const prevKey = prevBoundsKeyRef.current;
880
1413
  const prevSize = prevSizeRef.current;
@@ -941,7 +1474,7 @@ function FocusHandler({
941
1474
  store,
942
1475
  onInitialFit
943
1476
  ]);
944
- return /* @__PURE__ */ import_react7.default.createElement(
1477
+ return /* @__PURE__ */ import_react9.default.createElement(
945
1478
  "div",
946
1479
  {
947
1480
  ref: containerRef,
@@ -956,6 +1489,7 @@ function FocusHandler({
956
1489
  }
957
1490
 
958
1491
  // src/ui/Flow/FlowRenderer.tsx
1492
+ var NullFrameRenderer = () => null;
959
1493
  function FlowRenderer({
960
1494
  data,
961
1495
  className,
@@ -973,6 +1507,7 @@ function FlowRenderer({
973
1507
  onNodeMouseLeave,
974
1508
  onEdgeClick,
975
1509
  frameRenderer,
1510
+ hideFrames = false,
976
1511
  edgeRenderers,
977
1512
  nodeWrapper,
978
1513
  controls,
@@ -993,33 +1528,34 @@ function FlowRenderer({
993
1528
  maxZoom: maxZoomProp
994
1529
  }) {
995
1530
  var _a, _b;
996
- const nodeTypeDefsMap = import_react9.default.useMemo(() => {
1531
+ const resolvedFrameRenderer = hideFrames ? NullFrameRenderer : frameRenderer;
1532
+ const nodeTypeDefsMap = import_react11.default.useMemo(() => {
997
1533
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
998
1534
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
999
1535
  }, [nodeTypeDefs]);
1000
- const edgeTypeDefsMap = import_react9.default.useMemo(() => {
1536
+ const edgeTypeDefsMap = import_react11.default.useMemo(() => {
1001
1537
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
1002
1538
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
1003
1539
  }, [edgeTypeDefs]);
1004
- const nodeTypes = import_react9.default.useMemo(
1540
+ const nodeTypes = import_react11.default.useMemo(
1005
1541
  () => createNodeTypes(
1006
1542
  nodeRenderers,
1007
1543
  nodeTypeDefsMap,
1008
- frameRenderer,
1544
+ resolvedFrameRenderer,
1009
1545
  nodeWrapper,
1010
1546
  renderNode
1011
1547
  ),
1012
- [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
1548
+ [nodeRenderers, nodeTypeDefsMap, resolvedFrameRenderer, nodeWrapper, renderNode]
1013
1549
  );
1014
- const customEdgeTypes = import_react9.default.useMemo(
1550
+ const customEdgeTypes = import_react11.default.useMemo(
1015
1551
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
1016
1552
  [edgeRenderers, edgeTypeDefsMap]
1017
1553
  );
1018
- const mergedCSS = import_react9.default.useMemo(() => {
1554
+ const mergedCSS = import_react11.default.useMemo(() => {
1019
1555
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
1020
- return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
1556
+ return nodeTypeDefs.filter((d) => d.customCSS).map((d) => sanitizeCSS(d.customCSS, `flow-node--${d.slug}`)).join("\n");
1021
1557
  }, [nodeTypeDefs]);
1022
- const styledEdges = import_react9.default.useMemo(() => {
1558
+ const styledEdges = import_react11.default.useMemo(() => {
1023
1559
  var _a2;
1024
1560
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
1025
1561
  if (edgeRenderers) {
@@ -1033,7 +1569,7 @@ function FlowRenderer({
1033
1569
  }
1034
1570
  return edges;
1035
1571
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
1036
- const translateExtent = import_react9.default.useMemo(() => {
1572
+ const translateExtent = import_react11.default.useMemo(() => {
1037
1573
  if (translateExtentProp) return translateExtentProp;
1038
1574
  const es = clampBounds != null ? clampBounds : bounds;
1039
1575
  if (!es) return void 0;
@@ -1044,16 +1580,16 @@ function FlowRenderer({
1044
1580
  ];
1045
1581
  }, [translateExtentProp, clampBounds, bounds, focusPadding]);
1046
1582
  const boundsKey = bounds ? `${bounds.x},${bounds.y},${bounds.width},${bounds.height}` : "";
1047
- const extentReadyRef = import_react9.default.useRef(false);
1048
- const expandedExtentRef = import_react9.default.useRef(void 0);
1049
- const prevBoundsKeyRef = import_react9.default.useRef(boundsKey);
1583
+ const extentReadyRef = import_react11.default.useRef(false);
1584
+ const expandedExtentRef = import_react11.default.useRef(void 0);
1585
+ const prevBoundsKeyRef = import_react11.default.useRef(boundsKey);
1050
1586
  if (prevBoundsKeyRef.current !== boundsKey) {
1051
1587
  prevBoundsKeyRef.current = boundsKey;
1052
1588
  extentReadyRef.current = false;
1053
1589
  expandedExtentRef.current = void 0;
1054
1590
  }
1055
- const [, rerender] = import_react9.default.useReducer((x) => x + 1, 0);
1056
- const handleInitialFit = import_react9.default.useCallback(
1591
+ const [, rerender] = import_react11.default.useReducer((x) => x + 1, 0);
1592
+ const handleInitialFit = import_react11.default.useCallback(
1057
1593
  (expandedExtent) => {
1058
1594
  extentReadyRef.current = true;
1059
1595
  expandedExtentRef.current = expandedExtent;
@@ -1064,7 +1600,7 @@ function FlowRenderer({
1064
1600
  const activeExtent = !bounds || extentReadyRef.current ? (_a = expandedExtentRef.current) != null ? _a : translateExtent : void 0;
1065
1601
  if (!data) return null;
1066
1602
  const resolvedDefaultViewport = defaultViewportProp != null ? defaultViewportProp : !fitView && data.viewport ? data.viewport : void 0;
1067
- return /* @__PURE__ */ import_react9.default.createElement(import_react10.ReactFlowProvider, null, /* @__PURE__ */ import_react9.default.createElement(
1603
+ return /* @__PURE__ */ import_react11.default.createElement(import_react12.ReactFlowProvider, null, /* @__PURE__ */ import_react11.default.createElement(
1068
1604
  "div",
1069
1605
  {
1070
1606
  className,
@@ -1074,8 +1610,8 @@ function FlowRenderer({
1074
1610
  background: "transparent"
1075
1611
  }, style)
1076
1612
  },
1077
- /* @__PURE__ */ import_react9.default.createElement(
1078
- import_react10.ReactFlow,
1613
+ /* @__PURE__ */ import_react11.default.createElement(
1614
+ import_react12.ReactFlow,
1079
1615
  {
1080
1616
  nodes: (_b = data.nodes) != null ? _b : [],
1081
1617
  edges: styledEdges,
@@ -1104,16 +1640,16 @@ function FlowRenderer({
1104
1640
  maxZoom: maxZoomProp,
1105
1641
  proOptions: { hideAttribution: true }
1106
1642
  },
1107
- mergedCSS && /* @__PURE__ */ import_react9.default.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1108
- background && /* @__PURE__ */ import_react9.default.createElement(import_react10.Background, null),
1109
- controls && /* @__PURE__ */ import_react9.default.createElement(import_react10.Controls, null),
1110
- minimap && /* @__PURE__ */ import_react9.default.createElement(
1111
- import_react10.MiniMap,
1643
+ mergedCSS && /* @__PURE__ */ import_react11.default.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1644
+ background && /* @__PURE__ */ import_react11.default.createElement(import_react12.Background, null),
1645
+ controls && /* @__PURE__ */ import_react11.default.createElement(import_react12.Controls, null),
1646
+ minimap && /* @__PURE__ */ import_react11.default.createElement(
1647
+ import_react12.MiniMap,
1112
1648
  {
1113
1649
  nodeColor: minimapNodeColor
1114
1650
  }
1115
1651
  ),
1116
- bounds && /* @__PURE__ */ import_react9.default.createElement(
1652
+ bounds && /* @__PURE__ */ import_react11.default.createElement(
1117
1653
  FocusHandler,
1118
1654
  {
1119
1655
  bounds,
@@ -1131,4 +1667,68 @@ function FlowRenderer({
1131
1667
  )
1132
1668
  ));
1133
1669
  }
1670
+
1671
+ // src/ui/Flow/FlowFrame.tsx
1672
+ var import_react13 = __toESM(require("react"), 1);
1673
+ function FlowFrame(_a) {
1674
+ var _b = _a, {
1675
+ client,
1676
+ slug,
1677
+ id,
1678
+ frameId,
1679
+ loading = null,
1680
+ error: renderError,
1681
+ onDataReady
1682
+ } = _b, rendererProps = __objRest(_b, [
1683
+ "client",
1684
+ "slug",
1685
+ "id",
1686
+ "frameId",
1687
+ "loading",
1688
+ "error",
1689
+ "onDataReady"
1690
+ ]);
1691
+ const { data, nodeTypeDefs, edgeTypeDefs, flow, isLoading, error } = useFlow({
1692
+ client,
1693
+ slug,
1694
+ id
1695
+ });
1696
+ const frameData = (0, import_react13.useMemo)(
1697
+ () => data ? getFrameData(data, frameId) : void 0,
1698
+ [data, frameId]
1699
+ );
1700
+ const onDataReadyRef = (0, import_react13.useRef)(onDataReady);
1701
+ onDataReadyRef.current = onDataReady;
1702
+ (0, import_react13.useEffect)(() => {
1703
+ var _a2;
1704
+ if (frameData && flow) {
1705
+ (_a2 = onDataReadyRef.current) == null ? void 0 : _a2.call(onDataReadyRef, frameData, flow);
1706
+ }
1707
+ }, [frameData, flow]);
1708
+ if (process.env.NODE_ENV !== "production" && !slug && !id) {
1709
+ console.warn('[FlowFrame] Either "slug" or "id" must be provided.');
1710
+ }
1711
+ if (isLoading) return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, loading);
1712
+ if (error) return renderError ? /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, renderError(error)) : null;
1713
+ if (!frameData) {
1714
+ if (process.env.NODE_ENV !== "production" && data) {
1715
+ const frames = data.nodes.filter(isFrameNode).map((n) => n.id);
1716
+ console.warn(
1717
+ `[FlowFrame] Frame "${frameId}" not found. Available frames: ${frames.join(", ") || "(none)"}`
1718
+ );
1719
+ }
1720
+ return null;
1721
+ }
1722
+ return /* @__PURE__ */ import_react13.default.createElement(
1723
+ FlowRenderer,
1724
+ __spreadProps(__spreadValues({}, rendererProps), {
1725
+ data: frameData.data,
1726
+ nodeTypeDefs,
1727
+ edgeTypeDefs,
1728
+ bounds: frameData.fitBounds,
1729
+ clampBounds: frameData.clampBounds,
1730
+ hideFrames: true
1731
+ })
1732
+ );
1733
+ }
1134
1734
  //# sourceMappingURL=flow.cjs.map