kerfjs 5.0.0-beta.8 → 5.0.0-beta.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +2577 -117
  2. package/README.md +96 -37
  3. package/ai/cursorrules +110 -61
  4. package/ai/manifest.json +16 -7
  5. package/ai/skill.md +120 -75
  6. package/dist/actions.d.ts +1 -1
  7. package/dist/actions.js +10 -6
  8. package/dist/actions.js.map +1 -1
  9. package/dist/array-signal.js +1 -1
  10. package/dist/async.js +22 -19
  11. package/dist/async.js.map +1 -1
  12. package/dist/attach.js +1 -66
  13. package/dist/attach.js.map +1 -1
  14. package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
  15. package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
  16. package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
  17. package/dist/chunk-4TX6YJDM.js.map +1 -0
  18. package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
  19. package/dist/chunk-5WRGJZV6.js.map +1 -0
  20. package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
  21. package/dist/chunk-BI4HGICV.js.map +1 -0
  22. package/dist/chunk-FM6N2B4N.js +70 -0
  23. package/dist/chunk-FM6N2B4N.js.map +1 -0
  24. package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
  25. package/dist/chunk-H7WTRM6F.js.map +1 -0
  26. package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
  27. package/dist/chunk-IHMDBBUJ.js.map +1 -0
  28. package/dist/chunk-JN7EJM3B.js +86 -0
  29. package/dist/chunk-JN7EJM3B.js.map +1 -0
  30. package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
  31. package/dist/chunk-MK42GLPV.js.map +1 -0
  32. package/dist/{chunk-SVATPF5R.js → chunk-TLL3KXT6.js} +249 -160
  33. package/dist/chunk-TLL3KXT6.js.map +1 -0
  34. package/dist/{chunk-ZDCJZCNO.js → chunk-UGNPDLNI.js} +78 -45
  35. package/dist/chunk-UGNPDLNI.js.map +1 -0
  36. package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
  37. package/dist/chunk-WKIPLNVO.js.map +1 -0
  38. package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
  39. package/dist/dev.d.ts +31 -13
  40. package/dist/dev.js +101 -35
  41. package/dist/dev.js.map +1 -1
  42. package/dist/html.js +5 -4
  43. package/dist/html.js.map +1 -1
  44. package/dist/index.d.ts +14 -9
  45. package/dist/index.js +29 -20
  46. package/dist/index.js.map +1 -1
  47. package/dist/jsx-runtime.d.ts +13 -5
  48. package/dist/jsx-runtime.js +1 -1
  49. package/dist/list.d.ts +7 -3
  50. package/dist/list.js +126 -28
  51. package/dist/list.js.map +1 -1
  52. package/dist/overlay.d.ts +128 -129
  53. package/dist/overlay.js +675 -366
  54. package/dist/overlay.js.map +1 -1
  55. package/dist/remount.js +2 -2
  56. package/dist/router.d.ts +13 -4
  57. package/dist/router.js +37 -10
  58. package/dist/router.js.map +1 -1
  59. package/dist/scope.d.ts +35 -8
  60. package/dist/scope.js +61 -30
  61. package/dist/scope.js.map +1 -1
  62. package/dist/testing.js +1 -1
  63. package/dist/timing.d.ts +3 -2
  64. package/dist/timing.js +1 -1
  65. package/dist/timing.js.map +1 -1
  66. package/llms.txt +12 -5
  67. package/package.json +33 -8
  68. package/setup/cli.mjs +90 -0
  69. package/setup/index.d.mts +37 -0
  70. package/setup/index.mjs +1347 -0
  71. package/setup/jsonc.mjs +201 -0
  72. package/setup/state.schema.json +56 -0
  73. package/dist/chunk-GY4XV2UV.js.map +0 -1
  74. package/dist/chunk-KPXIOG2C.js +0 -54
  75. package/dist/chunk-KPXIOG2C.js.map +0 -1
  76. package/dist/chunk-KZJXHFIB.js.map +0 -1
  77. package/dist/chunk-LVH3GC6B.js.map +0 -1
  78. package/dist/chunk-QIP723L4.js.map +0 -1
  79. package/dist/chunk-SVATPF5R.js.map +0 -1
  80. package/dist/chunk-U6FK33SG.js.map +0 -1
  81. package/dist/chunk-UZJ6I4T6.js.map +0 -1
  82. package/dist/chunk-V46JKE44.js.map +0 -1
  83. package/dist/chunk-ZDCJZCNO.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { effect, isSignal } from './chunk-U6FK33SG.js';
2
- import { flatten, wrapWithTags, mergeChildSegments } from './chunk-GY4XV2UV.js';
3
- import { devHooks } from './chunk-HW7KSM2Y.js';
1
+ import { effect, isSignal } from './chunk-IHMDBBUJ.js';
2
+ import { OVERLAY_HOST_SELECTOR, isOverlayHost, flatten, wrapWithTags, mergeChildSegments } from './chunk-4TX6YJDM.js';
3
+ import { devHooks } from './chunk-3C6F6NM4.js';
4
4
 
5
5
  // src/utils/syncFormProp.ts
6
6
  function syncFormProp(el, name, value, present) {
@@ -17,7 +17,14 @@ function syncFormProp(el, name, value, present) {
17
17
  }
18
18
 
19
19
  // src/utils/url-screen.ts
20
- var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "xlink:href", "formaction", "action", "data"]);
20
+ var URL_ATTRS = /* @__PURE__ */ new Set([
21
+ "href",
22
+ "src",
23
+ "xlink:href",
24
+ "formaction",
25
+ "action",
26
+ "data"
27
+ ]);
21
28
  var DANGEROUS_SCHEMES = /* @__PURE__ */ new Set(["javascript", "vbscript"]);
22
29
  var INERT_JAVASCRIPT_URLS = /* @__PURE__ */ new Set([
23
30
  "javascript:",
@@ -38,10 +45,12 @@ function schemeOf(normalized) {
38
45
  }
39
46
  function isDangerousDataUrl(normalized) {
40
47
  const media = /^data:([^;,]*)/.exec(normalized.toLowerCase())?.[1].trim() ?? "";
41
- if (media === "" || media === "text/plain" || media === "text/css") return false;
48
+ if (media === "" || media === "text/plain" || media === "text/css")
49
+ return false;
42
50
  if (media === "image/svg+xml") return true;
43
51
  if (media.startsWith("image/")) return false;
44
- if (media.startsWith("font/") || media.startsWith("application/font")) return false;
52
+ if (media.startsWith("font/") || media.startsWith("application/font"))
53
+ return false;
45
54
  if (media.startsWith("audio/") || media.startsWith("video/")) return false;
46
55
  return true;
47
56
  }
@@ -125,45 +134,55 @@ function wireBindings(rootEl, ctx, prevDisposers) {
125
134
  for (const d of prevDisposers) d();
126
135
  if (ctx.list.length === 0) return NO_DISPOSERS;
127
136
  const disposers = [];
128
- wireInto(rootEl, ctx.list, disposers);
137
+ try {
138
+ wireInto(rootEl, ctx.list, disposers);
139
+ } catch (err) {
140
+ for (const d of disposers) d();
141
+ throw err;
142
+ }
129
143
  return disposers;
130
144
  }
131
145
  function wireRowBindings(rowNode, bindings) {
132
- const disposers = new Array(bindings.length);
146
+ const disposers = [];
133
147
  const rootIds = rowNode.getAttribute(BIND_ATTR_ROW);
134
148
  let rootIdSet = null;
135
149
  let descIndex = null;
136
150
  let textMarkers = null;
137
- for (let i = 0; i < bindings.length; i++) {
138
- const b = bindings[i];
139
- if (b.kind === "attr") {
140
- let onRoot = false;
141
- if (rootIds !== null) {
142
- if (rootIds === b.id) {
143
- onRoot = true;
144
- } else if (rootIds.indexOf(",") !== -1) {
145
- rootIdSet ??= new Set(rootIds.split(","));
146
- onRoot = rootIdSet.has(b.id);
151
+ try {
152
+ for (let i = 0; i < bindings.length; i++) {
153
+ const b = bindings[i];
154
+ if (b.kind === "attr") {
155
+ let onRoot = false;
156
+ if (rootIds !== null) {
157
+ if (rootIds === b.id) {
158
+ onRoot = true;
159
+ } else if (rootIds.indexOf(",") !== -1) {
160
+ rootIdSet ??= new Set(rootIds.split(","));
161
+ onRoot = rootIdSet.has(b.id);
162
+ }
147
163
  }
148
- }
149
- let el;
150
- if (onRoot) {
151
- el = rowNode;
164
+ let el;
165
+ if (onRoot) {
166
+ el = rowNode;
167
+ } else {
168
+ descIndex ??= indexAttrEls(rowNode, BIND_ATTR_ROW);
169
+ el = descIndex.get(b.id);
170
+ }
171
+ if (el === void 0) continue;
172
+ disposers.push(attachAttrEffect(el, b.attr, b.signal));
152
173
  } else {
153
- descIndex ??= indexAttrEls(rowNode, BIND_ATTR_ROW);
154
- el = descIndex.get(b.id);
155
- }
156
- if (el === void 0) continue;
157
- disposers[i] = attachAttrEffect(el, b.attr, b.signal);
158
- } else {
159
- if (textMarkers === null) {
160
- textMarkers = /* @__PURE__ */ new Map();
161
- collectComments(rowNode, ROW_TEXT_PREFIX, textMarkers);
174
+ if (textMarkers === null) {
175
+ textMarkers = /* @__PURE__ */ new Map();
176
+ collectComments(rowNode, ROW_TEXT_PREFIX, textMarkers);
177
+ }
178
+ const marker = textMarkers.get(b.id);
179
+ if (marker === void 0) continue;
180
+ disposers.push(attachTextEffect(marker, b.signal));
162
181
  }
163
- const marker = textMarkers.get(b.id);
164
- if (marker === void 0) continue;
165
- disposers[i] = attachTextEffect(marker, b.signal);
166
182
  }
183
+ } catch (err) {
184
+ for (const d of disposers) d();
185
+ throw err;
167
186
  }
168
187
  return disposers;
169
188
  }
@@ -208,8 +227,11 @@ function wireInto(scope, bindings, disposers) {
208
227
  }
209
228
  function indexAttrEls(scope, attrName) {
210
229
  const map = /* @__PURE__ */ new Map();
230
+ const hasHost = scope.querySelector(OVERLAY_HOST_SELECTOR);
211
231
  for (const el of scope.querySelectorAll(`[${attrName}]`)) {
212
- for (const id of el.getAttribute(attrName).split(",")) map.set(id, el);
232
+ if (hasHost && scope.contains(el.closest(OVERLAY_HOST_SELECTOR))) continue;
233
+ for (const id of el.getAttribute(attrName).split(","))
234
+ map.set(id, el);
213
235
  }
214
236
  return map;
215
237
  }
@@ -270,8 +292,9 @@ function collectComments(node, prefix, out) {
270
292
  for (let c = node.firstChild; c !== null; c = c.nextSibling) {
271
293
  if (c.nodeType === Node.COMMENT_NODE) {
272
294
  const data = c.data;
273
- if (data.startsWith(prefix)) out.set(data.slice(prefix.length), c);
274
- } else if (c.nodeType === Node.ELEMENT_NODE) {
295
+ if (data.startsWith(prefix))
296
+ out.set(data.slice(prefix.length), c);
297
+ } else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c)) {
275
298
  collectComments(c, prefix, out);
276
299
  }
277
300
  }
@@ -431,17 +454,22 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
431
454
  "wbr"
432
455
  ]);
433
456
  function toSegment(child) {
434
- if (child == null || typeof child === "boolean") return { kind: "static", html: "" };
457
+ if (child == null || typeof child === "boolean")
458
+ return { kind: "static", html: "" };
435
459
  if (isSignal(child)) {
436
460
  const marker = bindText(child);
437
461
  if (marker !== null) return { kind: "static", html: marker };
438
462
  const v = child.value;
439
- return { kind: "static", html: v == null || typeof v === "boolean" ? "" : escapeHtml(String(v)) };
463
+ return {
464
+ kind: "static",
465
+ html: v == null || typeof v === "boolean" ? "" : escapeHtml(String(v))
466
+ };
440
467
  }
441
468
  if (isSafeHtml(child)) {
442
469
  return child.__segment ?? { kind: "static", html: child.__html };
443
470
  }
444
- if (typeof child === "string") return { kind: "static", html: escapeHtml(child) };
471
+ if (typeof child === "string")
472
+ return { kind: "static", html: escapeHtml(child) };
445
473
  if (typeof child === "number") return { kind: "static", html: String(child) };
446
474
  if (Array.isArray(child)) return mergeChildSegments(child.map(toSegment));
447
475
  const maybeNode = child;
@@ -451,7 +479,7 @@ function toSegment(child) {
451
479
  );
452
480
  }
453
481
  throw new Error(
454
- `JSX: unsupported child of type ${describeValue(child)}. Children must be SafeHtml, string, number, boolean, null, undefined, or an array of those. Common mistakes: passing a Signal/Store object directly (use signal.value or store.state.value), passing a function (call it first), or passing a Promise (await it before render).`
482
+ `JSX: unsupported child of type ${describeValue(child)}. Children must be SafeHtml, string, number, boolean, null, undefined, a signal/computed, or a nested array of those. Common mistakes: passing a Store object directly (use store.state.value), passing a function (call it first), or passing a Promise (await it before render).`
455
483
  );
456
484
  }
457
485
  function describeValue(v) {
@@ -529,13 +557,18 @@ function jsx(tag, props) {
529
557
  }
530
558
  attrStr += renderAttr(k, v);
531
559
  }
532
- if (bindIds !== null) attrStr += ` ${bindMarkerAttr()}="${bindIds.join(",")}"`;
560
+ if (bindIds !== null)
561
+ attrStr += ` ${bindMarkerAttr()}="${bindIds.join(",")}"`;
533
562
  if (VOID_TAGS.has(tag)) return new SafeHtml(`<${tag}${attrStr}>`);
534
563
  const childSegment = children != null ? toSegment(children) : { kind: "static", html: "" };
535
- return new SafeHtml(wrapWithTags(childSegment, `<${tag}${attrStr}>`, `</${tag}>`));
564
+ return new SafeHtml(
565
+ wrapWithTags(childSegment, `<${tag}${attrStr}>`, `</${tag}>`)
566
+ );
536
567
  }
537
568
  function Fragment({ children }) {
538
- return new SafeHtml(children != null ? toSegment(children) : { kind: "static", html: "" });
569
+ return new SafeHtml(
570
+ children != null ? toSegment(children) : { kind: "static", html: "" }
571
+ );
539
572
  }
540
573
  function _toSegment(child) {
541
574
  return toSegment(child);
@@ -546,4 +579,4 @@ function _renderAttrVerbatim(name, value) {
546
579
 
547
580
  export { Fragment, ROW_TEXT_PREFIX, SafeHtml, TEXT_MARKER_PREFIX, _renderAttrVerbatim, _setBindingContext, _toSegment, assertEmittableAttrName, bindAttr, bindMarkerAttr, boundTextNodeOf, captureRowBindings, carryOrRewireRowBindings, disposeRowBindings, escapeHtml, granularListSafeHtml, isSafeHtml, jsx, listSafeHtml, newBindingContext, raw, syncFormProp, wireBindings, wireRowBindings };
548
581
 
549
- //# sourceMappingURL=chunk-ZDCJZCNO.js.map
582
+ //# sourceMappingURL=chunk-UGNPDLNI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/syncFormProp.ts","../src/utils/url-screen.ts","../src/bindings.ts","../src/utils/escapeHtml.ts","../src/utils/jsx-attr-aliases.ts","../src/jsx-runtime.ts"],"names":["context","SAFE_HTML_BRAND"],"mappings":"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","file":"chunk-UGNPDLNI.js","sourcesContent":["/**\n * Form-state property sync — shared by the morph's attribute writer and the\n * fine-grained binding writer (KF-335: controlled checked/value/selected went\n * stale after user interaction).\n *\n * Browsers detach a form control's live property from its attribute once the\n * control is \"dirty\" (the user — or script — has touched it): the attribute\n * becomes only the *default*. An attribute-only reconciler therefore updates\n * `checked=\"\"` / `value=\"…\"` / `selected=\"\"` while the visible state stays\n * stale.\n *\n * The rule: callers invoke this ONLY at the moment they actually MUTATE one of\n * these attributes (set, change, or remove). The property then follows the\n * attribute. Attribute-unchanged elements are never touched, so uncontrolled\n * usage — JSX that never mentions the attribute — keeps user-driven state\n * exactly as before. This is the same philosophy as the user-agent-owned\n * `open` carve-out on `<details>`/`<dialog>`: kerf only touches what the\n * template expressed an opinion about.\n *\n * The focused-element exception: a focused text-entry control's `value` is the\n * user's in-progress edit — the morph's preservation rule owns it, so `value`\n * sync is skipped for `document.activeElement`.\n */\nexport function syncFormProp(\n el: Element,\n name: string,\n value: string,\n present: boolean,\n): void {\n const tag = el.tagName;\n if (name === 'checked') {\n if (tag === 'INPUT') (el as HTMLInputElement).checked = present;\n } else if (name === 'value') {\n if (tag === 'INPUT' && el !== document.activeElement) {\n (el as HTMLInputElement).value = present ? value : '';\n }\n } else if (name === 'selected') {\n if (tag === 'OPTION') (el as HTMLOptionElement).selected = present;\n }\n}\n","/**\n * URL-attribute screening, shared by the JSX runtime's static-attribute\n * serializer (`renderAttr` in `jsx-runtime.ts`) and the fine-grained binding's\n * live-attribute writer (`setBoundAttr` in `bindings.ts`) — KF-297.\n *\n * A stored-XSS payload like `href={userInput}` with `javascript:alert(1)` is\n * dropped (attribute omitted from the string / removed from the live node)\n * rather than written. `raw(...)` / `SafeHtml` values bypass this — the\n * documented opt-out for legitimate cases (bookmarklet builders, values\n * sanitized by a separate trust layer).\n *\n * The screen is a scheme allow/deny check, not a full URL sanitizer, but it is\n * hardened against the obfuscations a real browser sees through:\n *\n * - **Control-character / whitespace obfuscation of the scheme.** Browsers\n * strip leading C0 controls + spaces and remove TAB/LF/CR from anywhere in\n * a URL before resolving the scheme, so `java\\tscript:`, `\\x01javascript:`,\n * and `javascript\\x00:` all resolve to `javascript:`. We normalize the same\n * way (strip every C0 control 0x00-0x1F + DEL, trim leading whitespace)\n * BEFORE extracting the scheme, so none of those slip past.\n *\n * - **The `javascript:` no-op placeholders are allowed.** `javascript:void(0)`\n * and its handful of spellings are the placeholder-link idiom, not an\n * attack; matching is against the whole normalized value so nothing can be\n * appended to them. See `INERT_JAVASCRIPT_URLS`.\n *\n * - **`data:` is subtype-specific.** `data:image/png` in `<img src>` is safe\n * and common; `data:text/html` / `data:image/svg+xml` / `data:*xml` load as\n * a document and run script (SVG `<script>`, HTML/XHTML `<script>`). We\n * allowlist the inert media families (raster images, fonts, audio, video,\n * plain text/css) and drop every other `data:` — so a novel dangerous\n * subtype fails closed.\n */\n\nimport { devHooks } from '../dev-hooks.js';\n\n/**\n * URL-bearing HTML/SVG attributes whose plain-string values are screened.\n * `data` covers `<object data>` (loads its target as a document, so\n * `data:text/html` there executes). Image-context URL attributes\n * (`srcset` / `poster` / `background` / `ping`) are intentionally NOT screened:\n * a `javascript:` / `data:` value in them does not execute as script in modern\n * browsers, so screening would add false positives without closing a real sink.\n */\nconst URL_ATTRS = new Set([\n 'href',\n 'src',\n 'xlink:href',\n 'formaction',\n 'action',\n 'data',\n]);\n\n/** Schemes that execute script when a browser resolves them as a URL. */\nconst DANGEROUS_SCHEMES = new Set(['javascript', 'vbscript']);\n\n/**\n * `javascript:` URLs that are provably no-ops — the placeholder-link idiom\n * (`<a href=\"javascript:void(0)\">`) that a decade of jQuery-era markup, CMS\n * output, and ported templates is full of.\n *\n * Screening them out was a false positive with a bad failure mode: the `href`\n * is dropped, so the anchor stops being a link — no keyboard focus, no `:link`\n * styling, no pointer cursor — and in production that happens behind a\n * `console.warn` nobody reads.\n *\n * Allowing them costs nothing. The match is against the **whole** normalized\n * value, so no attacker-supplied suffix survives it: `javascript:void(0)` is in\n * the set and `javascript:void(0);alert(1)` is not. The bodies here evaluate to\n * `undefined` or to nothing at all, and they carry no interpolation point.\n *\n * The alternative — telling authors to write `raw('javascript:void(0)')` — is\n * strictly worse for security: it teaches the general-purpose escape hatch as\n * the answer to a benign case, and an author who has learned `raw()` for one\n * `href` will reach for it on the next one.\n *\n * Matching is exact after `normalizeUrl` + lowercase + trailing-whitespace trim.\n * Internal whitespace is NOT collapsed, so each accepted spelling is listed\n * explicitly and the set stays auditable at a glance.\n */\nconst INERT_JAVASCRIPT_URLS = new Set([\n 'javascript:',\n 'javascript:;',\n 'javascript:void(0)',\n 'javascript:void(0);',\n 'javascript:void 0',\n 'javascript:void 0;',\n]);\n\n/** Matches every C0 control character (0x00-0x1F) and DEL (0x7F). */\n// eslint-disable-next-line no-control-regex -- deliberately matching C0 + DEL.\nconst CONTROL_CHARS = /[\\u0000-\\u001F\\u007F]/g;\n\n/** Bound diagnostic payloads so an unsafe, attacker-controlled URL cannot flood logs. */\nconst DANGEROUS_URL_DIAGNOSTIC_VALUE_LENGTH = 80;\n\n/**\n * Normalize a URL the way a browser does before scheme resolution: remove every\n * C0 control (0x00-0x1F) and DEL (0x7F) from anywhere — no legitimate URL\n * carries a raw control char (they must be percent-encoded), so this collapses\n * `java\\tscript:` / `\\x01javascript:` / `javascript\\x00:` back to their real\n * scheme — then trim leading whitespace. Internal spaces are left intact: a\n * space is not a valid scheme character, so a browser treats `java script:` as\n * a relative URL, not the `javascript:` scheme, and we must not \"repair\" it\n * into a false positive.\n */\nfunction normalizeUrl(value: string): string {\n return value.replace(CONTROL_CHARS, '').replace(/^\\s+/, '');\n}\n\n/**\n * The lowercased URL scheme (text before the first `:`) of an ALREADY-NORMALIZED\n * value, or null if none.\n */\nfunction schemeOf(normalized: string): string | null {\n const m = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(normalized);\n return m ? m[1].toLowerCase() : null;\n}\n\n/**\n * True if a `data:` URL (already normalized) is a script-executing document\n * type. Allowlists the inert media families and fails closed on everything else\n * — so `text/html`, `image/svg+xml`, XHTML/XML, and any unknown/novel subtype\n * are all treated as dangerous, while `data:image/png` / fonts / audio / video\n * / plain text pass.\n */\nfunction isDangerousDataUrl(normalized: string): boolean {\n const media =\n /^data:([^;,]*)/.exec(normalized.toLowerCase())?.[1].trim() ?? '';\n if (media === '' || media === 'text/plain' || media === 'text/css')\n return false;\n if (media === 'image/svg+xml') return true; // SVG can carry <script>\n if (media.startsWith('image/')) return false;\n if (media.startsWith('font/') || media.startsWith('application/font'))\n return false;\n if (media.startsWith('audio/') || media.startsWith('video/')) return false;\n return true; // text/html, application/xhtml+xml, *xml, unknown → block\n}\n\n/** True if `value` is a dangerous URL for the URL-bearing attribute `name`. */\nexport function isDangerousUrlValue(name: string, value: string): boolean {\n // HTML attribute names are ASCII-case-insensitive, and JSX can reach this\n // helper with the author-written casing on both static and bound paths.\n // Normalize before the allowlist check so `HREF` / `Src` / `XLINK:HREF`\n // cannot bypass the same browser sink written in lowercase.\n if (!URL_ATTRS.has(name.toLowerCase())) return false;\n // Normalized once here — every helper below reasons about the same string the\n // browser's scheme resolution would see.\n const normalized = normalizeUrl(value);\n const scheme = schemeOf(normalized);\n if (scheme === null) return false;\n if (DANGEROUS_SCHEMES.has(scheme)) {\n return !INERT_JAVASCRIPT_URLS.has(normalized.trimEnd().toLowerCase());\n }\n return scheme === 'data' && isDangerousDataUrl(normalized);\n}\n\n/**\n * The shared warning body for a dropped dangerous URL. Each caller prefixes\n * its own context (`JSX:` for the string serializer, `kerf binding:` for the\n * live-attribute writer).\n */\nexport function dangerousUrlWarning(name: string, value: string): string {\n return (\n `dropped dangerous URL value for ${name}=${JSON.stringify(value.slice(0, DANGEROUS_URL_DIAGNOSTIC_VALUE_LENGTH))}. ` +\n 'kerf blocks javascript:, vbscript:, and script-executing data: URLs ' +\n '(text/html, image/svg+xml, xml) in href/src/data/formaction/action/xlink:href by default. ' +\n 'Wrap in raw() if this is intentional (e.g. bookmarklets), or sanitize upstream.'\n );\n}\n\n/**\n * Report a screened dangerous URL, with the dev-vs-prod split (KF-340). Both\n * call sites (the JSX string serializer's `renderAttr` and the live-attribute\n * writer's `setBoundAttr`) still DROP the attribute; this only chooses how the\n * screen surfaces the diagnostic:\n *\n * - **DEV** (the consumer imported `kerfjs/dev`): THROW an `Error` with the\n * diagnostic, failing loudly at the developer's desk so a mistyped/unsafe\n * URL can't slip by unnoticed (nobody reads the console).\n * - **PRODUCTION** (no dev entry installed): `console.warn` and let the\n * caller drop — never crash a shipped app on attacker-influenced data.\n *\n * `context` is the caller's prefix (`JSX`/`kerf binding`); the composed message\n * is byte-identical to the pre-KF-340 warn text in production.\n */\nexport function reportDangerousUrl(\n context: string,\n name: string,\n value: string,\n): void {\n const message = `${context}: ${dangerousUrlWarning(name, value)}`;\n devHooks.urlScreenThrow?.(message);\n console.warn(message);\n}\n","/**\n * Fine-grained signal bindings (KF-294 spike).\n *\n * When a `Signal` is interpolated straight into a JSX attribute\n * (`class={sig}`) or a text child (`{sig}`) INSIDE a `mount()` render, the\n * JSX runtime stops stringifying it. Instead it emits a marker into the HTML\n * string and records a binding here; after the string is parsed to DOM, a\n * wiring pass attaches one `effect` per binding that writes straight to the\n * live node. A later change to that signal then updates the node WITHOUT\n * re-running the render function or walking the list reconciler.\n *\n * This reuses the \"marker in string, wire up after parse\" mechanism the\n * keyed-list reconciler already uses for `<!--kf-list:{id}-->` markers.\n *\n * TWO SCOPES of binding, with disjoint marker namespaces so their wiring\n * passes never collide:\n *\n * - GLOBAL holes — signals in the static surrounds (outside any `each()`\n * row). Markers: `data-kfb` attribute / `<!--kfb:{id}-->` comment. Ids come\n * from the mount render context's counter; wired by `wireBindings()` over\n * the whole mount root; disposed/re-wired by `mount()` each render.\n *\n * - ROW holes — signals inside an `each()` row. Markers: `data-kfbrow`\n * attribute / `<!--kfbr:{id}-->` comment. Ids are row-LOCAL (reset per row)\n * so they stay stable and collision-free as rows are inserted/removed/moved.\n * Captured per row by `captureRowBindings()`, carried on the list segment\n * item, and wired/disposed by the list reconciler at each row node's\n * create/remove — so a binding's lifetime tracks its row node's lifetime,\n * and row reorders (which reuse the same node) are free.\n *\n * Outside a `mount()` render (SSR / `SafeHtml.toString()`) neither scope is\n * active: the runtime snapshots `signal.value` and emits no markers, so server\n * output is correct and legacy `.toString()` callers are unaffected.\n *\n * Module-level mutable state note: `context` / `rowSink` (plus `rowCounter`,\n * the row-hole id counter that resets with each row capture) are a third\n * sanctioned module-level mutable location (alongside `store.ts:REGISTRY` and\n * `each.ts:context`). They hold the current render's binding sinks and are set\n * / cleared by `mount()` and `each()` around the render calls. The only other\n * module-level container here is `insertedTextNodes`, a WeakMap keyed on text\n * marker comments — a pure cache whose entries die with their nodes (GC-tied\n * lifetime), so it carries no cross-render semantics.\n */\n\nimport { effect, isSignal, type Signal } from './reactive.js';\nimport { isOverlayHost, OVERLAY_HOST_SELECTOR } from './utils/overlay-host.js';\nimport { syncFormProp } from './utils/syncFormProp.js';\nimport { isDangerousUrlValue, reportDangerousUrl } from './utils/url-screen.js';\n\n// RESERVED NAMESPACE (consumer contract, KF-314). The wiring pass finds these\n// markers by scanning the mounted subtree and matching by id, and resolves an\n// id collision by document order (last/first wins) with no ownership check — so\n// a consumer element that carries `data-kfb`/`data-kfbrow`, or a comment\n// beginning `kfb:`/`kfbr:`/`kf-list:`, can collide with a real binding's id and\n// steal its effect (the update wires to the wrong node). These names are\n// therefore reserved: consumers must not emit them (documented in\n// docs/2-reactivity.md § \"Reserved marker names\"). Kerf's own escaping already\n// prevents a plain text/attribute *value* from forging one; the remaining ways\n// in are a hand-written attribute or a `raw()` payload — i.e. the consumer's\n// own trust boundary. A dev-mode collision warning is a possible future\n// enhancement (fix direction (a) on KF-314) if silent mis-wiring proves easy to\n// hit in practice.\n\n/** Marker attribute for GLOBAL (static-surround) attribute bindings. */\nexport const BIND_ATTR = 'data-kfb';\n/** Comment-marker prefix for a GLOBAL text binding: `<!--kfb:{id}-->`. */\nexport const TEXT_MARKER_PREFIX = 'kfb:';\n/** Marker attribute for ROW (each()-scoped) attribute bindings. */\nexport const BIND_ATTR_ROW = 'data-kfbrow';\n/** Comment-marker prefix for a ROW text binding: `<!--kfbr:{id}-->`. */\nexport const ROW_TEXT_PREFIX = 'kfbr:';\n\ninterface AttrBinding {\n kind: 'attr';\n id: string;\n attr: string;\n signal: Signal<unknown>;\n}\ninterface TextBinding {\n kind: 'text';\n id: string;\n signal: Signal<unknown>;\n}\nexport type Binding = AttrBinding | TextBinding;\n\n/**\n * Per-render GLOBAL binding sink. `counter` assigns ids by registration order\n * (stable across renders since the JSX render is deterministic); `list`\n * accumulates this render's global (static-surround) holes.\n */\nexport interface BindingContext {\n counter: number;\n list: Binding[];\n}\n\nlet context: BindingContext | null = null;\n\n// Active row-capture sink + its row-local id counter. Non-null only while\n// `each()` renders a single row through `captureRowBindings()`.\nlet rowSink: Binding[] | null = null;\nlet rowCounter = 0;\n\n// Frozen shared sentinel for the no-holes case — readonly so no caller can\n// push into it and corrupt every other no-hole mount.\nconst NO_DISPOSERS: ReadonlyArray<() => void> = Object.freeze([]);\n\nexport function newBindingContext(): BindingContext {\n return { counter: 0, list: [] };\n}\n\nexport function _setBindingContext(c: BindingContext | null): void {\n context = c;\n}\n\n/**\n * Render one `each()` row while capturing the row-scoped bindings it emits.\n * Row ids are local (reset to 0) so they're stable + collision-free per row.\n * Returns the row HTML and its captured bindings (empty when the row has no\n * signal holes). Restores the prior capture state (supports nested `each()`).\n */\nexport function captureRowBindings(renderRow: () => string): {\n html: string;\n bindings: Binding[];\n} {\n const prevSink = rowSink;\n const prevCounter = rowCounter;\n rowSink = [];\n rowCounter = 0;\n try {\n const html = renderRow();\n return { html, bindings: rowSink };\n } finally {\n rowSink = prevSink;\n rowCounter = prevCounter;\n }\n}\n\n/**\n * Register a signal attribute. Routes to the row sink when a row capture is\n * active, else the global context. Returns the marker id,\n * or null to snapshot. The marker attribute NAME (`data-kfb` vs `data-kfbrow`)\n * is fetched once per element via `bindMarkerAttr()` — all of an element's\n * signal attrs share the same scope — so this avoids a per-attr object alloc.\n */\nexport function bindAttr(attr: string, signal: Signal<unknown>): string | null {\n if (rowSink !== null) {\n const id = `a${rowCounter++}`;\n rowSink.push({ kind: 'attr', id, attr, signal });\n return id;\n }\n if (context !== null) {\n const id = `a${context.counter++}`;\n context.list.push({ kind: 'attr', id, attr, signal });\n return id;\n }\n return null;\n}\n\n/**\n * The marker attribute name for the currently-active binding scope — row holes\n * use `data-kfbrow`, global holes `data-kfb`. Called once per element by the\n * JSX runtime after collecting the element's signal-attr ids.\n */\nexport function bindMarkerAttr(): string {\n return rowSink !== null ? BIND_ATTR_ROW : BIND_ATTR;\n}\n\n/**\n * Register a signal text hole. Returns the comment-marker HTML to emit, or\n * null to snapshot. Same routing as `bindAttr()`.\n */\nexport function bindText(signal: Signal<unknown>): string | null {\n if (rowSink !== null) {\n const id = `t${rowCounter++}`;\n rowSink.push({ kind: 'text', id, signal });\n return `<!--${ROW_TEXT_PREFIX}${id}-->`;\n }\n if (context !== null) {\n const id = `t${context.counter++}`;\n context.list.push({ kind: 'text', id, signal });\n return `<!--${TEXT_MARKER_PREFIX}${id}-->`;\n }\n return null;\n}\n\n/**\n * Wire this render's GLOBAL bindings against the freshly-built live DOM.\n * Disposes `prevDisposers` first (a surrounds-changed morph strips bound attrs\n * and removes inserted text nodes, so we re-establish them), then attaches one\n * effect per hole. Zero-cost when there are no global holes.\n */\nexport function wireBindings(\n rootEl: Element,\n ctx: BindingContext,\n prevDisposers: ReadonlyArray<() => void>,\n): ReadonlyArray<() => void> {\n for (const d of prevDisposers) d();\n if (ctx.list.length === 0) return NO_DISPOSERS;\n const disposers: Array<() => void> = [];\n try {\n wireInto(rootEl, ctx.list, disposers);\n } catch (err) {\n // A hole whose first write threw (a throwing `computed`, a screened URL in\n // dev) must not strand the effects wired before it: nobody would ever\n // receive their disposers.\n for (const d of disposers) d();\n throw err;\n }\n return disposers;\n}\n\n/**\n * Wire one row's ROW-scoped bindings against its freshly-built row node\n * (including the node itself, since a row's top-level element may carry the\n * marker, e.g. `<tr data-kfbrow>`). Returns the row's disposers for the\n * reconciler to store on the bound item and call when the row node is removed.\n */\nexport function wireRowBindings(\n rowNode: Element,\n bindings: Binding[],\n): Array<() => void> {\n // Callers (snapshot buildFreshNodes, mount first-render inline) only invoke\n // this for rows with at least one binding, so no empty-guard is needed.\n //\n // Hot path: most row holes are an attribute on the row's ROOT element (e.g.\n // `<tr class={sig}>`). For those we resolve the node with a single\n // `getAttribute` + allocation-free id membership check — no `querySelectorAll`,\n // no `collectComments` walk, no Map. The descendant-attr index and the\n // text-comment index are built lazily, only if a hole actually needs them.\n // Dense by construction: disposers are PUSHED, never index-assigned, so a\n // skipped hole (the defensive missing-marker `continue` below) leaves no gap\n // for `disposeRowBindings` / `mount()`'s release loop to trip over (KF-5FM1Z6:\n // a sparse slot would throw a TypeError when called during disposal).\n const disposers: Array<() => void> = [];\n const rootIds = rowNode.getAttribute(BIND_ATTR_ROW);\n // For the common single-root-binding row, `rootIds === b.id` resolves the\n // node with zero allocation. Only a row with 2+ root bindings builds the Set.\n let rootIdSet: Set<string> | null = null;\n let descIndex: Map<string, Element> | null = null;\n let textMarkers: Map<string, Comment> | null = null;\n\n try {\n for (let i = 0; i < bindings.length; i++) {\n const b = bindings[i];\n if (b.kind === 'attr') {\n let onRoot = false;\n if (rootIds !== null) {\n if (rootIds === b.id) {\n onRoot = true;\n } else if (rootIds.indexOf(',') !== -1) {\n rootIdSet ??= new Set(rootIds.split(','));\n onRoot = rootIdSet.has(b.id);\n }\n }\n let el: Element | undefined;\n if (onRoot) {\n el = rowNode;\n } else {\n descIndex ??= indexAttrEls(rowNode, BIND_ATTR_ROW);\n el = descIndex.get(b.id);\n }\n // Defensive: a registered binding always emits its marker into the row.\n if (el === undefined) continue;\n disposers.push(attachAttrEffect(el, b.attr, b.signal));\n } else {\n if (textMarkers === null) {\n textMarkers = new Map();\n collectComments(rowNode, ROW_TEXT_PREFIX, textMarkers);\n }\n const marker = textMarkers.get(b.id);\n // Defensive: same invariant as the attr branch.\n if (marker === undefined) continue;\n disposers.push(attachTextEffect(marker, b.signal));\n }\n }\n } catch (err) {\n // Same contract as `wireBindings`: a hole that throws on its first write\n // disposes the row's already-wired effects before the error propagates\n // (only wired holes were pushed, so every entry is callable).\n for (const d of disposers) d();\n throw err;\n }\n return disposers;\n}\n\n/** Dispose a row's binding effects (called when its node leaves the DOM). */\nexport function disposeRowBindings(\n disposers: Array<() => void> | undefined,\n): void {\n if (disposers === undefined) return;\n for (const d of disposers) d();\n}\n\n/**\n * KF-347 — decide, for a row whose DOM node survives an in-place update,\n * whether the live binding effects can be carried forward or must be\n * re-wired. The old effects closed over the signal INSTANCES captured at the\n * previous render; if this render's bindings reference the same instances\n * per hole (the cache-hit case, and rows binding stable external signals),\n * the old effects remain correct and re-wiring would be wasted work — the\n * select-row hot path stays free. If any hole's instance changed — the\n * typical shape after `arraySignal.update()` swaps the row object and the\n * row re-renders fresh `computed`s closing over the NEW item — the old\n * effects are stale (they'd keep reading the pre-update row), so dispose\n * them and wire the fresh bindings against the surviving node. Marker\n * alignment holds across every in-place route: row-local binding ids are\n * deterministic per render, the surgical fast paths leave marker attrs and\n * comments untouched, and `_morphElement` syncs them.\n *\n * Also covers the from-scratch case (`oldBindings` undefined with fresh\n * `newBindings`): the length mismatch routes to the wire branch, so a\n * replaceChild caller can hand its brand-new node straight here.\n */\nexport function carryOrRewireRowBindings(\n node: Element,\n oldBindings: Binding[] | undefined,\n oldDisposers: Array<() => void> | undefined,\n newBindings: Binding[] | undefined,\n): {\n bindings: Binding[] | undefined;\n bindingDisposers: Array<() => void> | undefined;\n} {\n const oldLen = oldBindings === undefined ? 0 : oldBindings.length;\n const newLen = newBindings === undefined ? 0 : newBindings.length;\n if (oldLen === newLen) {\n let same = true;\n for (let i = 0; i < newLen; i++) {\n if (\n (oldBindings as Binding[])[i].signal !==\n (newBindings as Binding[])[i].signal\n ) {\n same = false;\n break;\n }\n }\n if (same) return { bindings: oldBindings, bindingDisposers: oldDisposers };\n }\n disposeRowBindings(oldDisposers);\n return {\n bindings: newBindings,\n bindingDisposers:\n newLen > 0 ? wireRowBindings(node, newBindings as Binding[]) : undefined,\n };\n}\n\n/**\n * GLOBAL-scope wiring core. Indexes `data-kfb`-marked elements + `kfb:`\n * comment markers under `scope` by binding id, then attaches an effect per\n * binding. Row-scope wiring lives in `wireRowBindings` (with its own inlined\n * root-attr fast path), so this always operates on the GLOBAL marker names.\n */\nfunction wireInto(\n scope: Element,\n bindings: Binding[],\n disposers: Array<() => void>,\n): void {\n const attrEls = indexAttrEls(scope, BIND_ATTR);\n const textMarkers = new Map<string, Comment>();\n collectComments(scope, TEXT_MARKER_PREFIX, textMarkers);\n\n for (const b of bindings) {\n if (b.kind === 'attr') {\n const el = attrEls.get(b.id);\n /* c8 ignore next -- defensive: a registered binding always emits its marker into `scope`. */\n if (el === undefined) continue;\n disposers.push(attachAttrEffect(el, b.attr, b.signal));\n } else {\n const marker = textMarkers.get(b.id);\n /* c8 ignore next -- defensive: same invariant as the attr branch. */\n if (marker === undefined) continue;\n disposers.push(attachTextEffect(marker, b.signal));\n }\n }\n}\n\n/**\n * Index every DESCENDANT element under `scope` carrying `attrName` by binding\n * id. Callers that also need the scope root itself check it separately (row\n * roots via the `rootIds` fast path; global holes are never on the mount root).\n */\nfunction indexAttrEls(scope: Element, attrName: string): Map<string, Element> {\n const map = new Map<string, Element>();\n // Skip another mount's markers inside an overlay host slot below `scope`.\n // One lookup decides whether any slot exists, so the common case pays no\n // per-element walk.\n const hasHost = scope.querySelector(OVERLAY_HOST_SELECTOR);\n for (const el of scope.querySelectorAll(`[${attrName}]`)) {\n if (hasHost && scope.contains(el.closest(OVERLAY_HOST_SELECTOR))) continue;\n for (const id of (el.getAttribute(attrName) as string).split(','))\n map.set(id, el);\n }\n return map;\n}\n\n/** Attach a fine-grained attribute effect; returns its disposer. */\nfunction attachAttrEffect(\n el: Element,\n attr: string,\n signal: Signal<unknown>,\n): () => void {\n return effect(() => setBoundAttr(el, attr, signal.value));\n}\n\n/**\n * Tracks the live text node each text-hole marker owns, so RE-wiring a hole\n * (KF-347 — an in-place row update whose binding instances changed) reuses\n * the node it inserted the first time instead of stacking a second one next\n * to it. Keyed on the marker comment: markers persist exactly as long as\n * their row/surround nodes do, so the WeakMap entries die with them.\n */\nconst insertedTextNodes = new WeakMap<Comment, Text>();\n\n/**\n * The live text node `marker` currently owns, or null (KF-374 — morph drops\n * static siblings of a bound hole). Position-strict, same rule as the reuse\n * check below: the node counts as owned only while it still sits at\n * `marker.nextSibling`. The morph's child pairing uses this to step PAST the\n * binding-inserted node — the template never contains it (templates carry\n * only the marker comment), so without the skip a template static sibling\n * pairs positionally with the inserted node, the morph overwrites the bound\n * text with the static content, and the real static sibling falls off the\n * cursor's tail and is removed.\n */\nexport function boundTextNodeOf(marker: Comment): Text | null {\n const t = insertedTextNodes.get(marker);\n return t !== undefined && marker.nextSibling === t ? t : null;\n}\n\n/**\n * Bind a live text node after `marker` to `signal`; returns the effect\n * disposer. Reuses the marker's previously-inserted node when it is still\n * exactly where we put it (`marker.nextSibling`) — a rewire then just\n * retargets the write, preserving node identity. Anything else (fresh parse,\n * node removed by a morph) inserts anew. The reuse check is deliberately\n * position-strict so a static text node that legitimately follows the marker\n * in template output can never be hijacked: static content sits at\n * `nextSibling` only until the FIRST wiring inserts our node in front of it.\n */\nfunction attachTextEffect(\n marker: Comment,\n signal: Signal<unknown>,\n): () => void {\n let text = insertedTextNodes.get(marker);\n if (text === undefined || marker.nextSibling !== text) {\n text = (marker.ownerDocument as Document).createTextNode('');\n (marker.parentNode as Node).insertBefore(text, marker.nextSibling);\n insertedTextNodes.set(marker, text);\n }\n const node = text;\n return effect(() => {\n node.data = coerceText(signal.value);\n });\n}\n\n/**\n * Apply a bound value to a live attribute, mirroring the JSX runtime's\n * `renderAttr` rules: boolean/nullish toggling, `SafeHtml` (raw()) unwrapping,\n * and the KF-297 URL-screening that drops `javascript:` / `vbscript:` /\n * `data:text/html` on `href`/`src`/`formaction`/`action`/`xlink:href`.\n *\n * Unlike `renderAttr` (which builds an HTML string, so escapes with\n * `escapeAttr`), this writes the RAW value via `setAttribute` — the DOM stores\n * attribute values verbatim, so no HTML-escaping is applied here.\n *\n * The attribute NAME is trusted here: `on*` and malformed names are rejected at\n * binding-registration time in the JSX runtime's `jsx()` signal branch (shared\n * `assertEmittableAttrName`, KF-322), so `setAttribute('onclick', …)` — which\n * would install a live inline handler — can never reach this writer.\n */\nfunction setBoundAttr(el: Element, name: string, value: unknown): void {\n // KF-335: every attribute write below also syncs the matching form-state\n // property (checked/value/selected) — after user interaction the dirty flag\n // detaches the property from the attribute, so an attribute-only write\n // leaves the visible state stale. `syncFormProp` no-ops for other names.\n if (value == null || value === false) {\n el.removeAttribute(name);\n syncFormProp(el, name, '', false);\n return;\n }\n if (value === true) {\n el.setAttribute(name, '');\n syncFormProp(el, name, '', true);\n return;\n }\n // SafeHtml (raw()) is the documented opt-out — bypasses URL screening and is\n // written verbatim, matching renderAttr's SafeHtml branch.\n if (isSafeHtmlValue(value)) {\n el.setAttribute(name, value.__html);\n syncFormProp(el, name, value.__html, true);\n return;\n }\n const str = String(value);\n if (isDangerousUrlValue(name, str)) {\n // KF-340: throw in dev (fail loudly), warn+drop in prod.\n reportDangerousUrl('kerf binding', name, str);\n el.removeAttribute(name);\n return;\n }\n el.setAttribute(name, str);\n syncFormProp(el, name, str, true);\n}\n\n// Cross-bundle SafeHtml brand check (mirrors jsx-runtime's `isSafeHtml`).\n// Duplicated here — rather than importing `isSafeHtml` from jsx-runtime — to\n// keep the bindings ← jsx-runtime dependency acyclic. `Symbol.for` makes it\n// recognize SafeHtml instances from any copy of the module.\nconst SAFE_HTML_BRAND = Symbol.for('kerfjs.SafeHtml');\nfunction isSafeHtmlValue(v: unknown): v is { __html: string } {\n return (\n typeof v === 'object' &&\n v !== null &&\n (v as Record<symbol, unknown>)[SAFE_HTML_BRAND] === true\n );\n}\n\n/** Coerce a bound text value: nullish + boolean render nothing (React-style). */\nfunction coerceText(value: unknown): string {\n if (value == null || typeof value === 'boolean') return '';\n return String(value);\n}\n\n/**\n * Collect comment markers with the given prefix under `node`, keyed by id.\n * Hand-rolled recursion (same as `mount.ts`'s comment walker) because\n * happy-dom's `TreeWalker` does not surface comment nodes.\n */\nfunction collectComments(\n node: Node,\n prefix: string,\n out: Map<string, Comment>,\n): void {\n for (let c: Node | null = node.firstChild; c !== null; c = c.nextSibling) {\n if (c.nodeType === Node.COMMENT_NODE) {\n const data = (c as Comment).data;\n if (data.startsWith(prefix))\n out.set(data.slice(prefix.length), c as Comment);\n } else if (\n c.nodeType === Node.ELEMENT_NODE &&\n !isOverlayHost(c as Element) // another mount's markers live there\n ) {\n collectComments(c, prefix, out);\n }\n }\n}\n\n// Re-exported so the JSX runtime imports signal-detection + binding\n// registration from one place.\nexport { isSignal };\n","/**\n * HTML / attribute escaping for the JSX runtime. Identical to the helpers\n * used in any reasonable HTML emitter — included here so kerf has no extra\n * runtime dependencies beyond `@preact/signals-core`.\n */\n\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\nexport function escapeAttr(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","/**\n * JSX → HTML / SVG attribute name aliases.\n *\n * The JSX runtime translates camelCase attributes (React convention) to\n * the kebab-case / colon-form names the browser actually wants. Anything\n * not in this map is passed through verbatim — `data-*`, `aria-*`, and\n * any custom attribute work without ceremony.\n *\n * Lives in its own module so the alias data stays a separable concern from\n * `src/jsx-runtime.ts`'s runtime logic; the bulk of `jsx-runtime.ts` was this\n * table.\n *\n * **When a camelCase attribute needs an entry.** Pass-through is not the same\n * as \"no translation\": an unlisted `fooBar` is emitted verbatim, and the HTML\n * parser then lowercases it to `foobar`. That is silently *correct* whenever\n * the real attribute name is exactly the lowercased spelling (`fetchPriority`\n * → `fetchpriority`, `charSet` → `charset`, `playsInline` → `playsinline`), and\n * silently *wrong* whenever it isn't — `defaultSelected` rendered\n * `defaultselected`, an attribute no browser has ever read, so the option it\n * was supposed to pre-select never was. Any camelCase key whose HTML name is\n * not its own lowercase MUST have a row here.\n *\n * `tests/unit/jsx-attr-names.test.ts` pins that rule: it reads every attribute\n * key declared in `src/jsx-types.ts` and asserts the name kerf would actually\n * emit for each camelCase one, so a new typed attribute cannot join without\n * someone stating what it renders as.\n *\n * SVG is the reason the lowercasing shortcut can't be relied on in general —\n * SVG attribute names are case-sensitive, and only the fixed set in the HTML\n * parser's SVG adjustment table (`viewBox`, `preserveAspectRatio`,\n * `pathLength`, …) survives the round trip with its case intact.\n */\n\nexport const ATTR_ALIASES: Record<string, string> = {\n // HTML attributes\n className: 'class',\n htmlFor: 'for',\n httpEquiv: 'http-equiv',\n acceptCharset: 'accept-charset',\n accessKey: 'accesskey',\n autoCapitalize: 'autocapitalize',\n autoComplete: 'autocomplete',\n autoFocus: 'autofocus',\n autoPlay: 'autoplay',\n colSpan: 'colspan',\n contentEditable: 'contenteditable',\n crossOrigin: 'crossorigin',\n dateTime: 'datetime',\n defaultChecked: 'checked',\n defaultSelected: 'selected',\n defaultValue: 'value',\n encType: 'enctype',\n formAction: 'formaction',\n formEncType: 'formenctype',\n formMethod: 'formmethod',\n formNoValidate: 'formnovalidate',\n formTarget: 'formtarget',\n hrefLang: 'hreflang',\n inputMode: 'inputmode',\n maxLength: 'maxlength',\n minLength: 'minlength',\n noModule: 'nomodule',\n noValidate: 'novalidate',\n readOnly: 'readonly',\n referrerPolicy: 'referrerpolicy',\n rowSpan: 'rowspan',\n spellCheck: 'spellcheck',\n srcDoc: 'srcdoc',\n srcLang: 'srclang',\n srcSet: 'srcset',\n tabIndex: 'tabindex',\n useMap: 'usemap',\n\n // SVG presentation attributes (camelCase → kebab-case)\n strokeWidth: 'stroke-width',\n strokeLinecap: 'stroke-linecap',\n strokeLinejoin: 'stroke-linejoin',\n strokeDasharray: 'stroke-dasharray',\n strokeDashoffset: 'stroke-dashoffset',\n strokeMiterlimit: 'stroke-miterlimit',\n strokeOpacity: 'stroke-opacity',\n fillOpacity: 'fill-opacity',\n fillRule: 'fill-rule',\n clipPath: 'clip-path',\n clipRule: 'clip-rule',\n colorInterpolation: 'color-interpolation',\n colorInterpolationFilters: 'color-interpolation-filters',\n floodColor: 'flood-color',\n floodOpacity: 'flood-opacity',\n lightingColor: 'lighting-color',\n stopColor: 'stop-color',\n stopOpacity: 'stop-opacity',\n shapeRendering: 'shape-rendering',\n imageRendering: 'image-rendering',\n textRendering: 'text-rendering',\n pointerEvents: 'pointer-events',\n vectorEffect: 'vector-effect',\n paintOrder: 'paint-order',\n\n // SVG text/font attributes\n fontFamily: 'font-family',\n fontSize: 'font-size',\n fontStyle: 'font-style',\n fontVariant: 'font-variant',\n fontWeight: 'font-weight',\n fontStretch: 'font-stretch',\n textAnchor: 'text-anchor',\n textDecoration: 'text-decoration',\n dominantBaseline: 'dominant-baseline',\n alignmentBaseline: 'alignment-baseline',\n baselineShift: 'baseline-shift',\n letterSpacing: 'letter-spacing',\n wordSpacing: 'word-spacing',\n writingMode: 'writing-mode',\n\n // SVG marker attributes\n markerStart: 'marker-start',\n markerMid: 'marker-mid',\n markerEnd: 'marker-end',\n\n // SVG xlink (legacy but still used)\n xlinkHref: 'xlink:href',\n xlinkShow: 'xlink:show',\n xlinkActuate: 'xlink:actuate',\n xlinkType: 'xlink:type',\n xlinkRole: 'xlink:role',\n xlinkTitle: 'xlink:title',\n xlinkArcrole: 'xlink:arcrole',\n xmlBase: 'xml:base',\n xmlLang: 'xml:lang',\n xmlSpace: 'xml:space',\n xmlnsXlink: 'xmlns:xlink',\n};\n","/**\n * kerf JSX runtime.\n *\n * JSX renders to `SafeHtml`, which wraps both:\n * - `__html`: the flattened HTML string (what `toString()` returns; what\n * legacy/SSR consumers care about)\n * - `__segment`: a structured representation that distinguishes \"static\n * html\", \"keyed list\", and \"mixed\" content.\n *\n * Most renders are pure-static and the segment is just `{kind:'static',html}`.\n * When the tree contains a list (via `each()`) or a parent whose children\n * include a non-static segment, the runtime threads that structure up so\n * `mount()` can dispatch on it — running its native keyed reconciler for\n * the list parts and leaving the static surrounds to the general-purpose\n * diff.\n *\n * Configure in your `tsconfig.json`:\n *\n * \"jsx\": \"react-jsx\",\n * \"jsxImportSource\": \"kerfjs\"\n *\n * Then write JSX as you normally would — kerf provides the `jsx` /\n * `jsxs` / `jsxDEV` / `Fragment` exports the JSX transform looks for.\n */\n\nimport { bindAttr, bindMarkerAttr, bindText, isSignal } from './bindings.js';\nimport type { KerfBuiltinIntrinsicElements } from './jsx-types.js';\nimport type { ReadonlySignal, Signal } from './reactive.js';\nimport {\n flatten,\n type ListSegment,\n mergeChildSegments,\n type Segment,\n wrapWithTags,\n} from './segment.js';\nimport { escapeAttr, escapeHtml } from './utils/escapeHtml.js';\nimport { ATTR_ALIASES } from './utils/jsx-attr-aliases.js';\nimport { isDangerousUrlValue, reportDangerousUrl } from './utils/url-screen.js';\n\n// Cross-realm/cross-bundle brand. Using `Symbol.for` (the global registry)\n// means two `SafeHtml` classes from different module copies still recognize\n// each other. Same approach React uses for `$$typeof: Symbol.for('react.element')`.\n// Without this, `instanceof SafeHtml` fails when the consumer's bundler ends\n// up loading two copies of kerf (separate barrel + jsx-runtime entries,\n// monorepo dedup misses, ESM/CJS interop, etc.).\nconst SAFE_HTML_BRAND = Symbol.for('kerfjs.SafeHtml');\n\nexport class SafeHtml {\n readonly __html: string;\n readonly __segment: Segment;\n // Branded so `isSafeHtml()` recognizes instances from any copy of this module.\n readonly [SAFE_HTML_BRAND] = true as const;\n constructor(input: string | Segment) {\n if (typeof input === 'string') {\n this.__segment = { kind: 'static', html: input };\n this.__html = input;\n } else {\n this.__segment = input;\n this.__html = flatten(input, false);\n }\n }\n toString(): string {\n return this.__html;\n }\n}\n\n/**\n * Type guard for `SafeHtml`. Prefer this over `instanceof SafeHtml` — it works\n * across module copies (e.g. when the consumer's bundler loads kerf's barrel\n * and JSX-runtime entries as independent modules).\n *\n * Security note (KF-321): this is a duck-check on the global `Symbol.for` brand,\n * so same-realm code *can* forge a \"trusted\" value — `{ [SAFE_HTML_BRAND]: true,\n * __html: '<img onerror=…>' }` passes and bypasses escaping + the URL screen.\n * This is intentional and not a vulnerability: minting the brand requires a\n * Symbol key, which no data channel (JSON.parse, form/query/localStorage,\n * structuredClone, JSON-based prototype-pollution) can produce — those all yield\n * string keys. The only way to forge it is to run JS that writes the symbol, and\n * such code can equally `import { raw }`. Forgery therefore grants no capability\n * an attacker with code execution lacks — the same posture as React's\n * `$$typeof: Symbol.for('react.element')`. The global `Symbol.for` (vs a\n * module-private symbol) is a deliberate cross-bundle-recognition tradeoff (see\n * the `SAFE_HTML_BRAND` note above); a private symbol would additionally block\n * same-realm forgery, but only closes a non-threat at the cost of that\n * recognition, so it's kept global by design.\n */\nexport function isSafeHtml(value: unknown): value is SafeHtml {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[SAFE_HTML_BRAND] === true\n );\n}\n\n/**\n * Inject a pre-escaped HTML string verbatim, bypassing kerf's auto-escaping.\n *\n * **Reach for this rarely.** kerf escapes automatically everywhere else, so a lot\n * of `raw()` in a codebase is usually a sign the wrong tool is being used — the\n * common cases have a safer, first-class answer:\n * - Interpolating dynamic text/attributes? Plain JSX (`<p>{value}</p>`,\n * `class={sig}`) already escapes it; you don't need `raw()`.\n * - Composing markup? Build a {@link SafeHtml} the normal way — a JSX expression,\n * the `html` tagged template (`kerfjs/html`), `each()`, or a component function\n * that returns JSX. Those all produce trusted `SafeHtml` without hand-writing\n * an HTML string.\n * - A genuinely trusted, pre-escaped **dynamic** value (server output, config, a\n * hard-coded string)? That's the one legitimate use. The\n * `kerfjs/no-raw-with-dynamic-arg` lint rule flags a `raw()` whose argument is\n * not a literal — because unsanitized user input is the canonical XSS mistake —\n * so acknowledge it with a `// eslint-disable-next-line\n * kerfjs/no-raw-with-dynamic-arg` at that call. That explicit override is the\n * single sanctioned way to say \"I've reviewed this; it's trusted,\" and it\n * leaves a searchable audit trail.\n *\n * `raw()` is NOT a sanitizer — it does no escaping. For user-controlled input,\n * sanitize first (`raw(DOMPurify.sanitize(marked(userMarkdown)))`) or, better,\n * render it through escaping JSX instead.\n */\nexport function raw(html: string): SafeHtml {\n return new SafeHtml(html);\n}\n\n/**\n * Internal: build a `SafeHtml` representing a list segment. Used by\n * `each()` so the JSX runtime is the sole owner of `SafeHtml` construction.\n */\nexport function listSafeHtml(\n id: string,\n items: ListSegment['items'],\n source?: object,\n): SafeHtml {\n return new SafeHtml({ kind: 'list', id, items, source });\n}\n\n/**\n * Internal: build a `SafeHtml` representing a granular list segment with\n * patches (KF-92). The reconciler applies the patches to the existing\n * binding directly, skipping the per-item iteration that the snapshot\n * `listSafeHtml` requires. `items` is included for fall-through paths\n * (toString during SSR, fall-back when the binding doesn't exist yet).\n *\n * Patch HTML is rendered upstream (in `each()`) inside a try/catch — see\n * KF-99 — so by the time we get here every `update` / `insert` patch\n * already carries a `html` string, and the reconciler does no further\n * row rendering.\n */\nexport function granularListSafeHtml(\n id: string,\n items: ListSegment['items'],\n patches: NonNullable<ListSegment['patches']>,\n source?: object,\n): SafeHtml {\n return new SafeHtml({ kind: 'list', id, items, patches, source });\n}\n\n/**\n * Content accepted by a Kerf JSX child position.\n *\n * Use this for `children` (and equivalent content slots) on third-party\n * function components. It deliberately mirrors the runtime: arrays may be\n * readonly and nested to any depth, signals bind fine-grained inside a mount,\n * and boolean/nullish values render nothing.\n */\nexport type JSXChildren =\n | SafeHtml\n | string\n | number\n | boolean\n | null\n | undefined\n | ReadonlySignal<unknown>\n | readonly JSXChildren[];\n\ninterface Props {\n children?: JSXChildren;\n [key: string]: unknown;\n}\n\nconst VOID_TAGS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'source',\n 'track',\n 'wbr',\n]);\n\n/**\n * Convert a single JSX child into a Segment. Handles SafeHtml passthrough,\n * primitive coercion + escaping, arrays (recursive), and the nullish/false\n * skip cases.\n */\nfunction toSegment(child: JSXChildren): Segment {\n if (child == null || typeof child === 'boolean')\n return { kind: 'static', html: '' };\n // KF-294: a signal handed straight into a text position. Inside a mount\n // render, emit a comment marker and record a binding so the text node\n // updates fine-grained (no render re-run). Outside a mount (SSR/toString),\n // snapshot the current value as escaped text.\n if (isSignal(child)) {\n const marker = bindText(child);\n if (marker !== null) return { kind: 'static', html: marker };\n const v = (child as Signal<unknown>).value;\n return {\n kind: 'static',\n html: v == null || typeof v === 'boolean' ? '' : escapeHtml(String(v)),\n };\n }\n if (isSafeHtml(child)) {\n // Cross-bundle SafeHtml shims (KF-14 case) may have only `__html`.\n return child.__segment ?? { kind: 'static', html: child.__html };\n }\n if (typeof child === 'string')\n return { kind: 'static', html: escapeHtml(child) };\n if (typeof child === 'number') return { kind: 'static', html: String(child) };\n if (Array.isArray(child)) return mergeChildSegments(child.map(toSegment));\n // Catch the common mistake of passing a DOM element (e.g. the result of\n // toElement(...)) as a JSX child. The runtime renders to HTML strings, so\n // DOM nodes can't be composed — they'd silently serialize to \"\" and their\n // event listeners would be lost. Throw loudly so this can't sneak in.\n const maybeNode = child as unknown;\n if (\n typeof maybeNode === 'object' &&\n maybeNode !== null &&\n ('nodeType' in maybeNode || 'outerHTML' in maybeNode)\n ) {\n throw new Error(\n 'JSX: DOM elements cannot be passed as children (the JSX runtime renders to HTML strings). ' +\n 'Build the tree in one JSX expression and use querySelector after toElement() to get element refs.',\n );\n }\n throw new Error(\n `JSX: unsupported child of type ${describeValue(child)}. ` +\n 'Children must be SafeHtml, string, number, boolean, null, undefined, a signal/computed, or a nested array of those. ' +\n 'Common mistakes: passing a Store object directly (use store.state.value), ' +\n 'passing a function (call it first), or passing a Promise (await it before render).',\n );\n}\n\nfunction describeValue(v: unknown): string {\n if (Array.isArray(v)) return 'array';\n if (typeof v === 'object' && v !== null) {\n const ctor = (v as { constructor?: { name?: string } }).constructor?.name;\n return ctor && ctor !== 'Object' ? `object (${ctor})` : 'object';\n }\n return typeof v;\n}\n\n/**\n * A well-formed HTML/SVG attribute name: a letter/underscore/colon, then\n * letters, digits, or `_.:-` — covers `class`, `data-id`, `aria-label`,\n * `xlink:href`, `xmlns:xlink`, `stroke-width`, `viewBox`, etc. A name outside\n * this shape can only come from spreading untrusted KEYS into JSX\n * (`<div {...obj}>`), and is exactly the shape that could carry the whitespace,\n * `>`, `=`, quotes, or control chars needed to break out of the open tag and\n * inject markup — so `renderAttr` rejects it (KF-306).\n */\nconst SAFE_ATTR_NAME = /^[A-Za-z_:][\\w.:-]*$/;\n\n/**\n * Reject an attribute NAME that kerf must never route into HTML or the DOM.\n * Shared by BOTH attribute paths so they enforce one contract (KF-306, KF-322):\n *\n * - the static string path (`renderAttr`) — a name is emitted verbatim into\n * the open tag, and `on*` values become live inline handlers once parsed;\n * - the fine-grained bound path (`jsx()` signal branch → `bindAttr` →\n * `setBoundAttr` → `el.setAttribute(name, …)`) — `setAttribute('onclick', …)`\n * installs a LIVE handler in the browser, an XSS vector that would otherwise\n * bypass the static guard entirely.\n *\n * Two rejections:\n * 1. `on*` (any case) — kerf's model is event delegation, never inline\n * handlers. `isFn` tailors the message: function values get the\n * delegate() migration pointer; string/signal/other values get the\n * XSS-aware message.\n * 2. a malformed name (spread of untrusted keys, `<div {...obj}>`) that could\n * break out of the open tag. The bound path can't actually inject markup\n * this way — `setAttribute` throws `InvalidCharacterError` rather than\n * parsing — but the same guard runs on both paths for one consistent\n * contract. Validated post-alias; every `ATTR_ALIASES` value is itself a\n * valid name, so aliasing is unaffected.\n */\nfunction assertEmittableAttrName(\n key: string,\n name: string,\n isFn: boolean,\n): void {\n if (/^on[a-z]/i.test(name)) {\n if (isFn) {\n throw new Error(\n `JSX: inline event handlers like ${key}={fn} are not supported by kerf's JSX → HTML-string runtime. ` +\n 'Use event delegation from the mount root instead:\\n\\n' +\n \" delegate(rootEl, 'click', '[data-action=\\\"...\\\"]', (evt, target) => { ... });\\n\" +\n ' <button data-action=\"...\">click</button>\\n\\n' +\n 'See docs/5-event-delegation.md for the tier-1/tier-2/tier-3 model.',\n );\n }\n throw new Error(\n `JSX: event-handler attribute ${JSON.stringify(key)} is not allowed — an ` +\n '`on*` attribute (whether a string emitted into HTML or a signal bound via ' +\n 'setAttribute) installs a live inline handler, an XSS vector. ' +\n \"kerf uses event delegation: delegate(rootEl, 'click', '[data-action=\\\"...\\\"]', handler). \" +\n 'See docs/5-event-delegation.md.',\n );\n }\n if (!SAFE_ATTR_NAME.test(name)) {\n throw new Error(\n `JSX: invalid attribute name ${JSON.stringify(key)}. Attribute names must be a ` +\n 'letter/underscore/colon followed by letters, digits, or \"_.:-\" (e.g. class, ' +\n 'data-id, aria-label, xlink:href). This usually means an untrusted object was ' +\n 'spread into JSX ({...obj}) with attacker-controlled keys — validate keys first.',\n );\n }\n}\n\nfunction renderAttr(key: string, value: unknown): string {\n return renderAttrNamed(key, ATTR_ALIASES[key] ?? key, value);\n}\n\n/**\n * Core attribute renderer. `key` is the author-written attribute key (used in\n * error messages); `name` is the final emitted attribute name — post-alias for\n * JSX, verbatim for the `kerfjs/html` tagged-template path.\n */\nfunction renderAttrNamed(key: string, name: string, value: unknown): string {\n if (value == null || value === false) return '';\n assertEmittableAttrName(key, name, typeof value === 'function');\n if (value === true) return ` ${name}`;\n let strValue: string;\n if (isSafeHtml(value)) {\n strValue = value.__html;\n } else if (typeof value === 'number') {\n strValue = String(value);\n } else if (typeof value === 'string') {\n // URL-screening shared with the fine-grained binding writer (KF-297).\n // `SafeHtml` values (raw()) skip this — they hit the branch above.\n // KF-340: throw in dev (fail loudly), warn+drop in prod.\n if (isDangerousUrlValue(name, value)) {\n reportDangerousUrl('JSX', name, value);\n return '';\n }\n strValue = escapeAttr(value);\n } else {\n throw new Error(\n `JSX: unsupported value for attribute \"${key}\" — got ${describeValue(value)}. ` +\n 'Attribute values must be string, number, boolean, null, undefined, or SafeHtml. ' +\n 'Did you mean to read .value off a Signal, or stringify the object first?',\n );\n }\n return ` ${name}=\"${strValue}\"`;\n}\n\nexport function jsx(\n tag: string | ((props: Props) => SafeHtml),\n props: Props,\n): SafeHtml {\n if (typeof tag === 'function') return tag(props);\n\n const { children, ...attrs } = props;\n let attrStr = '';\n let bindIds: string[] | null = null;\n for (const [k, v] of Object.entries(attrs)) {\n // KF-294: a signal handed straight into an attribute. Inside a mount\n // render, register a binding and mark the element (via `data-kfb` for\n // static-surround holes / `data-kfbrow` for each()-row holes) instead of\n // emitting the attribute — the wiring pass sets it after parse and keeps\n // it fine-grained. Outside a mount, snapshot the current value.\n if (isSignal(v)) {\n const name = ATTR_ALIASES[k] ?? k;\n // Reject `on*` / malformed names on the bound path too (KF-322 completes\n // KF-306). Unconditional: a signal's value can change over time, so we\n // can't gate on the current value — an `onclick={signal}` binding would\n // otherwise install a live inline handler via setAttribute, bypassing the\n // static renderAttr guard entirely. `isFn` is false: a signal is not a\n // function value, so this routes to the XSS-aware message.\n assertEmittableAttrName(k, name, false);\n const id = bindAttr(name, v);\n if (id !== null) {\n (bindIds ??= []).push(id);\n continue;\n }\n attrStr += renderAttr(k, (v as Signal<unknown>).value);\n continue;\n }\n attrStr += renderAttr(k, v);\n }\n // All signal attrs on one element share a scope, so the marker attr name is\n // fetched once (avoids a per-attr object alloc from bindAttr).\n if (bindIds !== null)\n attrStr += ` ${bindMarkerAttr()}=\"${bindIds.join(',')}\"`;\n\n if (VOID_TAGS.has(tag)) return new SafeHtml(`<${tag}${attrStr}>`);\n\n const childSegment: Segment =\n children != null ? toSegment(children) : { kind: 'static', html: '' };\n return new SafeHtml(\n wrapWithTags(childSegment, `<${tag}${attrStr}>`, `</${tag}>`),\n );\n}\n\nexport { jsx as jsxs };\n// vitest's dev-mode JSX transform emits `jsxDEV(tag, props, ...)`; the\n// alias lets tests import this module without the production build pipeline\n// caring.\nexport { jsx as jsxDEV };\n\nexport function Fragment({ children }: { children?: JSXChildren }): SafeHtml {\n return new SafeHtml(\n children != null ? toSegment(children) : { kind: 'static', html: '' },\n );\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace JSX {\n export type Element = SafeHtml;\n export interface ElementChildrenAttribute {\n children: unknown;\n }\n // Per-tag attribute contracts live in `./jsx-types.ts` as\n // `KerfBuiltinIntrinsicElements`. Re-exposed as an **interface** (not a\n // type alias) so consumers can declaration-merge custom-element tags\n // (KF-100):\n //\n // declare module 'kerfjs/jsx-runtime' {\n // namespace JSX {\n // interface IntrinsicElements {\n // 'my-element': KerfCustomElement & { foo?: string };\n // }\n // }\n // }\n //\n // KF-123: the imported interface is named `KerfBuiltinIntrinsicElements`\n // upstream so tsup/tsc cannot strip an import alias and end up emitting\n // `interface IntrinsicElements extends IntrinsicElements {}` in the .d.ts\n // — that shadowed form self-resolves to empty and breaks every `<tag>` in\n // consumer .tsx with TS2339. Verified against `dist/jsx-runtime.d.ts` by\n // `tests/dist/jsx-typing/` on every `npm run build`.\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n export interface IntrinsicElements extends KerfBuiltinIntrinsicElements {}\n}\n\n/**\n * Internal coordination exports for the `kerfjs/html` tagged-template\n * front-end (`src/html.ts`). Underscore-prefixed and deliberately NOT in the\n * main barrel — the contract is that `html\\`\\`` routes through the exact same\n * value semantics as JSX (text holes: `_toSegment`; attribute holes:\n * `_assertEmittableAttrName` + `_renderAttrVerbatim`), so the two authoring\n * paths cannot drift. `_renderAttrVerbatim` skips the camelCase\n * `ATTR_ALIASES` table: template authors write real HTML attribute names\n * (`class`, not `className`).\n */\nexport function _toSegment(child: unknown): Segment {\n return toSegment(child as JSXChildren);\n}\n\nexport function _renderAttrVerbatim(name: string, value: unknown): string {\n return renderAttrNamed(name, name, value);\n}\n\nexport { assertEmittableAttrName as _assertEmittableAttrName };\n\n/**\n * Public re-exports of the JSX type primitives so consumers can compose\n * attribute interfaces for custom elements without reaching into\n * `kerfjs/jsx-types` (which is intentionally not in `package.json#exports`).\n */\nexport type {\n AttrLike,\n AttrValue,\n DataAriaAttrs,\n KerfBaseAttrs,\n KerfCustomElement,\n} from './jsx-types.js';\n"]}
@@ -55,4 +55,4 @@ function rowContractError(index, html, parent) {
55
55
 
56
56
  export { collectTemplateChildren, parseRowTemplate, parseSingleRow, rowContractError, truncateRowHtml };
57
57
 
58
- //# sourceMappingURL=chunk-V46JKE44.js.map
58
+ //# sourceMappingURL=chunk-WKIPLNVO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/row-contract.ts"],"names":[],"mappings":"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","file":"chunk-WKIPLNVO.js","sourcesContent":["/**\n * Shared \"exactly one top-level element per row\" contract helpers (KF-103).\n *\n * The keyed-list reconciler in `list-reconcile.ts` and the first-render\n * binding path in `mount.ts` both enforce the same contract on `each()`\n * row HTML. Centralizing the constants and helpers here keeps the three\n * call sites that build error snippets and the eight call sites that\n * parse a row's HTML into a template element from drifting (KF-111 +\n * KF-115).\n */\n\n/**\n * Truncation limit applied to row HTML when including it in a contract-\n * violation error message. Keeps the error readable when a row produced\n * megabytes of HTML by accident.\n */\nexport const ROW_HTML_SNIPPET_MAX = 120;\n\n/**\n * Truncate a row's HTML for inclusion in an error message. Returns the\n * raw string if it's short enough; otherwise the first\n * `ROW_HTML_SNIPPET_MAX` characters with a trailing ellipsis.\n */\nexport function truncateRowHtml(html: string): string {\n return html.length > ROW_HTML_SNIPPET_MAX\n ? html.slice(0, ROW_HTML_SNIPPET_MAX) + '…'\n : html;\n}\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst MATHML_NS = 'http://www.w3.org/1998/Math/MathML';\n\n/**\n * HTML integration points: foreign-content elements under which the HTML parser\n * UNCONDITIONALLY re-enters HTML content, so an `each()` row parented there is\n * ordinary HTML and must NOT be wrapped. SVG has three (`foreignObject`, `desc`,\n * `title`); MathML has the five text integration points (`mi`, `mo`, `mn`, `ms`,\n * `mtext`). localNames are case-preserving for both namespaces, so `foreignObject`\n * matches as authored.\n *\n * `annotation-xml` is deliberately absent: it is a MathML HTML integration point\n * only CONDITIONALLY (with `encoding=\"text/html\"`/`application/xhtml+xml`),\n * otherwise its children are MathML. That conditional rule for a rare element is\n * out of scope — a MathML parent not in the text set always wraps in `<math>`.\n */\nconst SVG_HTML_INTEGRATION = new Set(['foreignObject', 'desc', 'title']);\nconst MATHML_TEXT_INTEGRATION = new Set(['mi', 'mo', 'mn', 'ms', 'mtext']);\n\n/**\n * The foreign-content wrapper tag a row destined for `parent` must be parsed\n * inside — `'svg'` (KF-389) or `'math'` (KF-417) — or `null` for the ordinary\n * HTML path.\n *\n * A bare `<template>` parses in the HTML namespace, so an `each()` row like\n * `<circle/>` or `<mn/>` comes back an `HTMLUnknownElement` and never paints as\n * SVG/MathML. First render escapes this because rows are inlined into the mount\n * root's `innerHTML`, where the surrounding `<svg>`/`<math>` puts the parser in\n * foreign-content mode; every LATER parse (granular insert, snapshot rebuild,\n * structural update) goes through the bare template and produces dead nodes.\n * `parseRowTemplate` re-enters foreign content by wrapping the row HTML in this\n * tag, so those parses namespace the rows the same way the first render did.\n *\n * A parent that is itself an HTML integration point (see the sets above) takes\n * the ordinary HTML path — wrapping HTML rows in `<svg>`/`<math>` there would\n * either crash (a breakout tag pops the wrapper, emptying it) or mis-namespace\n * the row (KF-420).\n */\nfunction foreignWrapper(\n parent: Element | null | undefined,\n): 'svg' | 'math' | null {\n if (parent == null) return null;\n const { namespaceURI, localName } = parent;\n if (namespaceURI === SVG_NS) {\n return SVG_HTML_INTEGRATION.has(localName) ? null : 'svg';\n }\n if (namespaceURI === MATHML_NS) {\n return MATHML_TEXT_INTEGRATION.has(localName) ? null : 'math';\n }\n return null;\n}\n\n/**\n * Parse a row's HTML and report how many top-level elements it produced. The\n * reconciler uses the count to detect contract violations (count !== 1) on\n * both single-row and bulk-row paths.\n *\n * `parent` is the list's live parent when the caller knows it; it selects the\n * parse namespace (KF-389). Callers that only need the element COUNT for an\n * error message may omit it — the count is namespace-independent.\n *\n * Returns a `DocumentFragment` either way, so callers insert it identically.\n */\nexport function parseRowTemplate(\n html: string,\n parent?: Element | null,\n): { content: DocumentFragment; count: number } {\n const tpl = document.createElement('template');\n const wrapper = foreignWrapper(parent);\n if (wrapper === null) {\n tpl.innerHTML = html;\n return { content: tpl.content, count: tpl.content.children.length };\n }\n // Re-enter foreign content the same way the first render does: wrap in an\n // <svg>/<math> so the HTML parser namespaces the rows, then lift them out.\n // Using the parser (rather than DOMParser/XML) keeps row markup as forgiving\n // here as it is on first render — the two paths must accept the same input.\n tpl.innerHTML = `<${wrapper}>${html}</${wrapper}>`;\n const host = tpl.content.firstElementChild as Element;\n const content = document.createDocumentFragment();\n while (host.firstChild !== null) content.appendChild(host.firstChild);\n return { content, count: content.children.length };\n}\n\n/**\n * Parse one row's HTML to its single top-level element, enforcing the\n * \"exactly one top-level element per row\" contract with a row-precise error.\n * Shared by the granular reconciler's single-row paths and the snapshot\n * in-place morph path.\n */\nexport function parseSingleRow(\n html: string,\n index: number,\n parent?: Element | null,\n): Element {\n const { content, count } = parseRowTemplate(html, parent);\n if (count !== 1) throw rowContractError(index, html, parent);\n return content.firstElementChild as Element;\n}\n\n/**\n * Capture the first `n` element children of a parsed row fragment into an\n * array BEFORE a fragment insert empties it. Callers have already verified the\n * parse count equals `n`, so the walk never sees a null.\n */\nexport function collectTemplateChildren(\n content: DocumentFragment,\n n: number,\n): Element[] {\n const nodes = new Array<Element>(n);\n let child = content.firstElementChild;\n for (let k = 0; k < n; k++) {\n nodes[k] = child as Element;\n child = (child as Element).nextElementSibling;\n }\n return nodes;\n}\n\n/**\n * Build a precise contract-violation `Error` for the row at `index` whose\n * render produced the given `html`. The thrown message mentions the row\n * index, the actual element count, and the (truncated) HTML so the author\n * can locate and fix the offending row quickly.\n *\n * `parent` selects the parse namespace so the reported count matches what the\n * reconciler actually saw (KF-420): re-parsing without it could count a row\n * differently under a foreign-content parent and print a self-contradictory\n * \"produced 1 top-level elements; exactly one is required\".\n */\nexport function rowContractError(\n index: number,\n html: string,\n parent?: Element | null,\n): Error {\n const { count } = parseRowTemplate(html, parent);\n const reason =\n count === 0\n ? 'produced no top-level element'\n : `produced ${count} top-level elements; exactly one is required`;\n return new Error(\n `each(): row render at index ${index} ${reason}. ` +\n \"Each item's render must return exactly one element — \" +\n 'wrap multiple roots in a single parent (e.g. <li>...</li>). ' +\n `Got HTML: ${JSON.stringify(truncateRowHtml(html))}`,\n );\n}\n"]}
@@ -1,37 +1,3 @@
1
- /**
2
- * Tiny event-delegation helpers. Replace per-element `addEventListener` calls
3
- * (which don't survive morph re-renders for nodes the diff creates) with one
4
- * listener at the morph-root that dispatches via `closest()`.
5
- *
6
- * Three-tier listener model:
7
- *
8
- * - Tier 1 (bubbling events) — use `delegate()`.
9
- * click, input, change, submit, mousedown/up, keydown/up, pointerdown/up/move,
10
- * drag*, drop, contextmenu, wheel, copy/paste/cut, focusin/focusout.
11
- *
12
- * `delegate()` also auto-promotes the well-known non-bubbling event
13
- * types (`focus`, `blur`, `scroll`, `load`, `error`, `mouseenter`,
14
- * `mouseleave`) to the capture phase under the hood, so the call site
15
- * looks identical for "interactive thing happens on a descendant"
16
- * regardless of whether that event bubbles. Selector matching stays
17
- * `closest()`-style — the same as for bubbling events — so a wrapper
18
- * selector like `'.field-row'` still matches when the event fires on
19
- * a descendant `<input>`.
20
- *
21
- * - Tier 2 (explicit capture) — use `delegateCapture()`.
22
- * The escape hatch for cases the auto-promotion list doesn't cover
23
- * (custom non-bubbling events) or when you want capture-phase
24
- * interception. Selector matching is `closest()`-style by default —
25
- * the same walk-up as `delegate()`, and it passes the matched ancestor
26
- * (not the raw target) to the handler — so a click on any descendant of
27
- * the selected element climbs to it. Pass `{ match: 'direct' }` to opt
28
- * into strict `matches()`-style matching (fire only when the event lands
29
- * on the exact element the selector identifies).
30
- *
31
- * - Tier 3 (per-element instances / library-owned subtrees) — mark the
32
- * host element with `data-morph-skip` and manage the library's
33
- * lifecycle directly. No delegation helper applies.
34
- */
35
1
  /**
36
2
  * How the selector is matched against the event's target:
37
3
  *
package/dist/dev.d.ts CHANGED
@@ -137,7 +137,9 @@ interface DevHooks {
137
137
  signalFactory?: <T>(value: T) => Signal<T>;
138
138
  /**
139
139
  * Wraps an `effect()` body so `delegate()` can detect that it is running
140
- * inside one. Returns the body to actually run.
140
+ * inside one. Returns the body to actually run. Resolved at effect-CREATION
141
+ * time (the delegate-in-effect switch too), so an effect created before the
142
+ * install or the opt-in is never wrapped.
141
143
  */
142
144
  wrapEffect?: (fn: () => void | (() => void)) => () => void | (() => void);
143
145
  delegateInEffect?: (fn: 'delegate' | 'delegateCapture') => void;
@@ -146,6 +148,12 @@ interface DevHooks {
146
148
  storeReadonly?: <T extends object>(state: T) => T;
147
149
  /** Unwraps a proxy handed back through `set({ ...get() })`. */
148
150
  storeToRaw?: <T>(next: T) => T;
151
+ /**
152
+ * Attaches the rebuilt-listeners observer to a mount root. Called once per
153
+ * `mount()`, at creation — and it returns `null` when the warning is switched
154
+ * off then — so a mount created before the install or the opt-in is never
155
+ * observed.
156
+ */
149
157
  listenerRebuild?: (rootEl: Element) => MutationObserver | null;
150
158
  listIdShift?: (id: string) => void;
151
159
  parserRepair?: (html: string) => void;
@@ -164,6 +172,8 @@ interface DevHooks {
164
172
  duplicateCacheKeys?: (id: string, segItems: readonly {
165
173
  cacheKey: unknown;
166
174
  }[]) => void;
175
+ /** A surface opened over a modal `<dialog>` could not be made fully usable. */
176
+ overlayBlockedByModal?: (reason: 'hidden' | 'inert') => void;
167
177
  /**
168
178
  * When installed, a screened URL throws instead of warning-and-dropping.
169
179
  * A slot rather than a boolean so the check stays uniform with the rest.
@@ -282,17 +292,23 @@ interface DevWarningOptions {
282
292
  *
283
293
  * ## Install ordering
284
294
  *
285
- * Every hook except one is read at CALL time (render, reconcile, `set()`,
286
- * `delegate()`), so installing any time before your first `mount()` is enough.
295
+ * Most hooks are read at CALL time (render, reconcile, `set()`, `delegate()`).
296
+ * Three are decided once, when their host is CREATED, so both installation and
297
+ * the warning's switch must precede it:
298
+ *
299
+ * - `signalFactory` — `signal()` picks its constructor (untracked signals).
300
+ * - `wrapEffect` — `effect()` decides whether to wrap its body with the
301
+ * effect-depth counter (delegate-in-effect).
302
+ * - `listenerRebuild` — `mount()` decides whether to attach its
303
+ * rebuilt-listeners `MutationObserver`.
287
304
  *
288
- * The exception is `signalFactory`: `signal()` picks its constructor when the
289
- * signal is CREATED. Static imports are hoisted above a top-level
290
- * `await import()`, so module-scope signals in imported modules are created
291
- * before this module runs and the untracked-signal warning will not see them.
292
- * To cover those, make `import 'kerfjs/dev'` the FIRST STATIC import of a
293
- * dev-only entry file, then load the rest of your app.
305
+ * Static imports are hoisted above a top-level `await import()`, so
306
+ * module-scope signals and effects in imported modules are created before this
307
+ * module runs. To cover those, make `import 'kerfjs/dev'` the FIRST STATIC
308
+ * import of a dev-only entry file (calling `enableWarnings()` there), then load
309
+ * the rest of your app.
294
310
  *
295
- * Opting into that warning prints this boundary once, so the gap is loud
311
+ * Opting into the untracked-signal warning prints its boundary once, so the gap is loud
296
312
  * rather than silent. It cannot be closed by retro-fitting existing signals:
297
313
  * `Signal.prototype`'s `value` accessor is non-configurable, and reaching live
298
314
  * instances would require a per-signal registry that production would pay for.
@@ -326,9 +342,11 @@ interface DevWarningOptions {
326
342
  * goes through `globalThis.process` into a local binding), so the environment
327
343
  * variables are unreachable there.
328
344
  *
329
- * Call it as early as you can. Every diagnostic reads its switch at call time
330
- * except the untracked-signal warning, which is chosen when a signal is
331
- * CREATED — enabling that one prints its coverage boundary once.
345
+ * Call it as early as you can. Most diagnostics read their switch at call
346
+ * time, but three are decided when their host is CREATED: `untrackedSignals`
347
+ * per `signal()`, `delegateInEffect` per `effect()`, and `rebuiltListeners` per
348
+ * `mount()`. A signal, effect, or mount created before this call stays
349
+ * uncovered. Enabling `untrackedSignals` prints its coverage boundary once.
332
350
  */
333
351
  declare function enableWarnings(options: DevWarningOptions): void;
334
352
  /**