kerfjs 5.0.0-beta.92 → 5.0.0-beta.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/ai/manifest.json +1 -1
- package/dist/{chunk-VUAZFDLB.js → chunk-2MNLFTTW.js} +37 -41
- package/dist/chunk-2MNLFTTW.js.map +1 -0
- package/dist/{chunk-E53LC47B.js → chunk-ZNQSWFYU.js} +24 -12
- package/dist/chunk-ZNQSWFYU.js.map +1 -0
- package/dist/html.js +1 -1
- package/dist/index.js +2 -2
- package/dist/jsx-runtime.d.ts +3 -1
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.js +1 -1
- package/dist/overlay.js +2 -2
- package/dist/remount.js +1 -1
- package/dist/router.js +1 -1
- package/dist/scope.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-E53LC47B.js.map +0 -1
- package/dist/chunk-VUAZFDLB.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
8
8
|
|
|
9
9
|
### Fixed
|
|
10
10
|
|
|
11
|
+
- Re-renders of a large page whose rows come from a memoized `each()` now scale with the change instead of the row count. The change-scoped morph now also runs on renders that contain `each()` lists: a change beside the list, such as a details pane that follows the selection, is re-parsed and morphed without visiting the rows, and kerf falls back to the full diff when the change sits in the element that holds the rows. After such a render, the scan for list markers skips the bound rows. `SafeHtml` also builds its HTML string on first read instead of at construction, so each element above a list no longer copies every row's markup. A one-row selection on a 300-row memoized keyed page drops from about 3.7 ms to 0.3 ms per render in Chromium (about 29 ms to 3 ms in happy-dom). An attribute set imperatively on an element outside the changed markup now survives such a render, as it already did on list-free pages (`KF-03KWNV`).
|
|
12
|
+
|
|
13
|
+
- On the same kind of page, a mount that also has a fine-grained binding outside the list (for example `class={signal}` on a page wrapper) no longer searches every row for binding markers after a render that changed the page. Rows carry only their own row-scoped markers, so kerf now skips them. A selection flip on the 300-row keyed fixture with one such binding drops from about 3.7 ms to 0.4 ms in Chromium (about 66 ms to 6 ms in happy-dom) (`KF-5S9XPH`).
|
|
14
|
+
|
|
15
|
+
- `wireTabBars` no longer re-measures every tab strip after every DOM mutation in the root it watches. It used to read computed style and layout for each strip whenever anything under the root changed, such as terminal output or a list update. In Hot Sheet's terminal profile that work was the single hottest function. Now only the bars a mutation touches are re-measured: a change inside a bar, a newly inserted bar, or a `dir` change on an element above one. A strip with a pinned tab also re-measures when its own size changes (`KF-8H2MH2`).
|
|
16
|
+
|
|
11
17
|
- `resource().run()` can be called inside an `effect()`. `run()` read the resource's own state through a tracking read, so an effect that re-ran a resource from an input signal subscribed to that state, wrote it, and threw `Cycle detected`. `run()` and its progress and failure paths now read the state untracked; `resource.value` stays a tracking read (`KF-H5PNHK`).
|
|
12
18
|
|
|
13
19
|
- Re-renders of a large list-free page now scale with the change instead of the page. When a `mount()` render has no `each()` list and no fine-grained bindings, kerf compares the previous and next static HTML, finds the innermost element around the change whose markup parses on its own, and re-parses and morphs only that element's changed children; anything it cannot prove identical to a full diff still takes the full diff. A one-row selection on a 300-row, ~1.4 MB page drops from about 23 ms to 13.5 ms per render in Chromium. One documented behavior narrows: an attribute set imperatively on an element whose markup did not change now survives a render that changed something else, as it already did on a no-op render. The core grows by about 2 KB min+gzip (~13 KB to ~15 KB) (`KF-HA8R27`).
|
|
@@ -52,6 +58,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
52
58
|
|
|
53
59
|
### Added
|
|
54
60
|
|
|
61
|
+
- **A per-entry settings pane in `@kerfjs/ui/catalog`.** Pass an entry's variant and state controls as `Catalog`'s new `settings` prop and the Catalog renders a **Settings** pane beside the stage, with the standard collapse toggle: in the pane's header while open, at the end of the entry toolbar while collapsed. Entries without settings render neither the pane nor the toggle. The app owns `settingsCollapsed`; hand the same signal to `wireCatalog`'s new `settingsCollapsed` option and it flips the signal from the toggle (`onToggleSettings` reports it) and wires the pane as a Workbench rail, so below the `narrow` breakpoint it starts collapsed and opens as an overlay that Escape or an outside press closes, exclusive with the sidebar overlay. `settingsPanel` sets the title, width, resizing, and overlay options (`settingsSize` / `settingsStorageKey` remember a resized width). The UX catalog's Badge entry demonstrates it with a playground. Consumers no longer build their own settings aside and toggle (`KF-0AY6AE`).
|
|
62
|
+
|
|
63
|
+
- **`kerf-component-catalog` generates the selection catalog too.** Packages and apps that keep `kerf.components.json` no longer hand-maintain a second selection JSON (`useWhen`, `avoidWhen`, `publicClasses`, `publicTokens`, …) for `@kerfjs/ui`'s component-catalog extension. Set `package.json#kerfComponentCatalog.selectionOutput`, add a top-level `description` and a per-component `selection` block (`useWhen`, `avoidWhen`, optional `uses`, `styleSources`, `demoSource`, `documentation`, and a `geometry` override), and `--write` / `--check` emit and verify the selection catalog beside the composition catalog. `name`, `kind`, `purpose`, `source`, `publicClasses`, `publicTokens`, and `geometry` come from the existing metadata; the composition catalog's `compatibility.componentCatalog` points at the selection file. The output is validated against the canonical selection schema, which `create-kerf-component` now ships. Recipes are not selection entries, `composed` geometry needs an explicit override, and `uses` must name this package's components or installed `@kerfjs/ui` catalog ids (`KF-K1SY5X`).
|
|
64
|
+
|
|
65
|
+
- **UX Review capture in any `@kerfjs/ui/catalog` app.** The catalog's **Capture region** header link, which starts a UX Review region screenshot preset with the project and the active entry, now ships in the package instead of living in Kerf's own demo. Add `uxReviewPlugin()` from the new Node-only `@kerfjs/ui/vite-ux-review` subpath to the Vite config. Report the project with `wireCatalog`'s new `onUxReviewProject` callback, and pass it to `Catalog`'s new `uxReview` prop. The link appears on local dev and preview servers and stays hidden on static hosting. `uxReviewCaptureUrl` and `loadUxReviewProject` are exported from `@kerfjs/ui/catalog` for custom placements (`KF-XB1G3S`).
|
|
66
|
+
|
|
55
67
|
- Deep-inset Pane content supports `--kui-pane-scrollbar-gutter: stable` for opaque edge-to-edge content, and Pane/Workbench focus rings paint above such scrollers without blocking their controls (`KF-9NVNGT`).
|
|
56
68
|
|
|
57
69
|
- NavStack now has `separator="auto" | "hidden"`. Automatic separators frame a sunken active Pane at the top chrome and optional bottom toolbar; hidden suppresses stack-owned lines (`KF-1XRSRW`).
|
package/ai/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { effect, isSignal } from './chunk-IHMDBBUJ.js';
|
|
2
|
-
import {
|
|
2
|
+
import { isOverlayHost, flatten, wrapWithTags, mergeChildSegments } from './chunk-4TX6YJDM.js';
|
|
3
3
|
import { devHooks } from './chunk-3C6F6NM4.js';
|
|
4
4
|
|
|
5
5
|
// src/utils/syncFormProp.ts
|
|
@@ -136,12 +136,12 @@ function bindText(signal) {
|
|
|
136
136
|
}
|
|
137
137
|
return null;
|
|
138
138
|
}
|
|
139
|
-
function wireBindings(rootEl, ctx, prevDisposers) {
|
|
139
|
+
function wireBindings(rootEl, ctx, prevDisposers, rows) {
|
|
140
140
|
for (const d of prevDisposers) d();
|
|
141
141
|
if (ctx.list.length === 0) return NO_DISPOSERS;
|
|
142
142
|
const disposers = [];
|
|
143
143
|
try {
|
|
144
|
-
wireInto(rootEl, ctx.list, disposers);
|
|
144
|
+
wireInto(rootEl, ctx.list, disposers, rows);
|
|
145
145
|
} catch (err) {
|
|
146
146
|
for (const d of disposers) d();
|
|
147
147
|
throw err;
|
|
@@ -152,8 +152,8 @@ function wireRowBindings(rowNode, bindings) {
|
|
|
152
152
|
const disposers = [];
|
|
153
153
|
const rootIds = rowNode.getAttribute(BIND_ATTR_ROW);
|
|
154
154
|
let rootIdSet = null;
|
|
155
|
-
let
|
|
156
|
-
|
|
155
|
+
let markers = null;
|
|
156
|
+
const rowMarkers = () => markers ??= indexMarkers(rowNode, BIND_ATTR_ROW, ROW_TEXT_PREFIX);
|
|
157
157
|
try {
|
|
158
158
|
for (let i = 0; i < bindings.length; i++) {
|
|
159
159
|
const b = bindings[i];
|
|
@@ -171,17 +171,12 @@ function wireRowBindings(rowNode, bindings) {
|
|
|
171
171
|
if (onRoot) {
|
|
172
172
|
el = rowNode;
|
|
173
173
|
} else {
|
|
174
|
-
|
|
175
|
-
el = descIndex.get(b.id);
|
|
174
|
+
el = rowMarkers().attrs.get(b.id);
|
|
176
175
|
}
|
|
177
176
|
if (el === void 0) continue;
|
|
178
177
|
disposers.push(attachAttrEffect(el, b.attr, b.signal));
|
|
179
178
|
} else {
|
|
180
|
-
|
|
181
|
-
textMarkers = /* @__PURE__ */ new Map();
|
|
182
|
-
collectComments(rowNode, ROW_TEXT_PREFIX, textMarkers);
|
|
183
|
-
}
|
|
184
|
-
const marker = textMarkers.get(b.id);
|
|
179
|
+
const marker = rowMarkers().texts.get(b.id);
|
|
185
180
|
if (marker === void 0) continue;
|
|
186
181
|
disposers.push(attachTextEffect(marker, b.signal));
|
|
187
182
|
}
|
|
@@ -215,31 +210,35 @@ function carryOrRewireRowBindings(node, oldBindings, oldDisposers, newBindings)
|
|
|
215
210
|
bindingDisposers: newLen > 0 ? wireRowBindings(node, newBindings) : void 0
|
|
216
211
|
};
|
|
217
212
|
}
|
|
218
|
-
function wireInto(scope, bindings, disposers) {
|
|
219
|
-
const
|
|
220
|
-
const textMarkers = /* @__PURE__ */ new Map();
|
|
221
|
-
collectComments(scope, TEXT_MARKER_PREFIX, textMarkers);
|
|
213
|
+
function wireInto(scope, bindings, disposers, rows) {
|
|
214
|
+
const markers = indexMarkers(scope, BIND_ATTR, TEXT_MARKER_PREFIX, rows);
|
|
222
215
|
for (const b of bindings) {
|
|
223
216
|
if (b.kind === "attr") {
|
|
224
|
-
const el =
|
|
217
|
+
const el = markers.attrs.get(b.id);
|
|
225
218
|
if (el === void 0) continue;
|
|
226
219
|
disposers.push(attachAttrEffect(el, b.attr, b.signal));
|
|
227
220
|
} else {
|
|
228
|
-
const marker =
|
|
221
|
+
const marker = markers.texts.get(b.id);
|
|
229
222
|
if (marker === void 0) continue;
|
|
230
223
|
disposers.push(attachTextEffect(marker, b.signal));
|
|
231
224
|
}
|
|
232
225
|
}
|
|
233
226
|
}
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
227
|
+
function indexMarkers(node, attrName, textPrefix, skip, out = { attrs: /* @__PURE__ */ new Map(), texts: /* @__PURE__ */ new Map() }) {
|
|
228
|
+
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
229
|
+
if (c.nodeType === Node.COMMENT_NODE) {
|
|
230
|
+
const data = c.data;
|
|
231
|
+
if (data.startsWith(textPrefix))
|
|
232
|
+
out.texts.set(data.slice(textPrefix.length), c);
|
|
233
|
+
} else if (c.nodeType === Node.ELEMENT_NODE) {
|
|
234
|
+
const el = c;
|
|
235
|
+
if (skip?.has(el) || isOverlayHost(el)) continue;
|
|
236
|
+
const ids = el.getAttribute(attrName);
|
|
237
|
+
if (ids !== null) for (const id of ids.split(",")) out.attrs.set(id, el);
|
|
238
|
+
indexMarkers(el, attrName, textPrefix, skip, out);
|
|
239
|
+
}
|
|
241
240
|
}
|
|
242
|
-
return
|
|
241
|
+
return out;
|
|
243
242
|
}
|
|
244
243
|
function attachAttrEffect(el, attr, signal) {
|
|
245
244
|
return effect(() => setBoundAttr(el, attr, signal.value));
|
|
@@ -294,17 +293,6 @@ function coerceText(value) {
|
|
|
294
293
|
if (value == null || typeof value === "boolean") return "";
|
|
295
294
|
return String(value);
|
|
296
295
|
}
|
|
297
|
-
function collectComments(node, prefix, out) {
|
|
298
|
-
for (let c = node.firstChild; c !== null; c = c.nextSibling) {
|
|
299
|
-
if (c.nodeType === Node.COMMENT_NODE) {
|
|
300
|
-
const data = c.data;
|
|
301
|
-
if (data.startsWith(prefix))
|
|
302
|
-
out.set(data.slice(prefix.length), c);
|
|
303
|
-
} else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c)) {
|
|
304
|
-
collectComments(c, prefix, out);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
296
|
|
|
309
297
|
// src/utils/escapeHtml.ts
|
|
310
298
|
var ENTITIES = {
|
|
@@ -435,19 +423,27 @@ var ATTR_ALIASES = {
|
|
|
435
423
|
// src/jsx-runtime.ts
|
|
436
424
|
var SAFE_HTML_BRAND2 = /* @__PURE__ */ Symbol.for("kerfjs.SafeHtml");
|
|
437
425
|
var SafeHtml = class {
|
|
438
|
-
__html;
|
|
439
426
|
__segment;
|
|
440
427
|
// Branded so `isSafeHtml()` recognizes instances from any copy of this module.
|
|
441
428
|
[SAFE_HTML_BRAND2] = true;
|
|
429
|
+
// The flattened HTML, computed on first read. A structured segment wraps its
|
|
430
|
+
// children's segments rather than their strings, so flattening eagerly made
|
|
431
|
+
// every element above an `each()` copy all of the list's row HTML again —
|
|
432
|
+
// O(depth × rows) per render, while `mount()` reads only the segment.
|
|
433
|
+
#html;
|
|
442
434
|
constructor(input) {
|
|
443
435
|
if (typeof input === "string") {
|
|
444
436
|
this.__segment = { kind: "static", html: input };
|
|
445
|
-
this
|
|
437
|
+
this.#html = input;
|
|
446
438
|
} else {
|
|
447
439
|
this.__segment = input;
|
|
448
|
-
|
|
440
|
+
if (input.kind === "static") this.#html = input.html;
|
|
449
441
|
}
|
|
450
442
|
}
|
|
443
|
+
/** The flattened HTML string (what `toString()` returns). */
|
|
444
|
+
get __html() {
|
|
445
|
+
return this.#html ??= flatten(this.__segment, false);
|
|
446
|
+
}
|
|
451
447
|
toString() {
|
|
452
448
|
return this.__html;
|
|
453
449
|
}
|
|
@@ -618,4 +614,4 @@ function _renderAttrVerbatim(name, value) {
|
|
|
618
614
|
|
|
619
615
|
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 };
|
|
620
616
|
|
|
621
|
-
//# sourceMappingURL=chunk-
|
|
617
|
+
//# sourceMappingURL=chunk-2MNLFTTW.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-2MNLFTTW.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/**\n * True if `name` is one of the URL-bearing attributes the screen inspects.\n * A pure function of the name, so callers that see the same attribute name\n * over and over (the JSX serializer) may cache its answer per name.\n */\nexport function isUrlAttrName(name: 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 // Lowercase names (nearly all of them) are checked without allocating.\n return URL_ATTRS.has(/[A-Z]/.test(name) ? name.toLowerCase() : name);\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 return isUrlAttrName(name) && isDangerousUrl(value);\n}\n\n/**\n * True if `value`, written into a URL-bearing attribute (see `isUrlAttrName`),\n * would resolve to a script-executing URL.\n */\nexport function isDangerousUrl(value: string): boolean {\n // No `:` → no scheme, so nothing to screen: a relative URL (`/a/b`, `#x`,\n // `?q`, `logo.png`) is the common case in a large page. Exact: normalization\n // only ever REMOVES characters, so it can never introduce the `:` that\n // `schemeOf` needs.\n if (!value.includes(':')) 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 } 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 *\n * `rows` — the `each()` rows bound in this mount — are not searched: a row\n * renders in row scope, so it carries only `data-kfbrow` / `kfbr:` markers,\n * never a global one. Skipping them keeps the marker search proportional to\n * the surrounds instead of to every row's subtree.\n */\nexport function wireBindings(\n rootEl: Element,\n ctx: BindingContext,\n prevDisposers: ReadonlyArray<() => void>,\n rows?: ReadonlySet<Element>,\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, rows);\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 marker walk, no\n // 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 // The descendant-attr and text-comment indexes come from one walk of the\n // row, made the first time a hole needs either.\n let markers: Markers | null = null;\n const rowMarkers = (): Markers =>\n (markers ??= indexMarkers(rowNode, BIND_ATTR_ROW, ROW_TEXT_PREFIX));\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 el = rowMarkers().attrs.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 const marker = rowMarkers().texts.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 rows: ReadonlySet<Element> | undefined,\n): void {\n const markers = indexMarkers(scope, BIND_ATTR, TEXT_MARKER_PREFIX, rows);\n\n for (const b of bindings) {\n if (b.kind === 'attr') {\n const el = markers.attrs.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 = markers.texts.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 * One walk that indexes, by binding id, the DESCENDANT elements of `node`\n * carrying `attrName` and the comment markers starting with `textPrefix`,\n * stepping over overlay host slots (another mount's markers) and `skip` (the\n * bound rows, when wiring the global scope: rows carry only row-scope\n * markers). Hand-rolled because happy-dom's `TreeWalker` does not surface\n * comment nodes; the attribute index rides along on the same walk.\n */\nfunction indexMarkers(\n node: Node,\n attrName: string,\n textPrefix: string,\n skip?: ReadonlySet<Element>,\n out: Markers = { attrs: new Map(), texts: new Map() },\n): Markers {\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(textPrefix))\n out.texts.set(data.slice(textPrefix.length), c as Comment);\n } else if (c.nodeType === Node.ELEMENT_NODE) {\n const el = c as Element;\n if (skip?.has(el) || isOverlayHost(el)) continue;\n const ids = el.getAttribute(attrName);\n if (ids !== null) for (const id of ids.split(',')) out.attrs.set(id, el);\n indexMarkers(el, attrName, textPrefix, skip, out);\n }\n }\n return out;\n}\n\n/** Binding markers under a scope, by binding id. */\ninterface Markers {\n attrs: Map<string, Element>;\n texts: Map<string, Comment>;\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// 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\nconst ENTITIES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\n// One pass over the string, appending the unescaped runs between matches.\n// A value with nothing to escape (most ids, classes, SVG path data) comes back\n// as the same string with no allocation; one that needs escaping is about 2x\n// faster than a chain of one `replace` pass per character.\nfunction escapeWith(str: string, special: RegExp): string {\n special.lastIndex = 0;\n let out = '';\n let last = 0;\n let match;\n while ((match = special.exec(str))) {\n out += str.slice(last, match.index) + ENTITIES[match[0]];\n last = special.lastIndex;\n }\n return last ? out + str.slice(last) : str;\n}\n\nconst HTML_SPECIAL = /[&<>\"]/g;\nconst ATTR_SPECIAL = /[&\"'<>]/g;\n\nexport function escapeHtml(str: string): string {\n return escapeWith(str, HTML_SPECIAL);\n}\n\nexport function escapeAttr(str: string): string {\n return escapeWith(str, ATTR_SPECIAL);\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 {\n isDangerousUrl,\n isUrlAttrName,\n reportDangerousUrl,\n} 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 __segment: Segment;\n // Branded so `isSafeHtml()` recognizes instances from any copy of this module.\n readonly [SAFE_HTML_BRAND] = true as const;\n // The flattened HTML, computed on first read. A structured segment wraps its\n // children's segments rather than their strings, so flattening eagerly made\n // every element above an `each()` copy all of the list's row HTML again —\n // O(depth × rows) per render, while `mount()` reads only the segment.\n #html: string | undefined;\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 if (input.kind === 'static') this.#html = input.html;\n }\n }\n /** The flattened HTML string (what `toString()` returns). */\n get __html(): string {\n return (this.#html ??= flatten(this.__segment, false));\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\n/**\n * KF-VBQJAX: per-name memo of the two checks that depend ONLY on the emitted\n * attribute name — `assertEmittableAttrName` passing, and whether the name is\n * URL-bearing (`isUrlAttrName`). A large page re-renders the same few dozen\n * names hundreds of thousands of times, and both checks are regex work.\n *\n * Only names that PASSED validation are stored (value: is-URL-attribute), so a\n * rejected name throws on every attempt with its exact message, as before; the\n * `isFn` message variant only matters for rejected names. Bounded so spreading\n * untrusted keys into JSX can't grow it without limit: once full, unseen names\n * are validated uncached.\n */\nconst VALIDATED_ATTR_NAMES = new Map<string, boolean>();\nconst VALIDATED_ATTR_NAMES_MAX = 1024;\n\n/** Validate `name` (throws like `assertEmittableAttrName`); true if URL-bearing. */\nfunction checkAttrName(key: string, name: string, isFn: boolean): boolean {\n const cached = VALIDATED_ATTR_NAMES.get(name);\n if (cached !== undefined) return cached;\n assertEmittableAttrName(key, name, isFn);\n const isUrl = isUrlAttrName(name);\n if (VALIDATED_ATTR_NAMES.size < VALIDATED_ATTR_NAMES_MAX)\n VALIDATED_ATTR_NAMES.set(name, isUrl);\n return isUrl;\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 const isUrlAttr = checkAttrName(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 (isUrlAttr && isDangerousUrl(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 = props.children;\n let attrStr = '';\n let bindIds: string[] | null = null;\n // `for…in` with an own-key check, not a `{ children, ...attrs }` rest plus\n // `Object.entries`: same own-key order without two allocations per element,\n // and an inherited (e.g. prototype-polluted) key is never emitted.\n for (const k in props) {\n if (k === 'children' || !Object.hasOwn(props, k)) continue;\n const v = props[k];\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 checkAttrName(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"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { itemVersion } from './chunk-5WRGJZV6.js';
|
|
2
2
|
import { seedRenderedAttrs, RENDERED_ATTRS, isUserAgentOwnedAttr, hasCustomTag, parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-6LXUC4ZY.js';
|
|
3
|
-
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, wireBindings, disposeRowBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-
|
|
3
|
+
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, wireBindings, disposeRowBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-2MNLFTTW.js';
|
|
4
4
|
import { effect } from './chunk-IHMDBBUJ.js';
|
|
5
5
|
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, isOverlayHost, flatten } from './chunk-4TX6YJDM.js';
|
|
6
6
|
import { devHooks } from './chunk-3C6F6NM4.js';
|
|
@@ -1205,11 +1205,11 @@ function anyRebuiltListIsGranular(segment, rebuilt) {
|
|
|
1205
1205
|
}
|
|
1206
1206
|
return false;
|
|
1207
1207
|
}
|
|
1208
|
-
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
|
|
1208
|
+
function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists, ownedItems) {
|
|
1209
1209
|
const lists = collectLists(segment);
|
|
1210
1210
|
if (lists.size === 0 && bindings.size === 0) return;
|
|
1211
1211
|
const found = [];
|
|
1212
|
-
collectComments(rootEl, found);
|
|
1212
|
+
collectComments(rootEl, found, ownedItems);
|
|
1213
1213
|
for (const marker of found) {
|
|
1214
1214
|
if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
|
|
1215
1215
|
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
@@ -1294,11 +1294,11 @@ function cleanupOrphanBindings(segment, bindings, renderCtx) {
|
|
|
1294
1294
|
renderCtx.caches.delete(id);
|
|
1295
1295
|
}
|
|
1296
1296
|
}
|
|
1297
|
-
function collectComments(node, out) {
|
|
1297
|
+
function collectComments(node, out, skip) {
|
|
1298
1298
|
for (let c = node.firstChild; c; c = c.nextSibling) {
|
|
1299
1299
|
if (c.nodeType === Node.COMMENT_NODE) out.push(c);
|
|
1300
|
-
else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c))
|
|
1301
|
-
collectComments(c, out);
|
|
1300
|
+
else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c) && !skip?.has(c))
|
|
1301
|
+
collectComments(c, out, skip);
|
|
1302
1302
|
}
|
|
1303
1303
|
}
|
|
1304
1304
|
|
|
@@ -1625,6 +1625,7 @@ function mount(rootEl, render) {
|
|
|
1625
1625
|
let prevStaticHtml = "";
|
|
1626
1626
|
const scanCache = { html: "", scan: null };
|
|
1627
1627
|
const valueOnlyWarnCtx = { warned: false };
|
|
1628
|
+
const boundRows = () => bindingCtx.list.length > 0 && bindings.size > 0 ? collectOwnedItems(bindings) : void 0;
|
|
1628
1629
|
const runRenderPass = () => {
|
|
1629
1630
|
renderCtx.counter = 0;
|
|
1630
1631
|
renderCtx.keysThisRender.clear();
|
|
@@ -1656,7 +1657,12 @@ function mount(rootEl, render) {
|
|
|
1656
1657
|
runFirstRender(rootEl, segment, bindings);
|
|
1657
1658
|
prevStaticHtml = flattenWithoutListItems(segment);
|
|
1658
1659
|
devHooks.parserRepair?.(prevStaticHtml);
|
|
1659
|
-
bindingDisposers = wireBindings(
|
|
1660
|
+
bindingDisposers = wireBindings(
|
|
1661
|
+
rootEl,
|
|
1662
|
+
bindingCtx,
|
|
1663
|
+
bindingDisposers,
|
|
1664
|
+
boundRows()
|
|
1665
|
+
);
|
|
1660
1666
|
if (devHooks.staleBindingEnabled?.()) prevWiredBindings = bindingCtx.list;
|
|
1661
1667
|
isFirst = false;
|
|
1662
1668
|
} else {
|
|
@@ -1685,7 +1691,12 @@ function mount(rootEl, render) {
|
|
|
1685
1691
|
);
|
|
1686
1692
|
}
|
|
1687
1693
|
if (nextStaticHtml !== prevStaticHtml) {
|
|
1688
|
-
bindingDisposers = wireBindings(
|
|
1694
|
+
bindingDisposers = wireBindings(
|
|
1695
|
+
rootEl,
|
|
1696
|
+
bindingCtx,
|
|
1697
|
+
bindingDisposers,
|
|
1698
|
+
boundRows()
|
|
1699
|
+
);
|
|
1689
1700
|
if (devHooks.staleBindingEnabled?.())
|
|
1690
1701
|
prevWiredBindings = bindingCtx.list;
|
|
1691
1702
|
} else {
|
|
@@ -1767,13 +1778,13 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
|
|
|
1767
1778
|
);
|
|
1768
1779
|
cleanupOrphanBindings(segment, bindings, renderCtx);
|
|
1769
1780
|
const ownedItems = collectOwnedItems(bindings);
|
|
1770
|
-
if (!
|
|
1781
|
+
if (!morphStaticChange(
|
|
1771
1782
|
rootEl,
|
|
1772
1783
|
prevStaticHtml,
|
|
1773
1784
|
currentStaticHtml,
|
|
1774
1785
|
scanCache,
|
|
1775
1786
|
ownedItems
|
|
1776
|
-
))
|
|
1787
|
+
)) {
|
|
1777
1788
|
const template = rootEl.cloneNode(false);
|
|
1778
1789
|
template.innerHTML = currentStaticHtml;
|
|
1779
1790
|
morph(rootEl, template, ownedItems);
|
|
@@ -1783,7 +1794,8 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
|
|
|
1783
1794
|
segment,
|
|
1784
1795
|
bindings,
|
|
1785
1796
|
false,
|
|
1786
|
-
renderCtx.rebuiltLists
|
|
1797
|
+
renderCtx.rebuiltLists,
|
|
1798
|
+
ownedItems
|
|
1787
1799
|
);
|
|
1788
1800
|
return currentStaticHtml;
|
|
1789
1801
|
}
|
|
@@ -1797,4 +1809,4 @@ function resultToSegment(result) {
|
|
|
1797
1809
|
|
|
1798
1810
|
export { captureFocus, each, morph, mount, moveNode, restoreFocus };
|
|
1799
1811
|
|
|
1800
|
-
//# sourceMappingURL=chunk-
|
|
1812
|
+
//# sourceMappingURL=chunk-ZNQSWFYU.js.map
|