@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 CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-D79WbFRY.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;
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, e as s, o, p as t, h as d, c as i, q as l, u as n, w as c, f as g, d as N, l as P, g as m, j as u, i as f, x as p, m as x, n as T, k as v, a as B, y as w, t as y, b as E, v as z, z as I } from "./index-c6HOrx9r.js";
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 h = (r, t, e) => z(r, typeof t != "symbol" ? t + "" : t, e);
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
- h(this, "code");
8
- h(this, "details");
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 I = /* @__PURE__ */ new Set([
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"]), x = /* @__PURE__ */ new Set(["nofollow", "noopener", "noreferrer", "sponsored", "ugc"]), R = /^data:image\/(?:avif|bmp|gif|jpe?g|png|webp);base64,[a-z0-9+/=\s]+$/i;
77
- function A(r) {
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 A(r).replace(/\s+/g, "");
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) => x.has(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 = A(n.value).trim();
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 y(r, t, e) {
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
- y(s, d, e), t.appendChild(d);
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), y(s, a, e), i === "a" && !a.getAttribute("href")) {
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 y(n, s, e), s.innerHTML;
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 = I.has(e) ? e : "div";
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 I.has(s) ? s : n;
172
+ return v.has(s) ? s : n;
173
173
  }
174
174
  function f(r, t) {
175
- const e = A(r).trim();
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" && R.test(n) ? e : "";
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"]), w = 200, N = 5e3;
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 p(r, t, e, n = 0) {
242
- if (n > w) {
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(w)}) exceeded during validation.`
246
+ `Maximum node depth (${String(N)}) exceeded during validation.`
247
247
  ), e.depthGuardTriggered = !0);
248
248
  return;
249
249
  }
250
- if (e.visitedNodeCount >= N) {
250
+ if (e.visitedNodeCount >= w) {
251
251
  e.sizeGuardTriggered || (o(
252
252
  e,
253
253
  t,
254
- `Maximum node count (${String(N)}) exceeded during validation.`
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
- p(u[l], `${t}.children[${l}]`, e, n + 1);
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 p(r, t, e), {
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, content: n, layout: s, maxId: i, variables: a } = r;
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."), p(n, "content", t), p(s, "layout", t), !(typeof i == "number" && Number.isInteger(i)) || i < 0 ? o(t, "maxId", "maxId must be a non-negative integer.") : i < t.maxObservedId && o(
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 b(r) {
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 b(r)) {
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 T(r, t) {
353
- const e = b(r);
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 = T(e[n], t);
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 = T(r, t);
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 = T(r, t);
374
+ const i = I(r, t);
375
375
  if (!i) return !1;
376
- const a = b(i.parent), [d] = a.splice(i.index, 1);
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 v(r, t, e = 0) {
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 b(i))
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 v(r, () => {
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 v(r, (e) => {
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 v(r, (e) => {
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
- ne as b,
497
+ I as b,
498
498
  _ as c,
499
- T as d,
500
- se as e,
499
+ se as d,
500
+ de as e,
501
501
  C as f,
502
502
  ce as g,
503
- de as h,
503
+ X as h,
504
504
  le as i,
505
- X as j,
506
- ae as k,
507
- me as l,
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
- v as z
521
+ T as z
522
522
  };
523
- //# sourceMappingURL=index-c6HOrx9r.js.map
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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;');\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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}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('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;');\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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"}