@01.software/sdk 0.5.2 → 0.5.3

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 +718 -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 +705 -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,77 @@ 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;
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
+ });
437
476
  }
477
+ function useQuickJS() {
478
+ return (0, import_react3.useSyncExternalStore)(subscribeQuickJS, getQuickJSSnapshot, () => null);
479
+ }
480
+
481
+ // src/ui/Flow/template-compiler.ts
482
+ var MAX_CACHE_SIZE = 100;
483
+ var MAX_MATERIALIZE_DEPTH = 20;
484
+ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
485
+ "div",
486
+ "span",
487
+ "p",
488
+ "h1",
489
+ "h2",
490
+ "h3",
491
+ "h4",
492
+ "h5",
493
+ "h6",
494
+ "ul",
495
+ "ol",
496
+ "li",
497
+ "strong",
498
+ "em",
499
+ "b",
500
+ "i",
501
+ "br",
502
+ "img",
503
+ "figure",
504
+ "section",
505
+ "table",
506
+ "thead",
507
+ "tbody",
508
+ "tr",
509
+ "th",
510
+ "td",
511
+ "pre",
512
+ "code",
513
+ "svg",
514
+ "path",
515
+ "g",
516
+ "circle",
517
+ "rect"
518
+ ]);
438
519
  var BLOCKED_PATTERNS = [
439
520
  /\bdocument\s*\./,
440
521
  /\bwindow\s*\./,
@@ -454,75 +535,214 @@ var BLOCKED_PATTERNS = [
454
535
  /\bsetTimeout\s*\(/,
455
536
  /\bsetInterval\s*\(/,
456
537
  /\bsetImmediate\s*\(/,
457
- /\brequire\s*\(/
538
+ /\brequire\s*\(/,
539
+ /\bself\b/,
540
+ /\bconstructor\s*\.\s*constructor/,
541
+ /\bReflect\b/,
542
+ /\bProxy\b/,
543
+ /\b__proto__\b/
458
544
  ];
545
+ var VM_SETUP = `
546
+ var React = {
547
+ createElement: function(type, props) {
548
+ var args = Array.prototype.slice.call(arguments, 2);
549
+ return { $$t: 'el', type: String(type), props: props || {}, ch: args };
550
+ },
551
+ Fragment: '__frag__'
552
+ };
553
+ var exports = {};
554
+ var module = { exports: exports };
555
+ var window = undefined;
556
+ var document = undefined;
557
+ var globalThis = undefined;
558
+ var self = undefined;
559
+ var setTimeout = undefined;
560
+ var setInterval = undefined;
561
+ var setImmediate = undefined;
562
+ var fetch = undefined;
563
+ var XMLHttpRequest = undefined;
564
+ var navigator = undefined;
565
+ var location = undefined;
566
+ var localStorage = undefined;
567
+ var sessionStorage = undefined;
568
+ var cookie = undefined;
569
+ var postMessage = undefined;
570
+ `;
571
+ var rendererCache = /* @__PURE__ */ new Map();
572
+ function hashCode(str) {
573
+ let hash = 0;
574
+ for (let i = 0; i < str.length; i++) {
575
+ const char = str.charCodeAt(i);
576
+ hash = (hash << 5) - hash + char | 0;
577
+ }
578
+ return hash.toString(36);
579
+ }
459
580
  function validateTemplateCode(code) {
460
581
  return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
461
582
  }
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;
583
+ var SAFE_DATA_URI_PREFIXES = [
584
+ "data:image/png;",
585
+ "data:image/jpeg;",
586
+ "data:image/gif;",
587
+ "data:image/webp;",
588
+ "data:image/avif;"
589
+ ];
590
+ function isSafeUrl(value) {
591
+ if (typeof value !== "string") return false;
592
+ if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) return true;
593
+ if (SAFE_DATA_URI_PREFIXES.some((p) => value.startsWith(p))) return true;
594
+ try {
595
+ const url = new URL(value);
596
+ return url.protocol === "http:" || url.protocol === "https:";
597
+ } catch (e) {
598
+ return false;
469
599
  }
470
- if (!validateTemplateCode(code)) {
471
- console.warn(`[flow] Template "${slug}" contains blocked patterns`);
472
- return null;
600
+ }
601
+ var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
602
+ "backgroundImage",
603
+ "background",
604
+ "listStyleImage",
605
+ "content",
606
+ "borderImage",
607
+ "borderImageSource",
608
+ "maskImage",
609
+ "mask",
610
+ "filter",
611
+ "cursor"
612
+ ]);
613
+ function sanitizeStyle(style) {
614
+ if (!style || typeof style !== "object" || Array.isArray(style)) return {};
615
+ const safe = {};
616
+ for (const [k, v] of Object.entries(style)) {
617
+ if (BLOCKED_STYLE_PROPS.has(k)) continue;
618
+ if (typeof v === "string" && /url\s*\(/i.test(v)) continue;
619
+ if (k === "position" && typeof v === "string" && /fixed|absolute/i.test(v)) continue;
620
+ safe[k] = v;
621
+ }
622
+ return safe;
623
+ }
624
+ function materialize(node, depth = 0) {
625
+ var _a;
626
+ if (depth > MAX_MATERIALIZE_DEPTH) return null;
627
+ if (node == null) return null;
628
+ if (typeof node === "string" || typeof node === "number") return node;
629
+ if (Array.isArray(node)) {
630
+ return import_react4.default.createElement(
631
+ import_react4.default.Fragment,
632
+ null,
633
+ ...node.map((c) => materialize(c, depth + 1))
634
+ );
473
635
  }
636
+ if (typeof node !== "object") return null;
637
+ const d = node;
638
+ if (d.$$t === "frag") {
639
+ const ch = Array.isArray(d.ch) ? d.ch : [];
640
+ return import_react4.default.createElement(
641
+ import_react4.default.Fragment,
642
+ null,
643
+ ...ch.map((c) => materialize(c, depth + 1))
644
+ );
645
+ }
646
+ if (d.$$t === "el") {
647
+ const type = String((_a = d.type) != null ? _a : "");
648
+ const props = d.props && typeof d.props === "object" ? d.props : {};
649
+ const ch = Array.isArray(d.ch) ? d.ch : [];
650
+ if (!ALLOWED_ELEMENTS.has(type.toLowerCase())) return null;
651
+ const safeProps = {};
652
+ for (const [k, v] of Object.entries(props)) {
653
+ if (k.startsWith("on")) continue;
654
+ if (k === "dangerouslySetInnerHTML" || k === "ref") continue;
655
+ if ((k === "src" || k === "href") && !isSafeUrl(v)) continue;
656
+ if (k === "style") {
657
+ safeProps[k] = sanitizeStyle(v);
658
+ continue;
659
+ }
660
+ safeProps[k] = v;
661
+ }
662
+ const children = ch.map((c) => materialize(c, depth + 1));
663
+ return import_react4.default.createElement(type, safeProps, ...children);
664
+ }
665
+ return null;
666
+ }
667
+ function evalInContext(vm, code) {
668
+ const result = vm.evalCode(code);
669
+ if (result.error) {
670
+ result.error.dispose();
671
+ return { ok: false };
672
+ }
673
+ return { ok: true, handle: result.value };
674
+ }
675
+ var MAX_VM_CYCLES = 2e5;
676
+ function makeRenderer(jsCode) {
677
+ return function runInQuickJS(qjs, props) {
678
+ const vm = qjs.newContext();
679
+ let cycles = 0;
680
+ vm.runtime.setInterruptHandler(() => {
681
+ cycles++;
682
+ return cycles > MAX_VM_CYCLES;
683
+ });
684
+ try {
685
+ const setupResult = evalInContext(vm, VM_SETUP + jsCode);
686
+ if (!setupResult.ok) return null;
687
+ setupResult.handle.dispose();
688
+ const propsJson = JSON.stringify(props);
689
+ const callCode = `(function(){var __c=module.exports.default||module.exports;return JSON.stringify(__c(${propsJson}));})();`;
690
+ const callResult = evalInContext(vm, callCode);
691
+ if (!callResult.ok) return null;
692
+ const raw = vm.dump(callResult.handle);
693
+ callResult.handle.dispose();
694
+ return materialize(JSON.parse(String(raw)));
695
+ } catch (e) {
696
+ return null;
697
+ } finally {
698
+ vm.dispose();
699
+ }
700
+ };
701
+ }
702
+ function makeReactFC(renderer, qjs) {
703
+ return function QuickJSTemplateComponent(props) {
704
+ var _a;
705
+ return (_a = renderer(qjs, props)) != null ? _a : null;
706
+ };
707
+ }
708
+ function compileTemplate(code, slug) {
709
+ ensureQuickJSLoaded();
710
+ if (!validateTemplateCode(code)) return null;
711
+ const qjs = getQuickJSSnapshot();
712
+ if (!qjs) return null;
713
+ const cacheKey = `${slug}:${code.length}:${hashCode(code)}`;
714
+ if (rendererCache.has(cacheKey)) {
715
+ const renderer2 = rendererCache.get(cacheKey);
716
+ rendererCache.delete(cacheKey);
717
+ rendererCache.set(cacheKey, renderer2);
718
+ return makeReactFC(renderer2, qjs);
719
+ }
720
+ let jsCode;
474
721
  try {
475
- const { code: jsCode } = (0, import_sucrase.transform)(code, {
722
+ const result = (0, import_sucrase.transform)(code, {
476
723
  transforms: ["typescript", "jsx", "imports"],
477
724
  jsxRuntime: "classic",
478
725
  jsxPragma: "React.createElement",
479
726
  jsxFragmentPragma: "React.Fragment"
480
727
  });
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;
728
+ jsCode = result.code;
508
729
  } catch (e) {
509
- console.warn(`[flow] Failed to compile template for "${slug}":`, e);
510
730
  return null;
511
731
  }
732
+ const renderer = makeRenderer(jsCode);
733
+ if (rendererCache.size >= MAX_CACHE_SIZE) {
734
+ const oldest = rendererCache.keys().next().value;
735
+ if (oldest) rendererCache.delete(oldest);
736
+ }
737
+ rendererCache.set(cacheKey, renderer);
738
+ return makeReactFC(renderer, qjs);
512
739
  }
513
740
  function clearTemplateCache() {
514
- componentCache.clear();
741
+ rendererCache.clear();
515
742
  }
516
743
 
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
744
  // src/ui/Flow/node-renderers.tsx
525
- var import_react4 = __toESM(require("react"), 1);
745
+ var import_react5 = __toESM(require("react"), 1);
526
746
  function sanitizeUrl(url) {
527
747
  if (!url) return url;
528
748
  try {
@@ -540,7 +760,7 @@ function renderFieldValue(key, val, fieldDef) {
540
760
  const imgUrl = typeof val === "string" ? val : val == null ? void 0 : val.url;
541
761
  const safeUrl = sanitizeUrl(imgUrl);
542
762
  if (!safeUrl) return null;
543
- return /* @__PURE__ */ import_react4.default.createElement(
763
+ return /* @__PURE__ */ import_react5.default.createElement(
544
764
  "img",
545
765
  {
546
766
  key,
@@ -551,7 +771,7 @@ function renderFieldValue(key, val, fieldDef) {
551
771
  }
552
772
  );
553
773
  }
554
- return /* @__PURE__ */ import_react4.default.createElement(
774
+ return /* @__PURE__ */ import_react5.default.createElement(
555
775
  "div",
556
776
  {
557
777
  key,
@@ -568,7 +788,7 @@ function renderFieldValue(key, val, fieldDef) {
568
788
  }
569
789
  function DefaultDynamicNode({ data }) {
570
790
  const d = data;
571
- return /* @__PURE__ */ import_react4.default.createElement(
791
+ return /* @__PURE__ */ import_react5.default.createElement(
572
792
  "div",
573
793
  {
574
794
  style: {
@@ -581,7 +801,7 @@ function DefaultDynamicNode({ data }) {
581
801
  d.fields && Object.entries(d.fields).filter(([, v]) => v != null && v !== "").map(([key, val]) => renderFieldValue(key, val))
582
802
  );
583
803
  }
584
- var TemplateErrorBoundary = class extends import_react4.default.Component {
804
+ var TemplateErrorBoundary = class extends import_react5.default.Component {
585
805
  constructor() {
586
806
  super(...arguments);
587
807
  this.state = { error: null };
@@ -596,7 +816,7 @@ var TemplateErrorBoundary = class extends import_react4.default.Component {
596
816
  }
597
817
  render() {
598
818
  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));
819
+ 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
820
  }
601
821
  return this.props.children;
602
822
  }
@@ -607,16 +827,17 @@ function EnhancedDynamicNode({
607
827
  width,
608
828
  height
609
829
  }) {
830
+ useQuickJS();
610
831
  if (typeDef.template) {
611
832
  const Component = compileTemplate(typeDef.template, typeDef.slug);
612
833
  if (Component) {
613
- return /* @__PURE__ */ import_react4.default.createElement(
834
+ return /* @__PURE__ */ import_react5.default.createElement(
614
835
  "div",
615
836
  {
616
837
  className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? " flow-node--transparent-bg" : ""}`,
617
838
  style: { width: "100%", height: "100%" }
618
839
  },
619
- /* @__PURE__ */ import_react4.default.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ import_react4.default.createElement(
840
+ /* @__PURE__ */ import_react5.default.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ import_react5.default.createElement(
620
841
  Component,
621
842
  {
622
843
  fields: data.fields,
@@ -630,9 +851,10 @@ function EnhancedDynamicNode({
630
851
  );
631
852
  }
632
853
  }
633
- return /* @__PURE__ */ import_react4.default.createElement(
854
+ return /* @__PURE__ */ import_react5.default.createElement(
634
855
  "div",
635
856
  {
857
+ className: `flow-node flow-node--${typeDef.slug}`,
636
858
  style: {
637
859
  width: "100%",
638
860
  height: "100%",
@@ -659,7 +881,7 @@ function DefaultFrameNode({ data }) {
659
881
  if (m) return `rgba(${m[1]},${m[2]},${m[3]},${opacity})`;
660
882
  return baseColor;
661
883
  })();
662
- return /* @__PURE__ */ import_react4.default.createElement(
884
+ return /* @__PURE__ */ import_react5.default.createElement(
663
885
  "div",
664
886
  {
665
887
  style: {
@@ -670,7 +892,7 @@ function DefaultFrameNode({ data }) {
670
892
  border: borderStyle === "none" ? "none" : `2px ${borderStyle} rgba(128,128,128,0.3)`
671
893
  }
672
894
  },
673
- /* @__PURE__ */ import_react4.default.createElement(
895
+ /* @__PURE__ */ import_react5.default.createElement(
674
896
  "div",
675
897
  {
676
898
  style: {
@@ -685,14 +907,242 @@ function DefaultFrameNode({ data }) {
685
907
  );
686
908
  }
687
909
 
910
+ // src/ui/Flow/field-helpers.ts
911
+ function getImageField(fields, name) {
912
+ const val = fields == null ? void 0 : fields[name];
913
+ if (val && typeof val === "object" && val !== null && "url" in val)
914
+ return val;
915
+ return void 0;
916
+ }
917
+ function getTextField(fields, name) {
918
+ const val = fields == null ? void 0 : fields[name];
919
+ return typeof val === "string" ? val : void 0;
920
+ }
921
+ function getNumberField(fields, name) {
922
+ const val = fields == null ? void 0 : fields[name];
923
+ return typeof val === "number" ? val : void 0;
924
+ }
925
+ function getBooleanField(fields, name) {
926
+ const val = fields == null ? void 0 : fields[name];
927
+ return typeof val === "boolean" ? val : void 0;
928
+ }
929
+
930
+ // src/ui/Flow/css-sanitizer.ts
931
+ var import_postcss = __toESM(require("postcss"), 1);
932
+ var ALLOWED_AT_RULES = /* @__PURE__ */ new Set(["keyframes", "media", "supports", "container", "layer"]);
933
+ var BLOCKED_VALUE_PATTERNS = [/url\s*\(/i, /expression\s*\(/i, /paint\s*\(/i, /-moz-binding/i];
934
+ var DANGEROUS_SELECTOR = /(?:^|[\s,])(?::root|html|body)\b/;
935
+ function sanitizeCSS(css, scopeClass) {
936
+ let root;
937
+ try {
938
+ root = import_postcss.default.parse(css);
939
+ } catch (e) {
940
+ return "";
941
+ }
942
+ root.walkAtRules((node) => {
943
+ if (!ALLOWED_AT_RULES.has(node.name.toLowerCase())) {
944
+ node.remove();
945
+ }
946
+ });
947
+ root.walkDecls((node) => {
948
+ if (BLOCKED_VALUE_PATTERNS.some((p) => p.test(node.value))) {
949
+ node.remove();
950
+ }
951
+ });
952
+ root.walkRules((rule) => {
953
+ rule.selectors = rule.selectors.filter((s) => !DANGEROUS_SELECTOR.test(s));
954
+ if (!rule.selectors.length) {
955
+ rule.remove();
956
+ return;
957
+ }
958
+ if (scopeClass) {
959
+ const safeScopeClass = scopeClass.replace(/[{}()[\];,'"\\<>]/g, "");
960
+ if (!safeScopeClass) {
961
+ rule.remove();
962
+ return;
963
+ }
964
+ const parent = rule.parent;
965
+ if ((parent == null ? void 0 : parent.type) === "atrule" && parent.name.toLowerCase() === "keyframes") {
966
+ return;
967
+ }
968
+ rule.selectors = rule.selectors.map((sel) => `.${safeScopeClass} ${sel}`);
969
+ }
970
+ });
971
+ return root.toString().replace(/<\/style/gi, "<\\/style");
972
+ }
973
+
974
+ // src/ui/Flow/FlowRenderer.tsx
975
+ var import_react11 = __toESM(require("react"), 1);
976
+ var import_react12 = require("@xyflow/react");
977
+
978
+ // src/ui/Flow/node-types-factory.tsx
979
+ var import_react7 = __toESM(require("react"), 1);
980
+
981
+ // src/ui/Image/index.tsx
982
+ var import_react6 = __toESM(require("react"), 1);
983
+
984
+ // src/utils/image.ts
985
+ var IMAGE_SIZES = [384, 768, 1536];
986
+ function getImageSrcSet(image) {
987
+ const parts = [];
988
+ const sizes = image.sizes;
989
+ if (sizes) {
990
+ for (const size of IMAGE_SIZES) {
991
+ const entry = sizes[String(size)];
992
+ if ((entry == null ? void 0 : entry.url) && entry.width) {
993
+ parts.push(`${entry.url} ${entry.width}w`);
994
+ }
995
+ }
996
+ }
997
+ if (image.url && image.width) {
998
+ parts.push(`${image.url} ${image.width}w`);
999
+ }
1000
+ return parts.join(", ");
1001
+ }
1002
+ function getImagePlaceholderStyle(image, options) {
1003
+ var _a, _b, _c;
1004
+ const type = (_a = options == null ? void 0 : options.type) != null ? _a : "blur";
1005
+ const paletteColor = (_b = options == null ? void 0 : options.paletteColor) != null ? _b : "muted";
1006
+ if (type === "none") return {};
1007
+ const color = (_c = image.palette) == null ? void 0 : _c[paletteColor];
1008
+ if (type === "blur") {
1009
+ const lqip = image.lqip;
1010
+ if (lqip) {
1011
+ return {
1012
+ backgroundImage: `url(${lqip})`,
1013
+ backgroundSize: "cover",
1014
+ backgroundPosition: "center"
1015
+ };
1016
+ }
1017
+ if (color) {
1018
+ return { backgroundColor: color };
1019
+ }
1020
+ return {};
1021
+ }
1022
+ if (color) {
1023
+ return { backgroundColor: color };
1024
+ }
1025
+ return {};
1026
+ }
1027
+
1028
+ // src/ui/Image/index.tsx
1029
+ function Image({
1030
+ image,
1031
+ width,
1032
+ dpr = 1,
1033
+ placeholder: placeholderProp,
1034
+ className,
1035
+ style,
1036
+ imgClassName,
1037
+ imgStyle,
1038
+ sizes,
1039
+ loading: loadingProp,
1040
+ onLoad,
1041
+ objectFit = "cover",
1042
+ priority = false,
1043
+ fill = false,
1044
+ imageRendering
1045
+ }) {
1046
+ var _a, _b;
1047
+ const [loaded, setLoaded] = (0, import_react6.useState)(false);
1048
+ const firedRef = (0, import_react6.useRef)(false);
1049
+ const isPixelRendering = imageRendering === "pixelated" || imageRendering === "crisp-edges";
1050
+ const placeholder = placeholderProp != null ? placeholderProp : isPixelRendering ? "none" : "blur";
1051
+ const loading = priority ? "eager" : loadingProp != null ? loadingProp : "lazy";
1052
+ const aspectRatio = !fill && image.width && image.height ? `${image.width} / ${image.height}` : void 0;
1053
+ const srcSet = getImageSrcSet(image);
1054
+ const src = (_a = image.url) != null ? _a : void 0;
1055
+ const hasLqip = placeholder === "blur" && !!image.lqip;
1056
+ const placeholderStyle = getImagePlaceholderStyle(image, {
1057
+ type: placeholder
1058
+ });
1059
+ const placeholderColor = !hasLqip && "backgroundColor" in placeholderStyle ? placeholderStyle.backgroundColor : void 0;
1060
+ const fireLoad = (0, import_react6.useCallback)(() => {
1061
+ if (firedRef.current) return;
1062
+ firedRef.current = true;
1063
+ setLoaded(true);
1064
+ onLoad == null ? void 0 : onLoad();
1065
+ }, [onLoad]);
1066
+ const imgRef = (0, import_react6.useCallback)(
1067
+ (node) => {
1068
+ if (node && node.complete && node.naturalWidth > 0) {
1069
+ fireLoad();
1070
+ }
1071
+ },
1072
+ [fireLoad]
1073
+ );
1074
+ const containerStyle = __spreadValues(__spreadValues(__spreadValues({
1075
+ position: "relative",
1076
+ overflow: "hidden"
1077
+ }, fill ? { width: "100%", height: "100%" } : {}), aspectRatio ? { aspectRatio } : {}), style);
1078
+ const overlayBase = {
1079
+ position: "absolute",
1080
+ top: 0,
1081
+ left: 0,
1082
+ width: "100%",
1083
+ height: "100%",
1084
+ opacity: loaded ? 0 : 1,
1085
+ transition: "opacity 0.3s ease",
1086
+ pointerEvents: "none"
1087
+ };
1088
+ const mainImgStyle = __spreadValues(__spreadProps(__spreadValues({
1089
+ display: "block",
1090
+ width: "100%",
1091
+ height: "100%",
1092
+ objectFit
1093
+ }, imageRendering ? { imageRendering } : {}), {
1094
+ opacity: loaded ? 1 : 0,
1095
+ transition: "opacity 0.3s ease"
1096
+ }), imgStyle);
1097
+ return /* @__PURE__ */ import_react6.default.createElement("div", { className, style: containerStyle }, hasLqip && /* @__PURE__ */ import_react6.default.createElement(
1098
+ "img",
1099
+ {
1100
+ "aria-hidden": true,
1101
+ alt: "",
1102
+ src: image.lqip,
1103
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1104
+ display: "block",
1105
+ objectFit,
1106
+ filter: "blur(20px)",
1107
+ transform: "scale(1.1)"
1108
+ })
1109
+ }
1110
+ ), placeholderColor && /* @__PURE__ */ import_react6.default.createElement(
1111
+ "div",
1112
+ {
1113
+ "aria-hidden": true,
1114
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1115
+ backgroundColor: placeholderColor
1116
+ })
1117
+ }
1118
+ ), /* @__PURE__ */ import_react6.default.createElement(
1119
+ "img",
1120
+ {
1121
+ ref: imgRef,
1122
+ alt: (_b = image.alt) != null ? _b : "",
1123
+ src,
1124
+ srcSet: srcSet || void 0,
1125
+ sizes,
1126
+ width: width ? width * dpr : void 0,
1127
+ loading,
1128
+ decoding: "async",
1129
+ fetchPriority: priority ? "high" : void 0,
1130
+ onLoad: fireLoad,
1131
+ className: imgClassName,
1132
+ style: mainImgStyle
1133
+ }
1134
+ ));
1135
+ }
1136
+
688
1137
  // src/ui/Flow/node-types-factory.tsx
689
1138
  function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
690
1139
  const types = {};
691
1140
  types.dynamic = ((props) => {
1141
+ var _a;
692
1142
  const d = props.data;
693
1143
  const typeDef = nodeTypeDefsMap == null ? void 0 : nodeTypeDefsMap.get(d.nodeTypeSlug);
694
1144
  const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
695
- const defaultRender = typeDef ? /* @__PURE__ */ import_react5.default.createElement(
1145
+ const defaultRender = typeDef ? /* @__PURE__ */ import_react7.default.createElement(
696
1146
  EnhancedDynamicNode,
697
1147
  {
698
1148
  data: d,
@@ -700,7 +1150,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
700
1150
  width: props.width,
701
1151
  height: props.height
702
1152
  }
703
- ) : /* @__PURE__ */ import_react5.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
1153
+ ) : /* @__PURE__ */ import_react7.default.createElement(DefaultDynamicNode, __spreadValues({}, props));
704
1154
  const slotProps = {
705
1155
  id: props.id,
706
1156
  nodeTypeSlug: d.nodeTypeSlug,
@@ -712,14 +1162,35 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
712
1162
  height: props.height,
713
1163
  defaultRender
714
1164
  };
715
- let content = CustomRenderer ? /* @__PURE__ */ import_react5.default.createElement(CustomRenderer, __spreadValues({}, slotProps)) : defaultRender;
1165
+ let content;
1166
+ if (CustomRenderer) {
1167
+ content = /* @__PURE__ */ import_react7.default.createElement(CustomRenderer, __spreadValues({}, slotProps));
1168
+ } else if (d.nodeTypeSlug === "image") {
1169
+ const imageVal = (_a = d.fields) == null ? void 0 : _a.image;
1170
+ if (imageVal && typeof imageVal === "object" && "url" in imageVal) {
1171
+ content = /* @__PURE__ */ import_react7.default.createElement(
1172
+ Image,
1173
+ {
1174
+ image: imageVal,
1175
+ width: props.width,
1176
+ fill: true,
1177
+ priority: true,
1178
+ objectFit: "contain"
1179
+ }
1180
+ );
1181
+ } else {
1182
+ content = defaultRender;
1183
+ }
1184
+ } else {
1185
+ content = defaultRender;
1186
+ }
716
1187
  if (renderNode) {
717
1188
  const result = renderNode(slotProps, content);
718
1189
  if (result !== null) content = result;
719
1190
  }
720
1191
  if (nodeWrapper) {
721
1192
  const Wrapper = nodeWrapper;
722
- content = /* @__PURE__ */ import_react5.default.createElement(
1193
+ content = /* @__PURE__ */ import_react7.default.createElement(
723
1194
  Wrapper,
724
1195
  {
725
1196
  id: props.id,
@@ -736,7 +1207,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
736
1207
  types.frame = frameRenderer ? ((props) => {
737
1208
  const d = props.data;
738
1209
  const Renderer = frameRenderer;
739
- return /* @__PURE__ */ import_react5.default.createElement(
1210
+ return /* @__PURE__ */ import_react7.default.createElement(
740
1211
  Renderer,
741
1212
  {
742
1213
  id: props.id,
@@ -760,7 +1231,7 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
760
1231
  types[slug] = ((props) => {
761
1232
  var _a;
762
1233
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
763
- return /* @__PURE__ */ import_react5.default.createElement(
1234
+ return /* @__PURE__ */ import_react7.default.createElement(
764
1235
  Renderer,
765
1236
  {
766
1237
  id: props.id,
@@ -779,10 +1250,10 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
779
1250
  }
780
1251
 
781
1252
  // src/ui/Flow/edge-styles.ts
782
- var import_react6 = require("@xyflow/react");
1253
+ var import_react8 = require("@xyflow/react");
783
1254
  function toMarkerType(value) {
784
- if (value === "arrow") return import_react6.MarkerType.Arrow;
785
- if (value === "arrowclosed") return import_react6.MarkerType.ArrowClosed;
1255
+ if (value === "arrow") return import_react8.MarkerType.Arrow;
1256
+ if (value === "arrowclosed") return import_react8.MarkerType.ArrowClosed;
786
1257
  return void 0;
787
1258
  }
788
1259
  var EDGE_TYPE_MAP = {
@@ -826,8 +1297,8 @@ function applyEdgeStyles(edges, edgeTypeDefsMap) {
826
1297
  }
827
1298
 
828
1299
  // src/ui/Flow/focus-handler.tsx
829
- var import_react7 = __toESM(require("react"), 1);
830
- var import_react8 = require("@xyflow/react");
1300
+ var import_react9 = __toESM(require("react"), 1);
1301
+ var import_react10 = require("@xyflow/react");
831
1302
  function clampViewport(vp, cw, ch, extent) {
832
1303
  const left = -vp.x / vp.zoom;
833
1304
  const right = (cw - vp.x) / vp.zoom;
@@ -853,16 +1324,16 @@ function FocusHandler({
853
1324
  minZoomProp,
854
1325
  onInitialFit
855
1326
  }) {
856
- const { setViewport } = (0, import_react8.useReactFlow)();
857
- const store = (0, import_react8.useStoreApi)();
858
- const containerRef = import_react7.default.useRef(null);
1327
+ const { setViewport } = (0, import_react10.useReactFlow)();
1328
+ const store = (0, import_react10.useStoreApi)();
1329
+ const containerRef = import_react9.default.useRef(null);
859
1330
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
860
- const boundsRef = import_react7.default.useRef(bounds);
1331
+ const boundsRef = import_react9.default.useRef(bounds);
861
1332
  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(() => {
1333
+ const [containerSize, setContainerSize] = import_react9.default.useState({ w: 0, h: 0 });
1334
+ const prevBoundsKeyRef = import_react9.default.useRef(null);
1335
+ const prevSizeRef = import_react9.default.useRef({ w: 0, h: 0 });
1336
+ import_react9.default.useEffect(() => {
866
1337
  const el = containerRef.current;
867
1338
  if (!el) return;
868
1339
  const observer = new ResizeObserver((entries) => {
@@ -874,7 +1345,7 @@ function FocusHandler({
874
1345
  observer.observe(el);
875
1346
  return () => observer.disconnect();
876
1347
  }, []);
877
- import_react7.default.useEffect(() => {
1348
+ import_react9.default.useEffect(() => {
878
1349
  if (containerSize.w === 0 || containerSize.h === 0) return;
879
1350
  const prevKey = prevBoundsKeyRef.current;
880
1351
  const prevSize = prevSizeRef.current;
@@ -941,7 +1412,7 @@ function FocusHandler({
941
1412
  store,
942
1413
  onInitialFit
943
1414
  ]);
944
- return /* @__PURE__ */ import_react7.default.createElement(
1415
+ return /* @__PURE__ */ import_react9.default.createElement(
945
1416
  "div",
946
1417
  {
947
1418
  ref: containerRef,
@@ -956,6 +1427,7 @@ function FocusHandler({
956
1427
  }
957
1428
 
958
1429
  // src/ui/Flow/FlowRenderer.tsx
1430
+ var NullFrameRenderer = () => null;
959
1431
  function FlowRenderer({
960
1432
  data,
961
1433
  className,
@@ -973,6 +1445,7 @@ function FlowRenderer({
973
1445
  onNodeMouseLeave,
974
1446
  onEdgeClick,
975
1447
  frameRenderer,
1448
+ hideFrames = false,
976
1449
  edgeRenderers,
977
1450
  nodeWrapper,
978
1451
  controls,
@@ -993,33 +1466,34 @@ function FlowRenderer({
993
1466
  maxZoom: maxZoomProp
994
1467
  }) {
995
1468
  var _a, _b;
996
- const nodeTypeDefsMap = import_react9.default.useMemo(() => {
1469
+ const resolvedFrameRenderer = hideFrames ? NullFrameRenderer : frameRenderer;
1470
+ const nodeTypeDefsMap = import_react11.default.useMemo(() => {
997
1471
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
998
1472
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
999
1473
  }, [nodeTypeDefs]);
1000
- const edgeTypeDefsMap = import_react9.default.useMemo(() => {
1474
+ const edgeTypeDefsMap = import_react11.default.useMemo(() => {
1001
1475
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
1002
1476
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
1003
1477
  }, [edgeTypeDefs]);
1004
- const nodeTypes = import_react9.default.useMemo(
1478
+ const nodeTypes = import_react11.default.useMemo(
1005
1479
  () => createNodeTypes(
1006
1480
  nodeRenderers,
1007
1481
  nodeTypeDefsMap,
1008
- frameRenderer,
1482
+ resolvedFrameRenderer,
1009
1483
  nodeWrapper,
1010
1484
  renderNode
1011
1485
  ),
1012
- [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
1486
+ [nodeRenderers, nodeTypeDefsMap, resolvedFrameRenderer, nodeWrapper, renderNode]
1013
1487
  );
1014
- const customEdgeTypes = import_react9.default.useMemo(
1488
+ const customEdgeTypes = import_react11.default.useMemo(
1015
1489
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
1016
1490
  [edgeRenderers, edgeTypeDefsMap]
1017
1491
  );
1018
- const mergedCSS = import_react9.default.useMemo(() => {
1492
+ const mergedCSS = import_react11.default.useMemo(() => {
1019
1493
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
1020
- return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
1494
+ return nodeTypeDefs.filter((d) => d.customCSS).map((d) => sanitizeCSS(d.customCSS, `flow-node--${d.slug}`)).join("\n");
1021
1495
  }, [nodeTypeDefs]);
1022
- const styledEdges = import_react9.default.useMemo(() => {
1496
+ const styledEdges = import_react11.default.useMemo(() => {
1023
1497
  var _a2;
1024
1498
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
1025
1499
  if (edgeRenderers) {
@@ -1033,7 +1507,7 @@ function FlowRenderer({
1033
1507
  }
1034
1508
  return edges;
1035
1509
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
1036
- const translateExtent = import_react9.default.useMemo(() => {
1510
+ const translateExtent = import_react11.default.useMemo(() => {
1037
1511
  if (translateExtentProp) return translateExtentProp;
1038
1512
  const es = clampBounds != null ? clampBounds : bounds;
1039
1513
  if (!es) return void 0;
@@ -1044,16 +1518,16 @@ function FlowRenderer({
1044
1518
  ];
1045
1519
  }, [translateExtentProp, clampBounds, bounds, focusPadding]);
1046
1520
  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);
1521
+ const extentReadyRef = import_react11.default.useRef(false);
1522
+ const expandedExtentRef = import_react11.default.useRef(void 0);
1523
+ const prevBoundsKeyRef = import_react11.default.useRef(boundsKey);
1050
1524
  if (prevBoundsKeyRef.current !== boundsKey) {
1051
1525
  prevBoundsKeyRef.current = boundsKey;
1052
1526
  extentReadyRef.current = false;
1053
1527
  expandedExtentRef.current = void 0;
1054
1528
  }
1055
- const [, rerender] = import_react9.default.useReducer((x) => x + 1, 0);
1056
- const handleInitialFit = import_react9.default.useCallback(
1529
+ const [, rerender] = import_react11.default.useReducer((x) => x + 1, 0);
1530
+ const handleInitialFit = import_react11.default.useCallback(
1057
1531
  (expandedExtent) => {
1058
1532
  extentReadyRef.current = true;
1059
1533
  expandedExtentRef.current = expandedExtent;
@@ -1064,7 +1538,7 @@ function FlowRenderer({
1064
1538
  const activeExtent = !bounds || extentReadyRef.current ? (_a = expandedExtentRef.current) != null ? _a : translateExtent : void 0;
1065
1539
  if (!data) return null;
1066
1540
  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(
1541
+ return /* @__PURE__ */ import_react11.default.createElement(import_react12.ReactFlowProvider, null, /* @__PURE__ */ import_react11.default.createElement(
1068
1542
  "div",
1069
1543
  {
1070
1544
  className,
@@ -1074,8 +1548,8 @@ function FlowRenderer({
1074
1548
  background: "transparent"
1075
1549
  }, style)
1076
1550
  },
1077
- /* @__PURE__ */ import_react9.default.createElement(
1078
- import_react10.ReactFlow,
1551
+ /* @__PURE__ */ import_react11.default.createElement(
1552
+ import_react12.ReactFlow,
1079
1553
  {
1080
1554
  nodes: (_b = data.nodes) != null ? _b : [],
1081
1555
  edges: styledEdges,
@@ -1104,16 +1578,16 @@ function FlowRenderer({
1104
1578
  maxZoom: maxZoomProp,
1105
1579
  proOptions: { hideAttribution: true }
1106
1580
  },
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,
1581
+ mergedCSS && /* @__PURE__ */ import_react11.default.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1582
+ background && /* @__PURE__ */ import_react11.default.createElement(import_react12.Background, null),
1583
+ controls && /* @__PURE__ */ import_react11.default.createElement(import_react12.Controls, null),
1584
+ minimap && /* @__PURE__ */ import_react11.default.createElement(
1585
+ import_react12.MiniMap,
1112
1586
  {
1113
1587
  nodeColor: minimapNodeColor
1114
1588
  }
1115
1589
  ),
1116
- bounds && /* @__PURE__ */ import_react9.default.createElement(
1590
+ bounds && /* @__PURE__ */ import_react11.default.createElement(
1117
1591
  FocusHandler,
1118
1592
  {
1119
1593
  bounds,
@@ -1131,4 +1605,68 @@ function FlowRenderer({
1131
1605
  )
1132
1606
  ));
1133
1607
  }
1608
+
1609
+ // src/ui/Flow/FlowFrame.tsx
1610
+ var import_react13 = __toESM(require("react"), 1);
1611
+ function FlowFrame(_a) {
1612
+ var _b = _a, {
1613
+ client,
1614
+ slug,
1615
+ id,
1616
+ frameId,
1617
+ loading = null,
1618
+ error: renderError,
1619
+ onDataReady
1620
+ } = _b, rendererProps = __objRest(_b, [
1621
+ "client",
1622
+ "slug",
1623
+ "id",
1624
+ "frameId",
1625
+ "loading",
1626
+ "error",
1627
+ "onDataReady"
1628
+ ]);
1629
+ const { data, nodeTypeDefs, edgeTypeDefs, flow, isLoading, error } = useFlow({
1630
+ client,
1631
+ slug,
1632
+ id
1633
+ });
1634
+ const frameData = (0, import_react13.useMemo)(
1635
+ () => data ? getFrameData(data, frameId) : void 0,
1636
+ [data, frameId]
1637
+ );
1638
+ const onDataReadyRef = (0, import_react13.useRef)(onDataReady);
1639
+ onDataReadyRef.current = onDataReady;
1640
+ (0, import_react13.useEffect)(() => {
1641
+ var _a2;
1642
+ if (frameData && flow) {
1643
+ (_a2 = onDataReadyRef.current) == null ? void 0 : _a2.call(onDataReadyRef, frameData, flow);
1644
+ }
1645
+ }, [frameData, flow]);
1646
+ if (process.env.NODE_ENV !== "production" && !slug && !id) {
1647
+ console.warn('[FlowFrame] Either "slug" or "id" must be provided.');
1648
+ }
1649
+ if (isLoading) return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, loading);
1650
+ if (error) return renderError ? /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, renderError(error)) : null;
1651
+ if (!frameData) {
1652
+ if (process.env.NODE_ENV !== "production" && data) {
1653
+ const frames = data.nodes.filter(isFrameNode).map((n) => n.id);
1654
+ console.warn(
1655
+ `[FlowFrame] Frame "${frameId}" not found. Available frames: ${frames.join(", ") || "(none)"}`
1656
+ );
1657
+ }
1658
+ return null;
1659
+ }
1660
+ return /* @__PURE__ */ import_react13.default.createElement(
1661
+ FlowRenderer,
1662
+ __spreadProps(__spreadValues({}, rendererProps), {
1663
+ data: frameData.data,
1664
+ nodeTypeDefs,
1665
+ edgeTypeDefs,
1666
+ bounds: frameData.fitBounds,
1667
+ clampBounds: frameData.clampBounds,
1668
+ hideFrames: true
1669
+ })
1670
+ );
1671
+ }
1134
1672
  //# sourceMappingURL=flow.cjs.map