@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.js CHANGED
@@ -18,6 +18,18 @@ var __spreadValues = (a, b) => {
18
18
  return a;
19
19
  };
20
20
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __objRest = (source, exclude) => {
22
+ var target = {};
23
+ for (var prop in source)
24
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
+ target[prop] = source[prop];
26
+ if (source != null && __getOwnPropSymbols)
27
+ for (var prop of __getOwnPropSymbols(source)) {
28
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
+ target[prop] = source[prop];
30
+ }
31
+ return target;
32
+ };
21
33
  var __async = (__this, __arguments, generator) => {
22
34
  return new Promise((resolve, reject) => {
23
35
  var fulfilled = (value) => {
@@ -102,6 +114,43 @@ function collectionKeys(collection) {
102
114
  };
103
115
  }
104
116
 
117
+ // src/ui/Flow/query-options.ts
118
+ function flowQueryOptions(client, slug, id) {
119
+ var _a;
120
+ const identifier = (_a = id != null ? id : slug) != null ? _a : "";
121
+ return {
122
+ queryKey: collectionKeys("flows").detail(identifier),
123
+ queryFn: () => __async(null, null, function* () {
124
+ if (id) return client.from("flows").findById(id);
125
+ const result = yield client.from("flows").find({
126
+ where: { slug: { equals: slug } },
127
+ limit: 1
128
+ });
129
+ const doc = result.docs[0];
130
+ if (!doc) throw new Error(`Flow not found: ${slug}`);
131
+ return doc;
132
+ })
133
+ };
134
+ }
135
+ function nodeTypesQueryOptions(client) {
136
+ return {
137
+ queryKey: collectionKeys("flow-node-types").lists(),
138
+ queryFn: () => __async(null, null, function* () {
139
+ const result = yield client.from("flow-node-types").find({ limit: 0 });
140
+ return result.docs;
141
+ })
142
+ };
143
+ }
144
+ function edgeTypesQueryOptions(client) {
145
+ return {
146
+ queryKey: collectionKeys("flow-edge-types").lists(),
147
+ queryFn: () => __async(null, null, function* () {
148
+ const result = yield client.from("flow-edge-types").find({ limit: 0 });
149
+ return result.docs;
150
+ })
151
+ };
152
+ }
153
+
105
154
  // src/ui/Flow/useFlow.ts
106
155
  function toNodeTypeDef(doc) {
107
156
  var _a, _b, _c, _d, _e, _f;
@@ -134,49 +183,25 @@ function toEdgeTypeDef(doc) {
134
183
  };
135
184
  }
136
185
  function useFlow(options) {
137
- var _a, _b, _c;
186
+ var _a, _b;
138
187
  const { client, slug, id, enabled = true } = options;
139
188
  const hasIdentifier = !!(slug || id);
140
- const identifier = (_a = id != null ? id : slug) != null ? _a : "";
141
189
  const flowQuery = useQuery(
142
- {
143
- queryKey: collectionKeys("flows").detail(identifier),
144
- queryFn: () => __async(null, null, function* () {
145
- if (id) {
146
- return client.from("flows").findById(id);
147
- }
148
- const result = yield client.from("flows").find({
149
- where: { slug: { equals: slug } },
150
- limit: 1
151
- });
152
- const doc = result.docs[0];
153
- if (!doc) throw new Error(`Flow not found: ${slug}`);
154
- return doc;
155
- }),
190
+ __spreadProps(__spreadValues({}, flowQueryOptions(client, slug, id)), {
156
191
  enabled: enabled && hasIdentifier
157
- },
192
+ }),
158
193
  client.queryClient
159
194
  );
160
195
  const nodeTypesQuery = useQuery(
161
- {
162
- queryKey: collectionKeys("flow-node-types").lists(),
163
- queryFn: () => __async(null, null, function* () {
164
- const result = yield client.from("flow-node-types").find({ limit: 100 });
165
- return result.docs;
166
- }),
196
+ __spreadProps(__spreadValues({}, nodeTypesQueryOptions(client)), {
167
197
  enabled
168
- },
198
+ }),
169
199
  client.queryClient
170
200
  );
171
201
  const edgeTypesQuery = useQuery(
172
- {
173
- queryKey: collectionKeys("flow-edge-types").lists(),
174
- queryFn: () => __async(null, null, function* () {
175
- const result = yield client.from("flow-edge-types").find({ limit: 100 });
176
- return result.docs;
177
- }),
202
+ __spreadProps(__spreadValues({}, edgeTypesQueryOptions(client)), {
178
203
  enabled
179
- },
204
+ }),
180
205
  client.queryClient
181
206
  );
182
207
  const nodeTypeDefs = useMemo(() => {
@@ -201,44 +226,21 @@ function useFlow(options) {
201
226
  edgeTypeDefs,
202
227
  flow,
203
228
  isLoading: flowQuery.isLoading || nodeTypesQuery.isLoading || edgeTypesQuery.isLoading,
204
- error: (_c = (_b = flowQuery.error) != null ? _b : nodeTypesQuery.error) != null ? _c : edgeTypesQuery.error
229
+ error: (_b = (_a = flowQuery.error) != null ? _a : nodeTypesQuery.error) != null ? _b : edgeTypesQuery.error
205
230
  };
206
231
  }
207
232
 
208
233
  // src/ui/Flow/prefetchFlow.ts
209
234
  function prefetchFlow(options) {
210
235
  return __async(this, null, function* () {
211
- var _a;
212
236
  const { client, slug, id } = options;
213
- const identifier = (_a = id != null ? id : slug) != null ? _a : "";
237
+ if (!slug && !id) {
238
+ throw new Error("prefetchFlow requires either slug or id");
239
+ }
214
240
  yield Promise.all([
215
- client.queryClient.prefetchQuery({
216
- queryKey: collectionKeys("flows").detail(identifier),
217
- queryFn: () => __async(null, null, function* () {
218
- if (id) return client.from("flows").findById(id);
219
- const result = yield client.from("flows").find({
220
- where: { slug: { equals: slug } },
221
- limit: 1
222
- });
223
- const doc = result.docs[0];
224
- if (!doc) throw new Error(`Flow not found: ${slug}`);
225
- return doc;
226
- })
227
- }),
228
- client.queryClient.prefetchQuery({
229
- queryKey: collectionKeys("flow-node-types").lists(),
230
- queryFn: () => __async(null, null, function* () {
231
- const result = yield client.from("flow-node-types").find({ limit: 100 });
232
- return result.docs;
233
- })
234
- }),
235
- client.queryClient.prefetchQuery({
236
- queryKey: collectionKeys("flow-edge-types").lists(),
237
- queryFn: () => __async(null, null, function* () {
238
- const result = yield client.from("flow-edge-types").find({ limit: 100 });
239
- return result.docs;
240
- })
241
- })
241
+ client.queryClient.prefetchQuery(flowQueryOptions(client, slug, id)),
242
+ client.queryClient.prefetchQuery(nodeTypesQueryOptions(client)),
243
+ client.queryClient.prefetchQuery(edgeTypesQueryOptions(client))
242
244
  ]);
243
245
  });
244
246
  }
@@ -297,15 +299,28 @@ function getAbsolutePosition(node, nodeMap) {
297
299
  return { x, y };
298
300
  }
299
301
  function collectDescendants(nodes, rootId) {
302
+ const childrenMap = /* @__PURE__ */ new Map();
303
+ for (const n of nodes) {
304
+ if (n.parentId) {
305
+ let siblings = childrenMap.get(n.parentId);
306
+ if (!siblings) {
307
+ siblings = [];
308
+ childrenMap.set(n.parentId, siblings);
309
+ }
310
+ siblings.push(n.id);
311
+ }
312
+ }
300
313
  const result = /* @__PURE__ */ new Set([rootId]);
301
314
  const queue = [rootId];
302
315
  let i = 0;
303
316
  while (i < queue.length) {
304
- const current = queue[i++];
305
- for (const n of nodes) {
306
- if (n.parentId === current && !result.has(n.id)) {
307
- result.add(n.id);
308
- queue.push(n.id);
317
+ const children = childrenMap.get(queue[i++]);
318
+ if (children) {
319
+ for (const childId of children) {
320
+ if (!result.has(childId)) {
321
+ result.add(childId);
322
+ queue.push(childId);
323
+ }
309
324
  }
310
325
  }
311
326
  }
@@ -380,16 +395,76 @@ function getFrameData(data, frameId) {
380
395
  // src/ui/Flow/template-compiler.ts
381
396
  import React from "react";
382
397
  import { transform } from "sucrase";
383
- var MAX_CACHE_SIZE = 100;
384
- var componentCache = /* @__PURE__ */ new Map();
385
- function hashCode(str) {
386
- let hash = 0;
387
- for (let i = 0; i < str.length; i++) {
388
- const char = str.charCodeAt(i);
389
- hash = (hash << 5) - hash + char | 0;
390
- }
391
- return hash.toString(36);
398
+
399
+ // src/ui/Flow/quickjs-loader.ts
400
+ import { useSyncExternalStore } from "react";
401
+ var _state = { status: "idle", module: null };
402
+ var _listeners = /* @__PURE__ */ new Set();
403
+ function _notify() {
404
+ _listeners.forEach((fn) => fn());
392
405
  }
406
+ function subscribeQuickJS(cb) {
407
+ _listeners.add(cb);
408
+ return () => _listeners.delete(cb);
409
+ }
410
+ function getQuickJSSnapshot() {
411
+ return _state.module;
412
+ }
413
+ function ensureQuickJSLoaded() {
414
+ if (_state.status !== "idle") return;
415
+ _state = { status: "loading", module: null };
416
+ import("quickjs-emscripten").then(
417
+ (mod) => mod.getQuickJS()
418
+ ).then((module) => {
419
+ _state = { status: "ready", module };
420
+ _notify();
421
+ }).catch(() => {
422
+ _state = { status: "failed", module: null };
423
+ _notify();
424
+ });
425
+ }
426
+ function useQuickJS() {
427
+ return useSyncExternalStore(subscribeQuickJS, getQuickJSSnapshot, () => null);
428
+ }
429
+
430
+ // src/ui/Flow/template-compiler.ts
431
+ var MAX_CACHE_SIZE = 100;
432
+ var MAX_MATERIALIZE_DEPTH = 20;
433
+ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
434
+ "div",
435
+ "span",
436
+ "p",
437
+ "h1",
438
+ "h2",
439
+ "h3",
440
+ "h4",
441
+ "h5",
442
+ "h6",
443
+ "ul",
444
+ "ol",
445
+ "li",
446
+ "strong",
447
+ "em",
448
+ "b",
449
+ "i",
450
+ "br",
451
+ "img",
452
+ "figure",
453
+ "section",
454
+ "table",
455
+ "thead",
456
+ "tbody",
457
+ "tr",
458
+ "th",
459
+ "td",
460
+ "pre",
461
+ "code",
462
+ "svg",
463
+ "path",
464
+ "g",
465
+ "circle",
466
+ "rect"
467
+ ]);
393
468
  var BLOCKED_PATTERNS = [
394
469
  /\bdocument\s*\./,
395
470
  /\bwindow\s*\./,
@@ -409,79 +484,217 @@ var BLOCKED_PATTERNS = [
409
484
  /\bsetTimeout\s*\(/,
410
485
  /\bsetInterval\s*\(/,
411
486
  /\bsetImmediate\s*\(/,
412
- /\brequire\s*\(/
487
+ /\brequire\s*\(/,
488
+ /\bself\b/,
489
+ /\bconstructor\s*\.\s*constructor/,
490
+ /\bReflect\b/,
491
+ /\bProxy\b/,
492
+ /\b__proto__\b/
413
493
  ];
494
+ var VM_SETUP = `
495
+ var React = {
496
+ createElement: function(type, props) {
497
+ var args = Array.prototype.slice.call(arguments, 2);
498
+ return { $$t: 'el', type: String(type), props: props || {}, ch: args };
499
+ },
500
+ Fragment: '__frag__'
501
+ };
502
+ var exports = {};
503
+ var module = { exports: exports };
504
+ var window = undefined;
505
+ var document = undefined;
506
+ var globalThis = undefined;
507
+ var self = undefined;
508
+ var setTimeout = undefined;
509
+ var setInterval = undefined;
510
+ var setImmediate = undefined;
511
+ var fetch = undefined;
512
+ var XMLHttpRequest = undefined;
513
+ var navigator = undefined;
514
+ var location = undefined;
515
+ var localStorage = undefined;
516
+ var sessionStorage = undefined;
517
+ var cookie = undefined;
518
+ var postMessage = undefined;
519
+ `;
520
+ var rendererCache = /* @__PURE__ */ new Map();
521
+ function hashCode(str) {
522
+ let hash = 0;
523
+ for (let i = 0; i < str.length; i++) {
524
+ const char = str.charCodeAt(i);
525
+ hash = (hash << 5) - hash + char | 0;
526
+ }
527
+ return hash.toString(36);
528
+ }
414
529
  function validateTemplateCode(code) {
415
530
  return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
416
531
  }
417
- function compileTemplate(code, slug) {
418
- const cacheKey = `${slug}:${hashCode(code)}`;
419
- if (componentCache.has(cacheKey)) {
420
- const cached = componentCache.get(cacheKey);
421
- componentCache.delete(cacheKey);
422
- componentCache.set(cacheKey, cached);
423
- return cached;
532
+ var SAFE_DATA_URI_PREFIXES = [
533
+ "data:image/png;",
534
+ "data:image/jpeg;",
535
+ "data:image/gif;",
536
+ "data:image/webp;",
537
+ "data:image/avif;"
538
+ ];
539
+ function isSafeUrl(value) {
540
+ if (typeof value !== "string") return false;
541
+ if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) return true;
542
+ if (SAFE_DATA_URI_PREFIXES.some((p) => value.startsWith(p))) return true;
543
+ try {
544
+ const url = new URL(value);
545
+ return url.protocol === "http:" || url.protocol === "https:";
546
+ } catch (e) {
547
+ return false;
424
548
  }
425
- if (!validateTemplateCode(code)) {
426
- console.warn(`[flow] Template "${slug}" contains blocked patterns`);
427
- return null;
549
+ }
550
+ var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
551
+ "backgroundImage",
552
+ "background",
553
+ "listStyleImage",
554
+ "content",
555
+ "borderImage",
556
+ "borderImageSource",
557
+ "maskImage",
558
+ "mask",
559
+ "filter",
560
+ "cursor"
561
+ ]);
562
+ function sanitizeStyle(style) {
563
+ if (!style || typeof style !== "object" || Array.isArray(style)) return {};
564
+ const safe = {};
565
+ for (const [k, v] of Object.entries(style)) {
566
+ if (BLOCKED_STYLE_PROPS.has(k)) continue;
567
+ if (typeof v === "string" && /url\s*\(/i.test(v)) continue;
568
+ if (k === "position" && typeof v === "string" && /fixed|sticky/i.test(v)) continue;
569
+ if (k === "zIndex") {
570
+ const n = typeof v === "number" ? v : parseInt(String(v), 10);
571
+ if (isNaN(n) || n > 9998) continue;
572
+ }
573
+ safe[k] = v;
574
+ }
575
+ return safe;
576
+ }
577
+ function materialize(node, depth = 0) {
578
+ var _a;
579
+ if (depth > MAX_MATERIALIZE_DEPTH) return null;
580
+ if (node == null) return null;
581
+ if (typeof node === "string" || typeof node === "number") return node;
582
+ if (Array.isArray(node)) {
583
+ return React.createElement(
584
+ React.Fragment,
585
+ null,
586
+ ...node.map((c) => materialize(c, depth + 1))
587
+ );
588
+ }
589
+ if (typeof node !== "object") return null;
590
+ const d = node;
591
+ if (d.$$t === "frag") {
592
+ const ch = Array.isArray(d.ch) ? d.ch : [];
593
+ return React.createElement(
594
+ React.Fragment,
595
+ null,
596
+ ...ch.map((c) => materialize(c, depth + 1))
597
+ );
598
+ }
599
+ if (d.$$t === "el") {
600
+ const type = String((_a = d.type) != null ? _a : "");
601
+ const props = d.props && typeof d.props === "object" ? d.props : {};
602
+ const ch = Array.isArray(d.ch) ? d.ch : [];
603
+ if (!ALLOWED_ELEMENTS.has(type.toLowerCase())) return null;
604
+ const safeProps = {};
605
+ for (const [k, v] of Object.entries(props)) {
606
+ if (k.startsWith("on")) continue;
607
+ if (k === "dangerouslySetInnerHTML" || k === "ref") continue;
608
+ if ((k === "src" || k === "href") && !isSafeUrl(v)) continue;
609
+ if (k === "style") {
610
+ safeProps[k] = sanitizeStyle(v);
611
+ continue;
612
+ }
613
+ safeProps[k] = v;
614
+ }
615
+ const children = ch.map((c) => materialize(c, depth + 1));
616
+ return React.createElement(type, safeProps, ...children);
617
+ }
618
+ return null;
619
+ }
620
+ function evalInContext(vm, code) {
621
+ const result = vm.evalCode(code);
622
+ if (result.error) {
623
+ result.error.dispose();
624
+ return { ok: false };
428
625
  }
626
+ return { ok: true, handle: result.value };
627
+ }
628
+ var MAX_VM_CYCLES = 2e5;
629
+ function makeRenderer(jsCode) {
630
+ return function runInQuickJS(qjs, props) {
631
+ const vm = qjs.newContext();
632
+ let cycles = 0;
633
+ vm.runtime.setInterruptHandler(() => {
634
+ cycles++;
635
+ return cycles > MAX_VM_CYCLES;
636
+ });
637
+ try {
638
+ const setupResult = evalInContext(vm, VM_SETUP + jsCode);
639
+ if (!setupResult.ok) return null;
640
+ setupResult.handle.dispose();
641
+ const propsJson = JSON.stringify(props);
642
+ const callCode = `(function(){var __c=module.exports.default||module.exports;return JSON.stringify(__c(${propsJson}));})();`;
643
+ const callResult = evalInContext(vm, callCode);
644
+ if (!callResult.ok) return null;
645
+ const raw = vm.dump(callResult.handle);
646
+ callResult.handle.dispose();
647
+ return materialize(JSON.parse(String(raw)));
648
+ } catch (e) {
649
+ return null;
650
+ } finally {
651
+ vm.dispose();
652
+ }
653
+ };
654
+ }
655
+ function makeReactFC(renderer, qjs) {
656
+ return function QuickJSTemplateComponent(props) {
657
+ var _a;
658
+ return (_a = renderer(qjs, props)) != null ? _a : null;
659
+ };
660
+ }
661
+ function compileTemplate(code, slug) {
662
+ ensureQuickJSLoaded();
663
+ if (!validateTemplateCode(code)) return null;
664
+ const qjs = getQuickJSSnapshot();
665
+ if (!qjs) return null;
666
+ const cacheKey = `${slug}:${code.length}:${hashCode(code)}`;
667
+ if (rendererCache.has(cacheKey)) {
668
+ const entry = rendererCache.get(cacheKey);
669
+ rendererCache.delete(cacheKey);
670
+ rendererCache.set(cacheKey, entry);
671
+ return entry.fc;
672
+ }
673
+ let jsCode;
429
674
  try {
430
- const { code: jsCode } = transform(code, {
675
+ const result = transform(code, {
431
676
  transforms: ["typescript", "jsx", "imports"],
432
677
  jsxRuntime: "classic",
433
678
  jsxPragma: "React.createElement",
434
679
  jsxFragmentPragma: "React.Fragment"
435
680
  });
436
- const factory = new Function(
437
- "React",
438
- `
439
- var window = undefined;
440
- var document = undefined;
441
- var globalThis = undefined;
442
- var setTimeout = undefined;
443
- var setInterval = undefined;
444
- var setImmediate = undefined;
445
- var fetch = undefined;
446
- var XMLHttpRequest = undefined;
447
- var navigator = undefined;
448
- var location = undefined;
449
- var exports = {};
450
- var module = { exports: exports };
451
- ${jsCode}
452
- return module.exports.default || module.exports;
453
- `
454
- );
455
- const Component = factory(React);
456
- if (typeof Component !== "function") return null;
457
- if (componentCache.size >= MAX_CACHE_SIZE) {
458
- const oldestKey = componentCache.keys().next().value;
459
- if (oldestKey) componentCache.delete(oldestKey);
460
- }
461
- componentCache.set(cacheKey, Component);
462
- return Component;
681
+ jsCode = result.code;
463
682
  } catch (e) {
464
- console.warn(`[flow] Failed to compile template for "${slug}":`, e);
465
683
  return null;
466
684
  }
685
+ const renderer = makeRenderer(jsCode);
686
+ const fc = makeReactFC(renderer, qjs);
687
+ if (rendererCache.size >= MAX_CACHE_SIZE) {
688
+ const oldest = rendererCache.keys().next().value;
689
+ if (oldest) rendererCache.delete(oldest);
690
+ }
691
+ rendererCache.set(cacheKey, { renderer, fc });
692
+ return fc;
467
693
  }
468
694
  function clearTemplateCache() {
469
- componentCache.clear();
695
+ rendererCache.clear();
470
696
  }
471
697
 
472
- // src/ui/Flow/FlowRenderer.tsx
473
- import React5 from "react";
474
- import {
475
- ReactFlow,
476
- ReactFlowProvider,
477
- Background,
478
- Controls,
479
- MiniMap
480
- } from "@xyflow/react";
481
-
482
- // src/ui/Flow/node-types-factory.tsx
483
- import React3 from "react";
484
-
485
698
  // src/ui/Flow/node-renderers.tsx
486
699
  import React2 from "react";
487
700
  function sanitizeUrl(url) {
@@ -557,7 +770,7 @@ var TemplateErrorBoundary = class extends React2.Component {
557
770
  }
558
771
  render() {
559
772
  if (this.state.error) {
560
- return /* @__PURE__ */ React2.createElement("div", { style: { padding: 8, fontSize: 11, color: "#ef4444" } }, /* @__PURE__ */ React2.createElement("strong", null, "Render error"), /* @__PURE__ */ React2.createElement("pre", { style: { fontSize: 10, whiteSpace: "pre-wrap" } }, this.state.error.message));
773
+ return /* @__PURE__ */ React2.createElement("div", { style: { padding: 8, fontSize: 11, color: "#ef4444" } }, /* @__PURE__ */ React2.createElement("strong", null, "Render error"), /* @__PURE__ */ React2.createElement("pre", { style: { fontSize: 10, whiteSpace: "pre-wrap" } }, process.env.NODE_ENV === "development" ? this.state.error.message : "Template render failed"));
561
774
  }
562
775
  return this.props.children;
563
776
  }
@@ -568,6 +781,7 @@ function EnhancedDynamicNode({
568
781
  width,
569
782
  height
570
783
  }) {
784
+ useQuickJS();
571
785
  if (typeDef.template) {
572
786
  const Component = compileTemplate(typeDef.template, typeDef.slug);
573
787
  if (Component) {
@@ -594,6 +808,7 @@ function EnhancedDynamicNode({
594
808
  return /* @__PURE__ */ React2.createElement(
595
809
  "div",
596
810
  {
811
+ className: `flow-node flow-node--${typeDef.slug}`,
597
812
  style: {
598
813
  width: "100%",
599
814
  height: "100%",
@@ -646,14 +861,304 @@ function DefaultFrameNode({ data }) {
646
861
  );
647
862
  }
648
863
 
864
+ // src/ui/Flow/field-helpers.ts
865
+ function getImageField(fields, name) {
866
+ const val = fields == null ? void 0 : fields[name];
867
+ if (val && typeof val === "object" && val !== null && "url" in val)
868
+ return val;
869
+ return void 0;
870
+ }
871
+ function getTextField(fields, name) {
872
+ const val = fields == null ? void 0 : fields[name];
873
+ return typeof val === "string" ? val : void 0;
874
+ }
875
+ function getNumberField(fields, name) {
876
+ const val = fields == null ? void 0 : fields[name];
877
+ return typeof val === "number" ? val : void 0;
878
+ }
879
+ function getBooleanField(fields, name) {
880
+ const val = fields == null ? void 0 : fields[name];
881
+ return typeof val === "boolean" ? val : void 0;
882
+ }
883
+
884
+ // src/ui/Flow/css-sanitizer.ts
885
+ import postcss from "postcss";
886
+ var ALLOWED_AT_RULES = /* @__PURE__ */ new Set(["keyframes", "media", "supports", "container", "layer"]);
887
+ var BLOCKED_VALUE_PATTERNS = [/url\s*\(/i, /expression\s*\(/i, /paint\s*\(/i, /-moz-binding/i];
888
+ var DANGEROUS_SELECTOR = /(?:^|[\s,])(?::root|html|body)\b/;
889
+ var BLOCKED_PROPERTY_VALUES = {
890
+ // absolute is allowed — contained by nearest positioned ancestor (node wrapper)
891
+ position: /fixed|sticky/i
892
+ };
893
+ var Z_INDEX_MAX = 9998;
894
+ function sanitizeCSS(css, scopeClass) {
895
+ let root;
896
+ try {
897
+ root = postcss.parse(css);
898
+ } catch (e) {
899
+ return "";
900
+ }
901
+ const safeScopeClass = scopeClass ? scopeClass.replace(/[{}()[\];,'"\\<>\s]/g, "") : "";
902
+ const keyframeNameMap = /* @__PURE__ */ new Map();
903
+ if (safeScopeClass) {
904
+ root.walkAtRules(/^keyframes$/i, (node) => {
905
+ const rawName = node.params.trim().replace(/^['"]|['"]$/g, "");
906
+ const originalName = rawName;
907
+ const scopedName = `${safeScopeClass}__${rawName.replace(/\s+/g, "_")}`;
908
+ keyframeNameMap.set(originalName, scopedName);
909
+ node.params = scopedName;
910
+ });
911
+ }
912
+ if (safeScopeClass && keyframeNameMap.size > 0) {
913
+ const ANIM_KEYWORDS = /* @__PURE__ */ new Set(["none", "initial", "inherit", "unset", "revert"]);
914
+ const replaceAnimName = (token) => {
915
+ var _a;
916
+ const t = token.trim().replace(/^['"]|['"]$/g, "");
917
+ return ANIM_KEYWORDS.has(t) ? token : (_a = keyframeNameMap.get(t)) != null ? _a : token;
918
+ };
919
+ root.walkDecls(/^animation-name$/i, (node) => {
920
+ node.value = node.value.split(",").map(replaceAnimName).join(", ");
921
+ });
922
+ root.walkDecls(/^animation$/i, (node) => {
923
+ node.value = node.value.split(",").map(
924
+ (anim) => anim.split(/(\s+)/).map((token) => /\s/.test(token) ? token : replaceAnimName(token)).join("")
925
+ ).join(",");
926
+ });
927
+ }
928
+ root.walkAtRules((node) => {
929
+ if (!ALLOWED_AT_RULES.has(node.name.toLowerCase())) {
930
+ node.remove();
931
+ }
932
+ });
933
+ root.walkDecls((node) => {
934
+ const normalizedValue = node.value.replace(/\/\*[\s\S]*?\*\//g, "");
935
+ if (BLOCKED_VALUE_PATTERNS.some((p) => p.test(normalizedValue))) {
936
+ node.remove();
937
+ return;
938
+ }
939
+ const propLower = node.prop.toLowerCase();
940
+ const blockedValuePattern = BLOCKED_PROPERTY_VALUES[propLower];
941
+ if (blockedValuePattern) {
942
+ const deescaped = normalizedValue.replace(
943
+ /\\([0-9a-fA-F]{1,6})\s?/g,
944
+ (_, hex) => String.fromCodePoint(parseInt(hex, 16))
945
+ );
946
+ if (blockedValuePattern.test(deescaped)) {
947
+ node.remove();
948
+ return;
949
+ }
950
+ }
951
+ if (propLower === "z-index") {
952
+ const zv = node.value.trim();
953
+ if (zv === "auto" || zv === "initial" || zv === "inherit" || zv === "unset" || zv === "revert") {
954
+ } else {
955
+ const val = parseInt(zv, 10);
956
+ if (isNaN(val) || String(val) !== zv || val > Z_INDEX_MAX) {
957
+ node.remove();
958
+ return;
959
+ }
960
+ }
961
+ }
962
+ });
963
+ root.walkRules((rule) => {
964
+ rule.selectors = rule.selectors.filter((s) => !DANGEROUS_SELECTOR.test(s));
965
+ if (!rule.selectors.length) {
966
+ rule.remove();
967
+ return;
968
+ }
969
+ if (scopeClass) {
970
+ if (!safeScopeClass) {
971
+ rule.remove();
972
+ return;
973
+ }
974
+ const parent = rule.parent;
975
+ if ((parent == null ? void 0 : parent.type) === "atrule" && parent.name.toLowerCase() === "keyframes") {
976
+ return;
977
+ }
978
+ rule.selectors = rule.selectors.map((sel) => `.${safeScopeClass} ${sel}`);
979
+ }
980
+ });
981
+ return root.toString().replace(/<\/style/gi, "<\\/style");
982
+ }
983
+
984
+ // src/ui/Flow/FlowRenderer.tsx
985
+ import React6 from "react";
986
+ import {
987
+ ReactFlow,
988
+ ReactFlowProvider,
989
+ Background,
990
+ Controls,
991
+ MiniMap
992
+ } from "@xyflow/react";
993
+
994
+ // src/ui/Flow/node-types-factory.tsx
995
+ import React4 from "react";
996
+
997
+ // src/ui/Image/index.tsx
998
+ import React3, { useCallback, useRef, useState } from "react";
999
+
1000
+ // src/utils/image.ts
1001
+ var IMAGE_SIZES = [384, 768, 1536];
1002
+ function getImageSrcSet(image) {
1003
+ const parts = [];
1004
+ const sizes = image.sizes;
1005
+ if (sizes) {
1006
+ for (const size of IMAGE_SIZES) {
1007
+ const entry = sizes[String(size)];
1008
+ if ((entry == null ? void 0 : entry.url) && entry.width) {
1009
+ parts.push(`${entry.url} ${entry.width}w`);
1010
+ }
1011
+ }
1012
+ }
1013
+ if (image.url && image.width) {
1014
+ parts.push(`${image.url} ${image.width}w`);
1015
+ }
1016
+ return parts.join(", ");
1017
+ }
1018
+ function getImagePlaceholderStyle(image, options) {
1019
+ var _a, _b, _c;
1020
+ const type = (_a = options == null ? void 0 : options.type) != null ? _a : "blur";
1021
+ const paletteColor = (_b = options == null ? void 0 : options.paletteColor) != null ? _b : "muted";
1022
+ if (type === "none") return {};
1023
+ const color = (_c = image.palette) == null ? void 0 : _c[paletteColor];
1024
+ if (type === "blur") {
1025
+ const lqip = image.lqip;
1026
+ if (lqip) {
1027
+ return {
1028
+ backgroundImage: `url(${lqip})`,
1029
+ backgroundSize: "cover",
1030
+ backgroundPosition: "center"
1031
+ };
1032
+ }
1033
+ if (color) {
1034
+ return { backgroundColor: color };
1035
+ }
1036
+ return {};
1037
+ }
1038
+ if (color) {
1039
+ return { backgroundColor: color };
1040
+ }
1041
+ return {};
1042
+ }
1043
+
1044
+ // src/ui/Image/index.tsx
1045
+ function Image({
1046
+ image,
1047
+ width,
1048
+ dpr = 1,
1049
+ placeholder: placeholderProp,
1050
+ className,
1051
+ style,
1052
+ imgClassName,
1053
+ imgStyle,
1054
+ sizes,
1055
+ loading: loadingProp,
1056
+ onLoad,
1057
+ objectFit = "cover",
1058
+ priority = false,
1059
+ fill = false,
1060
+ imageRendering
1061
+ }) {
1062
+ var _a, _b;
1063
+ const [loaded, setLoaded] = useState(false);
1064
+ const firedRef = useRef(false);
1065
+ const isPixelRendering = imageRendering === "pixelated" || imageRendering === "crisp-edges";
1066
+ const placeholder = placeholderProp != null ? placeholderProp : isPixelRendering ? "none" : "blur";
1067
+ const loading = priority ? "eager" : loadingProp != null ? loadingProp : "lazy";
1068
+ const aspectRatio = !fill && image.width && image.height ? `${image.width} / ${image.height}` : void 0;
1069
+ const srcSet = getImageSrcSet(image);
1070
+ const src = (_a = image.url) != null ? _a : void 0;
1071
+ const hasLqip = placeholder === "blur" && !!image.lqip;
1072
+ const placeholderStyle = getImagePlaceholderStyle(image, {
1073
+ type: placeholder
1074
+ });
1075
+ const placeholderColor = !hasLqip && "backgroundColor" in placeholderStyle ? placeholderStyle.backgroundColor : void 0;
1076
+ const fireLoad = useCallback(() => {
1077
+ if (firedRef.current) return;
1078
+ firedRef.current = true;
1079
+ setLoaded(true);
1080
+ onLoad == null ? void 0 : onLoad();
1081
+ }, [onLoad]);
1082
+ const imgRef = useCallback(
1083
+ (node) => {
1084
+ if (node && node.complete && node.naturalWidth > 0) {
1085
+ fireLoad();
1086
+ }
1087
+ },
1088
+ [fireLoad]
1089
+ );
1090
+ const containerStyle = __spreadValues(__spreadValues(__spreadValues({
1091
+ position: "relative",
1092
+ overflow: "hidden"
1093
+ }, fill ? { width: "100%", height: "100%" } : {}), aspectRatio ? { aspectRatio } : {}), style);
1094
+ const overlayBase = {
1095
+ position: "absolute",
1096
+ top: 0,
1097
+ left: 0,
1098
+ width: "100%",
1099
+ height: "100%",
1100
+ opacity: loaded ? 0 : 1,
1101
+ transition: "opacity 0.3s ease",
1102
+ pointerEvents: "none"
1103
+ };
1104
+ const mainImgStyle = __spreadValues(__spreadProps(__spreadValues({
1105
+ display: "block",
1106
+ width: "100%",
1107
+ height: "100%",
1108
+ objectFit
1109
+ }, imageRendering ? { imageRendering } : {}), {
1110
+ opacity: loaded ? 1 : 0,
1111
+ transition: "opacity 0.3s ease"
1112
+ }), imgStyle);
1113
+ return /* @__PURE__ */ React3.createElement("div", { className, style: containerStyle }, hasLqip && /* @__PURE__ */ React3.createElement(
1114
+ "img",
1115
+ {
1116
+ "aria-hidden": true,
1117
+ alt: "",
1118
+ src: image.lqip,
1119
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1120
+ display: "block",
1121
+ objectFit,
1122
+ filter: "blur(20px)",
1123
+ transform: "scale(1.1)"
1124
+ })
1125
+ }
1126
+ ), placeholderColor && /* @__PURE__ */ React3.createElement(
1127
+ "div",
1128
+ {
1129
+ "aria-hidden": true,
1130
+ style: __spreadProps(__spreadValues({}, overlayBase), {
1131
+ backgroundColor: placeholderColor
1132
+ })
1133
+ }
1134
+ ), /* @__PURE__ */ React3.createElement(
1135
+ "img",
1136
+ {
1137
+ ref: imgRef,
1138
+ alt: (_b = image.alt) != null ? _b : "",
1139
+ src,
1140
+ srcSet: srcSet || void 0,
1141
+ sizes,
1142
+ width: width ? width * dpr : void 0,
1143
+ loading,
1144
+ decoding: "async",
1145
+ fetchPriority: priority ? "high" : void 0,
1146
+ onLoad: fireLoad,
1147
+ className: imgClassName,
1148
+ style: mainImgStyle
1149
+ }
1150
+ ));
1151
+ }
1152
+
649
1153
  // src/ui/Flow/node-types-factory.tsx
650
1154
  function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
651
1155
  const types = {};
652
1156
  types.dynamic = ((props) => {
1157
+ var _a;
653
1158
  const d = props.data;
654
1159
  const typeDef = nodeTypeDefsMap == null ? void 0 : nodeTypeDefsMap.get(d.nodeTypeSlug);
655
1160
  const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
656
- const defaultRender = typeDef ? /* @__PURE__ */ React3.createElement(
1161
+ const defaultRender = typeDef ? /* @__PURE__ */ React4.createElement(
657
1162
  EnhancedDynamicNode,
658
1163
  {
659
1164
  data: d,
@@ -661,7 +1166,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
661
1166
  width: props.width,
662
1167
  height: props.height
663
1168
  }
664
- ) : /* @__PURE__ */ React3.createElement(DefaultDynamicNode, __spreadValues({}, props));
1169
+ ) : /* @__PURE__ */ React4.createElement(DefaultDynamicNode, __spreadValues({}, props));
665
1170
  const slotProps = {
666
1171
  id: props.id,
667
1172
  nodeTypeSlug: d.nodeTypeSlug,
@@ -673,14 +1178,35 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
673
1178
  height: props.height,
674
1179
  defaultRender
675
1180
  };
676
- let content = CustomRenderer ? /* @__PURE__ */ React3.createElement(CustomRenderer, __spreadValues({}, slotProps)) : defaultRender;
1181
+ let content;
1182
+ if (CustomRenderer) {
1183
+ content = /* @__PURE__ */ React4.createElement(CustomRenderer, __spreadValues({}, slotProps));
1184
+ } else if (d.nodeTypeSlug === "image") {
1185
+ const imageVal = (_a = d.fields) == null ? void 0 : _a.image;
1186
+ if (imageVal && typeof imageVal === "object" && "url" in imageVal) {
1187
+ content = /* @__PURE__ */ React4.createElement(
1188
+ Image,
1189
+ {
1190
+ image: imageVal,
1191
+ width: props.width,
1192
+ fill: true,
1193
+ priority: true,
1194
+ objectFit: "contain"
1195
+ }
1196
+ );
1197
+ } else {
1198
+ content = defaultRender;
1199
+ }
1200
+ } else {
1201
+ content = defaultRender;
1202
+ }
677
1203
  if (renderNode) {
678
1204
  const result = renderNode(slotProps, content);
679
1205
  if (result !== null) content = result;
680
1206
  }
681
1207
  if (nodeWrapper) {
682
1208
  const Wrapper = nodeWrapper;
683
- content = /* @__PURE__ */ React3.createElement(
1209
+ content = /* @__PURE__ */ React4.createElement(
684
1210
  Wrapper,
685
1211
  {
686
1212
  id: props.id,
@@ -697,7 +1223,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
697
1223
  types.frame = frameRenderer ? ((props) => {
698
1224
  const d = props.data;
699
1225
  const Renderer = frameRenderer;
700
- return /* @__PURE__ */ React3.createElement(
1226
+ return /* @__PURE__ */ React4.createElement(
701
1227
  Renderer,
702
1228
  {
703
1229
  id: props.id,
@@ -721,7 +1247,7 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
721
1247
  types[slug] = ((props) => {
722
1248
  var _a;
723
1249
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
724
- return /* @__PURE__ */ React3.createElement(
1250
+ return /* @__PURE__ */ React4.createElement(
725
1251
  Renderer,
726
1252
  {
727
1253
  id: props.id,
@@ -787,7 +1313,7 @@ function applyEdgeStyles(edges, edgeTypeDefsMap) {
787
1313
  }
788
1314
 
789
1315
  // src/ui/Flow/focus-handler.tsx
790
- import React4 from "react";
1316
+ import React5 from "react";
791
1317
  import { useReactFlow, useStoreApi } from "@xyflow/react";
792
1318
  function clampViewport(vp, cw, ch, extent) {
793
1319
  const left = -vp.x / vp.zoom;
@@ -816,14 +1342,14 @@ function FocusHandler({
816
1342
  }) {
817
1343
  const { setViewport } = useReactFlow();
818
1344
  const store = useStoreApi();
819
- const containerRef = React4.useRef(null);
1345
+ const containerRef = React5.useRef(null);
820
1346
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
821
- const boundsRef = React4.useRef(bounds);
1347
+ const boundsRef = React5.useRef(bounds);
822
1348
  boundsRef.current = bounds;
823
- const [containerSize, setContainerSize] = React4.useState({ w: 0, h: 0 });
824
- const prevBoundsKeyRef = React4.useRef(null);
825
- const prevSizeRef = React4.useRef({ w: 0, h: 0 });
826
- React4.useEffect(() => {
1349
+ const [containerSize, setContainerSize] = React5.useState({ w: 0, h: 0 });
1350
+ const prevBoundsKeyRef = React5.useRef(null);
1351
+ const prevSizeRef = React5.useRef({ w: 0, h: 0 });
1352
+ React5.useEffect(() => {
827
1353
  const el = containerRef.current;
828
1354
  if (!el) return;
829
1355
  const observer = new ResizeObserver((entries) => {
@@ -835,7 +1361,7 @@ function FocusHandler({
835
1361
  observer.observe(el);
836
1362
  return () => observer.disconnect();
837
1363
  }, []);
838
- React4.useEffect(() => {
1364
+ React5.useEffect(() => {
839
1365
  if (containerSize.w === 0 || containerSize.h === 0) return;
840
1366
  const prevKey = prevBoundsKeyRef.current;
841
1367
  const prevSize = prevSizeRef.current;
@@ -902,7 +1428,7 @@ function FocusHandler({
902
1428
  store,
903
1429
  onInitialFit
904
1430
  ]);
905
- return /* @__PURE__ */ React4.createElement(
1431
+ return /* @__PURE__ */ React5.createElement(
906
1432
  "div",
907
1433
  {
908
1434
  ref: containerRef,
@@ -917,6 +1443,7 @@ function FocusHandler({
917
1443
  }
918
1444
 
919
1445
  // src/ui/Flow/FlowRenderer.tsx
1446
+ var NullFrameRenderer = () => null;
920
1447
  function FlowRenderer({
921
1448
  data,
922
1449
  className,
@@ -934,6 +1461,7 @@ function FlowRenderer({
934
1461
  onNodeMouseLeave,
935
1462
  onEdgeClick,
936
1463
  frameRenderer,
1464
+ hideFrames = false,
937
1465
  edgeRenderers,
938
1466
  nodeWrapper,
939
1467
  controls,
@@ -954,33 +1482,34 @@ function FlowRenderer({
954
1482
  maxZoom: maxZoomProp
955
1483
  }) {
956
1484
  var _a, _b;
957
- const nodeTypeDefsMap = React5.useMemo(() => {
1485
+ const resolvedFrameRenderer = hideFrames ? NullFrameRenderer : frameRenderer;
1486
+ const nodeTypeDefsMap = React6.useMemo(() => {
958
1487
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
959
1488
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
960
1489
  }, [nodeTypeDefs]);
961
- const edgeTypeDefsMap = React5.useMemo(() => {
1490
+ const edgeTypeDefsMap = React6.useMemo(() => {
962
1491
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
963
1492
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
964
1493
  }, [edgeTypeDefs]);
965
- const nodeTypes = React5.useMemo(
1494
+ const nodeTypes = React6.useMemo(
966
1495
  () => createNodeTypes(
967
1496
  nodeRenderers,
968
1497
  nodeTypeDefsMap,
969
- frameRenderer,
1498
+ resolvedFrameRenderer,
970
1499
  nodeWrapper,
971
1500
  renderNode
972
1501
  ),
973
- [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
1502
+ [nodeRenderers, nodeTypeDefsMap, resolvedFrameRenderer, nodeWrapper, renderNode]
974
1503
  );
975
- const customEdgeTypes = React5.useMemo(
1504
+ const customEdgeTypes = React6.useMemo(
976
1505
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
977
1506
  [edgeRenderers, edgeTypeDefsMap]
978
1507
  );
979
- const mergedCSS = React5.useMemo(() => {
1508
+ const mergedCSS = React6.useMemo(() => {
980
1509
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
981
- return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
1510
+ return nodeTypeDefs.filter((d) => d.customCSS).map((d) => sanitizeCSS(d.customCSS, `flow-node--${d.slug}`)).join("\n");
982
1511
  }, [nodeTypeDefs]);
983
- const styledEdges = React5.useMemo(() => {
1512
+ const styledEdges = React6.useMemo(() => {
984
1513
  var _a2;
985
1514
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
986
1515
  if (edgeRenderers) {
@@ -994,7 +1523,7 @@ function FlowRenderer({
994
1523
  }
995
1524
  return edges;
996
1525
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
997
- const translateExtent = React5.useMemo(() => {
1526
+ const translateExtent = React6.useMemo(() => {
998
1527
  if (translateExtentProp) return translateExtentProp;
999
1528
  const es = clampBounds != null ? clampBounds : bounds;
1000
1529
  if (!es) return void 0;
@@ -1005,16 +1534,16 @@ function FlowRenderer({
1005
1534
  ];
1006
1535
  }, [translateExtentProp, clampBounds, bounds, focusPadding]);
1007
1536
  const boundsKey = bounds ? `${bounds.x},${bounds.y},${bounds.width},${bounds.height}` : "";
1008
- const extentReadyRef = React5.useRef(false);
1009
- const expandedExtentRef = React5.useRef(void 0);
1010
- const prevBoundsKeyRef = React5.useRef(boundsKey);
1537
+ const extentReadyRef = React6.useRef(false);
1538
+ const expandedExtentRef = React6.useRef(void 0);
1539
+ const prevBoundsKeyRef = React6.useRef(boundsKey);
1011
1540
  if (prevBoundsKeyRef.current !== boundsKey) {
1012
1541
  prevBoundsKeyRef.current = boundsKey;
1013
1542
  extentReadyRef.current = false;
1014
1543
  expandedExtentRef.current = void 0;
1015
1544
  }
1016
- const [, rerender] = React5.useReducer((x) => x + 1, 0);
1017
- const handleInitialFit = React5.useCallback(
1545
+ const [, rerender] = React6.useReducer((x) => x + 1, 0);
1546
+ const handleInitialFit = React6.useCallback(
1018
1547
  (expandedExtent) => {
1019
1548
  extentReadyRef.current = true;
1020
1549
  expandedExtentRef.current = expandedExtent;
@@ -1025,7 +1554,7 @@ function FlowRenderer({
1025
1554
  const activeExtent = !bounds || extentReadyRef.current ? (_a = expandedExtentRef.current) != null ? _a : translateExtent : void 0;
1026
1555
  if (!data) return null;
1027
1556
  const resolvedDefaultViewport = defaultViewportProp != null ? defaultViewportProp : !fitView && data.viewport ? data.viewport : void 0;
1028
- return /* @__PURE__ */ React5.createElement(ReactFlowProvider, null, /* @__PURE__ */ React5.createElement(
1557
+ return /* @__PURE__ */ React6.createElement(ReactFlowProvider, null, /* @__PURE__ */ React6.createElement(
1029
1558
  "div",
1030
1559
  {
1031
1560
  className,
@@ -1035,7 +1564,7 @@ function FlowRenderer({
1035
1564
  background: "transparent"
1036
1565
  }, style)
1037
1566
  },
1038
- /* @__PURE__ */ React5.createElement(
1567
+ /* @__PURE__ */ React6.createElement(
1039
1568
  ReactFlow,
1040
1569
  {
1041
1570
  nodes: (_b = data.nodes) != null ? _b : [],
@@ -1065,16 +1594,16 @@ function FlowRenderer({
1065
1594
  maxZoom: maxZoomProp,
1066
1595
  proOptions: { hideAttribution: true }
1067
1596
  },
1068
- mergedCSS && /* @__PURE__ */ React5.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1069
- background && /* @__PURE__ */ React5.createElement(Background, null),
1070
- controls && /* @__PURE__ */ React5.createElement(Controls, null),
1071
- minimap && /* @__PURE__ */ React5.createElement(
1597
+ mergedCSS && /* @__PURE__ */ React6.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1598
+ background && /* @__PURE__ */ React6.createElement(Background, null),
1599
+ controls && /* @__PURE__ */ React6.createElement(Controls, null),
1600
+ minimap && /* @__PURE__ */ React6.createElement(
1072
1601
  MiniMap,
1073
1602
  {
1074
1603
  nodeColor: minimapNodeColor
1075
1604
  }
1076
1605
  ),
1077
- bounds && /* @__PURE__ */ React5.createElement(
1606
+ bounds && /* @__PURE__ */ React6.createElement(
1078
1607
  FocusHandler,
1079
1608
  {
1080
1609
  bounds,
@@ -1092,18 +1621,89 @@ function FlowRenderer({
1092
1621
  )
1093
1622
  ));
1094
1623
  }
1624
+
1625
+ // src/ui/Flow/FlowFrame.tsx
1626
+ import React7, { useMemo as useMemo3, useEffect, useRef as useRef2 } from "react";
1627
+ function FlowFrame(_a) {
1628
+ var _b = _a, {
1629
+ client,
1630
+ slug,
1631
+ id,
1632
+ frameId,
1633
+ loading = null,
1634
+ error: renderError,
1635
+ onDataReady
1636
+ } = _b, rendererProps = __objRest(_b, [
1637
+ "client",
1638
+ "slug",
1639
+ "id",
1640
+ "frameId",
1641
+ "loading",
1642
+ "error",
1643
+ "onDataReady"
1644
+ ]);
1645
+ const { data, nodeTypeDefs, edgeTypeDefs, flow, isLoading, error } = useFlow({
1646
+ client,
1647
+ slug,
1648
+ id
1649
+ });
1650
+ const frameData = useMemo3(
1651
+ () => data ? getFrameData(data, frameId) : void 0,
1652
+ [data, frameId]
1653
+ );
1654
+ const onDataReadyRef = useRef2(onDataReady);
1655
+ onDataReadyRef.current = onDataReady;
1656
+ useEffect(() => {
1657
+ var _a2;
1658
+ if (frameData && flow) {
1659
+ (_a2 = onDataReadyRef.current) == null ? void 0 : _a2.call(onDataReadyRef, frameData, flow);
1660
+ }
1661
+ }, [frameData, flow]);
1662
+ if (process.env.NODE_ENV !== "production" && !slug && !id) {
1663
+ console.warn('[FlowFrame] Either "slug" or "id" must be provided.');
1664
+ }
1665
+ if (isLoading) return /* @__PURE__ */ React7.createElement(React7.Fragment, null, loading);
1666
+ if (error) return renderError ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, renderError(error)) : null;
1667
+ if (!frameData) {
1668
+ if (process.env.NODE_ENV !== "production" && data) {
1669
+ const frames = data.nodes.filter(isFrameNode).map((n) => n.id);
1670
+ console.warn(
1671
+ `[FlowFrame] Frame "${frameId}" not found. Available frames: ${frames.join(", ") || "(none)"}`
1672
+ );
1673
+ }
1674
+ return null;
1675
+ }
1676
+ return /* @__PURE__ */ React7.createElement(
1677
+ FlowRenderer,
1678
+ __spreadProps(__spreadValues({}, rendererProps), {
1679
+ data: frameData.data,
1680
+ nodeTypeDefs,
1681
+ edgeTypeDefs,
1682
+ bounds: frameData.fitBounds,
1683
+ clampBounds: frameData.clampBounds,
1684
+ hideFrames: true
1685
+ })
1686
+ );
1687
+ }
1095
1688
  export {
1096
1689
  BUILT_IN_EDGE_TYPES,
1097
1690
  BUILT_IN_NODE_TYPES,
1691
+ FlowFrame,
1098
1692
  FlowRenderer,
1099
1693
  clearTemplateCache,
1100
1694
  compileTemplate,
1695
+ getBooleanField,
1101
1696
  getFrameData,
1102
1697
  getFrames,
1698
+ getImageField,
1103
1699
  getNodeBounds,
1700
+ getNumberField,
1701
+ getTextField,
1104
1702
  isDynamicNode,
1105
1703
  isFrameNode,
1106
1704
  prefetchFlow,
1705
+ renderFieldValue,
1706
+ sanitizeCSS,
1107
1707
  useFlow,
1108
1708
  useFlowData
1109
1709
  };