@camstack/ui-library 1.2.275 → 1.2.276

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.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Sanitize a small HTML subset for `ConfigInfoField.format='html'`.
3
+ *
4
+ * Only `<a>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<code>`,
5
+ * `<br>`, `<p>`, and `<span>` are kept. On anchors, only `href` and a
6
+ * normalised `target`/`rel` are emitted; everything else is dropped
7
+ * (including event handlers and inline styles).
8
+ *
9
+ * Allowed `href` schemes: `http(s):`, `mailto:`, and **root-relative**
10
+ * paths (`/addon/…`). Root-relative links stay same-tab (session cookie);
11
+ * absolute http(s) open in a new tab. Protocol-relative (`//…`) is refused.
12
+ *
13
+ * The input comes from addon-authored copy bundled at build time —
14
+ * sanitisation is defence in depth rather than primary trust boundary.
15
+ */
16
+ export declare function sanitizeInfoHtml(html: string): string;
package/dist/index.cjs CHANGED
@@ -7249,6 +7249,68 @@ function ConfigIconFieldControl({ label, value, defaultKey, disabled, onChange }
7249
7249
  });
7250
7250
  }
7251
7251
  //#endregion
7252
+ //#region src/composites/sanitize-info-html.ts
7253
+ /**
7254
+ * Sanitize a small HTML subset for `ConfigInfoField.format='html'`.
7255
+ *
7256
+ * Only `<a>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<code>`,
7257
+ * `<br>`, `<p>`, and `<span>` are kept. On anchors, only `href` and a
7258
+ * normalised `target`/`rel` are emitted; everything else is dropped
7259
+ * (including event handlers and inline styles).
7260
+ *
7261
+ * Allowed `href` schemes: `http(s):`, `mailto:`, and **root-relative**
7262
+ * paths (`/addon/…`). Root-relative links stay same-tab (session cookie);
7263
+ * absolute http(s) open in a new tab. Protocol-relative (`//…`) is refused.
7264
+ *
7265
+ * The input comes from addon-authored copy bundled at build time —
7266
+ * sanitisation is defence in depth rather than primary trust boundary.
7267
+ */
7268
+ function sanitizeInfoHtml(html) {
7269
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
7270
+ const ALLOWED_TAGS = new Set([
7271
+ "A",
7272
+ "UL",
7273
+ "OL",
7274
+ "LI",
7275
+ "STRONG",
7276
+ "EM",
7277
+ "CODE",
7278
+ "BR",
7279
+ "P",
7280
+ "SPAN",
7281
+ "B"
7282
+ ]);
7283
+ const doc = new DOMParser().parseFromString(`<div>${html}</div>`, "text/html");
7284
+ const root = doc.body.firstElementChild;
7285
+ if (!root) return "";
7286
+ const walk = (node) => {
7287
+ const children = Array.from(node.children);
7288
+ for (const child of children) {
7289
+ if (!ALLOWED_TAGS.has(child.tagName)) {
7290
+ const text = doc.createTextNode(child.textContent ?? "");
7291
+ child.replaceWith(text);
7292
+ continue;
7293
+ }
7294
+ const allowed = child.tagName === "A" ? new Set(["href"]) : /* @__PURE__ */ new Set();
7295
+ for (const attr of Array.from(child.attributes)) if (!allowed.has(attr.name)) child.removeAttribute(attr.name);
7296
+ if (child.tagName === "A") {
7297
+ const href = child.getAttribute("href") ?? "";
7298
+ const isHttp = /^https?:\/\//i.test(href);
7299
+ const isMailto = href.startsWith("mailto:");
7300
+ const isRootRelative = href.startsWith("/") && !href.startsWith("//");
7301
+ if (!isHttp && !isMailto && !isRootRelative) child.removeAttribute("href");
7302
+ else if (isHttp) {
7303
+ child.setAttribute("target", "_blank");
7304
+ child.setAttribute("rel", "noopener noreferrer");
7305
+ }
7306
+ }
7307
+ walk(child);
7308
+ }
7309
+ };
7310
+ walk(root);
7311
+ return root.innerHTML;
7312
+ }
7313
+ //#endregion
7252
7314
  //#region ../../node_modules/clsx/dist/clsx.mjs
7253
7315
  function r(e) {
7254
7316
  var t, f, n = "";
@@ -29020,57 +29082,6 @@ var INFO_VARIANT_STYLES = {
29020
29082
  text: "text-danger"
29021
29083
  }
29022
29084
  };
29023
- /**
29024
- * Sanitize a small HTML subset for `ConfigInfoField.format='html'`.
29025
- *
29026
- * Only `<a>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<code>`,
29027
- * `<br>`, and `<p>` are kept. On anchors, only `href` (http/https only)
29028
- * and a normalised `target/rel` are emitted; everything else is dropped
29029
- * (including event handlers and inline styles). The input comes from
29030
- * addon-authored copy bundled at build time — sanitisation is defence
29031
- * in depth rather than primary trust boundary.
29032
- */
29033
- function sanitizeInfoHtml(html) {
29034
- if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
29035
- const ALLOWED_TAGS = new Set([
29036
- "A",
29037
- "UL",
29038
- "OL",
29039
- "LI",
29040
- "STRONG",
29041
- "EM",
29042
- "CODE",
29043
- "BR",
29044
- "P",
29045
- "SPAN"
29046
- ]);
29047
- const doc = new DOMParser().parseFromString(`<div>${html}</div>`, "text/html");
29048
- const root = doc.body.firstElementChild;
29049
- if (!root) return "";
29050
- const walk = (node) => {
29051
- const children = Array.from(node.children);
29052
- for (const child of children) {
29053
- if (!ALLOWED_TAGS.has(child.tagName)) {
29054
- const text = doc.createTextNode(child.textContent ?? "");
29055
- child.replaceWith(text);
29056
- continue;
29057
- }
29058
- const allowed = child.tagName === "A" ? new Set(["href"]) : /* @__PURE__ */ new Set();
29059
- for (const attr of Array.from(child.attributes)) if (!allowed.has(attr.name)) child.removeAttribute(attr.name);
29060
- if (child.tagName === "A") {
29061
- const href = child.getAttribute("href") ?? "";
29062
- if (!/^https?:\/\//i.test(href) && !href.startsWith("mailto:")) child.removeAttribute("href");
29063
- else {
29064
- child.setAttribute("target", "_blank");
29065
- child.setAttribute("rel", "noopener noreferrer");
29066
- }
29067
- }
29068
- walk(child);
29069
- }
29070
- };
29071
- walk(root);
29072
- return root.innerHTML;
29073
- }
29074
29085
  function InfoBox({ field }) {
29075
29086
  const styles = INFO_VARIANT_STYLES[field.variant ?? "info"] ?? INFO_VARIANT_STYLES["info"];
29076
29087
  const isHtml = field.format === "html";
package/dist/index.js CHANGED
@@ -7225,6 +7225,68 @@ function ConfigIconFieldControl({ label, value, defaultKey, disabled, onChange }
7225
7225
  });
7226
7226
  }
7227
7227
  //#endregion
7228
+ //#region src/composites/sanitize-info-html.ts
7229
+ /**
7230
+ * Sanitize a small HTML subset for `ConfigInfoField.format='html'`.
7231
+ *
7232
+ * Only `<a>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<code>`,
7233
+ * `<br>`, `<p>`, and `<span>` are kept. On anchors, only `href` and a
7234
+ * normalised `target`/`rel` are emitted; everything else is dropped
7235
+ * (including event handlers and inline styles).
7236
+ *
7237
+ * Allowed `href` schemes: `http(s):`, `mailto:`, and **root-relative**
7238
+ * paths (`/addon/…`). Root-relative links stay same-tab (session cookie);
7239
+ * absolute http(s) open in a new tab. Protocol-relative (`//…`) is refused.
7240
+ *
7241
+ * The input comes from addon-authored copy bundled at build time —
7242
+ * sanitisation is defence in depth rather than primary trust boundary.
7243
+ */
7244
+ function sanitizeInfoHtml(html) {
7245
+ if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
7246
+ const ALLOWED_TAGS = new Set([
7247
+ "A",
7248
+ "UL",
7249
+ "OL",
7250
+ "LI",
7251
+ "STRONG",
7252
+ "EM",
7253
+ "CODE",
7254
+ "BR",
7255
+ "P",
7256
+ "SPAN",
7257
+ "B"
7258
+ ]);
7259
+ const doc = new DOMParser().parseFromString(`<div>${html}</div>`, "text/html");
7260
+ const root = doc.body.firstElementChild;
7261
+ if (!root) return "";
7262
+ const walk = (node) => {
7263
+ const children = Array.from(node.children);
7264
+ for (const child of children) {
7265
+ if (!ALLOWED_TAGS.has(child.tagName)) {
7266
+ const text = doc.createTextNode(child.textContent ?? "");
7267
+ child.replaceWith(text);
7268
+ continue;
7269
+ }
7270
+ const allowed = child.tagName === "A" ? new Set(["href"]) : /* @__PURE__ */ new Set();
7271
+ for (const attr of Array.from(child.attributes)) if (!allowed.has(attr.name)) child.removeAttribute(attr.name);
7272
+ if (child.tagName === "A") {
7273
+ const href = child.getAttribute("href") ?? "";
7274
+ const isHttp = /^https?:\/\//i.test(href);
7275
+ const isMailto = href.startsWith("mailto:");
7276
+ const isRootRelative = href.startsWith("/") && !href.startsWith("//");
7277
+ if (!isHttp && !isMailto && !isRootRelative) child.removeAttribute("href");
7278
+ else if (isHttp) {
7279
+ child.setAttribute("target", "_blank");
7280
+ child.setAttribute("rel", "noopener noreferrer");
7281
+ }
7282
+ }
7283
+ walk(child);
7284
+ }
7285
+ };
7286
+ walk(root);
7287
+ return root.innerHTML;
7288
+ }
7289
+ //#endregion
7228
7290
  //#region ../../node_modules/clsx/dist/clsx.mjs
7229
7291
  function r(e) {
7230
7292
  var t, f, n = "";
@@ -28996,57 +29058,6 @@ var INFO_VARIANT_STYLES = {
28996
29058
  text: "text-danger"
28997
29059
  }
28998
29060
  };
28999
- /**
29000
- * Sanitize a small HTML subset for `ConfigInfoField.format='html'`.
29001
- *
29002
- * Only `<a>`, `<ul>`, `<ol>`, `<li>`, `<strong>`, `<em>`, `<code>`,
29003
- * `<br>`, and `<p>` are kept. On anchors, only `href` (http/https only)
29004
- * and a normalised `target/rel` are emitted; everything else is dropped
29005
- * (including event handlers and inline styles). The input comes from
29006
- * addon-authored copy bundled at build time — sanitisation is defence
29007
- * in depth rather than primary trust boundary.
29008
- */
29009
- function sanitizeInfoHtml(html) {
29010
- if (typeof window === "undefined" || typeof DOMParser === "undefined") return "";
29011
- const ALLOWED_TAGS = new Set([
29012
- "A",
29013
- "UL",
29014
- "OL",
29015
- "LI",
29016
- "STRONG",
29017
- "EM",
29018
- "CODE",
29019
- "BR",
29020
- "P",
29021
- "SPAN"
29022
- ]);
29023
- const doc = new DOMParser().parseFromString(`<div>${html}</div>`, "text/html");
29024
- const root = doc.body.firstElementChild;
29025
- if (!root) return "";
29026
- const walk = (node) => {
29027
- const children = Array.from(node.children);
29028
- for (const child of children) {
29029
- if (!ALLOWED_TAGS.has(child.tagName)) {
29030
- const text = doc.createTextNode(child.textContent ?? "");
29031
- child.replaceWith(text);
29032
- continue;
29033
- }
29034
- const allowed = child.tagName === "A" ? new Set(["href"]) : /* @__PURE__ */ new Set();
29035
- for (const attr of Array.from(child.attributes)) if (!allowed.has(attr.name)) child.removeAttribute(attr.name);
29036
- if (child.tagName === "A") {
29037
- const href = child.getAttribute("href") ?? "";
29038
- if (!/^https?:\/\//i.test(href) && !href.startsWith("mailto:")) child.removeAttribute("href");
29039
- else {
29040
- child.setAttribute("target", "_blank");
29041
- child.setAttribute("rel", "noopener noreferrer");
29042
- }
29043
- }
29044
- walk(child);
29045
- }
29046
- };
29047
- walk(root);
29048
- return root.innerHTML;
29049
- }
29050
29061
  function InfoBox({ field }) {
29051
29062
  const styles = INFO_VARIANT_STYLES[field.variant ?? "info"] ?? INFO_VARIANT_STYLES["info"];
29052
29063
  const isHtml = field.format === "html";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/ui-library",
3
- "version": "1.2.275",
3
+ "version": "1.2.276",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",