@improba/page-builder 0.2.2 → 0.3.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/dist/core.cjs +1 -1
- package/dist/core.js +1 -1
- package/dist/{index-c6HOrx9r.js → index-DKHnf7j_.js} +49 -49
- package/dist/index-DKHnf7j_.js.map +1 -0
- package/dist/index-DUPqbN77.cjs +2 -0
- package/dist/index-DUPqbN77.cjs.map +1 -0
- package/dist/index.cjs +1657 -1652
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4945 -4994
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/types/built-in/PbColumn.vue.d.ts +9 -0
- package/dist/types/components/editor/IframeCanvas.vue.d.ts +2 -11
- package/dist/types/composables/use-node-tree.d.ts +7 -4
- package/dist/types/composables/use-page-builder.d.ts +4 -4
- package/dist/types/types/node.d.ts +5 -6
- package/package.json +1 -1
- package/dist/index-D79WbFRY.cjs +0 -2
- package/dist/index-D79WbFRY.cjs.map +0 -1
- package/dist/index-c6HOrx9r.js.map +0 -1
package/dist/core.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-DUPqbN77.cjs");exports.PageBuilderError=e.PageBuilderError;exports.cloneTree=e.cloneTree;exports.computeWindowRange=e.computeWindowRange;exports.countNodes=e.countNodes;exports.createNode=e.createNode;exports.createPageBuilderError=e.createPageBuilderError;exports.createStableNodeKey=e.createStableNodeKey;exports.createVirtualTreeIndexMaps=e.createVirtualTreeIndexMaps;exports.extractPlainText=e.extractPlainText;exports.findNodeById=e.findNodeById;exports.findParent=e.findParent;exports.flattenTree=e.flattenTree;exports.getMaxId=e.getMaxId;exports.insertNode=e.insertNode;exports.interpolateProps=e.interpolateProps;exports.isPageBuilderError=e.isPageBuilderError;exports.moveNode=e.moveNode;exports.normalizeSafeHtmlTag=e.normalizeSafeHtmlTag;exports.removeNode=e.removeNode;exports.sanitizeUrlByKind=e.sanitizeUrlByKind;exports.sliceWindow=e.sliceWindow;exports.toErrorMessage=e.toErrorMessage;exports.validateNode=e.validateNode;exports.validatePageData=e.validatePageData;exports.walkTree=e.walkTree;
|
|
2
2
|
//# sourceMappingURL=core.cjs.map
|
package/dist/core.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as r,
|
|
1
|
+
import { P as r, d as s, o, p as t, e as d, c as i, q as l, u as n, w as c, f as g, b as N, k as P, g as m, h as u, i as f, x as p, m as x, n as T, j as v, a as B, y as w, t as y, l as E, v as z, z as I } from "./index-DKHnf7j_.js";
|
|
2
2
|
export {
|
|
3
3
|
r as PageBuilderError,
|
|
4
4
|
s as cloneTree,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
var E = Object.defineProperty;
|
|
2
2
|
var z = (r, t, e) => t in r ? E(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
|
|
3
|
-
var
|
|
3
|
+
var b = (r, t, e) => z(r, typeof t != "symbol" ? t + "" : t, e);
|
|
4
4
|
class S extends Error {
|
|
5
5
|
constructor(e, n, s = {}) {
|
|
6
6
|
super(n);
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
b(this, "code");
|
|
8
|
+
b(this, "details");
|
|
9
9
|
this.name = "PageBuilderError", this.code = e, this.details = s.details ?? {}, this.cause = s.cause;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -20,7 +20,7 @@ function ee(r) {
|
|
|
20
20
|
}
|
|
21
21
|
function te(r, t, e) {
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const v = /* @__PURE__ */ new Set([
|
|
24
24
|
"div",
|
|
25
25
|
"p",
|
|
26
26
|
"span",
|
|
@@ -73,15 +73,15 @@ const I = /* @__PURE__ */ new Set([
|
|
|
73
73
|
"svg",
|
|
74
74
|
"template",
|
|
75
75
|
"textarea"
|
|
76
|
-
]), M = /* @__PURE__ */ new Set(["_blank", "_parent", "_self", "_top"]),
|
|
77
|
-
function
|
|
76
|
+
]), M = /* @__PURE__ */ new Set(["_blank", "_parent", "_self", "_top"]), R = /* @__PURE__ */ new Set(["nofollow", "noopener", "noreferrer", "sponsored", "ugc"]), x = /^data:image\/(?:avif|bmp|gif|jpe?g|png|webp);base64,[a-z0-9+/=\s]+$/i;
|
|
77
|
+
function y(r) {
|
|
78
78
|
return r.replace(/[\u0000-\u001F\u007F]/g, "");
|
|
79
79
|
}
|
|
80
80
|
function D(r) {
|
|
81
|
-
return
|
|
81
|
+
return y(r).replace(/\s+/g, "");
|
|
82
82
|
}
|
|
83
83
|
function P(r) {
|
|
84
|
-
const t = r.toLowerCase().split(/\s+/).filter(Boolean).filter((e) =>
|
|
84
|
+
const t = r.toLowerCase().split(/\s+/).filter(Boolean).filter((e) => R.has(e));
|
|
85
85
|
return Array.from(new Set(t)).join(" ");
|
|
86
86
|
}
|
|
87
87
|
function O(r) {
|
|
@@ -104,7 +104,7 @@ function F(r) {
|
|
|
104
104
|
function V(r, t) {
|
|
105
105
|
const e = t.tagName.toLowerCase();
|
|
106
106
|
for (const n of Array.from(r.attributes)) {
|
|
107
|
-
const s = n.name.toLowerCase(), i =
|
|
107
|
+
const s = n.name.toLowerCase(), i = y(n.value).trim();
|
|
108
108
|
if (!(i.length === 0 || s.startsWith("on"))) {
|
|
109
109
|
if (s === "title") {
|
|
110
110
|
t.setAttribute("title", i);
|
|
@@ -130,7 +130,7 @@ function V(r, t) {
|
|
|
130
130
|
}
|
|
131
131
|
e === "a" && t.getAttribute("target") === "_blank" && t.setAttribute("rel", O(t.getAttribute("rel")));
|
|
132
132
|
}
|
|
133
|
-
function
|
|
133
|
+
function h(r, t, e) {
|
|
134
134
|
for (const n of Array.from(r.childNodes)) {
|
|
135
135
|
if (n.nodeType === 3) {
|
|
136
136
|
t.appendChild(e.createTextNode(n.textContent ?? ""));
|
|
@@ -141,11 +141,11 @@ function y(r, t, e) {
|
|
|
141
141
|
if (L.has(i)) continue;
|
|
142
142
|
if (!$.has(i)) {
|
|
143
143
|
const d = e.createDocumentFragment();
|
|
144
|
-
|
|
144
|
+
h(s, d, e), t.appendChild(d);
|
|
145
145
|
continue;
|
|
146
146
|
}
|
|
147
147
|
const a = e.createElement(i);
|
|
148
|
-
if (V(s, a),
|
|
148
|
+
if (V(s, a), h(s, a, e), i === "a" && !a.getAttribute("href")) {
|
|
149
149
|
const d = e.createDocumentFragment();
|
|
150
150
|
for (; a.firstChild; )
|
|
151
151
|
d.appendChild(a.firstChild);
|
|
@@ -163,21 +163,21 @@ function re(r) {
|
|
|
163
163
|
const n = e.createElement("div");
|
|
164
164
|
n.innerHTML = t;
|
|
165
165
|
const s = e.createElement("div");
|
|
166
|
-
return
|
|
166
|
+
return h(n, s, e), s.innerHTML;
|
|
167
167
|
}
|
|
168
168
|
function G(r, t = "div") {
|
|
169
|
-
const e = typeof t == "string" ? t.trim().toLowerCase() : "div", n =
|
|
169
|
+
const e = typeof t == "string" ? t.trim().toLowerCase() : "div", n = v.has(e) ? e : "div";
|
|
170
170
|
if (typeof r != "string") return n;
|
|
171
171
|
const s = r.trim().toLowerCase();
|
|
172
|
-
return
|
|
172
|
+
return v.has(s) ? s : n;
|
|
173
173
|
}
|
|
174
174
|
function f(r, t) {
|
|
175
|
-
const e =
|
|
175
|
+
const e = y(r).trim();
|
|
176
176
|
if (e.length === 0) return "";
|
|
177
177
|
const n = D(e).toLowerCase(), s = n.match(/^([a-z][a-z0-9+.-]*):/i), i = s == null ? void 0 : s[1];
|
|
178
|
-
return !i || i === "http" || i === "https" || t === "link" && (i === "mailto" || i === "tel") || t === "media" && i === "blob" || (t === "media" || t === "background") && i === "data" &&
|
|
178
|
+
return !i || i === "http" || i === "https" || t === "link" && (i === "mailto" || i === "tel") || t === "media" && i === "blob" || (t === "media" || t === "background") && i === "data" && x.test(n) ? e : "";
|
|
179
179
|
}
|
|
180
|
-
const B = /* @__PURE__ */ new Set(["draft", "published", "archived"]),
|
|
180
|
+
const B = /* @__PURE__ */ new Set(["draft", "published", "archived"]), N = 200, w = 5e3;
|
|
181
181
|
function m(r) {
|
|
182
182
|
return typeof r == "object" && r !== null && !Array.isArray(r);
|
|
183
183
|
}
|
|
@@ -238,20 +238,20 @@ function q(r, t, e, n) {
|
|
|
238
238
|
}
|
|
239
239
|
r === "PbSection" && W(t, e, n);
|
|
240
240
|
}
|
|
241
|
-
function
|
|
242
|
-
if (n >
|
|
241
|
+
function A(r, t, e, n = 0) {
|
|
242
|
+
if (n > N) {
|
|
243
243
|
e.depthGuardTriggered || (o(
|
|
244
244
|
e,
|
|
245
245
|
t,
|
|
246
|
-
`Maximum node depth (${String(
|
|
246
|
+
`Maximum node depth (${String(N)}) exceeded during validation.`
|
|
247
247
|
), e.depthGuardTriggered = !0);
|
|
248
248
|
return;
|
|
249
249
|
}
|
|
250
|
-
if (e.visitedNodeCount >=
|
|
250
|
+
if (e.visitedNodeCount >= w) {
|
|
251
251
|
e.sizeGuardTriggered || (o(
|
|
252
252
|
e,
|
|
253
253
|
t,
|
|
254
|
-
`Maximum node count (${String(
|
|
254
|
+
`Maximum node count (${String(w)}) exceeded during validation.`
|
|
255
255
|
), e.sizeGuardTriggered = !0);
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
@@ -271,7 +271,7 @@ function p(r, t, e, n = 0) {
|
|
|
271
271
|
}
|
|
272
272
|
c === void 0 || typeof c == "boolean" || o(e, `${t}.readonly`, "readonly must be a boolean when provided.");
|
|
273
273
|
for (let l = 0; l < u.length && !e.sizeGuardTriggered; l++)
|
|
274
|
-
|
|
274
|
+
A(u[l], `${t}.children[${l}]`, e, n + 1);
|
|
275
275
|
}
|
|
276
276
|
function ne(r, t = "node") {
|
|
277
277
|
const e = {
|
|
@@ -283,7 +283,7 @@ function ne(r, t = "node") {
|
|
|
283
283
|
depthGuardTriggered: !1,
|
|
284
284
|
sizeGuardTriggered: !1
|
|
285
285
|
};
|
|
286
|
-
return
|
|
286
|
+
return A(r, t, e), {
|
|
287
287
|
isValid: e.errors.length === 0,
|
|
288
288
|
errors: e.errors
|
|
289
289
|
};
|
|
@@ -303,12 +303,12 @@ function ie(r) {
|
|
|
303
303
|
isValid: !1,
|
|
304
304
|
errors: t.errors
|
|
305
305
|
};
|
|
306
|
-
const { meta: e,
|
|
306
|
+
const { meta: e, tree: n, contentRootId: s, maxId: i, variables: a } = r;
|
|
307
307
|
if (m(e) ? ((typeof e.id != "string" || e.id.trim() === "") && o(t, "meta.id", "meta.id must be a non-empty string."), (typeof e.name != "string" || e.name.trim() === "") && o(t, "meta.name", "meta.name must be a non-empty string."), typeof e.url != "string" || e.url.trim() === "" ? o(t, "meta.url", "meta.url must be a non-empty string.") : f(e.url, "link") === "" && o(t, "meta.url", "meta.url contains an unsafe URL."), (typeof e.status != "string" || !B.has(e.status)) && o(
|
|
308
308
|
t,
|
|
309
309
|
"meta.status",
|
|
310
310
|
"meta.status must be one of: draft, published, archived."
|
|
311
|
-
), e.updatedAt === void 0 || typeof e.updatedAt == "string" || o(t, "meta.updatedAt", "meta.updatedAt must be a string when provided."), e.createdAt === void 0 || typeof e.createdAt == "string" || o(t, "meta.createdAt", "meta.createdAt must be a string when provided.")) : o(t, "meta", "meta must be an object."),
|
|
311
|
+
), e.updatedAt === void 0 || typeof e.updatedAt == "string" || o(t, "meta.updatedAt", "meta.updatedAt must be a string when provided."), e.createdAt === void 0 || typeof e.createdAt == "string" || o(t, "meta.createdAt", "meta.createdAt must be a string when provided.")) : o(t, "meta", "meta must be an object."), A(n, "tree", t), typeof s == "number" && Number.isInteger(s) && s > 0 || o(t, "contentRootId", "contentRootId must be a positive integer."), !(typeof i == "number" && Number.isInteger(i)) || i < 0 ? o(t, "maxId", "maxId must be a non-negative integer.") : i < t.maxObservedId && o(
|
|
312
312
|
t,
|
|
313
313
|
"maxId",
|
|
314
314
|
`maxId (${String(i)}) must be greater than or equal to the maximum node id (${String(t.maxObservedId)}).`
|
|
@@ -322,7 +322,7 @@ function ie(r) {
|
|
|
322
322
|
errors: t.errors
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
|
-
function
|
|
325
|
+
function p(r) {
|
|
326
326
|
return Array.isArray(r.children) ? r.children : [];
|
|
327
327
|
}
|
|
328
328
|
function k(r, t) {
|
|
@@ -344,22 +344,22 @@ function se(r) {
|
|
|
344
344
|
}
|
|
345
345
|
function C(r, t) {
|
|
346
346
|
if (r.id === t) return r;
|
|
347
|
-
for (const e of
|
|
347
|
+
for (const e of p(r)) {
|
|
348
348
|
const n = C(e, t);
|
|
349
349
|
if (n) return n;
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
|
-
function
|
|
353
|
-
const e =
|
|
352
|
+
function I(r, t) {
|
|
353
|
+
const e = p(r);
|
|
354
354
|
for (let n = 0; n < e.length; n++) {
|
|
355
355
|
if (e[n].id === t)
|
|
356
356
|
return { parent: r, index: n };
|
|
357
|
-
const s =
|
|
357
|
+
const s = I(e[n], t);
|
|
358
358
|
if (s) return s;
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
function ae(r, t) {
|
|
362
|
-
const e =
|
|
362
|
+
const e = I(r, t);
|
|
363
363
|
if (e)
|
|
364
364
|
return e.parent.children.splice(e.index, 1)[0];
|
|
365
365
|
}
|
|
@@ -371,9 +371,9 @@ function X(r, t, e, n, s = "default") {
|
|
|
371
371
|
return i.children.splice(a, 0, d), !0;
|
|
372
372
|
}
|
|
373
373
|
function oe(r, t, e, n, s = "default") {
|
|
374
|
-
const i =
|
|
374
|
+
const i = I(r, t);
|
|
375
375
|
if (!i) return !1;
|
|
376
|
-
const a =
|
|
376
|
+
const a = p(i.parent), [d] = a.splice(i.index, 1);
|
|
377
377
|
if (!d) return !1;
|
|
378
378
|
if (X(r, e, d, n, s)) return !0;
|
|
379
379
|
const c = k(i.index, a.length);
|
|
@@ -389,12 +389,12 @@ function de(r, t, e = {}) {
|
|
|
389
389
|
readonly: e.readonly
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
|
-
function
|
|
392
|
+
function T(r, t, e = 0) {
|
|
393
393
|
const n = /* @__PURE__ */ new WeakSet();
|
|
394
394
|
function s(i, a) {
|
|
395
395
|
if (n.has(i)) return !0;
|
|
396
396
|
if (n.add(i), t(i, a) === !1) return !1;
|
|
397
|
-
for (const d of
|
|
397
|
+
for (const d of p(i))
|
|
398
398
|
if (s(d, a + 1) === !1) return !1;
|
|
399
399
|
return !0;
|
|
400
400
|
}
|
|
@@ -402,13 +402,13 @@ function v(r, t, e = 0) {
|
|
|
402
402
|
}
|
|
403
403
|
function ue(r) {
|
|
404
404
|
let t = 0;
|
|
405
|
-
return
|
|
405
|
+
return T(r, () => {
|
|
406
406
|
t++;
|
|
407
407
|
}), t;
|
|
408
408
|
}
|
|
409
409
|
function ce(r) {
|
|
410
410
|
let t = r.id;
|
|
411
|
-
return
|
|
411
|
+
return T(r, (e) => {
|
|
412
412
|
Number.isFinite(e.id) && e.id > t && (t = e.id);
|
|
413
413
|
}), t;
|
|
414
414
|
}
|
|
@@ -422,7 +422,7 @@ function le(r, t) {
|
|
|
422
422
|
}
|
|
423
423
|
function fe(r) {
|
|
424
424
|
const t = [];
|
|
425
|
-
return
|
|
425
|
+
return T(r, (e) => {
|
|
426
426
|
if (e.props.content && typeof e.props.content == "string") {
|
|
427
427
|
const n = e.props.content.replace(/<[^>]*>/g, "");
|
|
428
428
|
n.trim() && t.push(n.trim());
|
|
@@ -494,17 +494,17 @@ function pe(r) {
|
|
|
494
494
|
export {
|
|
495
495
|
S as P,
|
|
496
496
|
f as a,
|
|
497
|
-
|
|
497
|
+
I as b,
|
|
498
498
|
_ as c,
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
se as d,
|
|
500
|
+
de as e,
|
|
501
501
|
C as f,
|
|
502
502
|
ce as g,
|
|
503
|
-
|
|
503
|
+
X as h,
|
|
504
504
|
le as i,
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
505
|
+
ae as j,
|
|
506
|
+
me as k,
|
|
507
|
+
ne as l,
|
|
508
508
|
oe as m,
|
|
509
509
|
G as n,
|
|
510
510
|
J as o,
|
|
@@ -518,6 +518,6 @@ export {
|
|
|
518
518
|
fe as w,
|
|
519
519
|
Z as x,
|
|
520
520
|
ge as y,
|
|
521
|
-
|
|
521
|
+
T as z
|
|
522
522
|
};
|
|
523
|
-
//# sourceMappingURL=index-
|
|
523
|
+
//# sourceMappingURL=index-DKHnf7j_.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DKHnf7j_.js","sources":["../src/core/errors.ts","../src/core/sanitize.ts","../src/core/validation.ts","../src/core/tree.ts","../src/core/virtual-tree.ts"],"sourcesContent":["export type PageBuilderErrorCode =\n | 'INVALID_PAGE_DATA'\n | 'INVALID_NODE'\n | 'INVALID_SNAPSHOT'\n | 'MISSING_COMPONENT'\n | 'DUPLICATE_COMPONENT'\n | 'RENDER_FAILURE'\n | 'UNKNOWN';\n\nexport interface PageBuilderErrorOptions {\n details?: Record<string, unknown>;\n cause?: unknown;\n}\n\nexport class PageBuilderError extends Error {\n readonly code: PageBuilderErrorCode;\n readonly details: Record<string, unknown>;\n\n constructor(code: PageBuilderErrorCode, message: string, options: PageBuilderErrorOptions = {}) {\n super(message);\n this.name = 'PageBuilderError';\n this.code = code;\n this.details = options.details ?? {};\n this.cause = options.cause;\n }\n}\n\nexport function isPageBuilderError(error: unknown): error is PageBuilderError {\n return error instanceof PageBuilderError;\n}\n\nexport function createPageBuilderError(\n code: PageBuilderErrorCode,\n message: string,\n options: PageBuilderErrorOptions = {},\n): PageBuilderError {\n return new PageBuilderError(code, message, options);\n}\n\nexport function toErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\nexport function reportDevDiagnostic(\n context: string,\n error: unknown,\n details?: Record<string, unknown>,\n): void {\n if (!import.meta.env.DEV) return;\n const normalized = isPageBuilderError(error)\n ? error\n : createPageBuilderError('UNKNOWN', toErrorMessage(error), {\n cause: error,\n details,\n });\n\n const mergedDetails = details\n ? {\n ...normalized.details,\n ...details,\n }\n : normalized.details;\n\n console.error(`[PageBuilder][${context}] ${normalized.message}`, {\n code: normalized.code,\n details: mergedDetails,\n cause: normalized.cause,\n });\n}\n","const SAFE_TEXT_CONTAINER_TAGS = new Set([\n 'div',\n 'p',\n 'span',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'section',\n 'article',\n 'blockquote',\n]);\n\nconst SAFE_RICH_TEXT_TAGS = new Set([\n 'a',\n 'b',\n 'blockquote',\n 'br',\n 'code',\n 'div',\n 'em',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'i',\n 'li',\n 'ol',\n 'p',\n 'pre',\n 's',\n 'span',\n 'strong',\n 'u',\n 'ul',\n]);\n\nconst DROP_ENTIRELY_TAGS = new Set([\n 'base',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'link',\n 'math',\n 'meta',\n 'noscript',\n 'object',\n 'script',\n 'style',\n 'svg',\n 'template',\n 'textarea',\n]);\n\nconst SAFE_LINK_TARGETS = new Set(['_blank', '_parent', '_self', '_top']);\nconst SAFE_LINK_REL_TOKENS = new Set(['nofollow', 'noopener', 'noreferrer', 'sponsored', 'ugc']);\nconst SAFE_DATA_IMAGE_URL_PATTERN = /^data:image\\/(?:avif|bmp|gif|jpe?g|png|webp);base64,[a-z0-9+/=\\s]+$/i;\n\nfunction stripControlCharacters(value: string): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, '');\n}\n\nfunction normalizeProtocolProbe(value: string): string {\n return stripControlCharacters(value).replace(/\\s+/g, '');\n}\n\nfunction sanitizeLinkRel(rel: string): string {\n const safeTokens = rel\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .filter((token) => SAFE_LINK_REL_TOKENS.has(token));\n return Array.from(new Set(safeTokens)).join(' ');\n}\n\nfunction withSafeBlankTargetRel(existingRel: string | null): string {\n const safeRel = sanitizeLinkRel(existingRel ?? '');\n const relTokens = new Set(safeRel.split(/\\s+/).filter(Boolean));\n relTokens.add('noopener');\n relTokens.add('noreferrer');\n return Array.from(relTokens).join(' ');\n}\n\nfunction createSanitizationDocument(): Document | null {\n if (\n typeof document !== 'undefined'\n && typeof document.implementation?.createHTMLDocument === 'function'\n ) {\n return document.implementation.createHTMLDocument('');\n }\n\n if (typeof DOMParser !== 'undefined') {\n const parsed = new DOMParser().parseFromString('<!doctype html><html><body></body></html>', 'text/html');\n if (parsed?.body) return parsed;\n }\n\n return null;\n}\n\nfunction escapeHtml(rawHtml: string): string {\n return rawHtml\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction sanitizeRichTextAttributes(source: Element, target: HTMLElement): void {\n const tagName = target.tagName.toLowerCase();\n\n for (const attribute of Array.from(source.attributes)) {\n const attrName = attribute.name.toLowerCase();\n const attrValue = stripControlCharacters(attribute.value).trim();\n\n if (attrValue.length === 0 || attrName.startsWith('on')) continue;\n\n if (attrName === 'title') {\n target.setAttribute('title', attrValue);\n continue;\n }\n\n if (tagName !== 'a') continue;\n\n if (attrName === 'href') {\n const sanitizedHref = sanitizeUrlByKind(attrValue, 'link');\n if (sanitizedHref.length > 0) target.setAttribute('href', sanitizedHref);\n continue;\n }\n\n if (attrName === 'target') {\n const normalizedTarget = attrValue.toLowerCase();\n if (SAFE_LINK_TARGETS.has(normalizedTarget)) {\n target.setAttribute('target', normalizedTarget);\n }\n continue;\n }\n\n if (attrName === 'rel') {\n const sanitizedRel = sanitizeLinkRel(attrValue);\n if (sanitizedRel.length > 0) target.setAttribute('rel', sanitizedRel);\n }\n }\n\n if (tagName === 'a' && target.getAttribute('target') === '_blank') {\n target.setAttribute('rel', withSafeBlankTargetRel(target.getAttribute('rel')));\n }\n}\n\nfunction sanitizeRichTextChildren(source: ParentNode, target: ParentNode, doc: Document): void {\n for (const node of Array.from(source.childNodes)) {\n if (node.nodeType === 3) {\n target.appendChild(doc.createTextNode(node.textContent ?? ''));\n continue;\n }\n\n if (node.nodeType !== 1) continue;\n\n const sourceElement = node as Element;\n const sourceTagName = sourceElement.tagName.toLowerCase();\n\n if (DROP_ENTIRELY_TAGS.has(sourceTagName)) continue;\n\n if (!SAFE_RICH_TEXT_TAGS.has(sourceTagName)) {\n const unwrappedChildren = doc.createDocumentFragment();\n sanitizeRichTextChildren(sourceElement, unwrappedChildren, doc);\n target.appendChild(unwrappedChildren);\n continue;\n }\n\n const sanitizedElement = doc.createElement(sourceTagName);\n sanitizeRichTextAttributes(sourceElement, sanitizedElement);\n sanitizeRichTextChildren(sourceElement, sanitizedElement, doc);\n\n if (sourceTagName === 'a' && !sanitizedElement.getAttribute('href')) {\n const safeChildren = doc.createDocumentFragment();\n while (sanitizedElement.firstChild) {\n safeChildren.appendChild(sanitizedElement.firstChild);\n }\n target.appendChild(safeChildren);\n continue;\n }\n\n target.appendChild(sanitizedElement);\n }\n}\n\nexport function sanitizeRichTextHtml(html: string): string {\n const rawHtml = typeof html === 'string' ? html : '';\n if (rawHtml.length === 0) return '';\n\n const doc = createSanitizationDocument();\n if (!doc) return escapeHtml(rawHtml);\n\n const source = doc.createElement('div');\n source.innerHTML = rawHtml;\n\n const output = doc.createElement('div');\n sanitizeRichTextChildren(source, output, doc);\n return output.innerHTML;\n}\n\nexport function normalizeSafeHtmlTag(tag: unknown, fallback = 'div'): string {\n const normalizedFallback = typeof fallback === 'string' ? fallback.trim().toLowerCase() : 'div';\n const safeFallback = SAFE_TEXT_CONTAINER_TAGS.has(normalizedFallback) ? normalizedFallback : 'div';\n if (typeof tag !== 'string') return safeFallback;\n\n const normalizedTag = tag.trim().toLowerCase();\n if (!SAFE_TEXT_CONTAINER_TAGS.has(normalizedTag)) return safeFallback;\n return normalizedTag;\n}\n\nexport function sanitizeUrlByKind(url: string, kind: 'link' | 'media' | 'background'): string {\n const sanitizedInput = stripControlCharacters(url).trim();\n if (sanitizedInput.length === 0) return '';\n\n const protocolProbe = normalizeProtocolProbe(sanitizedInput).toLowerCase();\n const schemeMatch = protocolProbe.match(/^([a-z][a-z0-9+.-]*):/i);\n const scheme = schemeMatch?.[1];\n\n if (!scheme) return sanitizedInput;\n\n if (scheme === 'http' || scheme === 'https') return sanitizedInput;\n\n if (kind === 'link' && (scheme === 'mailto' || scheme === 'tel')) {\n return sanitizedInput;\n }\n\n if (kind === 'media' && scheme === 'blob') {\n return sanitizedInput;\n }\n\n if ((kind === 'media' || kind === 'background') && scheme === 'data') {\n return SAFE_DATA_IMAGE_URL_PATTERN.test(protocolProbe) ? sanitizedInput : '';\n }\n\n return '';\n}\n","import type { INode, IPageData, IPageMeta } from '@/types/node';\nimport { normalizeSafeHtmlTag, sanitizeUrlByKind } from '@/core/sanitize';\n\nexport interface IValidationError {\n path: string;\n message: string;\n}\n\nexport interface IValidationResult {\n isValid: boolean;\n errors: IValidationError[];\n}\n\ninterface IValidationContext {\n errors: IValidationError[];\n seenIds: Set<number>;\n seenNodes: WeakSet<object>;\n maxObservedId: number;\n visitedNodeCount: number;\n depthGuardTriggered: boolean;\n sizeGuardTriggered: boolean;\n}\n\nconst PAGE_STATUSES = new Set<IPageMeta['status']>(['draft', 'published', 'archived']);\nconst MAX_VALIDATION_DEPTH = 200;\nconst MAX_VALIDATION_NODE_COUNT = 5000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction addError(context: IValidationContext, path: string, message: string): void {\n context.errors.push({ path, message });\n}\n\nfunction validatePbTextProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (props.tag === undefined) return;\n\n if (typeof props.tag !== 'string' || props.tag.trim() === '') {\n addError(context, `${path}.tag`, 'PbText props.tag must be a non-empty string.');\n return;\n }\n\n const normalizedTag = props.tag.trim().toLowerCase();\n if (normalizeSafeHtmlTag(props.tag) !== normalizedTag) {\n addError(\n context,\n `${path}.tag`,\n 'PbText props.tag must be one of: div, p, span, h1, h2, h3, h4, h5, h6, section, article, blockquote.',\n );\n }\n}\n\nfunction validatePbImageProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (typeof props.src !== 'string' || props.src.trim() === '') {\n addError(context, `${path}.src`, 'PbImage props.src must be a non-empty string.');\n return;\n }\n\n if (sanitizeUrlByKind(props.src, 'media') === '') {\n addError(context, `${path}.src`, 'PbImage props.src contains an unsafe URL.');\n }\n}\n\nfunction validatePbVideoProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (typeof props.src !== 'string' || props.src.trim() === '') {\n addError(context, `${path}.src`, 'PbVideo props.src must be a non-empty string.');\n return;\n }\n\n if (sanitizeUrlByKind(props.src, 'media') === '') {\n addError(context, `${path}.src`, 'PbVideo props.src contains an unsafe URL.');\n }\n\n const poster = props.poster;\n if (poster !== undefined && poster !== null && poster !== '') {\n if (typeof poster !== 'string') {\n addError(context, `${path}.poster`, 'PbVideo props.poster must be a string.');\n } else if (poster.trim() !== '' && sanitizeUrlByKind(poster, 'media') === '') {\n addError(context, `${path}.poster`, 'PbVideo props.poster contains an unsafe URL.');\n }\n }\n}\n\nfunction validatePbSectionProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n const backgroundImage = props.backgroundImage;\n if (backgroundImage === undefined || backgroundImage === null || backgroundImage === '') return;\n\n if (typeof backgroundImage !== 'string') {\n addError(context, `${path}.backgroundImage`, 'PbSection props.backgroundImage must be a string.');\n return;\n }\n\n if (backgroundImage.trim() !== '' && sanitizeUrlByKind(backgroundImage, 'background') === '') {\n addError(context, `${path}.backgroundImage`, 'PbSection props.backgroundImage contains an unsafe URL.');\n }\n}\n\nfunction validateKnownComponentPropsInto(\n name: string,\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (name === 'PbText') {\n validatePbTextProps(props, path, context);\n return;\n }\n\n if (name === 'PbImage') {\n validatePbImageProps(props, path, context);\n return;\n }\n\n if (name === 'PbVideo') {\n validatePbVideoProps(props, path, context);\n return;\n }\n\n if (name === 'PbSection') {\n validatePbSectionProps(props, path, context);\n }\n}\n\nfunction validateNodeInto(node: unknown, path: string, context: IValidationContext, depth = 0): void {\n if (depth > MAX_VALIDATION_DEPTH) {\n if (!context.depthGuardTriggered) {\n addError(\n context,\n path,\n `Maximum node depth (${String(MAX_VALIDATION_DEPTH)}) exceeded during validation.`,\n );\n context.depthGuardTriggered = true;\n }\n return;\n }\n\n if (context.visitedNodeCount >= MAX_VALIDATION_NODE_COUNT) {\n if (!context.sizeGuardTriggered) {\n addError(\n context,\n path,\n `Maximum node count (${String(MAX_VALIDATION_NODE_COUNT)}) exceeded during validation.`,\n );\n context.sizeGuardTriggered = true;\n }\n return;\n }\n\n if (!isRecord(node)) {\n addError(context, path, 'Node must be an object.');\n return;\n }\n\n if (context.seenNodes.has(node)) {\n addError(context, path, 'Cycle detected in node tree.');\n return;\n }\n context.seenNodes.add(node);\n context.visitedNodeCount++;\n\n const id = node.id;\n const name = node.name;\n const slot = node.slot;\n const props = node.props;\n const children = node.children;\n const readonly = node.readonly;\n\n if (!(typeof id === 'number' && Number.isInteger(id)) || id <= 0) {\n addError(context, `${path}.id`, 'id must be a positive integer.');\n } else {\n if (context.seenIds.has(id)) {\n addError(context, `${path}.id`, `Duplicate node id \"${id}\" found.`);\n }\n context.seenIds.add(id);\n if (id > context.maxObservedId) context.maxObservedId = id;\n }\n\n if (typeof name !== 'string' || name.trim() === '') {\n addError(context, `${path}.name`, 'name must be a non-empty string.');\n }\n\n if (!(slot === null || typeof slot === 'string')) {\n addError(context, `${path}.slot`, 'slot must be a string or null.');\n }\n\n if (!isRecord(props)) {\n addError(context, `${path}.props`, 'props must be an object.');\n } else if (typeof name === 'string') {\n validateKnownComponentPropsInto(name, props, `${path}.props`, context);\n }\n\n if (!Array.isArray(children)) {\n addError(context, `${path}.children`, 'children must be an array.');\n return;\n }\n\n if (!(readonly === undefined || typeof readonly === 'boolean')) {\n addError(context, `${path}.readonly`, 'readonly must be a boolean when provided.');\n }\n\n for (let index = 0; index < children.length; index++) {\n if (context.sizeGuardTriggered) break;\n validateNodeInto(children[index], `${path}.children[${index}]`, context, depth + 1);\n }\n}\n\nexport function validateNode(node: unknown, path = 'node'): IValidationResult {\n const context: IValidationContext = {\n errors: [],\n seenIds: new Set<number>(),\n seenNodes: new WeakSet<object>(),\n maxObservedId: 0,\n visitedNodeCount: 0,\n depthGuardTriggered: false,\n sizeGuardTriggered: false,\n };\n\n validateNodeInto(node, path, context);\n\n return {\n isValid: context.errors.length === 0,\n errors: context.errors,\n };\n}\n\nexport function validatePageData(pageData: unknown): IValidationResult {\n const context: IValidationContext = {\n errors: [],\n seenIds: new Set<number>(),\n seenNodes: new WeakSet<object>(),\n maxObservedId: 0,\n visitedNodeCount: 0,\n depthGuardTriggered: false,\n sizeGuardTriggered: false,\n };\n\n if (!isRecord(pageData)) {\n addError(context, 'pageData', 'pageData must be an object.');\n return {\n isValid: false,\n errors: context.errors,\n };\n }\n\n const { meta, tree, contentRootId, maxId, variables } = pageData;\n\n if (!isRecord(meta)) {\n addError(context, 'meta', 'meta must be an object.');\n } else {\n if (typeof meta.id !== 'string' || meta.id.trim() === '') {\n addError(context, 'meta.id', 'meta.id must be a non-empty string.');\n }\n if (typeof meta.name !== 'string' || meta.name.trim() === '') {\n addError(context, 'meta.name', 'meta.name must be a non-empty string.');\n }\n if (typeof meta.url !== 'string' || meta.url.trim() === '') {\n addError(context, 'meta.url', 'meta.url must be a non-empty string.');\n } else if (sanitizeUrlByKind(meta.url, 'link') === '') {\n addError(context, 'meta.url', 'meta.url contains an unsafe URL.');\n }\n if (typeof meta.status !== 'string' || !PAGE_STATUSES.has(meta.status as IPageMeta['status'])) {\n addError(\n context,\n 'meta.status',\n 'meta.status must be one of: draft, published, archived.',\n );\n }\n if (!(meta.updatedAt === undefined || typeof meta.updatedAt === 'string')) {\n addError(context, 'meta.updatedAt', 'meta.updatedAt must be a string when provided.');\n }\n if (!(meta.createdAt === undefined || typeof meta.createdAt === 'string')) {\n addError(context, 'meta.createdAt', 'meta.createdAt must be a string when provided.');\n }\n }\n\n validateNodeInto(tree, 'tree', context);\n\n if (!(typeof contentRootId === 'number' && Number.isInteger(contentRootId) && contentRootId > 0)) {\n addError(context, 'contentRootId', 'contentRootId must be a positive integer.');\n }\n\n if (!(typeof maxId === 'number' && Number.isInteger(maxId)) || maxId < 0) {\n addError(context, 'maxId', 'maxId must be a non-negative integer.');\n } else if (maxId < context.maxObservedId) {\n addError(\n context,\n 'maxId',\n `maxId (${String(maxId)}) must be greater than or equal to the maximum node id (${String(context.maxObservedId)}).`,\n );\n }\n\n if (!isRecord(variables)) {\n addError(context, 'variables', 'variables must be an object.');\n } else {\n for (const [key, value] of Object.entries(variables)) {\n if (typeof value !== 'string') {\n addError(context, `variables.${key}`, 'Variable values must be strings.');\n }\n }\n }\n\n return {\n isValid: context.errors.length === 0,\n errors: context.errors,\n };\n}\n","import type { INode } from '@/types/node';\nimport { createPageBuilderError } from '@/core/errors';\n\nfunction getChildren(node: INode): INode[] {\n return Array.isArray(node.children) ? node.children : [];\n}\n\nfunction clampIndex(index: number, length: number): number {\n const normalized = Number.isFinite(index) ? Math.trunc(index) : 0;\n return Math.max(0, Math.min(normalized, length));\n}\n\n/**\n * Deep clone a node tree using structured clone.\n */\nexport function cloneTree(node: INode): INode {\n try {\n return structuredClone(node);\n } catch (error) {\n throw createPageBuilderError(\n 'INVALID_NODE',\n '[PageBuilder] Failed to clone node tree. Ensure the tree is serializable and acyclic.',\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Find a node by ID in the tree. Returns undefined if not found.\n */\nexport function findNodeById(root: INode, id: number): INode | undefined {\n if (root.id === id) return root;\n for (const child of getChildren(root)) {\n const found = findNodeById(child, id);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Find the parent of a node by the child's ID.\n * Returns the parent node and the child's index, or undefined.\n */\nexport function findParent(\n root: INode,\n childId: number,\n): { parent: INode; index: number } | undefined {\n const children = getChildren(root);\n for (let i = 0; i < children.length; i++) {\n if (children[i].id === childId) {\n return { parent: root, index: i };\n }\n const found = findParent(children[i], childId);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Remove a node by ID from the tree. Returns the removed node or undefined.\n */\nexport function removeNode(root: INode, id: number): INode | undefined {\n const result = findParent(root, id);\n if (!result) return undefined;\n return result.parent.children.splice(result.index, 1)[0];\n}\n\n/**\n * Insert a node as a child of a target node at a specific index and slot.\n */\nexport function insertNode(\n root: INode,\n parentId: number,\n node: INode,\n index: number,\n slot: string = 'default',\n): boolean {\n const parent = findNodeById(root, parentId);\n if (!parent) return false;\n if (!Array.isArray(parent.children)) {\n parent.children = [];\n }\n\n const targetIndex = clampIndex(index, parent.children.length);\n const insertedNode = { ...node, slot };\n parent.children.splice(targetIndex, 0, insertedNode);\n return true;\n}\n\n/**\n * Move a node within the tree to a new parent at a specific index.\n */\nexport function moveNode(\n root: INode,\n nodeId: number,\n newParentId: number,\n index: number,\n slot: string = 'default',\n): boolean {\n const sourceParentResult = findParent(root, nodeId);\n if (!sourceParentResult) return false;\n\n const sourceChildren = getChildren(sourceParentResult.parent);\n const [node] = sourceChildren.splice(sourceParentResult.index, 1);\n if (!node) return false;\n\n const moved = insertNode(root, newParentId, node, index, slot);\n if (moved) return true;\n\n // Roll back on failure so the caller never loses nodes due to invalid moves.\n const rollbackIndex = clampIndex(sourceParentResult.index, sourceChildren.length);\n sourceChildren.splice(rollbackIndex, 0, node);\n return false;\n}\n\n/**\n * Create a new node with default values and a given ID.\n */\nexport function createNode(\n id: number,\n name: string,\n options: Partial<Pick<INode, 'slot' | 'props' | 'children' | 'readonly'>> = {},\n): INode {\n return {\n id,\n name,\n slot: options.slot ?? 'default',\n props: options.props ?? {},\n children: options.children ?? [],\n readonly: options.readonly,\n };\n}\n\n/**\n * Walk the tree depth-first and call visitor for each node.\n * Return `false` from visitor to stop the entire traversal (not just the subtree).\n */\nexport function walkTree(root: INode, visitor: (node: INode, depth: number) => boolean | void, depth = 0): boolean {\n const visited = new WeakSet<object>();\n\n function visitNode(node: INode, currentDepth: number): boolean {\n if (visited.has(node as object)) return true;\n visited.add(node as object);\n\n if (visitor(node, currentDepth) === false) return false;\n for (const child of getChildren(node)) {\n if (visitNode(child, currentDepth + 1) === false) return false;\n }\n return true;\n }\n\n return visitNode(root, depth);\n}\n\n/**\n * Count the total number of nodes in the tree.\n */\nexport function countNodes(root: INode): number {\n let count = 0;\n walkTree(root, () => { count++; });\n return count;\n}\n\n/**\n * Get the maximum ID in the tree.\n */\nexport function getMaxId(root: INode): number {\n let max = root.id;\n walkTree(root, (node) => {\n if (Number.isFinite(node.id) && node.id > max) max = node.id;\n });\n return max;\n}\n\n/**\n * Interpolate template variables in node props.\n * Replaces `{{ VAR }}` patterns with values from the variables map.\n */\nexport function interpolateProps(\n props: Record<string, unknown>,\n variables: Record<string, string>,\n): Record<string, unknown> {\n if (!props || typeof props !== 'object' || Array.isArray(props)) {\n return {};\n }\n\n const safeVariables =\n variables && typeof variables === 'object' && !Array.isArray(variables) ? variables : {};\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string') {\n result[key] = value.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (_, varName: string) => {\n return safeVariables[varName] ?? `{{ ${varName} }}`;\n });\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Extract plain text from a node tree by collecting text content\n * from PbText (and similar) components and stripping HTML tags.\n */\nexport function extractPlainText(node: INode): string {\n const texts: string[] = [];\n walkTree(node, (n) => {\n if (n.props.content && typeof n.props.content === 'string') {\n const stripped = n.props.content.replace(/<[^>]*>/g, '');\n if (stripped.trim()) texts.push(stripped.trim());\n }\n });\n return texts.join(' ').replace(/\\s+/g, ' ').trim();\n}\n","import type { INode } from '@/types/node';\n\nexport interface IVirtualTreeRow {\n node: INode;\n id: number;\n key: string;\n depth: number;\n index: number;\n parentId: number | null;\n}\n\nexport interface IVirtualWindowRange {\n start: number;\n end: number;\n size: number;\n total: number;\n}\n\nexport interface IVirtualTreeIndexMaps {\n keyByIndex: string[];\n indexByKey: Map<string, number>;\n indexByNodeId: Map<number, number>;\n}\n\nexport interface IFlattenTreeOptions {\n createKey?: (node: INode) => string;\n}\n\nfunction toSafeInteger(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.trunc(value);\n}\n\nexport function createStableNodeKey(nodeId: number): string {\n return `ipb-node-${nodeId}`;\n}\n\n/**\n * Flatten a node tree in depth-first pre-order with depth metadata.\n * Uses an iterative stack to avoid recursion depth issues on large trees.\n */\nexport function flattenTree(root: INode, options: IFlattenTreeOptions = {}): IVirtualTreeRow[] {\n const createKey = options.createKey ?? ((node: INode) => createStableNodeKey(node.id));\n const rows: IVirtualTreeRow[] = [];\n const stack: Array<{ node: INode; depth: number; parentId: number | null }> = [\n { node: root, depth: 0, parentId: null },\n ];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current) break;\n\n const index = rows.length;\n rows.push({\n node: current.node,\n id: current.node.id,\n key: createKey(current.node),\n depth: current.depth,\n index,\n parentId: current.parentId,\n });\n\n for (let i = current.node.children.length - 1; i >= 0; i--) {\n stack.push({\n node: current.node.children[i],\n depth: current.depth + 1,\n parentId: current.node.id,\n });\n }\n }\n\n return rows;\n}\n\n/**\n * Compute a clamped window range over a flat list.\n */\nexport function computeWindowRange(\n total: number,\n startIndex: number,\n windowSize: number,\n overscan = 0,\n): IVirtualWindowRange {\n const safeTotal = Math.max(0, toSafeInteger(total));\n const safeWindowSize = Math.max(0, toSafeInteger(windowSize));\n const safeOverscan = Math.max(0, toSafeInteger(overscan));\n\n if (safeTotal === 0 || safeWindowSize === 0) {\n return { start: 0, end: 0, size: 0, total: safeTotal };\n }\n\n const maxStart = safeTotal - 1;\n const safeStartIndex = Math.min(Math.max(toSafeInteger(startIndex), 0), maxStart);\n const start = Math.max(0, safeStartIndex - safeOverscan);\n const end = Math.min(safeTotal, safeStartIndex + safeWindowSize + safeOverscan);\n\n return {\n start,\n end,\n size: Math.max(0, end - start),\n total: safeTotal,\n };\n}\n\n/**\n * Slice a list using a computed virtual window.\n */\nexport function sliceWindow<T>(\n rows: readonly T[],\n startIndex: number,\n windowSize: number,\n overscan = 0,\n): { rows: T[]; range: IVirtualWindowRange } {\n const range = computeWindowRange(rows.length, startIndex, windowSize, overscan);\n return {\n rows: rows.slice(range.start, range.end),\n range,\n };\n}\n\n/**\n * Build stable key/index lookup maps from flattened tree rows.\n */\nexport function createVirtualTreeIndexMaps(rows: readonly IVirtualTreeRow[]): IVirtualTreeIndexMaps {\n const keyByIndex: string[] = [];\n const indexByKey = new Map<string, number>();\n const indexByNodeId = new Map<number, number>();\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n keyByIndex[i] = row.key;\n\n if (!indexByKey.has(row.key)) {\n indexByKey.set(row.key, i);\n }\n\n if (!indexByNodeId.has(row.id)) {\n indexByNodeId.set(row.id, i);\n }\n }\n\n return {\n keyByIndex,\n indexByKey,\n indexByNodeId,\n };\n}\n"],"names":["PageBuilderError","code","message","options","__publicField","isPageBuilderError","error","createPageBuilderError","toErrorMessage","reportDevDiagnostic","context","details","SAFE_TEXT_CONTAINER_TAGS","SAFE_RICH_TEXT_TAGS","DROP_ENTIRELY_TAGS","SAFE_LINK_TARGETS","SAFE_LINK_REL_TOKENS","SAFE_DATA_IMAGE_URL_PATTERN","stripControlCharacters","value","normalizeProtocolProbe","sanitizeLinkRel","rel","safeTokens","token","withSafeBlankTargetRel","existingRel","safeRel","relTokens","createSanitizationDocument","_a","parsed","escapeHtml","rawHtml","sanitizeRichTextAttributes","source","target","tagName","attribute","attrName","attrValue","sanitizedHref","sanitizeUrlByKind","normalizedTarget","sanitizedRel","sanitizeRichTextChildren","doc","node","sourceElement","sourceTagName","unwrappedChildren","sanitizedElement","safeChildren","sanitizeRichTextHtml","html","output","normalizeSafeHtmlTag","tag","fallback","normalizedFallback","safeFallback","normalizedTag","url","kind","sanitizedInput","protocolProbe","schemeMatch","scheme","PAGE_STATUSES","MAX_VALIDATION_DEPTH","MAX_VALIDATION_NODE_COUNT","isRecord","addError","path","validatePbTextProps","props","validatePbImageProps","validatePbVideoProps","poster","validatePbSectionProps","backgroundImage","validateKnownComponentPropsInto","name","validateNodeInto","depth","id","slot","children","readonly","index","validateNode","validatePageData","pageData","meta","tree","contentRootId","maxId","variables","key","getChildren","clampIndex","length","normalized","cloneTree","findNodeById","root","child","found","findParent","childId","i","removeNode","result","insertNode","parentId","parent","targetIndex","insertedNode","moveNode","nodeId","newParentId","sourceParentResult","sourceChildren","rollbackIndex","createNode","walkTree","visitor","visited","visitNode","currentDepth","countNodes","count","getMaxId","max","interpolateProps","safeVariables","_","varName","extractPlainText","texts","n","stripped","toSafeInteger","createStableNodeKey","flattenTree","createKey","rows","stack","current","computeWindowRange","total","startIndex","windowSize","overscan","safeTotal","safeWindowSize","safeOverscan","maxStart","safeStartIndex","start","end","sliceWindow","range","createVirtualTreeIndexMaps","keyByIndex","indexByKey","indexByNodeId","row"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var x=Object.defineProperty;var R=(r,t,e)=>t in r?x(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var y=(r,t,e)=>R(r,typeof t!="symbol"?t+"":t,e);class I extends Error{constructor(e,n,o={}){super(n);y(this,"code");y(this,"details");this.name="PageBuilderError",this.code=e,this.details=o.details??{},this.cause=o.cause}}function L(r){return r instanceof I}function E(r,t,e={}){return new I(r,t,e)}function D(r){return r instanceof Error?r.message:String(r)}function O(r,t,e){}const P=new Set(["div","p","span","h1","h2","h3","h4","h5","h6","section","article","blockquote"]),B=new Set(["a","b","blockquote","br","code","div","em","h1","h2","h3","h4","h5","h6","i","li","ol","p","pre","s","span","strong","u","ul"]),V=new Set(["base","embed","form","iframe","input","link","math","meta","noscript","object","script","style","svg","template","textarea"]),F=new Set(["_blank","_parent","_self","_top"]),j=new Set(["nofollow","noopener","noreferrer","sponsored","ugc"]),G=/^data:image\/(?:avif|bmp|gif|jpe?g|png|webp);base64,[a-z0-9+/=\s]+$/i;function v(r){return r.replace(/[\u0000-\u001F\u007F]/g,"")}function H(r){return v(r).replace(/\s+/g,"")}function k(r){const t=r.toLowerCase().split(/\s+/).filter(Boolean).filter(e=>j.has(e));return Array.from(new Set(t)).join(" ")}function K(r){const t=k(r??""),e=new Set(t.split(/\s+/).filter(Boolean));return e.add("noopener"),e.add("noreferrer"),Array.from(e).join(" ")}function U(){var r;if(typeof document<"u"&&typeof((r=document.implementation)==null?void 0:r.createHTMLDocument)=="function")return document.implementation.createHTMLDocument("");if(typeof DOMParser<"u"){const t=new DOMParser().parseFromString("<!doctype html><html><body></body></html>","text/html");if(t!=null&&t.body)return t}return null}function W(r){return r.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function q(r,t){const e=t.tagName.toLowerCase();for(const n of Array.from(r.attributes)){const o=n.name.toLowerCase(),i=v(n.value).trim();if(!(i.length===0||o.startsWith("on"))){if(o==="title"){t.setAttribute("title",i);continue}if(e==="a"){if(o==="href"){const a=f(i,"link");a.length>0&&t.setAttribute("href",a);continue}if(o==="target"){const a=i.toLowerCase();F.has(a)&&t.setAttribute("target",a);continue}if(o==="rel"){const a=k(i);a.length>0&&t.setAttribute("rel",a)}}}}e==="a"&&t.getAttribute("target")==="_blank"&&t.setAttribute("rel",K(t.getAttribute("rel")))}function T(r,t,e){for(const n of Array.from(r.childNodes)){if(n.nodeType===3){t.appendChild(e.createTextNode(n.textContent??""));continue}if(n.nodeType!==1)continue;const o=n,i=o.tagName.toLowerCase();if(V.has(i))continue;if(!B.has(i)){const d=e.createDocumentFragment();T(o,d,e),t.appendChild(d);continue}const a=e.createElement(i);if(q(o,a),T(o,a,e),i==="a"&&!a.getAttribute("href")){const d=e.createDocumentFragment();for(;a.firstChild;)d.appendChild(a.firstChild);t.appendChild(d);continue}t.appendChild(a)}}function X(r){const t=typeof r=="string"?r:"";if(t.length===0)return"";const e=U();if(!e)return W(t);const n=e.createElement("div");n.innerHTML=t;const o=e.createElement("div");return T(n,o,e),o.innerHTML}function C(r,t="div"){const e=typeof t=="string"?t.trim().toLowerCase():"div",n=P.has(e)?e:"div";if(typeof r!="string")return n;const o=r.trim().toLowerCase();return P.has(o)?o:n}function f(r,t){const e=v(r).trim();if(e.length===0)return"";const n=H(e).toLowerCase(),o=n.match(/^([a-z][a-z0-9+.-]*):/i),i=o==null?void 0:o[1];return!i||i==="http"||i==="https"||t==="link"&&(i==="mailto"||i==="tel")||t==="media"&&i==="blob"||(t==="media"||t==="background")&&i==="data"&&G.test(n)?e:""}const Y=new Set(["draft","published","archived"]),w=200,S=5e3;function m(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)}function s(r,t,e){r.errors.push({path:t,message:e})}function J(r,t,e){if(r.tag===void 0)return;if(typeof r.tag!="string"||r.tag.trim()===""){s(e,`${t}.tag`,"PbText props.tag must be a non-empty string.");return}const n=r.tag.trim().toLowerCase();C(r.tag)!==n&&s(e,`${t}.tag`,"PbText props.tag must be one of: div, p, span, h1, h2, h3, h4, h5, h6, section, article, blockquote.")}function Q(r,t,e){if(typeof r.src!="string"||r.src.trim()===""){s(e,`${t}.src`,"PbImage props.src must be a non-empty string.");return}f(r.src,"media")===""&&s(e,`${t}.src`,"PbImage props.src contains an unsafe URL.")}function Z(r,t,e){if(typeof r.src!="string"||r.src.trim()===""){s(e,`${t}.src`,"PbVideo props.src must be a non-empty string.");return}f(r.src,"media")===""&&s(e,`${t}.src`,"PbVideo props.src contains an unsafe URL.");const n=r.poster;n!=null&&n!==""&&(typeof n!="string"?s(e,`${t}.poster`,"PbVideo props.poster must be a string."):n.trim()!==""&&f(n,"media")===""&&s(e,`${t}.poster`,"PbVideo props.poster contains an unsafe URL."))}function ee(r,t,e){const n=r.backgroundImage;if(!(n==null||n==="")){if(typeof n!="string"){s(e,`${t}.backgroundImage`,"PbSection props.backgroundImage must be a string.");return}n.trim()!==""&&f(n,"background")===""&&s(e,`${t}.backgroundImage`,"PbSection props.backgroundImage contains an unsafe URL.")}}function te(r,t,e,n){if(r==="PbText"){J(t,e,n);return}if(r==="PbImage"){Q(t,e,n);return}if(r==="PbVideo"){Z(t,e,n);return}r==="PbSection"&&ee(t,e,n)}function A(r,t,e,n=0){if(n>w){e.depthGuardTriggered||(s(e,t,`Maximum node depth (${String(w)}) exceeded during validation.`),e.depthGuardTriggered=!0);return}if(e.visitedNodeCount>=S){e.sizeGuardTriggered||(s(e,t,`Maximum node count (${String(S)}) exceeded during validation.`),e.sizeGuardTriggered=!0);return}if(!m(r)){s(e,t,"Node must be an object.");return}if(e.seenNodes.has(r)){s(e,t,"Cycle detected in node tree.");return}e.seenNodes.add(r),e.visitedNodeCount++;const o=r.id,i=r.name,a=r.slot,d=r.props,u=r.children,c=r.readonly;if(!(typeof o=="number"&&Number.isInteger(o))||o<=0?s(e,`${t}.id`,"id must be a positive integer."):(e.seenIds.has(o)&&s(e,`${t}.id`,`Duplicate node id "${o}" found.`),e.seenIds.add(o),o>e.maxObservedId&&(e.maxObservedId=o)),(typeof i!="string"||i.trim()==="")&&s(e,`${t}.name`,"name must be a non-empty string."),a===null||typeof a=="string"||s(e,`${t}.slot`,"slot must be a string or null."),m(d)?typeof i=="string"&&te(i,d,`${t}.props`,e):s(e,`${t}.props`,"props must be an object."),!Array.isArray(u)){s(e,`${t}.children`,"children must be an array.");return}c===void 0||typeof c=="boolean"||s(e,`${t}.readonly`,"readonly must be a boolean when provided.");for(let l=0;l<u.length&&!e.sizeGuardTriggered;l++)A(u[l],`${t}.children[${l}]`,e,n+1)}function re(r,t="node"){const e={errors:[],seenIds:new Set,seenNodes:new WeakSet,maxObservedId:0,visitedNodeCount:0,depthGuardTriggered:!1,sizeGuardTriggered:!1};return A(r,t,e),{isValid:e.errors.length===0,errors:e.errors}}function ne(r){const t={errors:[],seenIds:new Set,seenNodes:new WeakSet,maxObservedId:0,visitedNodeCount:0,depthGuardTriggered:!1,sizeGuardTriggered:!1};if(!m(r))return s(t,"pageData","pageData must be an object."),{isValid:!1,errors:t.errors};const{meta:e,tree:n,contentRootId:o,maxId:i,variables:a}=r;if(m(e)?((typeof e.id!="string"||e.id.trim()==="")&&s(t,"meta.id","meta.id must be a non-empty string."),(typeof e.name!="string"||e.name.trim()==="")&&s(t,"meta.name","meta.name must be a non-empty string."),typeof e.url!="string"||e.url.trim()===""?s(t,"meta.url","meta.url must be a non-empty string."):f(e.url,"link")===""&&s(t,"meta.url","meta.url contains an unsafe URL."),(typeof e.status!="string"||!Y.has(e.status))&&s(t,"meta.status","meta.status must be one of: draft, published, archived."),e.updatedAt===void 0||typeof e.updatedAt=="string"||s(t,"meta.updatedAt","meta.updatedAt must be a string when provided."),e.createdAt===void 0||typeof e.createdAt=="string"||s(t,"meta.createdAt","meta.createdAt must be a string when provided.")):s(t,"meta","meta must be an object."),A(n,"tree",t),typeof o=="number"&&Number.isInteger(o)&&o>0||s(t,"contentRootId","contentRootId must be a positive integer."),!(typeof i=="number"&&Number.isInteger(i))||i<0?s(t,"maxId","maxId must be a non-negative integer."):i<t.maxObservedId&&s(t,"maxId",`maxId (${String(i)}) must be greater than or equal to the maximum node id (${String(t.maxObservedId)}).`),!m(a))s(t,"variables","variables must be an object.");else for(const[d,u]of Object.entries(a))typeof u!="string"&&s(t,`variables.${d}`,"Variable values must be strings.");return{isValid:t.errors.length===0,errors:t.errors}}function p(r){return Array.isArray(r.children)?r.children:[]}function z(r,t){const e=Number.isFinite(r)?Math.trunc(r):0;return Math.max(0,Math.min(e,t))}function ie(r){try{return structuredClone(r)}catch(t){throw E("INVALID_NODE","[PageBuilder] Failed to clone node tree. Ensure the tree is serializable and acyclic.",{cause:t})}}function N(r,t){if(r.id===t)return r;for(const e of p(r)){const n=N(e,t);if(n)return n}}function b(r,t){const e=p(r);for(let n=0;n<e.length;n++){if(e[n].id===t)return{parent:r,index:n};const o=b(e[n],t);if(o)return o}}function oe(r,t){const e=b(r,t);if(e)return e.parent.children.splice(e.index,1)[0]}function M(r,t,e,n,o="default"){const i=N(r,t);if(!i)return!1;Array.isArray(i.children)||(i.children=[]);const a=z(n,i.children.length),d={...e,slot:o};return i.children.splice(a,0,d),!0}function ae(r,t,e,n,o="default"){const i=b(r,t);if(!i)return!1;const a=p(i.parent),[d]=a.splice(i.index,1);if(!d)return!1;if(M(r,e,d,n,o))return!0;const c=z(i.index,a.length);return a.splice(c,0,d),!1}function se(r,t,e={}){return{id:r,name:t,slot:e.slot??"default",props:e.props??{},children:e.children??[],readonly:e.readonly}}function h(r,t,e=0){const n=new WeakSet;function o(i,a){if(n.has(i))return!0;if(n.add(i),t(i,a)===!1)return!1;for(const d of p(i))if(o(d,a+1)===!1)return!1;return!0}return o(r,e)}function de(r){let t=0;return h(r,()=>{t++}),t}function ue(r){let t=r.id;return h(r,e=>{Number.isFinite(e.id)&&e.id>t&&(t=e.id)}),t}function ce(r,t){if(!r||typeof r!="object"||Array.isArray(r))return{};const e=t&&typeof t=="object"&&!Array.isArray(t)?t:{},n={};for(const[o,i]of Object.entries(r))typeof i=="string"?n[o]=i.replace(/\{\{\s*(\w+)\s*\}\}/g,(a,d)=>e[d]??`{{ ${d} }}`):n[o]=i;return n}function le(r){const t=[];return h(r,e=>{if(e.props.content&&typeof e.props.content=="string"){const n=e.props.content.replace(/<[^>]*>/g,"");n.trim()&&t.push(n.trim())}}),t.join(" ").replace(/\s+/g," ").trim()}function g(r){return Number.isFinite(r)?Math.trunc(r):0}function _(r){return`ipb-node-${r}`}function fe(r,t={}){const e=t.createKey??(i=>_(i.id)),n=[],o=[{node:r,depth:0,parentId:null}];for(;o.length>0;){const i=o.pop();if(!i)break;const a=n.length;n.push({node:i.node,id:i.node.id,key:e(i.node),depth:i.depth,index:a,parentId:i.parentId});for(let d=i.node.children.length-1;d>=0;d--)o.push({node:i.node.children[d],depth:i.depth+1,parentId:i.node.id})}return n}function $(r,t,e,n=0){const o=Math.max(0,g(r)),i=Math.max(0,g(e)),a=Math.max(0,g(n));if(o===0||i===0)return{start:0,end:0,size:0,total:o};const d=o-1,u=Math.min(Math.max(g(t),0),d),c=Math.max(0,u-a),l=Math.min(o,u+i+a);return{start:c,end:l,size:Math.max(0,l-c),total:o}}function me(r,t,e,n=0){const o=$(r.length,t,e,n);return{rows:r.slice(o.start,o.end),range:o}}function ge(r){const t=[],e=new Map,n=new Map;for(let o=0;o<r.length;o++){const i=r[o];t[o]=i.key,e.has(i.key)||e.set(i.key,o),n.has(i.id)||n.set(i.id,o)}return{keyByIndex:t,indexByKey:e,indexByNodeId:n}}exports.PageBuilderError=I;exports.cloneTree=ie;exports.computeWindowRange=$;exports.countNodes=de;exports.createNode=se;exports.createPageBuilderError=E;exports.createStableNodeKey=_;exports.createVirtualTreeIndexMaps=ge;exports.extractPlainText=le;exports.findNodeById=N;exports.findParent=b;exports.flattenTree=fe;exports.getMaxId=ue;exports.insertNode=M;exports.interpolateProps=ce;exports.isPageBuilderError=L;exports.moveNode=ae;exports.normalizeSafeHtmlTag=C;exports.removeNode=oe;exports.reportDevDiagnostic=O;exports.sanitizeRichTextHtml=X;exports.sanitizeUrlByKind=f;exports.sliceWindow=me;exports.toErrorMessage=D;exports.validateNode=re;exports.validatePageData=ne;exports.walkTree=h;
|
|
2
|
+
//# sourceMappingURL=index-DUPqbN77.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DUPqbN77.cjs","sources":["../src/core/errors.ts","../src/core/sanitize.ts","../src/core/validation.ts","../src/core/tree.ts","../src/core/virtual-tree.ts"],"sourcesContent":["export type PageBuilderErrorCode =\n | 'INVALID_PAGE_DATA'\n | 'INVALID_NODE'\n | 'INVALID_SNAPSHOT'\n | 'MISSING_COMPONENT'\n | 'DUPLICATE_COMPONENT'\n | 'RENDER_FAILURE'\n | 'UNKNOWN';\n\nexport interface PageBuilderErrorOptions {\n details?: Record<string, unknown>;\n cause?: unknown;\n}\n\nexport class PageBuilderError extends Error {\n readonly code: PageBuilderErrorCode;\n readonly details: Record<string, unknown>;\n\n constructor(code: PageBuilderErrorCode, message: string, options: PageBuilderErrorOptions = {}) {\n super(message);\n this.name = 'PageBuilderError';\n this.code = code;\n this.details = options.details ?? {};\n this.cause = options.cause;\n }\n}\n\nexport function isPageBuilderError(error: unknown): error is PageBuilderError {\n return error instanceof PageBuilderError;\n}\n\nexport function createPageBuilderError(\n code: PageBuilderErrorCode,\n message: string,\n options: PageBuilderErrorOptions = {},\n): PageBuilderError {\n return new PageBuilderError(code, message, options);\n}\n\nexport function toErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\nexport function reportDevDiagnostic(\n context: string,\n error: unknown,\n details?: Record<string, unknown>,\n): void {\n if (!import.meta.env.DEV) return;\n const normalized = isPageBuilderError(error)\n ? error\n : createPageBuilderError('UNKNOWN', toErrorMessage(error), {\n cause: error,\n details,\n });\n\n const mergedDetails = details\n ? {\n ...normalized.details,\n ...details,\n }\n : normalized.details;\n\n console.error(`[PageBuilder][${context}] ${normalized.message}`, {\n code: normalized.code,\n details: mergedDetails,\n cause: normalized.cause,\n });\n}\n","const SAFE_TEXT_CONTAINER_TAGS = new Set([\n 'div',\n 'p',\n 'span',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'section',\n 'article',\n 'blockquote',\n]);\n\nconst SAFE_RICH_TEXT_TAGS = new Set([\n 'a',\n 'b',\n 'blockquote',\n 'br',\n 'code',\n 'div',\n 'em',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'i',\n 'li',\n 'ol',\n 'p',\n 'pre',\n 's',\n 'span',\n 'strong',\n 'u',\n 'ul',\n]);\n\nconst DROP_ENTIRELY_TAGS = new Set([\n 'base',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'link',\n 'math',\n 'meta',\n 'noscript',\n 'object',\n 'script',\n 'style',\n 'svg',\n 'template',\n 'textarea',\n]);\n\nconst SAFE_LINK_TARGETS = new Set(['_blank', '_parent', '_self', '_top']);\nconst SAFE_LINK_REL_TOKENS = new Set(['nofollow', 'noopener', 'noreferrer', 'sponsored', 'ugc']);\nconst SAFE_DATA_IMAGE_URL_PATTERN = /^data:image\\/(?:avif|bmp|gif|jpe?g|png|webp);base64,[a-z0-9+/=\\s]+$/i;\n\nfunction stripControlCharacters(value: string): string {\n return value.replace(/[\\u0000-\\u001F\\u007F]/g, '');\n}\n\nfunction normalizeProtocolProbe(value: string): string {\n return stripControlCharacters(value).replace(/\\s+/g, '');\n}\n\nfunction sanitizeLinkRel(rel: string): string {\n const safeTokens = rel\n .toLowerCase()\n .split(/\\s+/)\n .filter(Boolean)\n .filter((token) => SAFE_LINK_REL_TOKENS.has(token));\n return Array.from(new Set(safeTokens)).join(' ');\n}\n\nfunction withSafeBlankTargetRel(existingRel: string | null): string {\n const safeRel = sanitizeLinkRel(existingRel ?? '');\n const relTokens = new Set(safeRel.split(/\\s+/).filter(Boolean));\n relTokens.add('noopener');\n relTokens.add('noreferrer');\n return Array.from(relTokens).join(' ');\n}\n\nfunction createSanitizationDocument(): Document | null {\n if (\n typeof document !== 'undefined'\n && typeof document.implementation?.createHTMLDocument === 'function'\n ) {\n return document.implementation.createHTMLDocument('');\n }\n\n if (typeof DOMParser !== 'undefined') {\n const parsed = new DOMParser().parseFromString('<!doctype html><html><body></body></html>', 'text/html');\n if (parsed?.body) return parsed;\n }\n\n return null;\n}\n\nfunction escapeHtml(rawHtml: string): string {\n return rawHtml\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction sanitizeRichTextAttributes(source: Element, target: HTMLElement): void {\n const tagName = target.tagName.toLowerCase();\n\n for (const attribute of Array.from(source.attributes)) {\n const attrName = attribute.name.toLowerCase();\n const attrValue = stripControlCharacters(attribute.value).trim();\n\n if (attrValue.length === 0 || attrName.startsWith('on')) continue;\n\n if (attrName === 'title') {\n target.setAttribute('title', attrValue);\n continue;\n }\n\n if (tagName !== 'a') continue;\n\n if (attrName === 'href') {\n const sanitizedHref = sanitizeUrlByKind(attrValue, 'link');\n if (sanitizedHref.length > 0) target.setAttribute('href', sanitizedHref);\n continue;\n }\n\n if (attrName === 'target') {\n const normalizedTarget = attrValue.toLowerCase();\n if (SAFE_LINK_TARGETS.has(normalizedTarget)) {\n target.setAttribute('target', normalizedTarget);\n }\n continue;\n }\n\n if (attrName === 'rel') {\n const sanitizedRel = sanitizeLinkRel(attrValue);\n if (sanitizedRel.length > 0) target.setAttribute('rel', sanitizedRel);\n }\n }\n\n if (tagName === 'a' && target.getAttribute('target') === '_blank') {\n target.setAttribute('rel', withSafeBlankTargetRel(target.getAttribute('rel')));\n }\n}\n\nfunction sanitizeRichTextChildren(source: ParentNode, target: ParentNode, doc: Document): void {\n for (const node of Array.from(source.childNodes)) {\n if (node.nodeType === 3) {\n target.appendChild(doc.createTextNode(node.textContent ?? ''));\n continue;\n }\n\n if (node.nodeType !== 1) continue;\n\n const sourceElement = node as Element;\n const sourceTagName = sourceElement.tagName.toLowerCase();\n\n if (DROP_ENTIRELY_TAGS.has(sourceTagName)) continue;\n\n if (!SAFE_RICH_TEXT_TAGS.has(sourceTagName)) {\n const unwrappedChildren = doc.createDocumentFragment();\n sanitizeRichTextChildren(sourceElement, unwrappedChildren, doc);\n target.appendChild(unwrappedChildren);\n continue;\n }\n\n const sanitizedElement = doc.createElement(sourceTagName);\n sanitizeRichTextAttributes(sourceElement, sanitizedElement);\n sanitizeRichTextChildren(sourceElement, sanitizedElement, doc);\n\n if (sourceTagName === 'a' && !sanitizedElement.getAttribute('href')) {\n const safeChildren = doc.createDocumentFragment();\n while (sanitizedElement.firstChild) {\n safeChildren.appendChild(sanitizedElement.firstChild);\n }\n target.appendChild(safeChildren);\n continue;\n }\n\n target.appendChild(sanitizedElement);\n }\n}\n\nexport function sanitizeRichTextHtml(html: string): string {\n const rawHtml = typeof html === 'string' ? html : '';\n if (rawHtml.length === 0) return '';\n\n const doc = createSanitizationDocument();\n if (!doc) return escapeHtml(rawHtml);\n\n const source = doc.createElement('div');\n source.innerHTML = rawHtml;\n\n const output = doc.createElement('div');\n sanitizeRichTextChildren(source, output, doc);\n return output.innerHTML;\n}\n\nexport function normalizeSafeHtmlTag(tag: unknown, fallback = 'div'): string {\n const normalizedFallback = typeof fallback === 'string' ? fallback.trim().toLowerCase() : 'div';\n const safeFallback = SAFE_TEXT_CONTAINER_TAGS.has(normalizedFallback) ? normalizedFallback : 'div';\n if (typeof tag !== 'string') return safeFallback;\n\n const normalizedTag = tag.trim().toLowerCase();\n if (!SAFE_TEXT_CONTAINER_TAGS.has(normalizedTag)) return safeFallback;\n return normalizedTag;\n}\n\nexport function sanitizeUrlByKind(url: string, kind: 'link' | 'media' | 'background'): string {\n const sanitizedInput = stripControlCharacters(url).trim();\n if (sanitizedInput.length === 0) return '';\n\n const protocolProbe = normalizeProtocolProbe(sanitizedInput).toLowerCase();\n const schemeMatch = protocolProbe.match(/^([a-z][a-z0-9+.-]*):/i);\n const scheme = schemeMatch?.[1];\n\n if (!scheme) return sanitizedInput;\n\n if (scheme === 'http' || scheme === 'https') return sanitizedInput;\n\n if (kind === 'link' && (scheme === 'mailto' || scheme === 'tel')) {\n return sanitizedInput;\n }\n\n if (kind === 'media' && scheme === 'blob') {\n return sanitizedInput;\n }\n\n if ((kind === 'media' || kind === 'background') && scheme === 'data') {\n return SAFE_DATA_IMAGE_URL_PATTERN.test(protocolProbe) ? sanitizedInput : '';\n }\n\n return '';\n}\n","import type { INode, IPageData, IPageMeta } from '@/types/node';\nimport { normalizeSafeHtmlTag, sanitizeUrlByKind } from '@/core/sanitize';\n\nexport interface IValidationError {\n path: string;\n message: string;\n}\n\nexport interface IValidationResult {\n isValid: boolean;\n errors: IValidationError[];\n}\n\ninterface IValidationContext {\n errors: IValidationError[];\n seenIds: Set<number>;\n seenNodes: WeakSet<object>;\n maxObservedId: number;\n visitedNodeCount: number;\n depthGuardTriggered: boolean;\n sizeGuardTriggered: boolean;\n}\n\nconst PAGE_STATUSES = new Set<IPageMeta['status']>(['draft', 'published', 'archived']);\nconst MAX_VALIDATION_DEPTH = 200;\nconst MAX_VALIDATION_NODE_COUNT = 5000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction addError(context: IValidationContext, path: string, message: string): void {\n context.errors.push({ path, message });\n}\n\nfunction validatePbTextProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (props.tag === undefined) return;\n\n if (typeof props.tag !== 'string' || props.tag.trim() === '') {\n addError(context, `${path}.tag`, 'PbText props.tag must be a non-empty string.');\n return;\n }\n\n const normalizedTag = props.tag.trim().toLowerCase();\n if (normalizeSafeHtmlTag(props.tag) !== normalizedTag) {\n addError(\n context,\n `${path}.tag`,\n 'PbText props.tag must be one of: div, p, span, h1, h2, h3, h4, h5, h6, section, article, blockquote.',\n );\n }\n}\n\nfunction validatePbImageProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (typeof props.src !== 'string' || props.src.trim() === '') {\n addError(context, `${path}.src`, 'PbImage props.src must be a non-empty string.');\n return;\n }\n\n if (sanitizeUrlByKind(props.src, 'media') === '') {\n addError(context, `${path}.src`, 'PbImage props.src contains an unsafe URL.');\n }\n}\n\nfunction validatePbVideoProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (typeof props.src !== 'string' || props.src.trim() === '') {\n addError(context, `${path}.src`, 'PbVideo props.src must be a non-empty string.');\n return;\n }\n\n if (sanitizeUrlByKind(props.src, 'media') === '') {\n addError(context, `${path}.src`, 'PbVideo props.src contains an unsafe URL.');\n }\n\n const poster = props.poster;\n if (poster !== undefined && poster !== null && poster !== '') {\n if (typeof poster !== 'string') {\n addError(context, `${path}.poster`, 'PbVideo props.poster must be a string.');\n } else if (poster.trim() !== '' && sanitizeUrlByKind(poster, 'media') === '') {\n addError(context, `${path}.poster`, 'PbVideo props.poster contains an unsafe URL.');\n }\n }\n}\n\nfunction validatePbSectionProps(\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n const backgroundImage = props.backgroundImage;\n if (backgroundImage === undefined || backgroundImage === null || backgroundImage === '') return;\n\n if (typeof backgroundImage !== 'string') {\n addError(context, `${path}.backgroundImage`, 'PbSection props.backgroundImage must be a string.');\n return;\n }\n\n if (backgroundImage.trim() !== '' && sanitizeUrlByKind(backgroundImage, 'background') === '') {\n addError(context, `${path}.backgroundImage`, 'PbSection props.backgroundImage contains an unsafe URL.');\n }\n}\n\nfunction validateKnownComponentPropsInto(\n name: string,\n props: Record<string, unknown>,\n path: string,\n context: IValidationContext,\n): void {\n if (name === 'PbText') {\n validatePbTextProps(props, path, context);\n return;\n }\n\n if (name === 'PbImage') {\n validatePbImageProps(props, path, context);\n return;\n }\n\n if (name === 'PbVideo') {\n validatePbVideoProps(props, path, context);\n return;\n }\n\n if (name === 'PbSection') {\n validatePbSectionProps(props, path, context);\n }\n}\n\nfunction validateNodeInto(node: unknown, path: string, context: IValidationContext, depth = 0): void {\n if (depth > MAX_VALIDATION_DEPTH) {\n if (!context.depthGuardTriggered) {\n addError(\n context,\n path,\n `Maximum node depth (${String(MAX_VALIDATION_DEPTH)}) exceeded during validation.`,\n );\n context.depthGuardTriggered = true;\n }\n return;\n }\n\n if (context.visitedNodeCount >= MAX_VALIDATION_NODE_COUNT) {\n if (!context.sizeGuardTriggered) {\n addError(\n context,\n path,\n `Maximum node count (${String(MAX_VALIDATION_NODE_COUNT)}) exceeded during validation.`,\n );\n context.sizeGuardTriggered = true;\n }\n return;\n }\n\n if (!isRecord(node)) {\n addError(context, path, 'Node must be an object.');\n return;\n }\n\n if (context.seenNodes.has(node)) {\n addError(context, path, 'Cycle detected in node tree.');\n return;\n }\n context.seenNodes.add(node);\n context.visitedNodeCount++;\n\n const id = node.id;\n const name = node.name;\n const slot = node.slot;\n const props = node.props;\n const children = node.children;\n const readonly = node.readonly;\n\n if (!(typeof id === 'number' && Number.isInteger(id)) || id <= 0) {\n addError(context, `${path}.id`, 'id must be a positive integer.');\n } else {\n if (context.seenIds.has(id)) {\n addError(context, `${path}.id`, `Duplicate node id \"${id}\" found.`);\n }\n context.seenIds.add(id);\n if (id > context.maxObservedId) context.maxObservedId = id;\n }\n\n if (typeof name !== 'string' || name.trim() === '') {\n addError(context, `${path}.name`, 'name must be a non-empty string.');\n }\n\n if (!(slot === null || typeof slot === 'string')) {\n addError(context, `${path}.slot`, 'slot must be a string or null.');\n }\n\n if (!isRecord(props)) {\n addError(context, `${path}.props`, 'props must be an object.');\n } else if (typeof name === 'string') {\n validateKnownComponentPropsInto(name, props, `${path}.props`, context);\n }\n\n if (!Array.isArray(children)) {\n addError(context, `${path}.children`, 'children must be an array.');\n return;\n }\n\n if (!(readonly === undefined || typeof readonly === 'boolean')) {\n addError(context, `${path}.readonly`, 'readonly must be a boolean when provided.');\n }\n\n for (let index = 0; index < children.length; index++) {\n if (context.sizeGuardTriggered) break;\n validateNodeInto(children[index], `${path}.children[${index}]`, context, depth + 1);\n }\n}\n\nexport function validateNode(node: unknown, path = 'node'): IValidationResult {\n const context: IValidationContext = {\n errors: [],\n seenIds: new Set<number>(),\n seenNodes: new WeakSet<object>(),\n maxObservedId: 0,\n visitedNodeCount: 0,\n depthGuardTriggered: false,\n sizeGuardTriggered: false,\n };\n\n validateNodeInto(node, path, context);\n\n return {\n isValid: context.errors.length === 0,\n errors: context.errors,\n };\n}\n\nexport function validatePageData(pageData: unknown): IValidationResult {\n const context: IValidationContext = {\n errors: [],\n seenIds: new Set<number>(),\n seenNodes: new WeakSet<object>(),\n maxObservedId: 0,\n visitedNodeCount: 0,\n depthGuardTriggered: false,\n sizeGuardTriggered: false,\n };\n\n if (!isRecord(pageData)) {\n addError(context, 'pageData', 'pageData must be an object.');\n return {\n isValid: false,\n errors: context.errors,\n };\n }\n\n const { meta, tree, contentRootId, maxId, variables } = pageData;\n\n if (!isRecord(meta)) {\n addError(context, 'meta', 'meta must be an object.');\n } else {\n if (typeof meta.id !== 'string' || meta.id.trim() === '') {\n addError(context, 'meta.id', 'meta.id must be a non-empty string.');\n }\n if (typeof meta.name !== 'string' || meta.name.trim() === '') {\n addError(context, 'meta.name', 'meta.name must be a non-empty string.');\n }\n if (typeof meta.url !== 'string' || meta.url.trim() === '') {\n addError(context, 'meta.url', 'meta.url must be a non-empty string.');\n } else if (sanitizeUrlByKind(meta.url, 'link') === '') {\n addError(context, 'meta.url', 'meta.url contains an unsafe URL.');\n }\n if (typeof meta.status !== 'string' || !PAGE_STATUSES.has(meta.status as IPageMeta['status'])) {\n addError(\n context,\n 'meta.status',\n 'meta.status must be one of: draft, published, archived.',\n );\n }\n if (!(meta.updatedAt === undefined || typeof meta.updatedAt === 'string')) {\n addError(context, 'meta.updatedAt', 'meta.updatedAt must be a string when provided.');\n }\n if (!(meta.createdAt === undefined || typeof meta.createdAt === 'string')) {\n addError(context, 'meta.createdAt', 'meta.createdAt must be a string when provided.');\n }\n }\n\n validateNodeInto(tree, 'tree', context);\n\n if (!(typeof contentRootId === 'number' && Number.isInteger(contentRootId) && contentRootId > 0)) {\n addError(context, 'contentRootId', 'contentRootId must be a positive integer.');\n }\n\n if (!(typeof maxId === 'number' && Number.isInteger(maxId)) || maxId < 0) {\n addError(context, 'maxId', 'maxId must be a non-negative integer.');\n } else if (maxId < context.maxObservedId) {\n addError(\n context,\n 'maxId',\n `maxId (${String(maxId)}) must be greater than or equal to the maximum node id (${String(context.maxObservedId)}).`,\n );\n }\n\n if (!isRecord(variables)) {\n addError(context, 'variables', 'variables must be an object.');\n } else {\n for (const [key, value] of Object.entries(variables)) {\n if (typeof value !== 'string') {\n addError(context, `variables.${key}`, 'Variable values must be strings.');\n }\n }\n }\n\n return {\n isValid: context.errors.length === 0,\n errors: context.errors,\n };\n}\n","import type { INode } from '@/types/node';\nimport { createPageBuilderError } from '@/core/errors';\n\nfunction getChildren(node: INode): INode[] {\n return Array.isArray(node.children) ? node.children : [];\n}\n\nfunction clampIndex(index: number, length: number): number {\n const normalized = Number.isFinite(index) ? Math.trunc(index) : 0;\n return Math.max(0, Math.min(normalized, length));\n}\n\n/**\n * Deep clone a node tree using structured clone.\n */\nexport function cloneTree(node: INode): INode {\n try {\n return structuredClone(node);\n } catch (error) {\n throw createPageBuilderError(\n 'INVALID_NODE',\n '[PageBuilder] Failed to clone node tree. Ensure the tree is serializable and acyclic.',\n {\n cause: error,\n },\n );\n }\n}\n\n/**\n * Find a node by ID in the tree. Returns undefined if not found.\n */\nexport function findNodeById(root: INode, id: number): INode | undefined {\n if (root.id === id) return root;\n for (const child of getChildren(root)) {\n const found = findNodeById(child, id);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Find the parent of a node by the child's ID.\n * Returns the parent node and the child's index, or undefined.\n */\nexport function findParent(\n root: INode,\n childId: number,\n): { parent: INode; index: number } | undefined {\n const children = getChildren(root);\n for (let i = 0; i < children.length; i++) {\n if (children[i].id === childId) {\n return { parent: root, index: i };\n }\n const found = findParent(children[i], childId);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Remove a node by ID from the tree. Returns the removed node or undefined.\n */\nexport function removeNode(root: INode, id: number): INode | undefined {\n const result = findParent(root, id);\n if (!result) return undefined;\n return result.parent.children.splice(result.index, 1)[0];\n}\n\n/**\n * Insert a node as a child of a target node at a specific index and slot.\n */\nexport function insertNode(\n root: INode,\n parentId: number,\n node: INode,\n index: number,\n slot: string = 'default',\n): boolean {\n const parent = findNodeById(root, parentId);\n if (!parent) return false;\n if (!Array.isArray(parent.children)) {\n parent.children = [];\n }\n\n const targetIndex = clampIndex(index, parent.children.length);\n const insertedNode = { ...node, slot };\n parent.children.splice(targetIndex, 0, insertedNode);\n return true;\n}\n\n/**\n * Move a node within the tree to a new parent at a specific index.\n */\nexport function moveNode(\n root: INode,\n nodeId: number,\n newParentId: number,\n index: number,\n slot: string = 'default',\n): boolean {\n const sourceParentResult = findParent(root, nodeId);\n if (!sourceParentResult) return false;\n\n const sourceChildren = getChildren(sourceParentResult.parent);\n const [node] = sourceChildren.splice(sourceParentResult.index, 1);\n if (!node) return false;\n\n const moved = insertNode(root, newParentId, node, index, slot);\n if (moved) return true;\n\n // Roll back on failure so the caller never loses nodes due to invalid moves.\n const rollbackIndex = clampIndex(sourceParentResult.index, sourceChildren.length);\n sourceChildren.splice(rollbackIndex, 0, node);\n return false;\n}\n\n/**\n * Create a new node with default values and a given ID.\n */\nexport function createNode(\n id: number,\n name: string,\n options: Partial<Pick<INode, 'slot' | 'props' | 'children' | 'readonly'>> = {},\n): INode {\n return {\n id,\n name,\n slot: options.slot ?? 'default',\n props: options.props ?? {},\n children: options.children ?? [],\n readonly: options.readonly,\n };\n}\n\n/**\n * Walk the tree depth-first and call visitor for each node.\n * Return `false` from visitor to stop the entire traversal (not just the subtree).\n */\nexport function walkTree(root: INode, visitor: (node: INode, depth: number) => boolean | void, depth = 0): boolean {\n const visited = new WeakSet<object>();\n\n function visitNode(node: INode, currentDepth: number): boolean {\n if (visited.has(node as object)) return true;\n visited.add(node as object);\n\n if (visitor(node, currentDepth) === false) return false;\n for (const child of getChildren(node)) {\n if (visitNode(child, currentDepth + 1) === false) return false;\n }\n return true;\n }\n\n return visitNode(root, depth);\n}\n\n/**\n * Count the total number of nodes in the tree.\n */\nexport function countNodes(root: INode): number {\n let count = 0;\n walkTree(root, () => { count++; });\n return count;\n}\n\n/**\n * Get the maximum ID in the tree.\n */\nexport function getMaxId(root: INode): number {\n let max = root.id;\n walkTree(root, (node) => {\n if (Number.isFinite(node.id) && node.id > max) max = node.id;\n });\n return max;\n}\n\n/**\n * Interpolate template variables in node props.\n * Replaces `{{ VAR }}` patterns with values from the variables map.\n */\nexport function interpolateProps(\n props: Record<string, unknown>,\n variables: Record<string, string>,\n): Record<string, unknown> {\n if (!props || typeof props !== 'object' || Array.isArray(props)) {\n return {};\n }\n\n const safeVariables =\n variables && typeof variables === 'object' && !Array.isArray(variables) ? variables : {};\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string') {\n result[key] = value.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (_, varName: string) => {\n return safeVariables[varName] ?? `{{ ${varName} }}`;\n });\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Extract plain text from a node tree by collecting text content\n * from PbText (and similar) components and stripping HTML tags.\n */\nexport function extractPlainText(node: INode): string {\n const texts: string[] = [];\n walkTree(node, (n) => {\n if (n.props.content && typeof n.props.content === 'string') {\n const stripped = n.props.content.replace(/<[^>]*>/g, '');\n if (stripped.trim()) texts.push(stripped.trim());\n }\n });\n return texts.join(' ').replace(/\\s+/g, ' ').trim();\n}\n","import type { INode } from '@/types/node';\n\nexport interface IVirtualTreeRow {\n node: INode;\n id: number;\n key: string;\n depth: number;\n index: number;\n parentId: number | null;\n}\n\nexport interface IVirtualWindowRange {\n start: number;\n end: number;\n size: number;\n total: number;\n}\n\nexport interface IVirtualTreeIndexMaps {\n keyByIndex: string[];\n indexByKey: Map<string, number>;\n indexByNodeId: Map<number, number>;\n}\n\nexport interface IFlattenTreeOptions {\n createKey?: (node: INode) => string;\n}\n\nfunction toSafeInteger(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.trunc(value);\n}\n\nexport function createStableNodeKey(nodeId: number): string {\n return `ipb-node-${nodeId}`;\n}\n\n/**\n * Flatten a node tree in depth-first pre-order with depth metadata.\n * Uses an iterative stack to avoid recursion depth issues on large trees.\n */\nexport function flattenTree(root: INode, options: IFlattenTreeOptions = {}): IVirtualTreeRow[] {\n const createKey = options.createKey ?? ((node: INode) => createStableNodeKey(node.id));\n const rows: IVirtualTreeRow[] = [];\n const stack: Array<{ node: INode; depth: number; parentId: number | null }> = [\n { node: root, depth: 0, parentId: null },\n ];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current) break;\n\n const index = rows.length;\n rows.push({\n node: current.node,\n id: current.node.id,\n key: createKey(current.node),\n depth: current.depth,\n index,\n parentId: current.parentId,\n });\n\n for (let i = current.node.children.length - 1; i >= 0; i--) {\n stack.push({\n node: current.node.children[i],\n depth: current.depth + 1,\n parentId: current.node.id,\n });\n }\n }\n\n return rows;\n}\n\n/**\n * Compute a clamped window range over a flat list.\n */\nexport function computeWindowRange(\n total: number,\n startIndex: number,\n windowSize: number,\n overscan = 0,\n): IVirtualWindowRange {\n const safeTotal = Math.max(0, toSafeInteger(total));\n const safeWindowSize = Math.max(0, toSafeInteger(windowSize));\n const safeOverscan = Math.max(0, toSafeInteger(overscan));\n\n if (safeTotal === 0 || safeWindowSize === 0) {\n return { start: 0, end: 0, size: 0, total: safeTotal };\n }\n\n const maxStart = safeTotal - 1;\n const safeStartIndex = Math.min(Math.max(toSafeInteger(startIndex), 0), maxStart);\n const start = Math.max(0, safeStartIndex - safeOverscan);\n const end = Math.min(safeTotal, safeStartIndex + safeWindowSize + safeOverscan);\n\n return {\n start,\n end,\n size: Math.max(0, end - start),\n total: safeTotal,\n };\n}\n\n/**\n * Slice a list using a computed virtual window.\n */\nexport function sliceWindow<T>(\n rows: readonly T[],\n startIndex: number,\n windowSize: number,\n overscan = 0,\n): { rows: T[]; range: IVirtualWindowRange } {\n const range = computeWindowRange(rows.length, startIndex, windowSize, overscan);\n return {\n rows: rows.slice(range.start, range.end),\n range,\n };\n}\n\n/**\n * Build stable key/index lookup maps from flattened tree rows.\n */\nexport function createVirtualTreeIndexMaps(rows: readonly IVirtualTreeRow[]): IVirtualTreeIndexMaps {\n const keyByIndex: string[] = [];\n const indexByKey = new Map<string, number>();\n const indexByNodeId = new Map<number, number>();\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n keyByIndex[i] = row.key;\n\n if (!indexByKey.has(row.key)) {\n indexByKey.set(row.key, i);\n }\n\n if (!indexByNodeId.has(row.id)) {\n indexByNodeId.set(row.id, i);\n }\n }\n\n return {\n keyByIndex,\n indexByKey,\n indexByNodeId,\n };\n}\n"],"names":["PageBuilderError","code","message","options","__publicField","isPageBuilderError","error","createPageBuilderError","toErrorMessage","reportDevDiagnostic","context","details","SAFE_TEXT_CONTAINER_TAGS","SAFE_RICH_TEXT_TAGS","DROP_ENTIRELY_TAGS","SAFE_LINK_TARGETS","SAFE_LINK_REL_TOKENS","SAFE_DATA_IMAGE_URL_PATTERN","stripControlCharacters","value","normalizeProtocolProbe","sanitizeLinkRel","rel","safeTokens","token","withSafeBlankTargetRel","existingRel","safeRel","relTokens","createSanitizationDocument","_a","parsed","escapeHtml","rawHtml","sanitizeRichTextAttributes","source","target","tagName","attribute","attrName","attrValue","sanitizedHref","sanitizeUrlByKind","normalizedTarget","sanitizedRel","sanitizeRichTextChildren","doc","node","sourceElement","sourceTagName","unwrappedChildren","sanitizedElement","safeChildren","sanitizeRichTextHtml","html","output","normalizeSafeHtmlTag","tag","fallback","normalizedFallback","safeFallback","normalizedTag","url","kind","sanitizedInput","protocolProbe","schemeMatch","scheme","PAGE_STATUSES","MAX_VALIDATION_DEPTH","MAX_VALIDATION_NODE_COUNT","isRecord","addError","path","validatePbTextProps","props","validatePbImageProps","validatePbVideoProps","poster","validatePbSectionProps","backgroundImage","validateKnownComponentPropsInto","name","validateNodeInto","depth","id","slot","children","readonly","index","validateNode","validatePageData","pageData","meta","tree","contentRootId","maxId","variables","key","getChildren","clampIndex","length","normalized","cloneTree","findNodeById","root","child","found","findParent","childId","i","removeNode","result","insertNode","parentId","parent","targetIndex","insertedNode","moveNode","nodeId","newParentId","sourceParentResult","sourceChildren","rollbackIndex","createNode","walkTree","visitor","visited","visitNode","currentDepth","countNodes","count","getMaxId","max","interpolateProps","safeVariables","_","varName","extractPlainText","texts","n","stripped","toSafeInteger","createStableNodeKey","flattenTree","createKey","rows","stack","current","computeWindowRange","total","startIndex","windowSize","overscan","safeTotal","safeWindowSize","safeOverscan","maxStart","safeStartIndex","start","end","sliceWindow","range","createVirtualTreeIndexMaps","keyByIndex","indexByKey","indexByNodeId","row"],"mappings":"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"}
|