@01.software/sdk 0.44.0 → 0.46.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.
- package/CHANGELOG.md +366 -2
- package/MIGRATION.md +108 -10
- package/README.md +486 -98
- package/dist/{server-nXOezi4b.d.cts → catalog-mappers-sIshKNb3.d.cts} +14 -3
- package/dist/{server-nXOezi4b.d.ts → catalog-mappers-sIshKNb3.d.ts} +14 -3
- package/dist/client.cjs +2693 -345
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +10 -7
- package/dist/client.d.ts +10 -7
- package/dist/client.js +2693 -345
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-DWooaiTr.d.ts → collection-client-D6kKrYdq.d.ts} +8 -5
- package/dist/{collection-client-Dyl-CDcj.d.cts → collection-client-DolMjCoC.d.cts} +8 -5
- package/dist/{const-pGDnsy0o.d.ts → const-BAG0MH0u.d.cts} +13 -10
- package/dist/{const-1vleZ6ov.d.cts → const-CaPq2AE4.d.ts} +13 -10
- package/dist/errors.cjs +74 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +38 -1
- package/dist/errors.d.ts +38 -1
- package/dist/errors.js +74 -1
- package/dist/errors.js.map +1 -1
- package/dist/{index-DUNySyxV.d.ts → index-BNICJxBL.d.ts} +4 -4
- package/dist/{index-a6g7vqAD.d.cts → index-YGrZrxNn.d.cts} +4 -4
- package/dist/index.cjs +2754 -368
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -19
- package/dist/index.d.ts +18 -19
- package/dist/index.js +2754 -368
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-CDsuOEfM.d.cts → payload-types-CJZuXcgF.d.cts} +6671 -4001
- package/dist/{payload-types-CDsuOEfM.d.ts → payload-types-CJZuXcgF.d.ts} +6671 -4001
- package/dist/query.cjs +558 -101
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +43 -45
- package/dist/query.d.ts +43 -45
- package/dist/query.js +558 -101
- package/dist/query.js.map +1 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js.map +1 -1
- package/dist/server.cjs +2343 -437
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -10
- package/dist/server.d.ts +11 -10
- package/dist/server.js +2343 -437
- package/dist/server.js.map +1 -1
- package/dist/storefront-cache.cjs +65 -1
- package/dist/storefront-cache.cjs.map +1 -1
- package/dist/storefront-cache.js +65 -1
- package/dist/storefront-cache.js.map +1 -1
- package/dist/types-BC6rVmJa.d.ts +47 -0
- package/dist/{types-CDND3hzq.d.cts → types-C60vHIkY.d.ts} +1134 -509
- package/dist/types-D5p7-m1l.d.cts +47 -0
- package/dist/{types-Dt9FJisi.d.ts → types-xlqBeW2N.d.cts} +1134 -509
- package/dist/ui/canvas/server.cjs +244 -2
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +27 -1
- package/dist/ui/canvas/server.d.ts +27 -1
- package/dist/ui/canvas/server.js +244 -2
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +419 -103
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +19 -7
- package/dist/ui/canvas.d.ts +19 -7
- package/dist/ui/canvas.js +419 -103
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.cjs +2 -1
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +4 -4
- package/dist/webhook.d.ts +4 -4
- package/dist/webhook.js +2 -1
- package/dist/webhook.js.map +1 -1
- package/package.json +10 -9
- package/dist/types-SoXsbKhj.d.cts +0 -33
- package/dist/types-oiae_LMm.d.ts +0 -33
|
@@ -30,13 +30,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/ui/Canvas/server.ts
|
|
31
31
|
var server_exports = {};
|
|
32
32
|
__export(server_exports, {
|
|
33
|
+
ALLOWED_TEMPLATE_ELEMENTS: () => ALLOWED_TEMPLATE_ELEMENTS,
|
|
34
|
+
CANVAS_SHAPE_TYPES: () => CANVAS_SHAPE_TYPES,
|
|
35
|
+
TEMPLATE_BLOCKED_PATTERNS: () => TEMPLATE_BLOCKED_PATTERNS,
|
|
36
|
+
findBlockedTemplatePattern: () => findBlockedTemplatePattern,
|
|
33
37
|
getFrameData: () => getFrameData,
|
|
34
38
|
getFrames: () => getFrames,
|
|
35
39
|
getNodeBounds: () => getNodeBounds,
|
|
40
|
+
isAllowedTemplateElement: () => isAllowedTemplateElement,
|
|
36
41
|
isDynamicNode: () => isDynamicNode,
|
|
37
42
|
isFrameNode: () => isFrameNode,
|
|
43
|
+
isSafeTemplatePaintValue: () => isSafeTemplatePaintValue,
|
|
44
|
+
isSafeTemplateSrcSet: () => isSafeTemplateSrcSet,
|
|
45
|
+
isSafeTemplateUrl: () => isSafeTemplateUrl,
|
|
46
|
+
isShapeNode: () => isShapeNode,
|
|
47
|
+
isTemplatePaintProp: () => isTemplatePaintProp,
|
|
48
|
+
isTemplateSrcSetProp: () => isTemplateSrcSetProp,
|
|
49
|
+
isTemplateUrlProp: () => isTemplateUrlProp,
|
|
38
50
|
prefetchCanvas: () => prefetchCanvas,
|
|
39
51
|
sanitizeCSS: () => sanitizeCSS,
|
|
52
|
+
sanitizeTemplateProps: () => sanitizeTemplateProps,
|
|
53
|
+
sanitizeTemplateStyle: () => sanitizeTemplateStyle,
|
|
40
54
|
toEdgeTypeDef: () => toEdgeTypeDef,
|
|
41
55
|
toNodeTypeDef: () => toNodeTypeDef,
|
|
42
56
|
validateTenantCSS: () => validateTenantCSS
|
|
@@ -61,10 +75,13 @@ function collectionKeys(collection) {
|
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
// src/ui/Canvas/query-options.ts
|
|
78
|
+
var CANVAS_STALE_TIME_MS = 6e4;
|
|
79
|
+
var CANVAS_CATALOG_STALE_TIME_MS = 5 * 6e4;
|
|
64
80
|
function canvasQueryOptions(client, slug, id) {
|
|
65
81
|
const identifier = id ?? slug ?? "";
|
|
66
82
|
return {
|
|
67
83
|
queryKey: collectionKeys("canvases").detail(identifier),
|
|
84
|
+
staleTime: CANVAS_STALE_TIME_MS,
|
|
68
85
|
queryFn: async () => {
|
|
69
86
|
if (id) return client.collections.from("canvases").findById(id);
|
|
70
87
|
const result = await client.collections.from("canvases").find({
|
|
@@ -80,8 +97,9 @@ function canvasQueryOptions(client, slug, id) {
|
|
|
80
97
|
function nodeTypesQueryOptions(client) {
|
|
81
98
|
return {
|
|
82
99
|
queryKey: collectionKeys("canvas-node-types").lists(),
|
|
100
|
+
staleTime: CANVAS_CATALOG_STALE_TIME_MS,
|
|
83
101
|
queryFn: async () => {
|
|
84
|
-
const result = await client.collections.from("canvas-node-types").find({ limit:
|
|
102
|
+
const result = await client.collections.from("canvas-node-types").find({ limit: 100 });
|
|
85
103
|
return result.docs;
|
|
86
104
|
}
|
|
87
105
|
};
|
|
@@ -89,8 +107,9 @@ function nodeTypesQueryOptions(client) {
|
|
|
89
107
|
function edgeTypesQueryOptions(client) {
|
|
90
108
|
return {
|
|
91
109
|
queryKey: collectionKeys("canvas-edge-types").lists(),
|
|
110
|
+
staleTime: CANVAS_CATALOG_STALE_TIME_MS,
|
|
92
111
|
queryFn: async () => {
|
|
93
|
-
const result = await client.collections.from("canvas-edge-types").find({ limit:
|
|
112
|
+
const result = await client.collections.from("canvas-edge-types").find({ limit: 100 });
|
|
94
113
|
return result.docs;
|
|
95
114
|
}
|
|
96
115
|
};
|
|
@@ -227,12 +246,22 @@ function getFrameData(data, frameId) {
|
|
|
227
246
|
}
|
|
228
247
|
|
|
229
248
|
// src/ui/Canvas/types.ts
|
|
249
|
+
var CANVAS_SHAPE_TYPES = [
|
|
250
|
+
"terminator",
|
|
251
|
+
"process",
|
|
252
|
+
"decision",
|
|
253
|
+
"io",
|
|
254
|
+
"subprocess"
|
|
255
|
+
];
|
|
230
256
|
function isDynamicNode(node) {
|
|
231
257
|
return node.type === "dynamic";
|
|
232
258
|
}
|
|
233
259
|
function isFrameNode(node) {
|
|
234
260
|
return node.type === "frame";
|
|
235
261
|
}
|
|
262
|
+
function isShapeNode(node) {
|
|
263
|
+
return node.type === "shape";
|
|
264
|
+
}
|
|
236
265
|
|
|
237
266
|
// src/ui/Canvas/css-sanitizer.ts
|
|
238
267
|
var import_postcss = __toESM(require("postcss"), 1);
|
|
@@ -419,6 +448,219 @@ function validateTenantCSS(css) {
|
|
|
419
448
|
return null;
|
|
420
449
|
}
|
|
421
450
|
|
|
451
|
+
// src/ui/Canvas/template-security.ts
|
|
452
|
+
var ALLOWED_TEMPLATE_ELEMENTS = [
|
|
453
|
+
"div",
|
|
454
|
+
"span",
|
|
455
|
+
"p",
|
|
456
|
+
"h1",
|
|
457
|
+
"h2",
|
|
458
|
+
"h3",
|
|
459
|
+
"h4",
|
|
460
|
+
"h5",
|
|
461
|
+
"h6",
|
|
462
|
+
"ul",
|
|
463
|
+
"ol",
|
|
464
|
+
"li",
|
|
465
|
+
"strong",
|
|
466
|
+
"em",
|
|
467
|
+
"b",
|
|
468
|
+
"i",
|
|
469
|
+
"br",
|
|
470
|
+
"img",
|
|
471
|
+
"figure",
|
|
472
|
+
"section",
|
|
473
|
+
"table",
|
|
474
|
+
"thead",
|
|
475
|
+
"tbody",
|
|
476
|
+
"tr",
|
|
477
|
+
"th",
|
|
478
|
+
"td",
|
|
479
|
+
"pre",
|
|
480
|
+
"code",
|
|
481
|
+
"svg",
|
|
482
|
+
"path",
|
|
483
|
+
"g",
|
|
484
|
+
"circle",
|
|
485
|
+
"rect"
|
|
486
|
+
];
|
|
487
|
+
var ALLOWED_TEMPLATE_ELEMENT_SET = new Set(ALLOWED_TEMPLATE_ELEMENTS);
|
|
488
|
+
function isAllowedTemplateElement(value) {
|
|
489
|
+
return ALLOWED_TEMPLATE_ELEMENT_SET.has(value.toLowerCase());
|
|
490
|
+
}
|
|
491
|
+
var TEMPLATE_BLOCKED_PATTERNS = [
|
|
492
|
+
{ pattern: /\bdocument\s*\./, hint: "document API" },
|
|
493
|
+
{ pattern: /\bwindow\s*\./, hint: "window API" },
|
|
494
|
+
{ pattern: /\bwindow\s*\[/, hint: "window indexer" },
|
|
495
|
+
{ pattern: /\bglobalThis\s*\./, hint: "globalThis" },
|
|
496
|
+
{ pattern: /\bfetch\s*\(/, hint: "fetch()" },
|
|
497
|
+
{ pattern: /\bXMLHttpRequest/, hint: "XMLHttpRequest" },
|
|
498
|
+
{ pattern: /\beval\s*\(/, hint: "eval()" },
|
|
499
|
+
{ pattern: /\bFunction\s*\(/, hint: "Function constructor" },
|
|
500
|
+
{ pattern: /\bimport\s*\(/, hint: "dynamic import()" },
|
|
501
|
+
{ pattern: /\blocalStorage/, hint: "localStorage" },
|
|
502
|
+
{ pattern: /\bsessionStorage/, hint: "sessionStorage" },
|
|
503
|
+
{ pattern: /\bcookie/, hint: "cookie access" },
|
|
504
|
+
{ pattern: /\bpostMessage\s*\(/, hint: "postMessage()" },
|
|
505
|
+
{ pattern: /\blocation\s*[.=]/, hint: "location API" },
|
|
506
|
+
{ pattern: /\bnavigator\s*\./, hint: "navigator API" },
|
|
507
|
+
{ pattern: /\bsetTimeout\s*\(/, hint: "setTimeout()" },
|
|
508
|
+
{ pattern: /\bsetInterval\s*\(/, hint: "setInterval()" },
|
|
509
|
+
{ pattern: /\bsetImmediate\s*\(/, hint: "setImmediate()" },
|
|
510
|
+
{ pattern: /\brequire\s*\(/, hint: "require()" },
|
|
511
|
+
{ pattern: /\bself\b/, hint: "self alias" },
|
|
512
|
+
{
|
|
513
|
+
pattern: /\bconstructor\s*\.\s*constructor/,
|
|
514
|
+
hint: "constructor.constructor escape"
|
|
515
|
+
},
|
|
516
|
+
{ pattern: /\bReflect\b/, hint: "Reflect" },
|
|
517
|
+
{ pattern: /\bProxy\b/, hint: "Proxy" },
|
|
518
|
+
{ pattern: /\b__proto__\b/, hint: "__proto__" }
|
|
519
|
+
];
|
|
520
|
+
function findBlockedTemplatePattern(code) {
|
|
521
|
+
for (const { pattern, hint } of TEMPLATE_BLOCKED_PATTERNS) {
|
|
522
|
+
if (pattern.test(code)) return { hint };
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
var SAFE_DATA_URI_PREFIXES = [
|
|
527
|
+
"data:image/png;",
|
|
528
|
+
"data:image/jpeg;",
|
|
529
|
+
"data:image/gif;",
|
|
530
|
+
"data:image/webp;",
|
|
531
|
+
"data:image/avif;"
|
|
532
|
+
];
|
|
533
|
+
function isSafeTemplateUrl(value) {
|
|
534
|
+
if (typeof value !== "string") return false;
|
|
535
|
+
if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#")) {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
if (SAFE_DATA_URI_PREFIXES.some((prefix) => value.startsWith(prefix))) {
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
const url = new URL(value);
|
|
543
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
544
|
+
} catch {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
var URL_PROPS = /* @__PURE__ */ new Set([
|
|
549
|
+
"src",
|
|
550
|
+
"href",
|
|
551
|
+
"xlinkhref",
|
|
552
|
+
"xlink:href",
|
|
553
|
+
// Obsolete but still implemented URL-bearing attributes on allowed elements.
|
|
554
|
+
"background",
|
|
555
|
+
"longdesc",
|
|
556
|
+
"usemap"
|
|
557
|
+
]);
|
|
558
|
+
function isTemplateUrlProp(name) {
|
|
559
|
+
return URL_PROPS.has(name.toLowerCase());
|
|
560
|
+
}
|
|
561
|
+
var SRCSET_PROPS = /* @__PURE__ */ new Set([
|
|
562
|
+
"srcset",
|
|
563
|
+
// React spells the preload attribute `imageSrcSet`; keep it guarded if the
|
|
564
|
+
// element allowlist ever expands to include `link`.
|
|
565
|
+
"imagesrcset"
|
|
566
|
+
]);
|
|
567
|
+
function isTemplateSrcSetProp(name) {
|
|
568
|
+
return SRCSET_PROPS.has(name.toLowerCase());
|
|
569
|
+
}
|
|
570
|
+
function isSafeTemplateSrcSet(value) {
|
|
571
|
+
if (typeof value !== "string" || value.trim() === "") return false;
|
|
572
|
+
return value.split(",").every((candidate) => {
|
|
573
|
+
const parts = candidate.trim().split(/\s+/);
|
|
574
|
+
if (parts.length === 0 || parts.length > 2 || !isSafeTemplateUrl(parts[0])) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
const descriptor = parts[1];
|
|
578
|
+
if (descriptor === void 0) return true;
|
|
579
|
+
if (/^[1-9]\d*w$/.test(descriptor)) return true;
|
|
580
|
+
if (/^(?:\d+(?:\.\d+)?|\.\d+)x$/.test(descriptor)) {
|
|
581
|
+
return Number.parseFloat(descriptor) > 0;
|
|
582
|
+
}
|
|
583
|
+
return false;
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
var URL_FUNCTION_PATTERN = /url\s*\(/i;
|
|
587
|
+
var SVG_URL_FUNCTION_PROPS = /* @__PURE__ */ new Set([
|
|
588
|
+
"clip-path",
|
|
589
|
+
"clippath",
|
|
590
|
+
"color-profile",
|
|
591
|
+
"colorprofile",
|
|
592
|
+
"cursor",
|
|
593
|
+
"fill",
|
|
594
|
+
"filter",
|
|
595
|
+
"marker",
|
|
596
|
+
"marker-end",
|
|
597
|
+
"marker-mid",
|
|
598
|
+
"marker-start",
|
|
599
|
+
"markerend",
|
|
600
|
+
"markermid",
|
|
601
|
+
"markerstart",
|
|
602
|
+
"mask",
|
|
603
|
+
"stroke"
|
|
604
|
+
]);
|
|
605
|
+
function isTemplatePaintProp(name) {
|
|
606
|
+
return SVG_URL_FUNCTION_PROPS.has(name.toLowerCase());
|
|
607
|
+
}
|
|
608
|
+
function isSafeTemplatePaintValue(value) {
|
|
609
|
+
if (typeof value === "string") return !URL_FUNCTION_PATTERN.test(value);
|
|
610
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
611
|
+
return value.every(
|
|
612
|
+
(candidate) => typeof candidate === "string" && !URL_FUNCTION_PATTERN.test(candidate)
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
var BLOCKED_STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
616
|
+
"backgroundImage",
|
|
617
|
+
"background",
|
|
618
|
+
"listStyleImage",
|
|
619
|
+
"content",
|
|
620
|
+
"borderImage",
|
|
621
|
+
"borderImageSource",
|
|
622
|
+
"maskImage",
|
|
623
|
+
"mask",
|
|
624
|
+
"filter",
|
|
625
|
+
"cursor"
|
|
626
|
+
]);
|
|
627
|
+
function sanitizeTemplateStyle(style) {
|
|
628
|
+
if (!style || typeof style !== "object" || Array.isArray(style)) return {};
|
|
629
|
+
const safe = {};
|
|
630
|
+
for (const [key, value] of Object.entries(style)) {
|
|
631
|
+
if (BLOCKED_STYLE_PROPS.has(key)) continue;
|
|
632
|
+
if (typeof value === "string" && URL_FUNCTION_PATTERN.test(value)) continue;
|
|
633
|
+
if (key === "position" && typeof value === "string" && /fixed|sticky/i.test(value)) {
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (key === "zIndex") {
|
|
637
|
+
const numericValue = typeof value === "number" ? value : parseInt(String(value), 10);
|
|
638
|
+
if (isNaN(numericValue) || numericValue > 9998) continue;
|
|
639
|
+
}
|
|
640
|
+
safe[key] = value;
|
|
641
|
+
}
|
|
642
|
+
return safe;
|
|
643
|
+
}
|
|
644
|
+
function sanitizeTemplateProps(props) {
|
|
645
|
+
const safeProps = {};
|
|
646
|
+
for (const [key, value] of Object.entries(props)) {
|
|
647
|
+
const normalizedKey = key.toLowerCase();
|
|
648
|
+
if (normalizedKey.startsWith("on")) continue;
|
|
649
|
+
if (key === "dangerouslySetInnerHTML" || key === "ref") continue;
|
|
650
|
+
if (isTemplateUrlProp(key) && !isSafeTemplateUrl(value)) continue;
|
|
651
|
+
if (isTemplateSrcSetProp(key) && !isSafeTemplateSrcSet(value)) continue;
|
|
652
|
+
if (isTemplatePaintProp(key) && !isSafeTemplatePaintValue(value)) {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (normalizedKey === "style") {
|
|
656
|
+
safeProps.style = sanitizeTemplateStyle(value);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
safeProps[key] = value;
|
|
660
|
+
}
|
|
661
|
+
return safeProps;
|
|
662
|
+
}
|
|
663
|
+
|
|
422
664
|
// src/ui/Canvas/catalog-mappers.ts
|
|
423
665
|
function toNodeTypeDef(doc) {
|
|
424
666
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/Canvas/server.ts","../../../src/core/query/query-keys.ts","../../../src/ui/Canvas/query-options.ts","../../../src/ui/Canvas/prefetchCanvas.ts","../../../src/ui/Canvas/utils.ts","../../../src/ui/Canvas/types.ts","../../../src/ui/Canvas/css-sanitizer.ts","../../../src/ui/Canvas/catalog-mappers.ts"],"sourcesContent":["// Server-safe exports (no 'use client' directive).\n// Use `@01.software/sdk/ui/canvas/server` for server components and route loaders.\n\nexport { prefetchCanvas } from './prefetchCanvas'\nexport type { PrefetchCanvasOptions } from './prefetchCanvas'\nexport { getNodeBounds, getFrames, getFrameData } from './utils'\nexport type { FrameData } from './utils'\nexport type {\n CanvasData,\n CanvasNode,\n CanvasEdge,\n CanvasViewport,\n CanvasNodePosition,\n CanvasNodeData,\n DynamicNodeData,\n FrameNodeData,\n CanvasBounds,\n NodeTypeDef,\n NodeTypeFieldDef,\n EdgeTypeDef,\n} from './types'\nexport { isDynamicNode, isFrameNode } from './types'\nexport { sanitizeCSS, validateTenantCSS } from './css-sanitizer'\nexport { toEdgeTypeDef, toNodeTypeDef } from './catalog-mappers'\n","import type { ServerCollection, ApiQueryOptions } from '../client/types'\nimport type { ProductListingPageParams } from '../api'\nimport type { ProductListingGroupsQueryOptions } from './query-hooks'\n\ntype ProductListingPageKeyParams = {\n mode: 'catalog' | 'full'\n page?: number\n limit?: number\n search?: string\n sort?: ProductListingPageParams['sort']\n filters?: NonNullable<ProductListingPageParams['filters']>\n basePath?: string\n trailingSlash?: boolean\n emit?: ProductListingPageParams['emit']\n preferCompleteVariantFromHint?: ProductListingPageParams[\n 'preferCompleteVariantFromHint'\n ]\n}\n\nfunction nonEmpty(values: string[] | undefined): string[] | undefined {\n return values?.length ? values : undefined\n}\n\nfunction canonicalProductListingPageParams(\n params: ProductListingPageParams = {},\n limit?: number,\n): ProductListingPageKeyParams {\n const filters = params.filters\n const canonicalFilters: NonNullable<ProductListingPageParams['filters']> = {}\n const ids = nonEmpty(filters?.ids)\n if (ids) canonicalFilters.ids = ids\n const slugs = nonEmpty(filters?.slugs)\n if (slugs) canonicalFilters.slugs = slugs\n const handles = nonEmpty(filters?.handles)\n if (handles) canonicalFilters.handles = handles\n const categoryIds = nonEmpty(filters?.categoryIds)\n if (categoryIds) canonicalFilters.categoryIds = categoryIds\n const tagIds = nonEmpty(filters?.tagIds)\n if (tagIds) canonicalFilters.tagIds = tagIds\n if (filters?.price?.min != null || filters?.price?.max != null) {\n canonicalFilters.price = {\n ...(filters.price.min != null && { min: filters.price.min }),\n ...(filters.price.max != null && { max: filters.price.max }),\n }\n }\n if (filters?.availableForSale != null) {\n canonicalFilters.availableForSale = filters.availableForSale\n }\n\n const search = params.search?.trim()\n return {\n mode: params.mode ?? 'catalog',\n ...(params.page != null && { page: params.page }),\n ...(limit != null\n ? { limit }\n : params.limit != null\n ? { limit: params.limit }\n : {}),\n ...(search && { search }),\n ...(params.sort != null && { sort: params.sort }),\n ...(Object.keys(canonicalFilters).length > 0 && {\n filters: canonicalFilters,\n }),\n ...(params.basePath != null && { basePath: params.basePath }),\n ...(params.trailingSlash != null && { trailingSlash: params.trailingSlash }),\n ...(params.emit != null && { emit: params.emit }),\n ...(params.preferCompleteVariantFromHint != null && {\n preferCompleteVariantFromHint: params.preferCompleteVariantFromHint,\n }),\n }\n}\n\nexport function collectionKeys<T extends ServerCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>, pageSize?: number) =>\n [\n collection,\n 'infinite',\n pageSize === undefined ? options : { ...options, limit: pageSize },\n ] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n\nexport const productKeys = {\n listingGroups: (options?: ProductListingGroupsQueryOptions) =>\n ['products', 'listing-groups', 'list', options] as const,\n listingGroupsCatalog: (options?: ProductListingGroupsQueryOptions) =>\n ['products', 'listing-groups', 'catalog', options] as const,\n listingGroupsInfinite: (\n options?: Omit<ProductListingGroupsQueryOptions, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-groups',\n 'infinite',\n pageSize === undefined ? options : { ...options, limit: pageSize },\n ] as const,\n listingGroupsCatalogInfinite: (\n options?: Omit<ProductListingGroupsQueryOptions, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-groups',\n 'catalog',\n 'infinite',\n pageSize === undefined ? options : { ...options, limit: pageSize },\n ] as const,\n listingPage: (params?: ProductListingPageParams) =>\n ['products', 'listing-page', canonicalProductListingPageParams(params)] as const,\n listingPageInfinite: (\n params?: Omit<ProductListingPageParams, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-page',\n 'infinite',\n canonicalProductListingPageParams(\n params as ProductListingPageParams | undefined,\n pageSize,\n ),\n ] as const,\n detail: (params: { slug: string } | { id: string }) =>\n ['products', 'detail', params] as const,\n detailAll: () => ['products', 'detail'] as const,\n}\n","import { collectionKeys } from '../../core/query/query-keys'\nimport type { SDKClient } from './client-types'\n\nexport function canvasQueryOptions(\n client: SDKClient,\n slug?: string,\n id?: string,\n) {\n const identifier = id ?? slug ?? ''\n return {\n queryKey: collectionKeys('canvases').detail(identifier),\n queryFn: async () => {\n if (id) return client.collections.from('canvases').findById(id)\n const result = await client.collections.from('canvases').find({\n where: { slug: { equals: slug } },\n limit: 1,\n })\n const doc = result.docs[0]\n if (!doc) throw new Error(`Canvas not found: ${slug}`)\n return doc\n },\n }\n}\n\nexport function nodeTypesQueryOptions(client: SDKClient) {\n return {\n queryKey: collectionKeys('canvas-node-types').lists(),\n queryFn: async () => {\n // limit: 0 = fetch all documents (Payload CMS convention, disables pagination)\n const result = await client.collections.from('canvas-node-types').find({ limit: 0 })\n return result.docs\n },\n }\n}\n\nexport function edgeTypesQueryOptions(client: SDKClient) {\n return {\n queryKey: collectionKeys('canvas-edge-types').lists(),\n queryFn: async () => {\n const result = await client.collections.from('canvas-edge-types').find({ limit: 0 })\n return result.docs\n },\n }\n}\n","import type { SDKClient } from './client-types'\nimport {\n canvasQueryOptions,\n nodeTypesQueryOptions,\n edgeTypesQueryOptions,\n} from './query-options'\n\ninterface CanvasPrefetchQueryClient {\n prefetchQuery(options: unknown): Promise<unknown>\n}\n\nexport interface PrefetchCanvasOptions {\n /** SDK client instance (Client or ServerClient) */\n client: SDKClient\n /** React Query client to prefetch into. */\n queryClient: CanvasPrefetchQueryClient\n /** Canvas slug (URL-friendly identifier) */\n slug?: string\n /** Canvas document ID (UUID) */\n id?: string\n}\n\n/**\n * Prefetch canvas data into the query cache.\n * Call in route loaders or server components to eliminate loading states on mount.\n *\n * ```ts\n * // React Router loader / Server Component\n * await prefetchCanvas({ client, queryClient, slug: 'Home' })\n * ```\n */\nexport async function prefetchCanvas(\n options: PrefetchCanvasOptions,\n): Promise<void> {\n const { client, queryClient, slug, id } = options\n\n if (!slug && !id) {\n throw new Error('prefetchCanvas requires either slug or id')\n }\n\n await Promise.all([\n queryClient.prefetchQuery(canvasQueryOptions(client, slug, id)),\n queryClient.prefetchQuery(nodeTypesQueryOptions(client)),\n queryClient.prefetchQuery(edgeTypesQueryOptions(client)),\n ])\n}\n","import type { CanvasNode, CanvasEdge, CanvasBounds, CanvasData } from './types'\n\n// ── Shared helpers ──\n\nfunction getNodeSize(node: CanvasNode): { width: number; height: number } {\n const style = node.style as { width?: number; height?: number } | undefined\n\n return {\n width:\n style?.width ??\n node.measured?.width ??\n node.width ??\n 200,\n height:\n style?.height ??\n node.measured?.height ??\n node.height ??\n 200,\n }\n}\n\nfunction getAbsolutePosition(\n node: CanvasNode,\n nodeMap: Map<string, CanvasNode>,\n): { x: number; y: number } {\n let x = node.position.x\n let y = node.position.y\n let current = node\n const visited = new Set<string>([node.id])\n while (current.parentId) {\n const parentId = current.parentId\n if (visited.has(parentId)) break\n const parent = nodeMap.get(parentId)\n if (!parent) break\n visited.add(parent.id)\n x += parent.position.x\n y += parent.position.y\n current = parent\n }\n return { x, y }\n}\n\n/** Collect a node and all its descendants using a pre-built parent→children map (O(N)). */\nfunction collectDescendants(\n nodes: CanvasNode[],\n rootId: string,\n): Set<string> {\n // Build parent → children lookup in O(N)\n const childrenMap = new Map<string, string[]>()\n for (const n of nodes) {\n if (n.parentId) {\n let siblings = childrenMap.get(n.parentId)\n if (!siblings) {\n siblings = []\n childrenMap.set(n.parentId, siblings)\n }\n siblings.push(n.id)\n }\n }\n\n // BFS over children map in O(descendants)\n const result = new Set<string>([rootId])\n const queue = [rootId]\n let i = 0\n while (i < queue.length) {\n const children = childrenMap.get(queue[i++]!)\n if (children) {\n for (const childId of children) {\n if (!result.has(childId)) {\n result.add(childId)\n queue.push(childId)\n }\n }\n }\n }\n return result\n}\n\n// ── Public utilities ──\n\n/**\n * Calculate bounding box for given node IDs.\n * Pure function — usable in SSR, server components, or outside React.\n */\nexport function getNodeBounds(\n nodes: CanvasNode[],\n nodeIds: string[],\n): CanvasBounds | undefined {\n const idSet = new Set(nodeIds)\n const targetNodes = nodes.filter((n) => idSet.has(n.id))\n if (targetNodes.length === 0) return undefined\n\n const nodeMap = new Map(nodes.map((n) => [n.id, n]))\n\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const node of targetNodes) {\n const abs = getAbsolutePosition(node, nodeMap)\n const { width: w, height: h } = getNodeSize(node)\n minX = Math.min(minX, abs.x)\n minY = Math.min(minY, abs.y)\n maxX = Math.max(maxX, abs.x + w)\n maxY = Math.max(maxY, abs.y + h)\n }\n\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }\n}\n\n/**\n * Get all frame nodes with their bounds.\n * Sorted by position (top-left to bottom-right).\n */\nexport function getFrames(\n nodes: CanvasNode[],\n): Array<{ id: string; label: string; bounds: CanvasBounds }> {\n const frames = nodes.filter((n) => n.type === 'frame')\n if (frames.length === 0) return []\n\n const nodeMap = new Map(nodes.map((n) => [n.id, n]))\n\n return frames\n .map((f) => {\n const data = f.data as { label?: string }\n const abs = getAbsolutePosition(f, nodeMap)\n const { width: w, height: h } = getNodeSize(f)\n return {\n id: f.id,\n label: data.label ?? '',\n bounds: { x: abs.x, y: abs.y, width: w, height: h },\n }\n })\n .sort((a, b) => a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x)\n}\n\n/** Result of getFrameData — contains filtered canvas + dual bounds. */\nexport interface FrameData {\n /** Canvas data containing only the frame's descendants (nodes have draggable: false). */\n data: CanvasData\n /** Bounding box of child content nodes — use for initial viewport fit (centering). */\n fitBounds: CanvasBounds\n /** Bounding box of the frame itself — use for panning/zoom restriction (clamp). */\n clampBounds: CanvasBounds\n}\n\n/**\n * Extract a frame's descendants and related edges from canvas data.\n * Recursively collects all nested children (supports nested frames).\n * Returns undefined if frameId is not found or is not a frame node.\n *\n * Child nodes are marked `draggable: false` to prevent interfering with canvas panning.\n * The frame node itself is included (use `frameRenderer={() => null}` to hide it).\n *\n * Returns dual bounds:\n * - `fitBounds`: child content bounding box (for centering the viewport)\n * - `clampBounds`: frame area bounding box (for panning restriction)\n */\nexport function getFrameData(\n data: CanvasData,\n frameId: string,\n): FrameData | undefined {\n const frame = data.nodes.find((n) => n.id === frameId)\n if (!frame || frame.type !== 'frame') return undefined\n\n // Recursively collect frame + all descendants\n const descendantIds = collectDescendants(data.nodes, frameId)\n const childNodes = data.nodes\n .filter((n) => descendantIds.has(n.id))\n .map((n) => ({ ...n, draggable: false }))\n\n // Keep only edges where both source and target are within the frame\n const childEdges = data.edges.filter(\n (e: CanvasEdge) => descendantIds.has(e.source) && descendantIds.has(e.target),\n )\n\n // clampBounds: frame's own bounding box (for panning restriction)\n const frameBounds = getNodeBounds(data.nodes, [frameId])\n const { width: w, height: h } = getNodeSize(frame)\n const clampBounds: CanvasBounds = frameBounds ?? {\n x: frame.position.x,\n y: frame.position.y,\n width: w,\n height: h,\n }\n\n // fitBounds: child content bounding box (for centering)\n const contentNodeIds = childNodes\n .filter((n) => n.id !== frameId)\n .map((n) => n.id)\n const contentBounds = contentNodeIds.length > 0\n ? getNodeBounds(data.nodes, contentNodeIds)\n : undefined\n const fitBounds = contentBounds ?? clampBounds\n\n return {\n data: {\n nodes: childNodes,\n edges: childEdges,\n viewport: data.viewport,\n },\n fitBounds,\n clampBounds,\n }\n}\n","export type CanvasStyle = object\n\n// ── Dynamic node data ──\n\nexport interface DynamicNodeData {\n nodeTypeSlug: string\n label: string\n fields: Record<string, unknown>\n}\n\nexport type CanvasNodeData = (DynamicNodeData | FrameNodeData) &\n Record<string, unknown>\n\n// ── Canvas types (mirrors @xyflow/react but standalone) ──\n\nexport interface CanvasNodePosition {\n x: number\n y: number\n}\n\nexport interface CanvasNode {\n id: string\n type?: string\n position: CanvasNodePosition\n data: CanvasNodeData\n parentId?: string\n style?: CanvasStyle\n width?: number\n height?: number\n measured?: { width?: number; height?: number }\n draggable?: boolean\n selectable?: boolean\n [key: string]: unknown\n}\n\nexport interface CanvasEdge {\n id: string\n source: string\n target: string\n sourceHandle?: string | null\n targetHandle?: string | null\n type?: string\n style?: CanvasStyle\n animated?: boolean\n markerStart?: unknown\n markerEnd?: unknown\n edgeTypeSlug?: string\n fields?: Record<string, unknown>\n [key: string]: unknown\n}\n\nexport interface CanvasViewport {\n x: number\n y: number\n zoom: number\n}\n\nexport interface CanvasData {\n nodes: CanvasNode[]\n edges: CanvasEdge[]\n viewport: CanvasViewport\n}\n\n// ── Node type definitions (mirrors console's NodeTypeDef) ──\n\nexport interface NodeTypeFieldDef {\n name: string\n label: string\n fieldType:\n | 'text'\n | 'textarea'\n | 'number'\n | 'url'\n | 'color'\n | 'image'\n | 'date'\n | 'select'\n | 'toggle'\n options?: { label: string; value: string }[]\n defaultValue?: string\n required?: boolean\n}\n\nexport interface NodeTypeDef {\n slug: string\n name: string\n color: string\n defaultSize: { width: number; height: number }\n fields: NodeTypeFieldDef[]\n editorIntent?: 'text' | 'image'\n transparentBackground?: boolean\n template?: string | null\n customCSS?: string | null\n}\n\n// ── Edge type definitions (mirrors console's EdgeTypeDef) ──\n\nexport interface EdgeTypeDef {\n slug: string\n name: string\n color: string\n strokeWidth: number\n animated: boolean\n lineStyle: string\n markerStart: string\n markerEnd: string\n fields: NodeTypeFieldDef[]\n isDefaultEdge?: boolean\n}\n\n// ── Type guards ──\n\nexport function isDynamicNode(\n node: CanvasNode,\n): node is CanvasNode & { data: DynamicNodeData } {\n return node.type === 'dynamic'\n}\n\nexport function isFrameNode(\n node: CanvasNode,\n): node is CanvasNode & { data: FrameNodeData } {\n return node.type === 'frame'\n}\n\n// ── Component slot props ──\n\nexport interface DynamicNodeSlotProps {\n id: string\n nodeTypeSlug: string\n label: string\n fields: Record<string, unknown>\n nodeTypeDef?: NodeTypeDef\n /** Whether this node is currently selected */\n selected?: boolean\n /** Measured node width (undefined before first measurement) */\n width?: number\n /** Measured node height (undefined before first measurement) */\n height?: number\n /** The default rendering (template or field-based). Allows custom renderers to wrap/extend instead of replacing entirely. */\n defaultRender?: unknown\n}\n\n// ── Frame node data ──\n\nexport interface FrameNodeData {\n label: string\n color?: string\n padding?: number\n borderStyle?: 'dashed' | 'solid' | 'none'\n opacity?: number\n}\n\n// S1: Frame renderer slot\nexport interface FrameNodeSlotProps {\n id: string\n label: string\n color?: string\n padding?: number\n borderStyle?: 'dashed' | 'solid' | 'none'\n opacity?: number\n width?: number\n height?: number\n children?: unknown\n}\n\n// S2: Edge renderer slot\nexport interface EdgeSlotProps {\n id: string\n edgeTypeSlug?: string\n source: string\n target: string\n label?: string\n fields?: Record<string, unknown>\n edgeTypeDef?: EdgeTypeDef\n style?: CanvasStyle\n}\n\n// S3: Node wrapper slot\nexport interface NodeWrapperSlotProps {\n id: string\n nodeTypeSlug: string\n label: string\n selected?: boolean\n nodeTypeDef?: NodeTypeDef\n children: unknown\n}\n\n// S8: Viewport focus\nexport interface CanvasBounds {\n x: number\n y: number\n width: number\n height: number\n}\n","import postcss from 'postcss'\n\nconst ALLOWED_AT_RULES = new Set([\n 'keyframes',\n 'media',\n 'supports',\n 'container',\n 'layer',\n])\nconst BLOCKED_VALUE_PATTERNS = [\n /url\\s*\\(/i,\n /expression\\s*\\(/i,\n /paint\\s*\\(/i,\n /-moz-binding/i,\n]\nconst DANGEROUS_SELECTOR = /(?:^|[\\s,])(?::root|html|body)\\b/\nconst BLOCKED_PROPERTY_VALUES: Record<string, RegExp> = {\n // absolute is allowed — contained by nearest positioned ancestor (node wrapper)\n position: /fixed|sticky/i,\n}\nconst TENANT_CSS_BLOCKED_PATTERNS = [\n { pattern: /<\\/style/i, label: '</style' },\n { pattern: /<script/i, label: '<script' },\n { pattern: /@import/i, label: '@import' },\n { pattern: /url\\s*\\(/i, label: 'url(' },\n] as const\nconst Z_INDEX_MAX = 9998\n\ntype SanitizeCSSStats = {\n css: string\n removedRules: number\n removedDecls: number\n removedAtRules: number\n}\n\nfunction normalizeCSSEscapes(value: string): string {\n return value.replace(\n /\\\\(?:([0-9a-fA-F]{1,6})(?:\\r\\n|[ \\t\\r\\n\\f])?|([^\\r\\n\\f]))/g,\n (match, hex: string | undefined, escaped: string | undefined) => {\n if (hex) {\n const cp = parseInt(hex, 16)\n if (Number.isNaN(cp) || cp > 0x10ffff) return match\n try {\n return String.fromCodePoint(cp)\n } catch {\n return match\n }\n }\n return escaped ?? match\n },\n )\n}\n\n/**\n * Sanitize tenant-authored CSS using PostCSS AST parsing.\n *\n * - At-rules: allowlist only (@keyframes, @media, @supports, @container, @layer)\n * - Declarations: blocks url(), expression(), paint(), -moz-binding\n * - Selectors: removes :root/html/body targets; scopes remainder under scopeClass\n *\n * Returns empty string on parse failure (fail-closed).\n *\n * @param css - Raw CSS string from tenant\n * @param scopeClass - Container class to scope selectors (e.g. 'flow-node--my-type')\n */\nexport function sanitizeCSS(css: string, scopeClass?: string): string {\n return sanitizeCSSWithStats(css, scopeClass).css\n}\n\nfunction sanitizeCSSWithStats(\n css: string,\n scopeClass?: string,\n): SanitizeCSSStats {\n let root: postcss.Root\n try {\n root = postcss.parse(css)\n } catch {\n return { css: '', removedRules: 0, removedDecls: 0, removedAtRules: 0 }\n }\n const stats: SanitizeCSSStats = {\n css: '',\n removedRules: 0,\n removedDecls: 0,\n removedAtRules: 0,\n }\n\n // Compute safe scope class once (strip CSS metacharacters and whitespace)\n const safeScopeClass = scopeClass\n ? scopeClass.replace(/[{}()[\\];,'\"\\\\<>\\s]/g, '')\n : ''\n\n // @keyframes name scoping (skip when no scope)\n const keyframeNameMap = new Map<string, string>()\n if (safeScopeClass) {\n root.walkAtRules(/^keyframes$/i, (node) => {\n // Strip surrounding quotes; replace spaces with underscores for valid identifier output\n // e.g. @keyframes \"fade in\" → flow-node--slug__fade_in\n const rawName = node.params.trim().replace(/^['\"]|['\"]$/g, '')\n const originalName = rawName // keep spaces for map key lookup\n const scopedName = `${safeScopeClass}__${rawName.replace(/\\s+/g, '_')}`\n keyframeNameMap.set(originalName, scopedName)\n node.params = scopedName\n })\n }\n\n // Rewrite animation-name / animation shorthand to use scoped keyframe names\n if (safeScopeClass && keyframeNameMap.size > 0) {\n const ANIM_KEYWORDS = new Set([\n 'none',\n 'initial',\n 'inherit',\n 'unset',\n 'revert',\n ])\n // Strip quotes from token before map lookup (animation-name: 'fade' → look up 'fade')\n const replaceAnimName = (token: string) => {\n const t = token.trim().replace(/^['\"]|['\"]$/g, '')\n return ANIM_KEYWORDS.has(t) ? token : (keyframeNameMap.get(t) ?? token)\n }\n // Use regex filter for case-insensitive match (Animation:, ANIMATION-NAME:)\n root.walkDecls(/^animation-name$/i, (node) => {\n node.value = node.value.split(',').map(replaceAnimName).join(', ')\n })\n root.walkDecls(/^animation$/i, (node) => {\n node.value = node.value\n .split(',')\n .map((anim) =>\n anim\n .split(/(\\s+)/)\n .map((token) => (/\\s/.test(token) ? token : replaceAnimName(token)))\n .join(''),\n )\n .join(',')\n })\n }\n\n // At-rules: remove anything not in the allowlist\n root.walkAtRules((node) => {\n if (!ALLOWED_AT_RULES.has(node.name.toLowerCase())) {\n stats.removedAtRules++\n node.remove()\n }\n })\n\n // Declarations: remove dangerous values\n root.walkDecls((node) => {\n // Normalize value: strip CSS block comments so fi/**/xed → fixed (browser behaviour)\n const normalizedValue = normalizeCSSEscapes(\n node.value.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ''),\n )\n if (BLOCKED_VALUE_PATTERNS.some((p) => p.test(normalizedValue))) {\n stats.removedDecls++\n node.remove()\n return\n }\n // Block dangerous property+value combinations (position: fixed/sticky, oversized z-index)\n const propLower = node.prop.toLowerCase()\n const blockedValuePattern = BLOCKED_PROPERTY_VALUES[propLower]\n if (blockedValuePattern) {\n if (blockedValuePattern.test(normalizedValue)) {\n stats.removedDecls++\n node.remove()\n return\n }\n }\n if (propLower === 'z-index') {\n const zv = node.value.trim()\n // Allow CSS-wide keywords and 'auto'\n if (\n zv === 'auto' ||\n zv === 'initial' ||\n zv === 'inherit' ||\n zv === 'unset' ||\n zv === 'revert'\n ) {\n // pass through\n } else {\n // Only allow plain integers ≤ Z_INDEX_MAX. calc(), var(), and non-integers are removed.\n const val = parseInt(zv, 10)\n if (isNaN(val) || String(val) !== zv || val > Z_INDEX_MAX) {\n stats.removedDecls++\n node.remove()\n return\n }\n }\n }\n })\n\n // Rules: strip dangerous selectors, then scope the rest\n root.walkRules((rule) => {\n const safeSelectors = rule.selectors.filter(\n (s) => s.trim() && !DANGEROUS_SELECTOR.test(s),\n )\n if (!safeSelectors.length) {\n stats.removedRules++\n rule.remove()\n return\n }\n if (safeSelectors.length !== rule.selectors.length) {\n stats.removedRules++\n }\n rule.selectors = safeSelectors\n if (scopeClass) {\n if (!safeScopeClass) {\n stats.removedRules++\n rule.remove()\n return\n }\n // Skip scoping inside @keyframes — from/to/0% are not element selectors\n const parent = rule.parent\n if (\n parent?.type === 'atrule' &&\n (parent as postcss.AtRule).name.toLowerCase() === 'keyframes'\n ) {\n return\n }\n rule.selectors = rule.selectors.map((sel) => `.${safeScopeClass} ${sel}`)\n }\n })\n\n // Post-process: escape </style to prevent HTML parser closing the style tag in SSR contexts.\n // postcss parses this as valid CSS string content and reproduces it verbatim.\n stats.css = root.toString().replace(/<\\/style/gi, '<\\\\/style')\n return stats\n}\n\nexport function validateTenantCSS(css: string): string | null {\n const trimmed = css.trim()\n if (!trimmed) return null\n\n const deescapedCSS = normalizeCSSEscapes(css)\n for (const { pattern, label } of TENANT_CSS_BLOCKED_PATTERNS) {\n if (pattern.test(deescapedCSS)) {\n return `CSS contains blocked pattern: ${label}`\n }\n }\n\n try {\n postcss.parse(css)\n } catch {\n return 'CSS parse error'\n }\n\n const sanitized = sanitizeCSSWithStats(\n css,\n 'flow-node--tenant-css-validation',\n )\n if (!sanitized.css.trim()) {\n return 'CSS must contain safe rules'\n }\n if (\n sanitized.removedRules > 0 ||\n sanitized.removedDecls > 0 ||\n sanitized.removedAtRules > 0\n ) {\n return 'CSS contains unsafe rules or selectors'\n }\n\n return null\n}\n","import type { EdgeTypeDef, NodeTypeDef } from './types'\n\nexport function toNodeTypeDef(doc: Record<string, unknown>): NodeTypeDef {\n return {\n slug: String(doc.slug ?? ''),\n name: String(doc.title ?? ''),\n color: String(doc.color ?? '#e5e7eb'),\n defaultSize: (doc.defaultSize as NodeTypeDef['defaultSize']) ?? {\n width: 200,\n height: 200,\n },\n fields: Array.isArray(doc.fields)\n ? (doc.fields as NodeTypeDef['fields'])\n : [],\n editorIntent:\n doc.editorIntent === 'text' || doc.editorIntent === 'image'\n ? doc.editorIntent\n : undefined,\n transparentBackground: Boolean(\n doc.hasTransparentBackground ?? doc.transparentBackground,\n ),\n template: (doc.template as string) ?? null,\n customCSS: (doc.customCSS as string) ?? null,\n }\n}\n\nexport function toEdgeTypeDef(doc: Record<string, unknown>): EdgeTypeDef {\n return {\n slug: String(doc.slug ?? ''),\n name: String(doc.title ?? ''),\n color: String(doc.color ?? ''),\n strokeWidth: (doc.strokeWidth as number) ?? 2,\n animated:\n (doc.isAnimated as boolean | undefined) ??\n (doc.animated as boolean | undefined) ??\n false,\n lineStyle: String(doc.lineStyle ?? 'default'),\n markerStart: String(doc.markerStart ?? 'none'),\n markerEnd: String(doc.markerEnd ?? 'arrow'),\n fields: Array.isArray(doc.fields)\n ? (doc.fields as EdgeTypeDef['fields'])\n : [],\n isDefaultEdge: Boolean(doc.isDefaultEdge),\n }\n}\n"],"mappings":"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","names":["postcss"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/Canvas/server.ts","../../../src/core/query/query-keys.ts","../../../src/ui/Canvas/query-options.ts","../../../src/ui/Canvas/prefetchCanvas.ts","../../../src/ui/Canvas/utils.ts","../../../src/ui/Canvas/types.ts","../../../src/ui/Canvas/css-sanitizer.ts","../../../src/ui/Canvas/template-security.ts","../../../src/ui/Canvas/catalog-mappers.ts"],"sourcesContent":["// Server-safe exports (no 'use client' directive).\n// Use `@01.software/sdk/ui/canvas/server` for server components and route loaders.\n\nexport { prefetchCanvas } from './prefetchCanvas'\nexport type { PrefetchCanvasOptions } from './prefetchCanvas'\nexport { getNodeBounds, getFrames, getFrameData } from './utils'\nexport type { FrameData } from './utils'\nexport type {\n CanvasData,\n CanvasNode,\n CanvasEdge,\n CanvasViewport,\n CanvasNodePosition,\n CanvasNodeData,\n DynamicNodeData,\n FrameNodeData,\n ShapeNodeData,\n ShapeType,\n CanvasNodeType,\n CanvasBounds,\n NodeTypeDef,\n NodeTypeFieldDef,\n EdgeTypeDef,\n} from './types'\nexport {\n CANVAS_SHAPE_TYPES,\n isDynamicNode,\n isFrameNode,\n isShapeNode,\n} from './types'\nexport { sanitizeCSS, validateTenantCSS } from './css-sanitizer'\nexport {\n ALLOWED_TEMPLATE_ELEMENTS,\n TEMPLATE_BLOCKED_PATTERNS,\n findBlockedTemplatePattern,\n isAllowedTemplateElement,\n isSafeTemplatePaintValue,\n isSafeTemplateSrcSet,\n isSafeTemplateUrl,\n isTemplatePaintProp,\n isTemplateSrcSetProp,\n isTemplateUrlProp,\n sanitizeTemplateProps,\n sanitizeTemplateStyle,\n} from './template-security'\nexport { toEdgeTypeDef, toNodeTypeDef } from './catalog-mappers'\n","import type { ServerCollection, ApiQueryOptions } from '../client/types'\nimport type { ProductDetailParams, ProductListingPageParams } from '../api'\nimport type { ProductListingGroupsQueryOptions } from './query-hooks'\n\ntype ProductListingPageKeyParams = {\n mode: 'catalog' | 'full'\n market?: string\n locale?: ProductListingPageParams['locale']\n fallbackLocale?: ProductListingPageParams['fallbackLocale']\n page?: number\n limit?: number\n search?: string\n sort?: ProductListingPageParams['sort']\n filters?: NonNullable<ProductListingPageParams['filters']>\n basePath?: string\n trailingSlash?: boolean\n emit?: ProductListingPageParams['emit']\n preferCompleteVariantFromHint?: ProductListingPageParams['preferCompleteVariantFromHint']\n}\n\nfunction nonEmpty(values: string[] | undefined): string[] | undefined {\n return values?.length ? values : undefined\n}\n\nfunction canonicalProductListingPageParams(\n params: ProductListingPageParams = {},\n limit?: number,\n): ProductListingPageKeyParams {\n const filters = params.filters\n const canonicalFilters: NonNullable<ProductListingPageParams['filters']> = {}\n const ids = nonEmpty(filters?.ids)\n if (ids) canonicalFilters.ids = ids\n const slugs = nonEmpty(filters?.slugs)\n if (slugs) canonicalFilters.slugs = slugs\n const handles = nonEmpty(filters?.handles)\n if (handles) canonicalFilters.handles = handles\n const categoryIds = nonEmpty(filters?.categoryIds)\n if (categoryIds) canonicalFilters.categoryIds = categoryIds\n const tagIds = nonEmpty(filters?.tagIds)\n if (tagIds) canonicalFilters.tagIds = tagIds\n if (filters?.price?.min != null || filters?.price?.max != null) {\n canonicalFilters.price = {\n ...(filters.price.min != null && { min: filters.price.min }),\n ...(filters.price.max != null && { max: filters.price.max }),\n }\n }\n if (filters?.availableForSale != null) {\n canonicalFilters.availableForSale = filters.availableForSale\n }\n\n const search = params.search?.trim()\n const market = params.market?.trim()\n return {\n mode: params.mode ?? 'catalog',\n ...(market && { market }),\n ...(params.locale != null && { locale: params.locale }),\n ...(params.fallbackLocale !== undefined && {\n fallbackLocale: params.fallbackLocale,\n }),\n ...(params.page != null && { page: params.page }),\n ...(limit != null\n ? { limit }\n : params.limit != null\n ? { limit: params.limit }\n : {}),\n ...(search && { search }),\n ...(params.sort != null && { sort: params.sort }),\n ...(Object.keys(canonicalFilters).length > 0 && {\n filters: canonicalFilters,\n }),\n ...(params.basePath != null && { basePath: params.basePath }),\n ...(params.trailingSlash != null && {\n trailingSlash: params.trailingSlash,\n }),\n ...(params.emit != null && { emit: params.emit }),\n ...(params.preferCompleteVariantFromHint != null && {\n preferCompleteVariantFromHint: params.preferCompleteVariantFromHint,\n }),\n }\n}\n\nfunction canonicalMarket(value: string | undefined): string | undefined {\n const market = value?.trim()\n return market || undefined\n}\n\nexport function canonicalProductListingGroupsQueryOptions(\n options?: ProductListingGroupsQueryOptions,\n limit?: number,\n): ProductListingGroupsQueryOptions | undefined {\n const { market: rawMarket, ...rest } = options ?? {}\n const market = canonicalMarket(rawMarket)\n const canonical: ProductListingGroupsQueryOptions = {\n ...rest,\n ...(limit !== undefined ? { limit } : {}),\n ...(market ? { market } : {}),\n }\n return Object.keys(canonical).length > 0 ? canonical : undefined\n}\n\nfunction canonicalProductDetailParams(\n params: ProductDetailParams,\n): ProductDetailParams {\n const market = params.market?.trim()\n const localeOptions = {\n ...(params.locale != null && { locale: params.locale }),\n ...(params.fallbackLocale !== undefined && {\n fallbackLocale: params.fallbackLocale,\n }),\n }\n if ('slug' in params) {\n return {\n slug: params.slug,\n ...(market ? { market } : {}),\n ...localeOptions,\n }\n }\n return {\n id: params.id,\n ...(market ? { market } : {}),\n ...localeOptions,\n }\n}\n\nexport function collectionKeys<T extends ServerCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>, pageSize?: number) =>\n [\n collection,\n 'infinite',\n pageSize === undefined ? options : { ...options, limit: pageSize },\n ] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n\nexport const productKeys = {\n listingGroups: (options?: ProductListingGroupsQueryOptions) =>\n [\n 'products',\n 'listing-groups',\n 'list',\n canonicalProductListingGroupsQueryOptions(options),\n ] as const,\n listingGroupsCatalog: (options?: ProductListingGroupsQueryOptions) =>\n [\n 'products',\n 'listing-groups',\n 'catalog',\n canonicalProductListingGroupsQueryOptions(options),\n ] as const,\n listingGroupsInfinite: (\n options?: Omit<ProductListingGroupsQueryOptions, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-groups',\n 'infinite',\n canonicalProductListingGroupsQueryOptions(options, pageSize),\n ] as const,\n listingGroupsCatalogInfinite: (\n options?: Omit<ProductListingGroupsQueryOptions, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-groups',\n 'catalog',\n 'infinite',\n canonicalProductListingGroupsQueryOptions(options, pageSize),\n ] as const,\n listingPage: (params?: ProductListingPageParams) =>\n [\n 'products',\n 'listing-page',\n canonicalProductListingPageParams(params),\n ] as const,\n listingPageInfinite: (\n params?: Omit<ProductListingPageParams, 'page' | 'limit'>,\n pageSize?: number,\n ) =>\n [\n 'products',\n 'listing-page',\n 'infinite',\n canonicalProductListingPageParams(\n params as ProductListingPageParams | undefined,\n pageSize,\n ),\n ] as const,\n detail: (params: ProductDetailParams) =>\n ['products', 'detail', canonicalProductDetailParams(params)] as const,\n detailAll: () => ['products', 'detail'] as const,\n}\n","import { collectionKeys } from '../../core/query/query-keys'\nimport type { SDKClient } from './client-types'\n\nconst CANVAS_STALE_TIME_MS = 60_000\nconst CANVAS_CATALOG_STALE_TIME_MS = 5 * 60_000\n\nexport function canvasQueryOptions(\n client: SDKClient,\n slug?: string,\n id?: string,\n) {\n const identifier = id ?? slug ?? ''\n return {\n queryKey: collectionKeys('canvases').detail(identifier),\n staleTime: CANVAS_STALE_TIME_MS,\n queryFn: async () => {\n if (id) return client.collections.from('canvases').findById(id)\n const result = await client.collections.from('canvases').find({\n where: { slug: { equals: slug } },\n limit: 1,\n })\n const doc = result.docs[0]\n if (!doc) throw new Error(`Canvas not found: ${slug}`)\n return doc\n },\n }\n}\n\nexport function nodeTypesQueryOptions(client: SDKClient) {\n return {\n queryKey: collectionKeys('canvas-node-types').lists(),\n staleTime: CANVAS_CATALOG_STALE_TIME_MS,\n queryFn: async () => {\n // Bounded fetch: publishable-key (browser) raw reads cap at 100 rows, and\n // node type definitions are a small config set well under the cap.\n const result = await client.collections\n .from('canvas-node-types')\n .find({ limit: 100 })\n return result.docs\n },\n }\n}\n\nexport function edgeTypesQueryOptions(client: SDKClient) {\n return {\n queryKey: collectionKeys('canvas-edge-types').lists(),\n staleTime: CANVAS_CATALOG_STALE_TIME_MS,\n queryFn: async () => {\n // Bounded fetch: see nodeTypesQueryOptions — edge type definitions are a\n // small config set well under the 100-row publishable-key read cap.\n const result = await client.collections\n .from('canvas-edge-types')\n .find({ limit: 100 })\n return result.docs\n },\n }\n}\n","import type { SDKClient } from './client-types'\nimport {\n canvasQueryOptions,\n nodeTypesQueryOptions,\n edgeTypesQueryOptions,\n} from './query-options'\n\ninterface CanvasPrefetchQueryClient {\n prefetchQuery(options: unknown): Promise<unknown>\n}\n\nexport interface PrefetchCanvasOptions {\n /** SDK client instance (Client or ServerClient) */\n client: SDKClient\n /** React Query client to prefetch into. */\n queryClient: CanvasPrefetchQueryClient\n /** Canvas slug (URL-friendly identifier) */\n slug?: string\n /** Canvas document ID (UUID) */\n id?: string\n}\n\n/**\n * Prefetch canvas data into the query cache.\n * Call in route loaders or server components to eliminate loading states on mount.\n *\n * ```ts\n * // React Router loader / Server Component\n * await prefetchCanvas({ client, queryClient, slug: 'Home' })\n * ```\n */\nexport async function prefetchCanvas(\n options: PrefetchCanvasOptions,\n): Promise<void> {\n const { client, queryClient, slug, id } = options\n\n if (!slug && !id) {\n throw new Error('prefetchCanvas requires either slug or id')\n }\n\n await Promise.all([\n queryClient.prefetchQuery(canvasQueryOptions(client, slug, id)),\n queryClient.prefetchQuery(nodeTypesQueryOptions(client)),\n queryClient.prefetchQuery(edgeTypesQueryOptions(client)),\n ])\n}\n","import type { CanvasNode, CanvasEdge, CanvasBounds, CanvasData } from './types'\n\n// ── Shared helpers ──\n\nfunction getNodeSize(node: CanvasNode): { width: number; height: number } {\n const style = node.style as { width?: number; height?: number } | undefined\n\n return {\n width:\n style?.width ??\n node.measured?.width ??\n node.width ??\n 200,\n height:\n style?.height ??\n node.measured?.height ??\n node.height ??\n 200,\n }\n}\n\nfunction getAbsolutePosition(\n node: CanvasNode,\n nodeMap: Map<string, CanvasNode>,\n): { x: number; y: number } {\n let x = node.position.x\n let y = node.position.y\n let current = node\n const visited = new Set<string>([node.id])\n while (current.parentId) {\n const parentId = current.parentId\n if (visited.has(parentId)) break\n const parent = nodeMap.get(parentId)\n if (!parent) break\n visited.add(parent.id)\n x += parent.position.x\n y += parent.position.y\n current = parent\n }\n return { x, y }\n}\n\n/** Collect a node and all its descendants using a pre-built parent→children map (O(N)). */\nfunction collectDescendants(\n nodes: CanvasNode[],\n rootId: string,\n): Set<string> {\n // Build parent → children lookup in O(N)\n const childrenMap = new Map<string, string[]>()\n for (const n of nodes) {\n if (n.parentId) {\n let siblings = childrenMap.get(n.parentId)\n if (!siblings) {\n siblings = []\n childrenMap.set(n.parentId, siblings)\n }\n siblings.push(n.id)\n }\n }\n\n // BFS over children map in O(descendants)\n const result = new Set<string>([rootId])\n const queue = [rootId]\n let i = 0\n while (i < queue.length) {\n const children = childrenMap.get(queue[i++]!)\n if (children) {\n for (const childId of children) {\n if (!result.has(childId)) {\n result.add(childId)\n queue.push(childId)\n }\n }\n }\n }\n return result\n}\n\n// ── Public utilities ──\n\n/**\n * Calculate bounding box for given node IDs.\n * Pure function — usable in SSR, server components, or outside React.\n */\nexport function getNodeBounds(\n nodes: CanvasNode[],\n nodeIds: string[],\n): CanvasBounds | undefined {\n const idSet = new Set(nodeIds)\n const targetNodes = nodes.filter((n) => idSet.has(n.id))\n if (targetNodes.length === 0) return undefined\n\n const nodeMap = new Map(nodes.map((n) => [n.id, n]))\n\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const node of targetNodes) {\n const abs = getAbsolutePosition(node, nodeMap)\n const { width: w, height: h } = getNodeSize(node)\n minX = Math.min(minX, abs.x)\n minY = Math.min(minY, abs.y)\n maxX = Math.max(maxX, abs.x + w)\n maxY = Math.max(maxY, abs.y + h)\n }\n\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }\n}\n\n/**\n * Get all frame nodes with their bounds.\n * Sorted by position (top-left to bottom-right).\n */\nexport function getFrames(\n nodes: CanvasNode[],\n): Array<{ id: string; label: string; bounds: CanvasBounds }> {\n const frames = nodes.filter((n) => n.type === 'frame')\n if (frames.length === 0) return []\n\n const nodeMap = new Map(nodes.map((n) => [n.id, n]))\n\n return frames\n .map((f) => {\n const data = f.data as { label?: string }\n const abs = getAbsolutePosition(f, nodeMap)\n const { width: w, height: h } = getNodeSize(f)\n return {\n id: f.id,\n label: data.label ?? '',\n bounds: { x: abs.x, y: abs.y, width: w, height: h },\n }\n })\n .sort((a, b) => a.bounds.y - b.bounds.y || a.bounds.x - b.bounds.x)\n}\n\n/** Result of getFrameData — contains filtered canvas + dual bounds. */\nexport interface FrameData {\n /** Canvas data containing only the frame's descendants (nodes have draggable: false). */\n data: CanvasData\n /** Bounding box of child content nodes — use for initial viewport fit (centering). */\n fitBounds: CanvasBounds\n /** Bounding box of the frame itself — use for panning/zoom restriction (clamp). */\n clampBounds: CanvasBounds\n}\n\n/**\n * Extract a frame's descendants and related edges from canvas data.\n * Recursively collects all nested children (supports nested frames).\n * Returns undefined if frameId is not found or is not a frame node.\n *\n * Child nodes are marked `draggable: false` to prevent interfering with canvas panning.\n * The frame node itself is included (use `frameRenderer={() => null}` to hide it).\n *\n * Returns dual bounds:\n * - `fitBounds`: child content bounding box (for centering the viewport)\n * - `clampBounds`: frame area bounding box (for panning restriction)\n */\nexport function getFrameData(\n data: CanvasData,\n frameId: string,\n): FrameData | undefined {\n const frame = data.nodes.find((n) => n.id === frameId)\n if (!frame || frame.type !== 'frame') return undefined\n\n // Recursively collect frame + all descendants\n const descendantIds = collectDescendants(data.nodes, frameId)\n const childNodes = data.nodes\n .filter((n) => descendantIds.has(n.id))\n .map((n) => ({ ...n, draggable: false }))\n\n // Keep only edges where both source and target are within the frame\n const childEdges = data.edges.filter(\n (e: CanvasEdge) => descendantIds.has(e.source) && descendantIds.has(e.target),\n )\n\n // clampBounds: frame's own bounding box (for panning restriction)\n const frameBounds = getNodeBounds(data.nodes, [frameId])\n const { width: w, height: h } = getNodeSize(frame)\n const clampBounds: CanvasBounds = frameBounds ?? {\n x: frame.position.x,\n y: frame.position.y,\n width: w,\n height: h,\n }\n\n // fitBounds: child content bounding box (for centering)\n const contentNodeIds = childNodes\n .filter((n) => n.id !== frameId)\n .map((n) => n.id)\n const contentBounds = contentNodeIds.length > 0\n ? getNodeBounds(data.nodes, contentNodeIds)\n : undefined\n const fitBounds = contentBounds ?? clampBounds\n\n return {\n data: {\n nodes: childNodes,\n edges: childEdges,\n viewport: data.viewport,\n },\n fitBounds,\n clampBounds,\n }\n}\n","export type CanvasStyle = object\n\n// ── Dynamic node data ──\n\nexport interface DynamicNodeData {\n nodeTypeSlug: string\n label: string\n fields: Record<string, unknown>\n}\n\nexport const CANVAS_SHAPE_TYPES = [\n 'terminator',\n 'process',\n 'decision',\n 'io',\n 'subprocess',\n] as const\n\nexport type ShapeType = (typeof CANVAS_SHAPE_TYPES)[number]\n\nexport interface ShapeNodeData {\n shape: ShapeType\n label: string\n color?: string\n}\n\nexport type CanvasNodeType = 'dynamic' | 'frame' | 'shape'\n\nexport type CanvasNodeData = (DynamicNodeData | FrameNodeData | ShapeNodeData) &\n Record<string, unknown>\n\n// ── Canvas types (mirrors @xyflow/react but standalone) ──\n\nexport interface CanvasNodePosition {\n x: number\n y: number\n}\n\nexport interface CanvasNode {\n id: string\n type: CanvasNodeType\n position: CanvasNodePosition\n data: CanvasNodeData\n parentId?: string\n style?: CanvasStyle\n width?: number\n height?: number\n measured?: { width?: number; height?: number }\n draggable?: boolean\n selectable?: boolean\n [key: string]: unknown\n}\n\nexport interface CanvasEdge {\n id: string\n source: string\n target: string\n sourceHandle?: string | null\n targetHandle?: string | null\n type?: string\n style?: CanvasStyle\n animated?: boolean\n markerStart?: unknown\n markerEnd?: unknown\n edgeTypeSlug?: string\n fields?: Record<string, unknown>\n [key: string]: unknown\n}\n\nexport interface CanvasViewport {\n x: number\n y: number\n zoom: number\n}\n\nexport interface CanvasData {\n nodes: CanvasNode[]\n edges: CanvasEdge[]\n viewport: CanvasViewport\n}\n\n// ── Node type definitions (mirrors console's NodeTypeDef) ──\n\nexport interface NodeTypeFieldDef {\n name: string\n label: string\n fieldType:\n | 'text'\n | 'textarea'\n | 'number'\n | 'url'\n | 'color'\n | 'image'\n | 'date'\n | 'select'\n | 'toggle'\n options?: { label: string; value: string }[]\n defaultValue?: string\n required?: boolean\n}\n\nexport interface NodeTypeDef {\n slug: string\n name: string\n color: string\n defaultSize: { width: number; height: number }\n fields: NodeTypeFieldDef[]\n editorIntent?: 'text' | 'image'\n transparentBackground?: boolean\n template?: string | null\n customCSS?: string | null\n}\n\n// ── Edge type definitions (mirrors console's EdgeTypeDef) ──\n\nexport interface EdgeTypeDef {\n slug: string\n name: string\n color: string\n strokeWidth: number\n animated: boolean\n lineStyle: string\n markerStart: string\n markerEnd: string\n fields: NodeTypeFieldDef[]\n isDefaultEdge?: boolean\n}\n\n// ── Type guards ──\n\nexport function isDynamicNode(\n node: CanvasNode,\n): node is CanvasNode & { data: DynamicNodeData } {\n return node.type === 'dynamic'\n}\n\nexport function isFrameNode(\n node: CanvasNode,\n): node is CanvasNode & { data: FrameNodeData } {\n return node.type === 'frame'\n}\n\nexport function isShapeNode(\n node: CanvasNode,\n): node is CanvasNode & { data: ShapeNodeData } {\n return node.type === 'shape'\n}\n\n// ── Component slot props ──\n\nexport interface DynamicNodeSlotProps {\n id: string\n nodeTypeSlug: string\n label: string\n fields: Record<string, unknown>\n nodeTypeDef?: NodeTypeDef\n /** Whether this node is currently selected */\n selected?: boolean\n /** Measured node width (undefined before first measurement) */\n width?: number\n /** Measured node height (undefined before first measurement) */\n height?: number\n /** The default rendering (template or field-based). Allows custom renderers to wrap/extend instead of replacing entirely. */\n defaultRender?: unknown\n}\n\n// ── Frame node data ──\n\nexport interface FrameNodeData {\n label: string\n color?: string\n padding?: number\n borderStyle?: 'dashed' | 'solid' | 'none'\n opacity?: number\n}\n\n// S1: Frame renderer slot\nexport interface FrameNodeSlotProps {\n id: string\n label: string\n color?: string\n padding?: number\n borderStyle?: 'dashed' | 'solid' | 'none'\n opacity?: number\n width?: number\n height?: number\n children?: unknown\n}\n\n// S2: Edge renderer slot\nexport interface EdgeSlotProps {\n id: string\n edgeTypeSlug?: string\n source: string\n target: string\n label?: string\n fields?: Record<string, unknown>\n edgeTypeDef?: EdgeTypeDef\n style?: CanvasStyle\n}\n\n// S3: Node wrapper slot\nexport interface NodeWrapperSlotProps {\n id: string\n nodeTypeSlug: string\n label: string\n selected?: boolean\n nodeTypeDef?: NodeTypeDef\n children: unknown\n}\n\n// S8: Viewport focus\nexport interface CanvasBounds {\n x: number\n y: number\n width: number\n height: number\n}\n","import postcss from 'postcss'\n\nconst ALLOWED_AT_RULES = new Set([\n 'keyframes',\n 'media',\n 'supports',\n 'container',\n 'layer',\n])\nconst BLOCKED_VALUE_PATTERNS = [\n /url\\s*\\(/i,\n /expression\\s*\\(/i,\n /paint\\s*\\(/i,\n /-moz-binding/i,\n]\nconst DANGEROUS_SELECTOR = /(?:^|[\\s,])(?::root|html|body)\\b/\nconst BLOCKED_PROPERTY_VALUES: Record<string, RegExp> = {\n // absolute is allowed — contained by nearest positioned ancestor (node wrapper)\n position: /fixed|sticky/i,\n}\nconst TENANT_CSS_BLOCKED_PATTERNS = [\n { pattern: /<\\/style/i, label: '</style' },\n { pattern: /<script/i, label: '<script' },\n { pattern: /@import/i, label: '@import' },\n { pattern: /url\\s*\\(/i, label: 'url(' },\n] as const\nconst Z_INDEX_MAX = 9998\n\ntype SanitizeCSSStats = {\n css: string\n removedRules: number\n removedDecls: number\n removedAtRules: number\n}\n\nfunction normalizeCSSEscapes(value: string): string {\n return value.replace(\n /\\\\(?:([0-9a-fA-F]{1,6})(?:\\r\\n|[ \\t\\r\\n\\f])?|([^\\r\\n\\f]))/g,\n (match, hex: string | undefined, escaped: string | undefined) => {\n if (hex) {\n const cp = parseInt(hex, 16)\n if (Number.isNaN(cp) || cp > 0x10ffff) return match\n try {\n return String.fromCodePoint(cp)\n } catch {\n return match\n }\n }\n return escaped ?? match\n },\n )\n}\n\n/**\n * Sanitize tenant-authored CSS using PostCSS AST parsing.\n *\n * - At-rules: allowlist only (@keyframes, @media, @supports, @container, @layer)\n * - Declarations: blocks url(), expression(), paint(), -moz-binding\n * - Selectors: removes :root/html/body targets; scopes remainder under scopeClass\n *\n * Returns empty string on parse failure (fail-closed).\n *\n * @param css - Raw CSS string from tenant\n * @param scopeClass - Container class to scope selectors (e.g. 'flow-node--my-type')\n */\nexport function sanitizeCSS(css: string, scopeClass?: string): string {\n return sanitizeCSSWithStats(css, scopeClass).css\n}\n\nfunction sanitizeCSSWithStats(\n css: string,\n scopeClass?: string,\n): SanitizeCSSStats {\n let root: postcss.Root\n try {\n root = postcss.parse(css)\n } catch {\n return { css: '', removedRules: 0, removedDecls: 0, removedAtRules: 0 }\n }\n const stats: SanitizeCSSStats = {\n css: '',\n removedRules: 0,\n removedDecls: 0,\n removedAtRules: 0,\n }\n\n // Compute safe scope class once (strip CSS metacharacters and whitespace)\n const safeScopeClass = scopeClass\n ? scopeClass.replace(/[{}()[\\];,'\"\\\\<>\\s]/g, '')\n : ''\n\n // @keyframes name scoping (skip when no scope)\n const keyframeNameMap = new Map<string, string>()\n if (safeScopeClass) {\n root.walkAtRules(/^keyframes$/i, (node) => {\n // Strip surrounding quotes; replace spaces with underscores for valid identifier output\n // e.g. @keyframes \"fade in\" → flow-node--slug__fade_in\n const rawName = node.params.trim().replace(/^['\"]|['\"]$/g, '')\n const originalName = rawName // keep spaces for map key lookup\n const scopedName = `${safeScopeClass}__${rawName.replace(/\\s+/g, '_')}`\n keyframeNameMap.set(originalName, scopedName)\n node.params = scopedName\n })\n }\n\n // Rewrite animation-name / animation shorthand to use scoped keyframe names\n if (safeScopeClass && keyframeNameMap.size > 0) {\n const ANIM_KEYWORDS = new Set([\n 'none',\n 'initial',\n 'inherit',\n 'unset',\n 'revert',\n ])\n // Strip quotes from token before map lookup (animation-name: 'fade' → look up 'fade')\n const replaceAnimName = (token: string) => {\n const t = token.trim().replace(/^['\"]|['\"]$/g, '')\n return ANIM_KEYWORDS.has(t) ? token : (keyframeNameMap.get(t) ?? token)\n }\n // Use regex filter for case-insensitive match (Animation:, ANIMATION-NAME:)\n root.walkDecls(/^animation-name$/i, (node) => {\n node.value = node.value.split(',').map(replaceAnimName).join(', ')\n })\n root.walkDecls(/^animation$/i, (node) => {\n node.value = node.value\n .split(',')\n .map((anim) =>\n anim\n .split(/(\\s+)/)\n .map((token) => (/\\s/.test(token) ? token : replaceAnimName(token)))\n .join(''),\n )\n .join(',')\n })\n }\n\n // At-rules: remove anything not in the allowlist\n root.walkAtRules((node) => {\n if (!ALLOWED_AT_RULES.has(node.name.toLowerCase())) {\n stats.removedAtRules++\n node.remove()\n }\n })\n\n // Declarations: remove dangerous values\n root.walkDecls((node) => {\n // Normalize value: strip CSS block comments so fi/**/xed → fixed (browser behaviour)\n const normalizedValue = normalizeCSSEscapes(\n node.value.replace(/\\/\\*[\\s\\S]*?\\*\\//g, ''),\n )\n if (BLOCKED_VALUE_PATTERNS.some((p) => p.test(normalizedValue))) {\n stats.removedDecls++\n node.remove()\n return\n }\n // Block dangerous property+value combinations (position: fixed/sticky, oversized z-index)\n const propLower = node.prop.toLowerCase()\n const blockedValuePattern = BLOCKED_PROPERTY_VALUES[propLower]\n if (blockedValuePattern) {\n if (blockedValuePattern.test(normalizedValue)) {\n stats.removedDecls++\n node.remove()\n return\n }\n }\n if (propLower === 'z-index') {\n const zv = node.value.trim()\n // Allow CSS-wide keywords and 'auto'\n if (\n zv === 'auto' ||\n zv === 'initial' ||\n zv === 'inherit' ||\n zv === 'unset' ||\n zv === 'revert'\n ) {\n // pass through\n } else {\n // Only allow plain integers ≤ Z_INDEX_MAX. calc(), var(), and non-integers are removed.\n const val = parseInt(zv, 10)\n if (isNaN(val) || String(val) !== zv || val > Z_INDEX_MAX) {\n stats.removedDecls++\n node.remove()\n return\n }\n }\n }\n })\n\n // Rules: strip dangerous selectors, then scope the rest\n root.walkRules((rule) => {\n const safeSelectors = rule.selectors.filter(\n (s) => s.trim() && !DANGEROUS_SELECTOR.test(s),\n )\n if (!safeSelectors.length) {\n stats.removedRules++\n rule.remove()\n return\n }\n if (safeSelectors.length !== rule.selectors.length) {\n stats.removedRules++\n }\n rule.selectors = safeSelectors\n if (scopeClass) {\n if (!safeScopeClass) {\n stats.removedRules++\n rule.remove()\n return\n }\n // Skip scoping inside @keyframes — from/to/0% are not element selectors\n const parent = rule.parent\n if (\n parent?.type === 'atrule' &&\n (parent as postcss.AtRule).name.toLowerCase() === 'keyframes'\n ) {\n return\n }\n rule.selectors = rule.selectors.map((sel) => `.${safeScopeClass} ${sel}`)\n }\n })\n\n // Post-process: escape </style to prevent HTML parser closing the style tag in SSR contexts.\n // postcss parses this as valid CSS string content and reproduces it verbatim.\n stats.css = root.toString().replace(/<\\/style/gi, '<\\\\/style')\n return stats\n}\n\nexport function validateTenantCSS(css: string): string | null {\n const trimmed = css.trim()\n if (!trimmed) return null\n\n const deescapedCSS = normalizeCSSEscapes(css)\n for (const { pattern, label } of TENANT_CSS_BLOCKED_PATTERNS) {\n if (pattern.test(deescapedCSS)) {\n return `CSS contains blocked pattern: ${label}`\n }\n }\n\n try {\n postcss.parse(css)\n } catch {\n return 'CSS parse error'\n }\n\n const sanitized = sanitizeCSSWithStats(\n css,\n 'flow-node--tenant-css-validation',\n )\n if (!sanitized.css.trim()) {\n return 'CSS must contain safe rules'\n }\n if (\n sanitized.removedRules > 0 ||\n sanitized.removedDecls > 0 ||\n sanitized.removedAtRules > 0\n ) {\n return 'CSS contains unsafe rules or selectors'\n }\n\n return null\n}\n","/**\n * Pure Canvas template policy shared by the browser materializer and\n * server-side save validation. Keep this module free of React, QuickJS, and\n * browser-only imports so `@01.software/sdk/ui/canvas/server` can expose the\n * exact runtime boundary without loading the renderer.\n */\n\n// `iframe` is intentionally excluded: allowing scripts plus same-origin would\n// let embedded code reach the parent document. Third-party embeds need a\n// separate renderer and origin boundary rather than this template allowlist.\nexport const ALLOWED_TEMPLATE_ELEMENTS = [\n 'div',\n 'span',\n 'p',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'ul',\n 'ol',\n 'li',\n 'strong',\n 'em',\n 'b',\n 'i',\n 'br',\n 'img',\n 'figure',\n 'section',\n 'table',\n 'thead',\n 'tbody',\n 'tr',\n 'th',\n 'td',\n 'pre',\n 'code',\n 'svg',\n 'path',\n 'g',\n 'circle',\n 'rect',\n] as const\n\nconst ALLOWED_TEMPLATE_ELEMENT_SET = new Set<string>(ALLOWED_TEMPLATE_ELEMENTS)\n\nexport function isAllowedTemplateElement(value: string): boolean {\n return ALLOWED_TEMPLATE_ELEMENT_SET.has(value.toLowerCase())\n}\n\nexport const TEMPLATE_BLOCKED_PATTERNS: ReadonlyArray<{\n pattern: RegExp\n hint: string\n}> = [\n { pattern: /\\bdocument\\s*\\./, hint: 'document API' },\n { pattern: /\\bwindow\\s*\\./, hint: 'window API' },\n { pattern: /\\bwindow\\s*\\[/, hint: 'window indexer' },\n { pattern: /\\bglobalThis\\s*\\./, hint: 'globalThis' },\n { pattern: /\\bfetch\\s*\\(/, hint: 'fetch()' },\n { pattern: /\\bXMLHttpRequest/, hint: 'XMLHttpRequest' },\n { pattern: /\\beval\\s*\\(/, hint: 'eval()' },\n { pattern: /\\bFunction\\s*\\(/, hint: 'Function constructor' },\n { pattern: /\\bimport\\s*\\(/, hint: 'dynamic import()' },\n { pattern: /\\blocalStorage/, hint: 'localStorage' },\n { pattern: /\\bsessionStorage/, hint: 'sessionStorage' },\n { pattern: /\\bcookie/, hint: 'cookie access' },\n { pattern: /\\bpostMessage\\s*\\(/, hint: 'postMessage()' },\n { pattern: /\\blocation\\s*[.=]/, hint: 'location API' },\n { pattern: /\\bnavigator\\s*\\./, hint: 'navigator API' },\n { pattern: /\\bsetTimeout\\s*\\(/, hint: 'setTimeout()' },\n { pattern: /\\bsetInterval\\s*\\(/, hint: 'setInterval()' },\n { pattern: /\\bsetImmediate\\s*\\(/, hint: 'setImmediate()' },\n { pattern: /\\brequire\\s*\\(/, hint: 'require()' },\n { pattern: /\\bself\\b/, hint: 'self alias' },\n {\n pattern: /\\bconstructor\\s*\\.\\s*constructor/,\n hint: 'constructor.constructor escape',\n },\n { pattern: /\\bReflect\\b/, hint: 'Reflect' },\n { pattern: /\\bProxy\\b/, hint: 'Proxy' },\n { pattern: /\\b__proto__\\b/, hint: '__proto__' },\n]\n\nexport function findBlockedTemplatePattern(\n code: string,\n): { hint: string } | null {\n for (const { pattern, hint } of TEMPLATE_BLOCKED_PATTERNS) {\n if (pattern.test(code)) return { hint }\n }\n return null\n}\n\nconst SAFE_DATA_URI_PREFIXES = [\n 'data:image/png;',\n 'data:image/jpeg;',\n 'data:image/gif;',\n 'data:image/webp;',\n 'data:image/avif;',\n]\n\nexport function isSafeTemplateUrl(value: unknown): boolean {\n if (typeof value !== 'string') return false\n if (\n value.startsWith('/') ||\n value.startsWith('./') ||\n value.startsWith('../') ||\n value.startsWith('#')\n ) {\n return true\n }\n if (SAFE_DATA_URI_PREFIXES.some((prefix) => value.startsWith(prefix))) {\n return true\n }\n try {\n const url = new URL(value)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\nconst URL_PROPS = new Set([\n 'src',\n 'href',\n 'xlinkhref',\n 'xlink:href',\n // Obsolete but still implemented URL-bearing attributes on allowed elements.\n 'background',\n 'longdesc',\n 'usemap',\n])\n\nexport function isTemplateUrlProp(name: string): boolean {\n return URL_PROPS.has(name.toLowerCase())\n}\n\nconst SRCSET_PROPS = new Set([\n 'srcset',\n // React spells the preload attribute `imageSrcSet`; keep it guarded if the\n // element allowlist ever expands to include `link`.\n 'imagesrcset',\n])\n\nexport function isTemplateSrcSetProp(name: string): boolean {\n return SRCSET_PROPS.has(name.toLowerCase())\n}\n\nexport function isSafeTemplateSrcSet(value: unknown): boolean {\n if (typeof value !== 'string' || value.trim() === '') return false\n\n return value.split(',').every((candidate) => {\n const parts = candidate.trim().split(/\\s+/)\n if (\n parts.length === 0 ||\n parts.length > 2 ||\n !isSafeTemplateUrl(parts[0])\n ) {\n return false\n }\n\n const descriptor = parts[1]\n if (descriptor === undefined) return true\n if (/^[1-9]\\d*w$/.test(descriptor)) return true\n if (/^(?:\\d+(?:\\.\\d+)?|\\.\\d+)x$/.test(descriptor)) {\n return Number.parseFloat(descriptor) > 0\n }\n return false\n })\n}\n\n// Keep the raw-spelling url() check shared with style and SVG presentation\n// attributes. CSS-escaped spellings are a known pre-existing gap that should\n// be hardened across both boundaries together.\nconst URL_FUNCTION_PATTERN = /url\\s*\\(/i\n\nconst SVG_URL_FUNCTION_PROPS = new Set([\n 'clip-path',\n 'clippath',\n 'color-profile',\n 'colorprofile',\n 'cursor',\n 'fill',\n 'filter',\n 'marker',\n 'marker-end',\n 'marker-mid',\n 'marker-start',\n 'markerend',\n 'markermid',\n 'markerstart',\n 'mask',\n 'stroke',\n])\n\nexport function isTemplatePaintProp(name: string): boolean {\n return SVG_URL_FUNCTION_PROPS.has(name.toLowerCase())\n}\n\nexport function isSafeTemplatePaintValue(value: unknown): boolean {\n if (typeof value === 'string') return !URL_FUNCTION_PATTERN.test(value)\n if (!Array.isArray(value) || value.length === 0) return false\n\n // React stringifies array-valued attributes. Require every member to be a\n // safe string, and reject nested arrays/objects instead of relying on that\n // coercion behavior.\n return value.every(\n (candidate) =>\n typeof candidate === 'string' && !URL_FUNCTION_PATTERN.test(candidate),\n )\n}\n\nconst BLOCKED_STYLE_PROPS = new Set([\n 'backgroundImage',\n 'background',\n 'listStyleImage',\n 'content',\n 'borderImage',\n 'borderImageSource',\n 'maskImage',\n 'mask',\n 'filter',\n 'cursor',\n])\n\nexport function sanitizeTemplateStyle(style: unknown): Record<string, unknown> {\n if (!style || typeof style !== 'object' || Array.isArray(style)) return {}\n const safe: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(style as Record<string, unknown>)) {\n if (BLOCKED_STYLE_PROPS.has(key)) continue\n if (typeof value === 'string' && URL_FUNCTION_PATTERN.test(value)) continue\n if (\n key === 'position' &&\n typeof value === 'string' &&\n /fixed|sticky/i.test(value)\n ) {\n continue\n }\n if (key === 'zIndex') {\n const numericValue =\n typeof value === 'number' ? value : parseInt(String(value), 10)\n if (isNaN(numericValue) || numericValue > 9998) continue\n }\n safe[key] = value\n }\n return safe\n}\n\nexport function sanitizeTemplateProps(\n props: Record<string, unknown>,\n): Record<string, unknown> {\n const safeProps: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(props)) {\n const normalizedKey = key.toLowerCase()\n if (normalizedKey.startsWith('on')) continue\n if (key === 'dangerouslySetInnerHTML' || key === 'ref') continue\n if (isTemplateUrlProp(key) && !isSafeTemplateUrl(value)) continue\n if (isTemplateSrcSetProp(key) && !isSafeTemplateSrcSet(value)) continue\n if (isTemplatePaintProp(key) && !isSafeTemplatePaintValue(value)) {\n continue\n }\n if (normalizedKey === 'style') {\n safeProps.style = sanitizeTemplateStyle(value)\n continue\n }\n safeProps[key] = value\n }\n return safeProps\n}\n","import type { EdgeTypeDef, NodeTypeDef } from './types'\n\nexport function toNodeTypeDef(doc: Record<string, unknown>): NodeTypeDef {\n return {\n slug: String(doc.slug ?? ''),\n name: String(doc.title ?? ''),\n color: String(doc.color ?? '#e5e7eb'),\n defaultSize: (doc.defaultSize as NodeTypeDef['defaultSize']) ?? {\n width: 200,\n height: 200,\n },\n fields: Array.isArray(doc.fields)\n ? (doc.fields as NodeTypeDef['fields'])\n : [],\n editorIntent:\n doc.editorIntent === 'text' || doc.editorIntent === 'image'\n ? doc.editorIntent\n : undefined,\n transparentBackground: Boolean(\n doc.hasTransparentBackground ?? doc.transparentBackground,\n ),\n template: (doc.template as string) ?? null,\n customCSS: (doc.customCSS as string) ?? null,\n }\n}\n\nexport function toEdgeTypeDef(doc: Record<string, unknown>): EdgeTypeDef {\n return {\n slug: String(doc.slug ?? ''),\n name: String(doc.title ?? ''),\n color: String(doc.color ?? ''),\n strokeWidth: (doc.strokeWidth as number) ?? 2,\n animated:\n (doc.isAnimated as boolean | undefined) ??\n (doc.animated as boolean | undefined) ??\n false,\n lineStyle: String(doc.lineStyle ?? 'default'),\n markerStart: String(doc.markerStart ?? 'none'),\n markerEnd: String(doc.markerEnd ?? 'arrow'),\n fields: Array.isArray(doc.fields)\n ? (doc.fields as EdgeTypeDef['fields'])\n : [],\n isDefaultEdge: Boolean(doc.isDefaultEdge),\n }\n}\n"],"mappings":"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","names":["postcss"]}
|
|
@@ -1 +1,27 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { C as CANVAS_SHAPE_TYPES, a as CanvasBounds, b as CanvasData, c as CanvasEdge, d as CanvasNode, e as CanvasNodeData, f as CanvasNodePosition, g as CanvasNodeType, h as CanvasViewport, D as DynamicNodeData, E as EdgeTypeDef, F as FrameData, i as FrameNodeData, N as NodeTypeDef, j as NodeTypeFieldDef, P as PrefetchCanvasOptions, S as ShapeNodeData, k as ShapeType, l as getFrameData, m as getFrames, n as getNodeBounds, o as isDynamicNode, p as isFrameNode, q as isShapeNode, r as prefetchCanvas, s as sanitizeCSS, t as toEdgeTypeDef, u as toNodeTypeDef, v as validateTenantCSS } from '../../catalog-mappers-sIshKNb3.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure Canvas template policy shared by the browser materializer and
|
|
5
|
+
* server-side save validation. Keep this module free of React, QuickJS, and
|
|
6
|
+
* browser-only imports so `@01.software/sdk/ui/canvas/server` can expose the
|
|
7
|
+
* exact runtime boundary without loading the renderer.
|
|
8
|
+
*/
|
|
9
|
+
declare const ALLOWED_TEMPLATE_ELEMENTS: readonly ["div", "span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "strong", "em", "b", "i", "br", "img", "figure", "section", "table", "thead", "tbody", "tr", "th", "td", "pre", "code", "svg", "path", "g", "circle", "rect"];
|
|
10
|
+
declare function isAllowedTemplateElement(value: string): boolean;
|
|
11
|
+
declare const TEMPLATE_BLOCKED_PATTERNS: ReadonlyArray<{
|
|
12
|
+
pattern: RegExp;
|
|
13
|
+
hint: string;
|
|
14
|
+
}>;
|
|
15
|
+
declare function findBlockedTemplatePattern(code: string): {
|
|
16
|
+
hint: string;
|
|
17
|
+
} | null;
|
|
18
|
+
declare function isSafeTemplateUrl(value: unknown): boolean;
|
|
19
|
+
declare function isTemplateUrlProp(name: string): boolean;
|
|
20
|
+
declare function isTemplateSrcSetProp(name: string): boolean;
|
|
21
|
+
declare function isSafeTemplateSrcSet(value: unknown): boolean;
|
|
22
|
+
declare function isTemplatePaintProp(name: string): boolean;
|
|
23
|
+
declare function isSafeTemplatePaintValue(value: unknown): boolean;
|
|
24
|
+
declare function sanitizeTemplateStyle(style: unknown): Record<string, unknown>;
|
|
25
|
+
declare function sanitizeTemplateProps(props: Record<string, unknown>): Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
export { ALLOWED_TEMPLATE_ELEMENTS, TEMPLATE_BLOCKED_PATTERNS, findBlockedTemplatePattern, isAllowedTemplateElement, isSafeTemplatePaintValue, isSafeTemplateSrcSet, isSafeTemplateUrl, isTemplatePaintProp, isTemplateSrcSetProp, isTemplateUrlProp, sanitizeTemplateProps, sanitizeTemplateStyle };
|
|
@@ -1 +1,27 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { C as CANVAS_SHAPE_TYPES, a as CanvasBounds, b as CanvasData, c as CanvasEdge, d as CanvasNode, e as CanvasNodeData, f as CanvasNodePosition, g as CanvasNodeType, h as CanvasViewport, D as DynamicNodeData, E as EdgeTypeDef, F as FrameData, i as FrameNodeData, N as NodeTypeDef, j as NodeTypeFieldDef, P as PrefetchCanvasOptions, S as ShapeNodeData, k as ShapeType, l as getFrameData, m as getFrames, n as getNodeBounds, o as isDynamicNode, p as isFrameNode, q as isShapeNode, r as prefetchCanvas, s as sanitizeCSS, t as toEdgeTypeDef, u as toNodeTypeDef, v as validateTenantCSS } from '../../catalog-mappers-sIshKNb3.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure Canvas template policy shared by the browser materializer and
|
|
5
|
+
* server-side save validation. Keep this module free of React, QuickJS, and
|
|
6
|
+
* browser-only imports so `@01.software/sdk/ui/canvas/server` can expose the
|
|
7
|
+
* exact runtime boundary without loading the renderer.
|
|
8
|
+
*/
|
|
9
|
+
declare const ALLOWED_TEMPLATE_ELEMENTS: readonly ["div", "span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "strong", "em", "b", "i", "br", "img", "figure", "section", "table", "thead", "tbody", "tr", "th", "td", "pre", "code", "svg", "path", "g", "circle", "rect"];
|
|
10
|
+
declare function isAllowedTemplateElement(value: string): boolean;
|
|
11
|
+
declare const TEMPLATE_BLOCKED_PATTERNS: ReadonlyArray<{
|
|
12
|
+
pattern: RegExp;
|
|
13
|
+
hint: string;
|
|
14
|
+
}>;
|
|
15
|
+
declare function findBlockedTemplatePattern(code: string): {
|
|
16
|
+
hint: string;
|
|
17
|
+
} | null;
|
|
18
|
+
declare function isSafeTemplateUrl(value: unknown): boolean;
|
|
19
|
+
declare function isTemplateUrlProp(name: string): boolean;
|
|
20
|
+
declare function isTemplateSrcSetProp(name: string): boolean;
|
|
21
|
+
declare function isSafeTemplateSrcSet(value: unknown): boolean;
|
|
22
|
+
declare function isTemplatePaintProp(name: string): boolean;
|
|
23
|
+
declare function isSafeTemplatePaintValue(value: unknown): boolean;
|
|
24
|
+
declare function sanitizeTemplateStyle(style: unknown): Record<string, unknown>;
|
|
25
|
+
declare function sanitizeTemplateProps(props: Record<string, unknown>): Record<string, unknown>;
|
|
26
|
+
|
|
27
|
+
export { ALLOWED_TEMPLATE_ELEMENTS, TEMPLATE_BLOCKED_PATTERNS, findBlockedTemplatePattern, isAllowedTemplateElement, isSafeTemplatePaintValue, isSafeTemplateSrcSet, isSafeTemplateUrl, isTemplatePaintProp, isTemplateSrcSetProp, isTemplateUrlProp, sanitizeTemplateProps, sanitizeTemplateStyle };
|