@01.software/sdk 0.44.0 → 0.45.0

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 (80) hide show
  1. package/CHANGELOG.md +358 -2
  2. package/MIGRATION.md +89 -10
  3. package/README.md +441 -98
  4. package/dist/{server-nXOezi4b.d.cts → catalog-mappers-sIshKNb3.d.cts} +14 -3
  5. package/dist/{server-nXOezi4b.d.ts → catalog-mappers-sIshKNb3.d.ts} +14 -3
  6. package/dist/client.cjs +2680 -345
  7. package/dist/client.cjs.map +1 -1
  8. package/dist/client.d.cts +10 -7
  9. package/dist/client.d.ts +10 -7
  10. package/dist/client.js +2680 -345
  11. package/dist/client.js.map +1 -1
  12. package/dist/{collection-client-Dyl-CDcj.d.cts → collection-client-BSo7VXQE.d.cts} +8 -5
  13. package/dist/{collection-client-DWooaiTr.d.ts → collection-client-Ckfusuee.d.ts} +8 -5
  14. package/dist/{const-pGDnsy0o.d.ts → const-BAG0MH0u.d.cts} +13 -10
  15. package/dist/{const-1vleZ6ov.d.cts → const-CaPq2AE4.d.ts} +13 -10
  16. package/dist/errors.cjs +74 -1
  17. package/dist/errors.cjs.map +1 -1
  18. package/dist/errors.d.cts +38 -1
  19. package/dist/errors.d.ts +38 -1
  20. package/dist/errors.js +74 -1
  21. package/dist/errors.js.map +1 -1
  22. package/dist/{index-DUNySyxV.d.ts → index-BNICJxBL.d.ts} +4 -4
  23. package/dist/{index-a6g7vqAD.d.cts → index-YGrZrxNn.d.cts} +4 -4
  24. package/dist/index.cjs +2741 -368
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +18 -19
  27. package/dist/index.d.ts +18 -19
  28. package/dist/index.js +2741 -368
  29. package/dist/index.js.map +1 -1
  30. package/dist/{payload-types-CDsuOEfM.d.cts → payload-types-CJZuXcgF.d.cts} +6671 -4001
  31. package/dist/{payload-types-CDsuOEfM.d.ts → payload-types-CJZuXcgF.d.ts} +6671 -4001
  32. package/dist/query.cjs +550 -101
  33. package/dist/query.cjs.map +1 -1
  34. package/dist/query.d.cts +43 -45
  35. package/dist/query.d.ts +43 -45
  36. package/dist/query.js +550 -101
  37. package/dist/query.js.map +1 -1
  38. package/dist/realtime.cjs.map +1 -1
  39. package/dist/realtime.d.cts +2 -2
  40. package/dist/realtime.d.ts +2 -2
  41. package/dist/realtime.js.map +1 -1
  42. package/dist/server.cjs +2252 -444
  43. package/dist/server.cjs.map +1 -1
  44. package/dist/server.d.cts +11 -10
  45. package/dist/server.d.ts +11 -10
  46. package/dist/server.js +2252 -444
  47. package/dist/server.js.map +1 -1
  48. package/dist/storefront-cache.cjs +65 -1
  49. package/dist/storefront-cache.cjs.map +1 -1
  50. package/dist/storefront-cache.js +65 -1
  51. package/dist/storefront-cache.js.map +1 -1
  52. package/dist/types-BC6rVmJa.d.ts +47 -0
  53. package/dist/{types-CDND3hzq.d.cts → types-BlOvjoTX.d.ts} +1095 -509
  54. package/dist/{types-Dt9FJisi.d.ts → types-C4WuJfw7.d.cts} +1095 -509
  55. package/dist/types-D5p7-m1l.d.cts +47 -0
  56. package/dist/ui/canvas/server.cjs +244 -2
  57. package/dist/ui/canvas/server.cjs.map +1 -1
  58. package/dist/ui/canvas/server.d.cts +27 -1
  59. package/dist/ui/canvas/server.d.ts +27 -1
  60. package/dist/ui/canvas/server.js +244 -2
  61. package/dist/ui/canvas/server.js.map +1 -1
  62. package/dist/ui/canvas.cjs +419 -103
  63. package/dist/ui/canvas.cjs.map +1 -1
  64. package/dist/ui/canvas.d.cts +19 -7
  65. package/dist/ui/canvas.d.ts +19 -7
  66. package/dist/ui/canvas.js +419 -103
  67. package/dist/ui/canvas.js.map +1 -1
  68. package/dist/ui/form.d.cts +1 -1
  69. package/dist/ui/form.d.ts +1 -1
  70. package/dist/ui/video.d.cts +1 -1
  71. package/dist/ui/video.d.ts +1 -1
  72. package/dist/webhook.cjs +2 -1
  73. package/dist/webhook.cjs.map +1 -1
  74. package/dist/webhook.d.cts +4 -4
  75. package/dist/webhook.d.ts +4 -4
  76. package/dist/webhook.js +2 -1
  77. package/dist/webhook.js.map +1 -1
  78. package/package.json +12 -11
  79. package/dist/types-SoXsbKhj.d.cts +0 -33
  80. package/dist/types-oiae_LMm.d.ts +0 -33
package/dist/ui/canvas.js CHANGED
@@ -1,12 +1,22 @@
1
1
  "use client";
2
2
 
3
3
  // src/ui/Canvas/types.ts
4
+ var CANVAS_SHAPE_TYPES = [
5
+ "terminator",
6
+ "process",
7
+ "decision",
8
+ "io",
9
+ "subprocess"
10
+ ];
4
11
  function isDynamicNode(node) {
5
12
  return node.type === "dynamic";
6
13
  }
7
14
  function isFrameNode(node) {
8
15
  return node.type === "frame";
9
16
  }
17
+ function isShapeNode(node) {
18
+ return node.type === "shape";
19
+ }
10
20
 
11
21
  // src/ui/Canvas/useCanvas.ts
12
22
  import { useQuery } from "@tanstack/react-query";
@@ -30,10 +40,13 @@ function collectionKeys(collection) {
30
40
  }
31
41
 
32
42
  // src/ui/Canvas/query-options.ts
43
+ var CANVAS_STALE_TIME_MS = 6e4;
44
+ var CANVAS_CATALOG_STALE_TIME_MS = 5 * 6e4;
33
45
  function canvasQueryOptions(client, slug, id) {
34
46
  const identifier = id ?? slug ?? "";
35
47
  return {
36
48
  queryKey: collectionKeys("canvases").detail(identifier),
49
+ staleTime: CANVAS_STALE_TIME_MS,
37
50
  queryFn: async () => {
38
51
  if (id) return client.collections.from("canvases").findById(id);
39
52
  const result = await client.collections.from("canvases").find({
@@ -49,8 +62,9 @@ function canvasQueryOptions(client, slug, id) {
49
62
  function nodeTypesQueryOptions(client) {
50
63
  return {
51
64
  queryKey: collectionKeys("canvas-node-types").lists(),
65
+ staleTime: CANVAS_CATALOG_STALE_TIME_MS,
52
66
  queryFn: async () => {
53
- const result = await client.collections.from("canvas-node-types").find({ limit: 0 });
67
+ const result = await client.collections.from("canvas-node-types").find({ limit: 100 });
54
68
  return result.docs;
55
69
  }
56
70
  };
@@ -58,8 +72,9 @@ function nodeTypesQueryOptions(client) {
58
72
  function edgeTypesQueryOptions(client) {
59
73
  return {
60
74
  queryKey: collectionKeys("canvas-edge-types").lists(),
75
+ staleTime: CANVAS_CATALOG_STALE_TIME_MS,
61
76
  queryFn: async () => {
62
- const result = await client.collections.from("canvas-edge-types").find({ limit: 0 });
77
+ const result = await client.collections.from("canvas-edge-types").find({ limit: 100 });
63
78
  return result.docs;
64
79
  }
65
80
  };
@@ -328,10 +343,9 @@ function getFrameData(data, frameId) {
328
343
  // src/ui/Canvas/template-compiler.ts
329
344
  import React from "react";
330
345
  import { transform } from "sucrase";
331
- var WIRE_FORMAT_VERSION = 1;
332
- var MAX_CACHE_SIZE = 100;
333
- var MAX_MATERIALIZE_DEPTH = 20;
334
- var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
346
+
347
+ // src/ui/Canvas/template-security.ts
348
+ var ALLOWED_TEMPLATE_ELEMENTS = [
335
349
  "div",
336
350
  "span",
337
351
  "p",
@@ -365,33 +379,188 @@ var ALLOWED_ELEMENTS = /* @__PURE__ */ new Set([
365
379
  "g",
366
380
  "circle",
367
381
  "rect"
368
- ]);
369
- var BLOCKED_PATTERNS = [
370
- /\bdocument\s*\./,
371
- /\bwindow\s*\./,
372
- /\bwindow\s*\[/,
373
- /\bglobalThis\s*\./,
374
- /\bfetch\s*\(/,
375
- /\bXMLHttpRequest/,
376
- /\beval\s*\(/,
377
- /\bFunction\s*\(/,
378
- /\bimport\s*\(/,
379
- /\blocalStorage/,
380
- /\bsessionStorage/,
381
- /\bcookie/,
382
- /\bpostMessage\s*\(/,
383
- /\blocation\s*[.=]/,
384
- /\bnavigator\s*\./,
385
- /\bsetTimeout\s*\(/,
386
- /\bsetInterval\s*\(/,
387
- /\bsetImmediate\s*\(/,
388
- /\brequire\s*\(/,
389
- /\bself\b/,
390
- /\bconstructor\s*\.\s*constructor/,
391
- /\bReflect\b/,
392
- /\bProxy\b/,
393
- /\b__proto__\b/
394
382
  ];
383
+ var ALLOWED_TEMPLATE_ELEMENT_SET = new Set(ALLOWED_TEMPLATE_ELEMENTS);
384
+ function isAllowedTemplateElement(value) {
385
+ return ALLOWED_TEMPLATE_ELEMENT_SET.has(value.toLowerCase());
386
+ }
387
+ var TEMPLATE_BLOCKED_PATTERNS = [
388
+ { pattern: /\bdocument\s*\./, hint: "document API" },
389
+ { pattern: /\bwindow\s*\./, hint: "window API" },
390
+ { pattern: /\bwindow\s*\[/, hint: "window indexer" },
391
+ { pattern: /\bglobalThis\s*\./, hint: "globalThis" },
392
+ { pattern: /\bfetch\s*\(/, hint: "fetch()" },
393
+ { pattern: /\bXMLHttpRequest/, hint: "XMLHttpRequest" },
394
+ { pattern: /\beval\s*\(/, hint: "eval()" },
395
+ { pattern: /\bFunction\s*\(/, hint: "Function constructor" },
396
+ { pattern: /\bimport\s*\(/, hint: "dynamic import()" },
397
+ { pattern: /\blocalStorage/, hint: "localStorage" },
398
+ { pattern: /\bsessionStorage/, hint: "sessionStorage" },
399
+ { pattern: /\bcookie/, hint: "cookie access" },
400
+ { pattern: /\bpostMessage\s*\(/, hint: "postMessage()" },
401
+ { pattern: /\blocation\s*[.=]/, hint: "location API" },
402
+ { pattern: /\bnavigator\s*\./, hint: "navigator API" },
403
+ { pattern: /\bsetTimeout\s*\(/, hint: "setTimeout()" },
404
+ { pattern: /\bsetInterval\s*\(/, hint: "setInterval()" },
405
+ { pattern: /\bsetImmediate\s*\(/, hint: "setImmediate()" },
406
+ { pattern: /\brequire\s*\(/, hint: "require()" },
407
+ { pattern: /\bself\b/, hint: "self alias" },
408
+ {
409
+ pattern: /\bconstructor\s*\.\s*constructor/,
410
+ hint: "constructor.constructor escape"
411
+ },
412
+ { pattern: /\bReflect\b/, hint: "Reflect" },
413
+ { pattern: /\bProxy\b/, hint: "Proxy" },
414
+ { pattern: /\b__proto__\b/, hint: "__proto__" }
415
+ ];
416
+ function findBlockedTemplatePattern(code) {
417
+ for (const { pattern, hint } of TEMPLATE_BLOCKED_PATTERNS) {
418
+ if (pattern.test(code)) return { hint };
419
+ }
420
+ return null;
421
+ }
422
+ var SAFE_DATA_URI_PREFIXES = [
423
+ "data:image/png;",
424
+ "data:image/jpeg;",
425
+ "data:image/gif;",
426
+ "data:image/webp;",
427
+ "data:image/avif;"
428
+ ];
429
+ function isSafeTemplateUrl(value) {
430
+ if (typeof value !== "string") return false;
431
+ if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#")) {
432
+ return true;
433
+ }
434
+ if (SAFE_DATA_URI_PREFIXES.some((prefix) => value.startsWith(prefix))) {
435
+ return true;
436
+ }
437
+ try {
438
+ const url = new URL(value);
439
+ return url.protocol === "http:" || url.protocol === "https:";
440
+ } catch {
441
+ return false;
442
+ }
443
+ }
444
+ var URL_PROPS = /* @__PURE__ */ new Set([
445
+ "src",
446
+ "href",
447
+ "xlinkhref",
448
+ "xlink:href",
449
+ // Obsolete but still implemented URL-bearing attributes on allowed elements.
450
+ "background",
451
+ "longdesc",
452
+ "usemap"
453
+ ]);
454
+ function isTemplateUrlProp(name) {
455
+ return URL_PROPS.has(name.toLowerCase());
456
+ }
457
+ var SRCSET_PROPS = /* @__PURE__ */ new Set([
458
+ "srcset",
459
+ // React spells the preload attribute `imageSrcSet`; keep it guarded if the
460
+ // element allowlist ever expands to include `link`.
461
+ "imagesrcset"
462
+ ]);
463
+ function isTemplateSrcSetProp(name) {
464
+ return SRCSET_PROPS.has(name.toLowerCase());
465
+ }
466
+ function isSafeTemplateSrcSet(value) {
467
+ if (typeof value !== "string" || value.trim() === "") return false;
468
+ return value.split(",").every((candidate) => {
469
+ const parts = candidate.trim().split(/\s+/);
470
+ if (parts.length === 0 || parts.length > 2 || !isSafeTemplateUrl(parts[0])) {
471
+ return false;
472
+ }
473
+ const descriptor = parts[1];
474
+ if (descriptor === void 0) return true;
475
+ if (/^[1-9]\d*w$/.test(descriptor)) return true;
476
+ if (/^(?:\d+(?:\.\d+)?|\.\d+)x$/.test(descriptor)) {
477
+ return Number.parseFloat(descriptor) > 0;
478
+ }
479
+ return false;
480
+ });
481
+ }
482
+ var URL_FUNCTION_PATTERN = /url\s*\(/i;
483
+ var SVG_URL_FUNCTION_PROPS = /* @__PURE__ */ new Set([
484
+ "clip-path",
485
+ "clippath",
486
+ "color-profile",
487
+ "colorprofile",
488
+ "cursor",
489
+ "fill",
490
+ "filter",
491
+ "marker",
492
+ "marker-end",
493
+ "marker-mid",
494
+ "marker-start",
495
+ "markerend",
496
+ "markermid",
497
+ "markerstart",
498
+ "mask",
499
+ "stroke"
500
+ ]);
501
+ function isTemplatePaintProp(name) {
502
+ return SVG_URL_FUNCTION_PROPS.has(name.toLowerCase());
503
+ }
504
+ function isSafeTemplatePaintValue(value) {
505
+ if (typeof value === "string") return !URL_FUNCTION_PATTERN.test(value);
506
+ if (!Array.isArray(value) || value.length === 0) return false;
507
+ return value.every(
508
+ (candidate) => typeof candidate === "string" && !URL_FUNCTION_PATTERN.test(candidate)
509
+ );
510
+ }
511
+ var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
512
+ "backgroundImage",
513
+ "background",
514
+ "listStyleImage",
515
+ "content",
516
+ "borderImage",
517
+ "borderImageSource",
518
+ "maskImage",
519
+ "mask",
520
+ "filter",
521
+ "cursor"
522
+ ]);
523
+ function sanitizeTemplateStyle(style) {
524
+ if (!style || typeof style !== "object" || Array.isArray(style)) return {};
525
+ const safe = {};
526
+ for (const [key, value] of Object.entries(style)) {
527
+ if (BLOCKED_STYLE_PROPS.has(key)) continue;
528
+ if (typeof value === "string" && URL_FUNCTION_PATTERN.test(value)) continue;
529
+ if (key === "position" && typeof value === "string" && /fixed|sticky/i.test(value)) {
530
+ continue;
531
+ }
532
+ if (key === "zIndex") {
533
+ const numericValue = typeof value === "number" ? value : parseInt(String(value), 10);
534
+ if (isNaN(numericValue) || numericValue > 9998) continue;
535
+ }
536
+ safe[key] = value;
537
+ }
538
+ return safe;
539
+ }
540
+ function sanitizeTemplateProps(props) {
541
+ const safeProps = {};
542
+ for (const [key, value] of Object.entries(props)) {
543
+ const normalizedKey = key.toLowerCase();
544
+ if (normalizedKey.startsWith("on")) continue;
545
+ if (key === "dangerouslySetInnerHTML" || key === "ref") continue;
546
+ if (isTemplateUrlProp(key) && !isSafeTemplateUrl(value)) continue;
547
+ if (isTemplateSrcSetProp(key) && !isSafeTemplateSrcSet(value)) continue;
548
+ if (isTemplatePaintProp(key) && !isSafeTemplatePaintValue(value)) {
549
+ continue;
550
+ }
551
+ if (normalizedKey === "style") {
552
+ safeProps.style = sanitizeTemplateStyle(value);
553
+ continue;
554
+ }
555
+ safeProps[key] = value;
556
+ }
557
+ return safeProps;
558
+ }
559
+
560
+ // src/ui/Canvas/template-compiler.ts
561
+ var WIRE_FORMAT_VERSION = 1;
562
+ var MAX_CACHE_SIZE = 100;
563
+ var MAX_MATERIALIZE_DEPTH = 20;
395
564
  var VM_SETUP = `
396
565
  var React = {
397
566
  createElement: function(type, props) {
@@ -428,52 +597,7 @@ function hashCode(str) {
428
597
  return hash.toString(36);
429
598
  }
430
599
  function validateTemplateCode(code) {
431
- return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
432
- }
433
- var SAFE_DATA_URI_PREFIXES = [
434
- "data:image/png;",
435
- "data:image/jpeg;",
436
- "data:image/gif;",
437
- "data:image/webp;",
438
- "data:image/avif;"
439
- ];
440
- function isSafeUrl(value) {
441
- if (typeof value !== "string") return false;
442
- if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) return true;
443
- if (SAFE_DATA_URI_PREFIXES.some((p) => value.startsWith(p))) return true;
444
- try {
445
- const url = new URL(value);
446
- return url.protocol === "http:" || url.protocol === "https:";
447
- } catch {
448
- return false;
449
- }
450
- }
451
- var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
452
- "backgroundImage",
453
- "background",
454
- "listStyleImage",
455
- "content",
456
- "borderImage",
457
- "borderImageSource",
458
- "maskImage",
459
- "mask",
460
- "filter",
461
- "cursor"
462
- ]);
463
- function sanitizeStyle(style) {
464
- if (!style || typeof style !== "object" || Array.isArray(style)) return {};
465
- const safe = {};
466
- for (const [k, v] of Object.entries(style)) {
467
- if (BLOCKED_STYLE_PROPS.has(k)) continue;
468
- if (typeof v === "string" && /url\s*\(/i.test(v)) continue;
469
- if (k === "position" && typeof v === "string" && /fixed|sticky/i.test(v)) continue;
470
- if (k === "zIndex") {
471
- const n = typeof v === "number" ? v : parseInt(String(v), 10);
472
- if (isNaN(n) || n > 9998) continue;
473
- }
474
- safe[k] = v;
475
- }
476
- return safe;
600
+ return findBlockedTemplatePattern(code) === null;
477
601
  }
478
602
  function materialize(node, depth = 0) {
479
603
  if (depth > MAX_MATERIALIZE_DEPTH) return null;
@@ -501,18 +625,8 @@ function materialize(node, depth = 0) {
501
625
  const type = String(d.type ?? "");
502
626
  const props = d.props && typeof d.props === "object" ? d.props : {};
503
627
  const ch = Array.isArray(d.ch) ? d.ch : [];
504
- if (!ALLOWED_ELEMENTS.has(type.toLowerCase())) return null;
505
- const safeProps = {};
506
- for (const [k, v] of Object.entries(props)) {
507
- if (k.startsWith("on")) continue;
508
- if (k === "dangerouslySetInnerHTML" || k === "ref") continue;
509
- if ((k === "src" || k === "href") && !isSafeUrl(v)) continue;
510
- if (k === "style") {
511
- safeProps[k] = sanitizeStyle(v);
512
- continue;
513
- }
514
- safeProps[k] = v;
515
- }
628
+ if (!isAllowedTemplateElement(type)) return null;
629
+ const safeProps = sanitizeTemplateProps(props);
516
630
  const children = ch.map((c) => materialize(c, depth + 1));
517
631
  return React.createElement(type, safeProps, ...children);
518
632
  }
@@ -796,19 +910,22 @@ function sanitizeImageData(image, safeUrl) {
796
910
  ) : image.sizes
797
911
  };
798
912
  }
799
- function renderFieldValue(key, val, fieldDef) {
913
+ function renderFieldValue(key, val, fieldDef, imageAlt) {
800
914
  if (val == null || val === "") return null;
801
915
  const fieldType = fieldDef?.fieldType;
802
916
  if (fieldType === "image" || typeof val === "object" && val !== null && "url" in val) {
803
917
  const imgUrl = typeof val === "string" ? val : val?.url;
804
918
  const safeUrl = sanitizeUrl(imgUrl);
805
919
  if (!safeUrl) return null;
920
+ const embeddedAlt = typeof val === "object" && val !== null && "alt" in val && typeof val.alt === "string" ? val.alt : void 0;
921
+ const alt = imageAlt?.authored !== void 0 ? imageAlt.authored : embeddedAlt !== void 0 ? embeddedAlt : imageAlt?.fallback ?? "";
806
922
  if (fieldType === "image" && typeof val === "object" && val !== null) {
807
923
  return /* @__PURE__ */ React3.createElement(
808
924
  Image,
809
925
  {
810
926
  key,
811
927
  image: sanitizeImageData(val, safeUrl),
928
+ alt,
812
929
  fill: true,
813
930
  objectFit: "contain",
814
931
  style: { flex: 1, minHeight: 0, width: "100%" }
@@ -820,7 +937,7 @@ function renderFieldValue(key, val, fieldDef) {
820
937
  {
821
938
  key,
822
939
  src: safeUrl,
823
- alt: "",
940
+ alt,
824
941
  draggable: false,
825
942
  style: { flex: 1, minHeight: 0, width: "100%", objectFit: "contain" }
826
943
  }
@@ -843,6 +960,7 @@ function renderFieldValue(key, val, fieldDef) {
843
960
  }
844
961
  function DefaultDynamicNode({ data }) {
845
962
  const d = data;
963
+ const authoredImageAlt = typeof d.fields?.alt === "string" ? d.fields.alt : void 0;
846
964
  return /* @__PURE__ */ React3.createElement(
847
965
  "div",
848
966
  {
@@ -853,7 +971,12 @@ function DefaultDynamicNode({ data }) {
853
971
  flexDirection: "column"
854
972
  }
855
973
  },
856
- d.fields && Object.entries(d.fields).filter(([, v]) => v != null && v !== "").map(([key, val]) => renderFieldValue(key, val))
974
+ d.fields && Object.entries(d.fields).filter(([, v]) => v != null && v !== "").map(
975
+ ([key, val]) => renderFieldValue(key, val, void 0, {
976
+ authored: authoredImageAlt,
977
+ fallback: d.label
978
+ })
979
+ )
857
980
  );
858
981
  }
859
982
  var TemplateErrorBoundary = class extends React3.Component {
@@ -883,13 +1006,14 @@ function EnhancedDynamicNode({
883
1006
  height
884
1007
  }) {
885
1008
  useQuickJS();
1009
+ const authoredImageAlt = typeof data.fields?.alt === "string" ? data.fields.alt : void 0;
886
1010
  if (typeDef.template) {
887
1011
  const Component = compileTemplate(typeDef.template, typeDef.slug);
888
1012
  if (Component) {
889
1013
  return /* @__PURE__ */ React3.createElement(
890
1014
  "div",
891
1015
  {
892
- className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? " flow-node--transparent-bg" : ""}`,
1016
+ className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? "flow-node--transparent-bg" : ""}`,
893
1017
  style: { width: "100%", height: "100%" }
894
1018
  },
895
1019
  /* @__PURE__ */ React3.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ React3.createElement(
@@ -920,7 +1044,10 @@ function EnhancedDynamicNode({
920
1044
  typeDef.fields.map((f) => {
921
1045
  const val = data.fields[f.name];
922
1046
  if (val == null || val === "") return null;
923
- return renderFieldValue(f.name, val, f);
1047
+ return renderFieldValue(f.name, val, f, {
1048
+ authored: authoredImageAlt,
1049
+ fallback: data.label
1050
+ });
924
1051
  })
925
1052
  );
926
1053
  }
@@ -966,6 +1093,90 @@ function DefaultFrameNode({ data }) {
966
1093
  )
967
1094
  );
968
1095
  }
1096
+ var DEFAULT_SHAPE_COLOR = "#dbeafe";
1097
+ var SHAPE_STROKE = "#334155";
1098
+ function ShapeGeometry({ shape, color }) {
1099
+ const common = {
1100
+ fill: color,
1101
+ stroke: SHAPE_STROKE,
1102
+ strokeWidth: 2,
1103
+ vectorEffect: "non-scaling-stroke"
1104
+ };
1105
+ switch (shape) {
1106
+ case "terminator":
1107
+ return /* @__PURE__ */ React3.createElement("rect", { x: "1", y: "1", width: "198", height: "98", rx: "49", ...common });
1108
+ case "decision":
1109
+ return /* @__PURE__ */ React3.createElement("polygon", { points: "100,1 199,50 100,99 1,50", ...common });
1110
+ case "io":
1111
+ return /* @__PURE__ */ React3.createElement("polygon", { points: "25,1 199,1 175,99 1,99", ...common });
1112
+ case "subprocess":
1113
+ return /* @__PURE__ */ React3.createElement("g", null, /* @__PURE__ */ React3.createElement("rect", { x: "1", y: "1", width: "198", height: "98", rx: "3", ...common }), /* @__PURE__ */ React3.createElement(
1114
+ "path",
1115
+ {
1116
+ d: "M20 1V99M180 1V99",
1117
+ fill: "none",
1118
+ stroke: SHAPE_STROKE,
1119
+ strokeWidth: "2",
1120
+ vectorEffect: "non-scaling-stroke"
1121
+ }
1122
+ ));
1123
+ case "process":
1124
+ default:
1125
+ return /* @__PURE__ */ React3.createElement("rect", { x: "1", y: "1", width: "198", height: "98", rx: "3", ...common });
1126
+ }
1127
+ }
1128
+ function DefaultShapeNode({ data }) {
1129
+ const shapeData = data;
1130
+ return /* @__PURE__ */ React3.createElement(
1131
+ "div",
1132
+ {
1133
+ className: `flow-node flow-node--shape flow-node--shape-${shapeData.shape}`,
1134
+ style: { width: "100%", height: "100%", position: "relative" },
1135
+ "data-shape": shapeData.shape
1136
+ },
1137
+ /* @__PURE__ */ React3.createElement(
1138
+ "svg",
1139
+ {
1140
+ "aria-hidden": "true",
1141
+ focusable: "false",
1142
+ viewBox: "0 0 200 100",
1143
+ preserveAspectRatio: "none",
1144
+ style: {
1145
+ position: "absolute",
1146
+ inset: 0,
1147
+ width: "100%",
1148
+ height: "100%"
1149
+ }
1150
+ },
1151
+ /* @__PURE__ */ React3.createElement(
1152
+ ShapeGeometry,
1153
+ {
1154
+ shape: shapeData.shape,
1155
+ color: shapeData.color ?? DEFAULT_SHAPE_COLOR
1156
+ }
1157
+ )
1158
+ ),
1159
+ /* @__PURE__ */ React3.createElement(
1160
+ "div",
1161
+ {
1162
+ style: {
1163
+ position: "absolute",
1164
+ inset: "12%",
1165
+ display: "flex",
1166
+ alignItems: "center",
1167
+ justifyContent: "center",
1168
+ overflow: "hidden",
1169
+ color: "#0f172a",
1170
+ fontSize: 12,
1171
+ fontWeight: 600,
1172
+ textAlign: "center",
1173
+ wordBreak: "break-word"
1174
+ }
1175
+ },
1176
+ shapeData.label
1177
+ )
1178
+ );
1179
+ }
969
1180
 
970
1181
  // src/ui/Canvas/field-helpers.ts
971
1182
  function getImageField(fields, name) {
@@ -1184,9 +1395,12 @@ import {
1184
1395
 
1185
1396
  // src/ui/Canvas/node-types-factory.tsx
1186
1397
  import React4 from "react";
1398
+ function areNodePropsEqual(prev, next) {
1399
+ return prev.id === next.id && prev.data === next.data && prev.selected === next.selected && prev.dragging === next.dragging && prev.width === next.width && prev.height === next.height;
1400
+ }
1187
1401
  function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
1188
1402
  const types = {};
1189
- types.dynamic = ((props) => {
1403
+ const DynamicNode = (props) => {
1190
1404
  const d = props.data;
1191
1405
  const typeDef = nodeTypeDefsMap?.get(d.nodeTypeSlug);
1192
1406
  const CustomRenderer = nodeRenderers?.[d.nodeTypeSlug];
@@ -1235,8 +1449,12 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
1235
1449
  );
1236
1450
  }
1237
1451
  return content;
1238
- });
1239
- types.frame = frameRenderer ? ((props) => {
1452
+ };
1453
+ types.dynamic = React4.memo(
1454
+ DynamicNode,
1455
+ areNodePropsEqual
1456
+ );
1457
+ const FrameNode = frameRenderer ? (props) => {
1240
1458
  const d = props.data;
1241
1459
  const Renderer = frameRenderer;
1242
1460
  return /* @__PURE__ */ React4.createElement(
@@ -1252,7 +1470,10 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
1252
1470
  height: props.height
1253
1471
  }
1254
1472
  );
1255
- }) : DefaultFrameNode;
1473
+ } : DefaultFrameNode;
1474
+ types.frame = React4.memo(FrameNode, areNodePropsEqual);
1475
+ const ShapeNode = (props) => /* @__PURE__ */ React4.createElement(DefaultShapeNode, { ...props });
1476
+ types.shape = React4.memo(ShapeNode, areNodePropsEqual);
1256
1477
  return types;
1257
1478
  }
1258
1479
  function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
@@ -1462,18 +1683,82 @@ function FocusHandler({
1462
1683
  );
1463
1684
  }
1464
1685
 
1686
+ // src/ui/Canvas/accessibility.ts
1687
+ var CANVAS_ACCESSIBILITY_LABELS = {
1688
+ ko: {
1689
+ canvas: "\uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8",
1690
+ node: "\uB178\uB4DC",
1691
+ frame: "\uD504\uB808\uC784",
1692
+ ariaLabelConfig: {
1693
+ "node.a11yDescription.default": "\uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC758 \uB178\uB4DC\uC785\uB2C8\uB2E4.",
1694
+ "node.a11yDescription.keyboardDisabled": "\uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC758 \uB178\uB4DC\uC785\uB2C8\uB2E4.",
1695
+ "edge.a11yDescription.default": "\uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8\uC758 \uC5F0\uACB0\uC120\uC785\uB2C8\uB2E4.",
1696
+ "controls.ariaLabel": "\uCE94\uBC84\uC2A4 \uC81C\uC5B4\uD310",
1697
+ "controls.zoomIn.ariaLabel": "\uD655\uB300",
1698
+ "controls.zoomOut.ariaLabel": "\uCD95\uC18C",
1699
+ "controls.fitView.ariaLabel": "\uD654\uBA74\uC5D0 \uB9DE\uCD94\uAE30",
1700
+ "controls.interactive.ariaLabel": "\uC0C1\uD638\uC791\uC6A9 \uC804\uD658",
1701
+ "minimap.ariaLabel": "\uBBF8\uB2C8\uB9F5",
1702
+ "handle.ariaLabel": "\uC5F0\uACB0\uC810"
1703
+ }
1704
+ },
1705
+ en: {
1706
+ canvas: "Canvas diagram",
1707
+ node: "Node",
1708
+ frame: "Frame",
1709
+ ariaLabelConfig: {
1710
+ "node.a11yDescription.default": "A node in the canvas diagram.",
1711
+ "node.a11yDescription.keyboardDisabled": "A node in the canvas diagram.",
1712
+ "edge.a11yDescription.default": "A connection in the canvas diagram.",
1713
+ "controls.ariaLabel": "Canvas controls",
1714
+ "controls.zoomIn.ariaLabel": "Zoom in",
1715
+ "controls.zoomOut.ariaLabel": "Zoom out",
1716
+ "controls.fitView.ariaLabel": "Fit view",
1717
+ "controls.interactive.ariaLabel": "Toggle interactivity",
1718
+ "minimap.ariaLabel": "Mini map",
1719
+ "handle.ariaLabel": "Handle"
1720
+ }
1721
+ }
1722
+ };
1723
+ function nodeAriaLabel(node, labels) {
1724
+ const explicitLabel = typeof node.ariaLabel === "string" ? node.ariaLabel.trim() : "";
1725
+ if (explicitLabel) return explicitLabel;
1726
+ const isFrame = node.type === "frame";
1727
+ const label = node.data?.label?.trim();
1728
+ if (!label) return isFrame ? labels.frame : labels.node;
1729
+ return isFrame ? `${label} (${labels.frame})` : label;
1730
+ }
1731
+
1465
1732
  // src/ui/Canvas/CanvasRenderer.tsx
1733
+ function usePrefersReducedMotion() {
1734
+ const query = "(prefers-reduced-motion: reduce)";
1735
+ const [prefersReducedMotion, setPrefersReducedMotion] = React6.useState(
1736
+ () => typeof window !== "undefined" && window.matchMedia?.(query).matches
1737
+ );
1738
+ React6.useEffect(() => {
1739
+ if (typeof window === "undefined" || !window.matchMedia) return;
1740
+ const mediaQuery = window.matchMedia(query);
1741
+ const handleChange = (event) => setPrefersReducedMotion(event.matches);
1742
+ setPrefersReducedMotion(mediaQuery.matches);
1743
+ mediaQuery.addEventListener("change", handleChange);
1744
+ return () => mediaQuery.removeEventListener("change", handleChange);
1745
+ }, []);
1746
+ return prefersReducedMotion;
1747
+ }
1466
1748
  var NullFrameRenderer = () => null;
1467
1749
  function CanvasRenderer({
1468
1750
  data,
1469
1751
  className,
1470
1752
  style,
1753
+ ariaLabel,
1754
+ locale = "en",
1471
1755
  nodeRenderers,
1472
1756
  nodeTypeDefs,
1473
1757
  edgeTypeDefs,
1474
1758
  background = false,
1475
1759
  interactive = false,
1476
1760
  fitView = true,
1761
+ onlyRenderVisibleElements = true,
1477
1762
  onNodeClick,
1478
1763
  onNodeDoubleClick,
1479
1764
  onNodeContextMenu,
@@ -1501,6 +1786,8 @@ function CanvasRenderer({
1501
1786
  minZoom: minZoomProp,
1502
1787
  maxZoom: maxZoomProp
1503
1788
  }) {
1789
+ const prefersReducedMotion = usePrefersReducedMotion();
1790
+ const accessibilityLabels = CANVAS_ACCESSIBILITY_LABELS[locale];
1504
1791
  const resolvedFrameRenderer = hideFrames ? NullFrameRenderer : frameRenderer;
1505
1792
  const nodeTypeDefsMap = React6.useMemo(() => {
1506
1793
  if (!nodeTypeDefs?.length) return void 0;
@@ -1518,7 +1805,13 @@ function CanvasRenderer({
1518
1805
  nodeWrapper,
1519
1806
  renderNode
1520
1807
  ),
1521
- [nodeRenderers, nodeTypeDefsMap, resolvedFrameRenderer, nodeWrapper, renderNode]
1808
+ [
1809
+ nodeRenderers,
1810
+ nodeTypeDefsMap,
1811
+ resolvedFrameRenderer,
1812
+ nodeWrapper,
1813
+ renderNode
1814
+ ]
1522
1815
  );
1523
1816
  const customEdgeTypes = React6.useMemo(
1524
1817
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
@@ -1541,6 +1834,21 @@ function CanvasRenderer({
1541
1834
  }
1542
1835
  return edges;
1543
1836
  }, [data?.edges, edgeTypeDefsMap, edgeRenderers]);
1837
+ const accessibleNodes = React6.useMemo(
1838
+ () => (data?.nodes ?? []).map((node) => ({
1839
+ ...node,
1840
+ ...node.type === "shape" ? {
1841
+ style: {
1842
+ ...node.style,
1843
+ background: "transparent",
1844
+ border: "none",
1845
+ padding: 0
1846
+ }
1847
+ } : {},
1848
+ ariaLabel: nodeAriaLabel(node, accessibilityLabels)
1849
+ })),
1850
+ [data?.nodes, accessibilityLabels]
1851
+ );
1544
1852
  const translateExtent = React6.useMemo(() => {
1545
1853
  if (translateExtentProp) return translateExtentProp;
1546
1854
  const es = clampBounds ?? bounds;
@@ -1575,6 +1883,8 @@ function CanvasRenderer({
1575
1883
  return /* @__PURE__ */ React6.createElement(ReactFlowProvider, null, /* @__PURE__ */ React6.createElement(
1576
1884
  "div",
1577
1885
  {
1886
+ role: "region",
1887
+ "aria-label": ariaLabel ?? accessibilityLabels.canvas,
1578
1888
  className,
1579
1889
  style: {
1580
1890
  width: "100%",
@@ -1586,12 +1896,13 @@ function CanvasRenderer({
1586
1896
  /* @__PURE__ */ React6.createElement(
1587
1897
  ReactFlow,
1588
1898
  {
1589
- nodes: data.nodes ?? [],
1899
+ nodes: accessibleNodes,
1590
1900
  edges: styledEdges,
1591
1901
  nodeTypes,
1592
1902
  edgeTypes: customEdgeTypes,
1593
1903
  defaultViewport: resolvedDefaultViewport,
1594
1904
  fitView: bounds ? false : fitView,
1905
+ onlyRenderVisibleElements,
1595
1906
  translateExtent: activeExtent,
1596
1907
  onNodeClick,
1597
1908
  onNodeDoubleClick,
@@ -1605,13 +1916,15 @@ function CanvasRenderer({
1605
1916
  nodesDraggable: interactive,
1606
1917
  nodesConnectable: false,
1607
1918
  elementsSelectable: interactive || !!onNodeClick || !!onNodeDoubleClick || !!onEdgeClick,
1919
+ autoPanOnSelection: false,
1608
1920
  panOnDrag: interactive,
1609
1921
  zoomOnScroll: interactive,
1610
1922
  zoomOnPinch: interactive,
1611
1923
  zoomOnDoubleClick: false,
1612
1924
  minZoom: minZoomProp,
1613
1925
  maxZoom: maxZoomProp,
1614
- proOptions: { hideAttribution: true }
1926
+ proOptions: { hideAttribution: true },
1927
+ ariaLabelConfig: accessibilityLabels.ariaLabelConfig
1615
1928
  },
1616
1929
  mergedCSS && /* @__PURE__ */ React6.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1617
1930
  background && /* @__PURE__ */ React6.createElement(Background, null),
@@ -1627,7 +1940,7 @@ function CanvasRenderer({
1627
1940
  {
1628
1941
  bounds,
1629
1942
  padding: focusPadding ?? 0.1,
1630
- animation: focusAnimation ?? true,
1943
+ animation: prefersReducedMotion ? false : focusAnimation ?? true,
1631
1944
  mode: focusMode,
1632
1945
  responsive: responsiveFit ?? true,
1633
1946
  extent: translateExtent,
@@ -1697,8 +2010,10 @@ function CanvasFrame({
1697
2010
  );
1698
2011
  }
1699
2012
  export {
2013
+ CANVAS_SHAPE_TYPES,
1700
2014
  CanvasFrame,
1701
2015
  CanvasRenderer,
2016
+ DefaultShapeNode,
1702
2017
  clearTemplateCache,
1703
2018
  compileTemplate,
1704
2019
  getBooleanField,
@@ -1710,6 +2025,7 @@ export {
1710
2025
  getTextField,
1711
2026
  isDynamicNode,
1712
2027
  isFrameNode,
2028
+ isShapeNode,
1713
2029
  prefetchCanvas,
1714
2030
  renderFieldValue,
1715
2031
  sanitizeCSS,