kerfjs 5.0.0-beta.3 → 5.0.0-beta.30
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 +315 -115
- package/README.md +43 -23
- package/ai/cursorrules +87 -48
- package/ai/manifest.json +10 -7
- package/ai/skill.md +101 -67
- package/dist/actions.js +2 -2
- package/dist/actions.js.map +1 -1
- package/dist/array-signal.js +1 -1
- package/dist/async.js +22 -19
- package/dist/async.js.map +1 -1
- package/dist/attach.js +3 -1
- package/dist/attach.js.map +1 -1
- package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
- package/dist/chunk-5WRGJZV6.js.map +1 -0
- package/dist/{chunk-GY4XV2UV.js → chunk-BDX3R4OM.js} +3 -2
- package/dist/chunk-BDX3R4OM.js.map +1 -0
- package/dist/{chunk-HW7KSM2Y.js → chunk-CEQMZYLR.js} +1 -1
- package/dist/{chunk-HW7KSM2Y.js.map → chunk-CEQMZYLR.js.map} +1 -1
- package/dist/{chunk-SVATPF5R.js → chunk-DLY444MN.js} +121 -49
- package/dist/chunk-DLY444MN.js.map +1 -0
- package/dist/{chunk-KPXIOG2C.js → chunk-E5R5GNKE.js} +14 -4
- package/dist/chunk-E5R5GNKE.js.map +1 -0
- package/dist/{chunk-LVH3GC6B.js → chunk-ELXVRKY2.js} +3 -3
- package/dist/chunk-ELXVRKY2.js.map +1 -0
- package/dist/{chunk-ZDCJZCNO.js → chunk-JRKE6XEK.js} +37 -16
- package/dist/chunk-JRKE6XEK.js.map +1 -0
- package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
- package/dist/chunk-MK42GLPV.js.map +1 -0
- package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
- package/dist/chunk-WKIPLNVO.js.map +1 -0
- package/dist/{chunk-U6FK33SG.js → chunk-Y2FOYPBV.js} +2 -2
- package/dist/{chunk-U6FK33SG.js.map → chunk-Y2FOYPBV.js.map} +1 -1
- package/dist/{chunk-KZJXHFIB.js → chunk-ZOIERTUW.js} +3 -3
- package/dist/chunk-ZOIERTUW.js.map +1 -0
- package/dist/dev.js +64 -17
- package/dist/dev.js.map +1 -1
- package/dist/html.js +5 -4
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +23 -14
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +12 -5
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.js +43 -14
- package/dist/list.js.map +1 -1
- package/dist/overlay.js +99 -32
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +2 -2
- package/dist/router.js +33 -10
- package/dist/router.js.map +1 -1
- package/dist/scope.js +5 -4
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/timing.js +1 -1
- package/dist/timing.js.map +1 -1
- package/llms.txt +6 -2
- package/package.json +28 -6
- package/setup/cli.mjs +90 -0
- package/setup/index.d.mts +37 -0
- package/setup/index.mjs +1347 -0
- package/setup/jsonc.mjs +201 -0
- package/setup/state.schema.json +56 -0
- package/dist/chunk-GY4XV2UV.js.map +0 -1
- package/dist/chunk-KPXIOG2C.js.map +0 -1
- package/dist/chunk-KZJXHFIB.js.map +0 -1
- package/dist/chunk-LVH3GC6B.js.map +0 -1
- package/dist/chunk-QIP723L4.js.map +0 -1
- package/dist/chunk-SVATPF5R.js.map +0 -1
- package/dist/chunk-UZJ6I4T6.js.map +0 -1
- package/dist/chunk-V46JKE44.js.map +0 -1
- package/dist/chunk-ZDCJZCNO.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/segment.ts"],"names":[],"mappings":"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","file":"chunk-BDX3R4OM.js","sourcesContent":["/**\n * `Segment` — kerf's structured render output.\n *\n * The JSX runtime emits a `SafeHtml` wrapping a `Segment`. Most renders\n * produce a single static segment (just an HTML string), which behaves\n * exactly like a string for backward compatibility. When the tree\n * contains a list (`each()`) or a parent whose children include a list,\n * the runtime emits a structured segment that `mount()` can dispatch\n * on — running its native keyed reconciler for the list parts and\n * leaving the static surrounds to the general-purpose diff.\n *\n * Why have a structured form at all: the perf bottleneck for huge\n * keyed lists isn't the per-row JSX work (which `each()` already\n * memoizes). It's that flattening every render's whole tree to one\n * big HTML string forces a full `innerHTML` parse and a tree walk\n * over rows we know are unchanged. The segment shape lets mount()\n * skip both for the list parts.\n */\n\nimport type { Binding } from './bindings.js';\n\nexport type Segment = StaticSegment | ListSegment | MixedSegment;\n\nexport interface StaticSegment {\n kind: 'static';\n html: string;\n}\n\nexport interface ListItem {\n /**\n * The row's object identity. Used by the reconciler to match new items\n * against live DOM nodes across renders. Unchanged ref → reuse the\n * existing live node; replaced ref → build a fresh node.\n */\n ref: object;\n /**\n * KF-294: the row's fine-grained binding specs (signals in row attrs/text).\n * Undefined for granular-path rows (which snapshot in this spike). The\n * snapshot reconciler wires these to the fresh row node and disposes them\n * when the row is removed.\n */\n bindings?: Binding[];\n /**\n * Optional cache-invalidation key that captures external state affecting\n * this row's render (e.g. selection class). Different cacheKey on the\n * same `ref` triggers a cache miss for that row. `undefined` when the\n * user didn't pass a `key` callback to `each()`.\n */\n cacheKey: unknown;\n html: string;\n}\n\nexport interface ListSegment {\n kind: 'list';\n id: string;\n items: ListItem[];\n /**\n * Optional granular patches (KF-92). When present, the list reconciler\n * applies these directly to the existing binding instead of doing a\n * full classify+reconcile pass. Emitted by `each()` when bound to an\n * `arraySignal`. Mutually exclusive with the `items` snapshot in the\n * sense that the snapshot is treated as informational/fall-back when\n * patches are present.\n */\n patches?: ArrayPatchInternal[];\n /**\n * KF-388: the identity of the data this list renders — the `arraySignal`\n * instance, or `undefined` for a plain array.\n *\n * A list's `id` is its call-order index, so a render that changes how many\n * `each()` calls precede this one hands this segment a DIFFERENT list's\n * binding. Patches are only meaningful against the binding they were queued\n * for, so the reconciler compares this against the binding's recorded source\n * before trusting the patch queue. It is an identity check, not a value\n * check — the instance is never read.\n */\n source?: object;\n}\n\n/**\n * Internal patch shape used inside list segments. Mirrors `ArrayPatch<T>`\n * from `array-signal.ts` but typed against `object` so the segment layer\n * doesn't need to be generic. `update` / `insert` patches carry the row's\n * pre-rendered HTML — `each()` renders them at JSX-evaluation time inside a\n * try/catch so a throwing render falls back to the snapshot path (KF-99)\n * instead of leaving the signal and DOM divergent.\n */\nexport type ArrayPatchInternal =\n | {\n type: 'update';\n index: number;\n item: object;\n html: string;\n bindings?: Binding[];\n }\n | {\n type: 'insert';\n index: number;\n item: object;\n html: string;\n bindings?: Binding[];\n }\n | { type: 'remove'; index: number }\n | { type: 'move'; from: number; to: number }\n | { type: 'replace'; items: readonly object[] };\n\n/**\n * Narrowed patch aliases. Array indexing loses the union discriminant, so\n * the granular reconciler casts through these instead of restating the full\n * object type at every site — a field rename is then a one-place edit.\n */\nexport type UpdatePatch = Extract<ArrayPatchInternal, { type: 'update' }>;\nexport type InsertPatch = Extract<ArrayPatchInternal, { type: 'insert' }>;\n\nexport interface MixedSegment {\n kind: 'mixed';\n parts: Segment[];\n}\n\n/**\n * Flatten a segment to a complete HTML string. Used for first render\n * (bulk innerHTML), for SSR-style consumption via `toString()`, and\n * for diagnostics.\n *\n * If `withMarkers` is set, list segments are wrapped in\n * `<!--kf-list:{id}-->` comments so the post-parse walk can find each\n * list's live parent. Plain (non-marker) flatten is what JSX consumers\n * see when they call `.toString()` on the SafeHtml.\n */\n/**\n * Comment-marker prefix emitted before each list (`<!--kf-list:{id}-->`).\n * `mount()` consumes it when binding lists from the live DOM — the emitter\n * (here) and the consumer must agree byte-for-byte, so both import this one\n * constant. Part of the reserved marker namespace (see `bindings.ts`).\n */\nexport const LIST_MARKER_PREFIX = 'kf-list:';\n\nexport function flatten(segment: Segment, withMarkers: boolean): string {\n if (segment.kind === 'static') return segment.html;\n if (segment.kind === 'list') {\n const items = segment.items.map((i) => i.html).join('');\n return withMarkers\n ? `<!--${LIST_MARKER_PREFIX}${segment.id}-->${items}`\n : items;\n }\n return segment.parts.map((p) => flatten(p, withMarkers)).join('');\n}\n\n/**\n * Variant of `flatten` for the static-only diff path on subsequent\n * renders. Lists are reduced to a single marker comment with no items\n * inside — the actual list children stay in the live DOM and are\n * reconciled separately. Keeping list items out of this string is\n * what makes the morph cheap on huge lists where most rows are\n * unchanged.\n */\nexport function flattenWithoutListItems(segment: Segment): string {\n if (segment.kind === 'static') return segment.html;\n if (segment.kind === 'list')\n return `<!--${LIST_MARKER_PREFIX}${segment.id}-->`;\n return segment.parts.map(flattenWithoutListItems).join('');\n}\n\n/** Collect every `ListSegment` in the tree, keyed by its id. */\nexport function collectLists(\n segment: Segment,\n out: Map<string, ListSegment> = new Map(),\n): Map<string, ListSegment> {\n if (segment.kind === 'list') out.set(segment.id, segment);\n else if (segment.kind === 'mixed') {\n for (const part of segment.parts) collectLists(part, out);\n }\n return out;\n}\n\n/**\n * Combine a list of child segments into the smallest equivalent\n * representation: collapses adjacent statics into one static, returns\n * a single static if everything is static, otherwise a mixed segment\n * with statics coalesced.\n */\nexport function mergeChildSegments(parts: Segment[]): Segment {\n if (parts.length === 0) return { kind: 'static', html: '' };\n if (parts.every((p) => p.kind === 'static')) {\n return {\n kind: 'static',\n html: parts.map((p) => (p as StaticSegment).html).join(''),\n };\n }\n const merged: Segment[] = [];\n let coalesced = '';\n for (const p of parts) {\n if (p.kind === 'static') {\n coalesced += p.html;\n } else {\n if (coalesced !== '') {\n merged.push({ kind: 'static', html: coalesced });\n coalesced = '';\n }\n merged.push(p);\n }\n }\n if (coalesced !== '') merged.push({ kind: 'static', html: coalesced });\n return { kind: 'mixed', parts: merged };\n}\n\n/**\n * Wrap a child segment with surrounding open/close tags from the\n * parent JSX element. Used by the JSX runtime when constructing\n * `_jsx(tag, ...)` output.\n */\nexport function wrapWithTags(\n child: Segment,\n openTag: string,\n closeTag: string,\n): Segment {\n if (child.kind === 'static') {\n return { kind: 'static', html: openTag + child.html + closeTag };\n }\n if (child.kind === 'mixed') {\n return {\n kind: 'mixed',\n parts: [\n { kind: 'static', html: openTag },\n ...child.parts,\n { kind: 'static', html: closeTag },\n ],\n };\n }\n return {\n kind: 'mixed',\n parts: [\n { kind: 'static', html: openTag },\n child,\n { kind: 'static', html: closeTag },\n ],\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dev-hooks.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../src/dev-hooks.ts"],"names":[],"mappings":";AAwJO,IAAM,WAAqB;AAS3B,SAAS,gBAAgB,KAAA,EAAuB;AACrD,EAAA,MAAA,CAAO,MAAA,CAAO,UAAU,KAAK,CAAA;AAC/B;AAOO,SAAS,aAAA,GAAsB;AACpC,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAAG;AACvC,IAAA,OAAQ,SAAqC,GAAG,CAAA;AAAA,EAClD;AACF","file":"chunk-CEQMZYLR.js","sourcesContent":["/**\n * The dev-hook registry — kerf's single seam between production code and the\n * opt-in development diagnostics.\n *\n * ## Why this exists\n *\n * kerf used to INFER whether it was running in development, by reading\n * `globalThis.process?.env?.NODE_ENV` through `utils/devMode.ts`. That\n * inference cannot be made correct, and it was wrong in the most common case:\n * bundlers substitute the BARE `process.env.NODE_ENV` token and never create a\n * `globalThis.process` object for browser targets, so the read returned\n * `undefined` and `undefined !== 'production'` resolved to DEVELOPMENT inside\n * production browser bundles.\n *\n * It also could not be fixed by rewriting the expression. Only the\n * *production* answer can be made static: `X && false` folds to a constant for\n * any side-effect-free `X`, while `X && true` does not. So any form that a\n * bundler can eliminate is also a form that treats \"no `process` binding\" as\n * production — which silently disables every warning in the no-build/CDN path\n * and in browser dev bundles.\n *\n * The fix is to stop guessing. Every environment already has a correct,\n * statically-foldable dev flag; what none of them offers is a way to hand that\n * flag to a *library*. So the consumer writes the conditional, in their own\n * code, with their own flag:\n *\n * ```js\n * if (import.meta.env.DEV) await import('kerfjs/dev'); // Vite\n * if (process.env.NODE_ENV !== 'production') await import('kerfjs/dev');\n * ```\n *\n * Because that condition folds to `false` in the consumer's production build,\n * the entire statement is eliminated and the dev chunk is never emitted or\n * fetched. Installation IS the development signal — there is nothing left to\n * detect, and no environment kerf can be wrong about.\n *\n * ## The contract\n *\n * Core modules never import a `dev-*` module. They read a nullable slot off\n * `devHooks` and call through it:\n *\n * ```ts\n * devHooks.listRebind?.(id, marker.parentElement as Element);\n * ```\n *\n * When nothing is installed every slot is `undefined`, so the cost is one\n * property read per call site and the `dev-*` modules are unreachable from the\n * main entry — which is what lets a bundler drop them. This is why the gate\n * lives at the CALL SITE rather than inside each warner: an unconditional call\n * into a self-gating warner keeps the module reachable no matter how the gate\n * is written, so no amount of dead-code elimination can reclaim it.\n *\n * Slots ending in `Enabled` are predicates rather than warnings. They exist for\n * the handful of call sites that must decide whether to do *expensive\n * preparatory work* — capturing the previous render's binding list, allocating\n * a per-render `Map` — before there is anything to warn about. Core must check\n * those before paying the cost, exactly as it checked the old `isOptedIn()`\n * exports.\n *\n * Each opt-in warner keeps its own internal switch check through `devFlag()`;\n * an `enableWarnings()` override wins over the matching `KERF_DEV_WARN_*`\n * environment variable. Installation decides whether the diagnostics are\n * *present*; the opt-in warner decides whether it is *switched on*. Always-on\n * hooks skip that second gate.\n *\n * @see docs/11-dev-warnings.md\n */\n\nimport type { Binding } from './bindings.js';\nimport type { ListBinding } from './list-binding.js';\nimport type { Signal } from './reactive.js';\n\n/** Per-mount / per-store one-shot dedup flag, owned by the caller in core. */\nexport interface WarnOnceContext {\n warned: boolean;\n}\n\nexport interface DevHooks {\n // --- reactive.ts -------------------------------------------------------\n /**\n * Replaces `signal()`'s constructor so writes to never-subscribed signals can\n * warn. Resolved at signal-CREATION time, so signals created before the dev\n * entry is installed stay plain — see `signalsCreatedBeforeInstall`.\n */\n signalFactory?: <T>(value: T) => Signal<T>;\n /**\n * Wraps an `effect()` body so `delegate()` can detect that it is running\n * inside one. Returns the body to actually run.\n */\n wrapEffect?: (fn: () => void | (() => void)) => () => void | (() => void);\n\n // --- delegate.ts -------------------------------------------------------\n delegateInEffect?: (fn: 'delegate' | 'delegateCapture') => void;\n\n // --- store.ts ----------------------------------------------------------\n narrowSet?: (prev: unknown, next: unknown, ctx: WarnOnceContext) => void;\n /** Deep read-only proxy for the `get()` snapshot, so stray writes throw. */\n storeReadonly?: <T extends object>(state: T) => T;\n /** Unwraps a proxy handed back through `set({ ...get() })`. */\n storeToRaw?: <T>(next: T) => T;\n\n // --- mount.ts ----------------------------------------------------------\n listenerRebuild?: (rootEl: Element) => MutationObserver | null;\n listIdShift?: (id: string) => void;\n parserRepair?: (html: string) => void;\n staleBindingEnabled?: () => boolean;\n staleBinding?: (\n prevWired: readonly Binding[],\n current: readonly Binding[],\n ) => void;\n listInvariantsEnabled?: () => boolean;\n listInvariants?: (\n rootEl: Element,\n bindings: ReadonlyMap<string, ListBinding>,\n expectedCounts?: ReadonlyMap<string, number>,\n ) => void;\n valueOnlyRerender?: (\n prevHtml: string,\n nextHtml: string,\n ctx: WarnOnceContext,\n ) => void;\n listRebind?: (id: string, liveParent: Element) => void;\n eachInMorphSkip?: (id: string, liveParent: Element, rootEl: Element) => void;\n missingRowKey?: (\n rowEl: Element,\n rowHtml: string,\n binding: { warnedMissingKey?: boolean },\n ) => void;\n\n // --- each.ts -----------------------------------------------------------\n staleIndexEnabled?: () => boolean;\n staleIndex?: (id: string) => void;\n duplicateCacheKeys?: (\n id: string,\n segItems: readonly { cacheKey: unknown }[],\n ) => void;\n\n // --- utils/url-screen.ts -----------------------------------------------\n /**\n * When installed, a screened URL throws instead of warning-and-dropping.\n * A slot rather than a boolean so the check stays uniform with the rest.\n */\n urlScreenThrow?: (message: string) => never;\n}\n\n/**\n * The live slot table. Mutable by design — this is the fourth sanctioned\n * module-level mutable location (Design rule 5), and like `store.ts:REGISTRY`\n * it depends on there being exactly ONE copy at runtime. `tsup`'s\n * `splitting: true` guarantees that: shared modules are promoted into a single\n * chunk that both the main entry and the `kerfjs/dev` entry import.\n */\nexport const devHooks: DevHooks = {};\n\n/**\n * Install (or extend) the dev hooks. Called by the `kerfjs/dev` entry; not part\n * of the public API surface.\n *\n * Merges rather than replaces, so a consumer can install the standard bundle\n * and then override a single slot in a test.\n */\nexport function installDevHooks(hooks: DevHooks): void {\n Object.assign(devHooks, hooks);\n}\n\n/**\n * Remove every installed hook. Exists for test isolation — a suite that asserts\n * the not-installed (production-shaped) path needs to get back to a clean slate\n * without reloading modules.\n */\nexport function clearDevHooks(): void {\n for (const key of Object.keys(devHooks)) {\n delete (devHooks as Record<string, unknown>)[key];\n }\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { itemVersion } from './chunk-
|
|
2
|
-
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-
|
|
3
|
-
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, disposeRowBindings, wireBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-
|
|
4
|
-
import { effect } from './chunk-
|
|
5
|
-
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, flatten } from './chunk-
|
|
6
|
-
import { devHooks } from './chunk-
|
|
1
|
+
import { itemVersion } from './chunk-5WRGJZV6.js';
|
|
2
|
+
import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-WKIPLNVO.js';
|
|
3
|
+
import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, disposeRowBindings, wireBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-JRKE6XEK.js';
|
|
4
|
+
import { effect } from './chunk-Y2FOYPBV.js';
|
|
5
|
+
import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, flatten } from './chunk-BDX3R4OM.js';
|
|
6
|
+
import { devHooks } from './chunk-CEQMZYLR.js';
|
|
7
7
|
|
|
8
8
|
// src/list-render-state.ts
|
|
9
9
|
function deriveListRenderState(bindingCount) {
|
|
@@ -18,7 +18,8 @@ function decideListPath(state, patches, snapshotLength, previousBindingCount) {
|
|
|
18
18
|
for (const p of patches) {
|
|
19
19
|
if (p.type === "insert") netDelta += 1;
|
|
20
20
|
else if (p.type === "remove") netDelta -= 1;
|
|
21
|
-
else if (p.type === "replace")
|
|
21
|
+
else if (p.type === "replace")
|
|
22
|
+
return { path: "snapshot", reason: "replace" };
|
|
22
23
|
}
|
|
23
24
|
const count = previousBindingCount ?? 0;
|
|
24
25
|
if (count + netDelta !== snapshotLength) {
|
|
@@ -153,10 +154,12 @@ function eachGranular(sig, render, cacheKey, listKey) {
|
|
|
153
154
|
return eachSnapshotById(snapshot, render, cacheKey, id, sig);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
|
-
const renderRow = (item, index) => captureRowBindings(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
const renderRow = (item, index) => captureRowBindings(
|
|
158
|
+
() => inRowScope(() => {
|
|
159
|
+
const out = render(item, index);
|
|
160
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
161
|
+
})
|
|
162
|
+
);
|
|
160
163
|
const internalPatches = new Array(patches.length);
|
|
161
164
|
const cache = ctx.caches.get(id);
|
|
162
165
|
try {
|
|
@@ -223,13 +226,16 @@ function eachSnapshotById(items, render, cacheKey, id, source) {
|
|
|
223
226
|
devHooks.staleIndex?.(id);
|
|
224
227
|
}
|
|
225
228
|
} else {
|
|
226
|
-
const captured = captureRowBindings(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
const captured = captureRowBindings(
|
|
230
|
+
() => inRowScope(() => {
|
|
231
|
+
const out = render(item, i);
|
|
232
|
+
return isSafeHtml(out) ? out.toString() : out;
|
|
233
|
+
})
|
|
234
|
+
);
|
|
230
235
|
html = captured.html;
|
|
231
236
|
bindings = captured.bindings;
|
|
232
|
-
if (cache !== null)
|
|
237
|
+
if (cache !== null)
|
|
238
|
+
cache.set(item, { cacheKey: k, html, bindings, version, index: i });
|
|
233
239
|
}
|
|
234
240
|
segItems[i] = { ref: item, cacheKey: k, html, bindings };
|
|
235
241
|
}
|
|
@@ -281,12 +287,7 @@ function restoreFocus(snap) {
|
|
|
281
287
|
if (domSelection === null) return;
|
|
282
288
|
const [anchorNode, anchorOffset, focusNode, focusOffset] = domSelection;
|
|
283
289
|
try {
|
|
284
|
-
document.getSelection()?.setBaseAndExtent(
|
|
285
|
-
anchorNode,
|
|
286
|
-
anchorOffset,
|
|
287
|
-
focusNode,
|
|
288
|
-
focusOffset
|
|
289
|
-
);
|
|
290
|
+
document.getSelection()?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
|
|
290
291
|
} catch {
|
|
291
292
|
}
|
|
292
293
|
}
|
|
@@ -343,7 +344,11 @@ function protectionTag(node) {
|
|
|
343
344
|
const { dataset } = node;
|
|
344
345
|
return (dataset.morphSkip !== void 0 ? "s" : "") + (dataset.morphSkipChildren !== void 0 ? "c" : "") + (dataset.morphPreserve !== void 0 ? "p" : "");
|
|
345
346
|
}
|
|
346
|
-
var MARKER_PREFIXES = [
|
|
347
|
+
var MARKER_PREFIXES = [
|
|
348
|
+
LIST_MARKER_PREFIX,
|
|
349
|
+
TEXT_MARKER_PREFIX,
|
|
350
|
+
ROW_TEXT_PREFIX
|
|
351
|
+
];
|
|
347
352
|
function isMarker(node) {
|
|
348
353
|
if (node.nodeType !== COMMENT_NODE) return false;
|
|
349
354
|
const { data } = node;
|
|
@@ -421,7 +426,8 @@ function morphChildren(fromParent, toParent, ownedItems) {
|
|
|
421
426
|
if (matched === null && fromChild !== null && toChild.nodeType === COMMENT_NODE && toChild.data.startsWith(LIST_MARKER_PREFIX)) {
|
|
422
427
|
const wantData = toChild.data;
|
|
423
428
|
for (let scan = fromChild.nextSibling; scan !== null; scan = scan.nextSibling) {
|
|
424
|
-
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
429
|
+
if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
|
|
430
|
+
continue;
|
|
425
431
|
const regionEnd = afterListRegion(scan, ownedItems);
|
|
426
432
|
const run = [];
|
|
427
433
|
for (let r = scan; r !== null && r !== regionEnd; r = r.nextSibling) {
|
|
@@ -534,7 +540,10 @@ function preserveTextEntryState(fromEl, toEl) {
|
|
|
534
540
|
const toInput = toEl;
|
|
535
541
|
toInput.value = fromInput.value;
|
|
536
542
|
try {
|
|
537
|
-
toInput.setSelectionRange(
|
|
543
|
+
toInput.setSelectionRange(
|
|
544
|
+
fromInput.selectionStart,
|
|
545
|
+
fromInput.selectionEnd
|
|
546
|
+
);
|
|
538
547
|
} catch {
|
|
539
548
|
}
|
|
540
549
|
}
|
|
@@ -567,7 +576,8 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
567
576
|
if (oldGt === -1 || newGt === -1) return false;
|
|
568
577
|
if (oldHtml.length - oldGt !== newHtml.length - newGt) return false;
|
|
569
578
|
if (oldHtml.slice(oldGt) !== newHtml.slice(newGt)) return false;
|
|
570
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
579
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
580
|
+
return false;
|
|
571
581
|
const oldTag = parseOpeningTag(oldHtml, oldGt);
|
|
572
582
|
const newTag = parseOpeningTag(newHtml, newGt);
|
|
573
583
|
if (oldTag === null || newTag === null) return false;
|
|
@@ -595,7 +605,8 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
|
|
|
595
605
|
return true;
|
|
596
606
|
}
|
|
597
607
|
function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
598
|
-
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
608
|
+
if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
|
|
609
|
+
return false;
|
|
599
610
|
let p = 0;
|
|
600
611
|
const minLen = Math.min(oldHtml.length, newHtml.length);
|
|
601
612
|
while (p < minLen && oldHtml.charCodeAt(p) === newHtml.charCodeAt(p)) p++;
|
|
@@ -610,7 +621,8 @@ function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
|
|
|
610
621
|
if (!isPureTextWindow(newHtml, p, newWinEnd)) return false;
|
|
611
622
|
if (p === 0) return false;
|
|
612
623
|
const boundaryCc = oldHtml.charCodeAt(p - 1);
|
|
613
|
-
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
624
|
+
if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
|
|
625
|
+
return false;
|
|
614
626
|
const textStart = lastIndexOfChar(oldHtml, GT, p - 1);
|
|
615
627
|
if (textStart === -1) return false;
|
|
616
628
|
const textEnd = oldHtml.indexOf("<", p);
|
|
@@ -637,7 +649,8 @@ function containsDataMorphSkip(html) {
|
|
|
637
649
|
function isPureTextWindow(html, start, end) {
|
|
638
650
|
for (let i = start; i < end; i++) {
|
|
639
651
|
const cc = html.charCodeAt(i);
|
|
640
|
-
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
652
|
+
if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
|
|
653
|
+
return false;
|
|
641
654
|
}
|
|
642
655
|
return true;
|
|
643
656
|
}
|
|
@@ -769,7 +782,14 @@ function reconcileGranular(binding, patches) {
|
|
|
769
782
|
if (runLen === 1) {
|
|
770
783
|
applySingleInsert(liveParent, items, patch, endAnchor(binding));
|
|
771
784
|
} else {
|
|
772
|
-
applyBulkInsert(
|
|
785
|
+
applyBulkInsert(
|
|
786
|
+
liveParent,
|
|
787
|
+
items,
|
|
788
|
+
patches,
|
|
789
|
+
i,
|
|
790
|
+
runEnd,
|
|
791
|
+
endAnchor(binding)
|
|
792
|
+
);
|
|
773
793
|
}
|
|
774
794
|
i = runEnd;
|
|
775
795
|
continue;
|
|
@@ -881,7 +901,10 @@ function applyBulkUpdate(liveParent, items, patches, start, end) {
|
|
|
881
901
|
morphChanges.push({ patchIdx: k, html: p.html });
|
|
882
902
|
}
|
|
883
903
|
if (morphChanges.length === 0) return;
|
|
884
|
-
const { content, count } = parseRowTemplate(
|
|
904
|
+
const { content, count } = parseRowTemplate(
|
|
905
|
+
morphChanges.map((c) => c.html).join(""),
|
|
906
|
+
liveParent
|
|
907
|
+
);
|
|
885
908
|
if (count !== morphChanges.length) {
|
|
886
909
|
throw findOffendingChange(patches, morphChanges, liveParent);
|
|
887
910
|
}
|
|
@@ -924,18 +947,30 @@ function applyBulkInsert(liveParent, items, patches, start, end, tailAnchor) {
|
|
|
924
947
|
function findOffendingInsert(patches, start, htmls, liveParent) {
|
|
925
948
|
for (let i = 0; i < htmls.length; i++) {
|
|
926
949
|
if (parseRowTemplate(htmls[i], liveParent).count !== 1) {
|
|
927
|
-
return rowContractError(
|
|
950
|
+
return rowContractError(
|
|
951
|
+
patches[start + i].index,
|
|
952
|
+
htmls[i],
|
|
953
|
+
liveParent
|
|
954
|
+
);
|
|
928
955
|
}
|
|
929
956
|
}
|
|
930
|
-
return new Error(
|
|
957
|
+
return new Error(
|
|
958
|
+
"each(): bulk-insert mismatch with no per-row offender (kerf bug)."
|
|
959
|
+
);
|
|
931
960
|
}
|
|
932
961
|
function findOffendingChange(patches, changes, liveParent) {
|
|
933
962
|
for (const c of changes) {
|
|
934
963
|
if (parseRowTemplate(c.html, liveParent).count !== 1) {
|
|
935
|
-
return rowContractError(
|
|
964
|
+
return rowContractError(
|
|
965
|
+
patches[c.patchIdx].index,
|
|
966
|
+
c.html,
|
|
967
|
+
liveParent
|
|
968
|
+
);
|
|
936
969
|
}
|
|
937
970
|
}
|
|
938
|
-
return new Error(
|
|
971
|
+
return new Error(
|
|
972
|
+
"each(): bulk-update mismatch with no per-row offender (kerf bug)."
|
|
973
|
+
);
|
|
939
974
|
}
|
|
940
975
|
|
|
941
976
|
// src/list-reconcile-inplace.ts
|
|
@@ -960,7 +995,12 @@ function tryInPlaceContentUpdate(binding, listSeg) {
|
|
|
960
995
|
}
|
|
961
996
|
function updateRowInPlace(liveParent, old, ni, index) {
|
|
962
997
|
if (old.html === ni.html || tryAttributeOnlyFastPath(old.node, old.html, ni.html) || tryTextContentFastPath(old.node, old.html, ni.html)) {
|
|
963
|
-
const kept = carryOrRewireRowBindings(
|
|
998
|
+
const kept = carryOrRewireRowBindings(
|
|
999
|
+
old.node,
|
|
1000
|
+
old.bindings,
|
|
1001
|
+
old.bindingDisposers,
|
|
1002
|
+
ni.bindings
|
|
1003
|
+
);
|
|
964
1004
|
return {
|
|
965
1005
|
ref: ni.ref,
|
|
966
1006
|
cacheKey: ni.cacheKey,
|
|
@@ -973,7 +1013,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
973
1013
|
const newNode = parseSingleRow(ni.html, index, liveParent);
|
|
974
1014
|
if (old.node.tagName === newNode.tagName) {
|
|
975
1015
|
_morphElement(old.node, newNode);
|
|
976
|
-
const kept = carryOrRewireRowBindings(
|
|
1016
|
+
const kept = carryOrRewireRowBindings(
|
|
1017
|
+
old.node,
|
|
1018
|
+
old.bindings,
|
|
1019
|
+
old.bindingDisposers,
|
|
1020
|
+
ni.bindings
|
|
1021
|
+
);
|
|
977
1022
|
return {
|
|
978
1023
|
ref: ni.ref,
|
|
979
1024
|
cacheKey: ni.cacheKey,
|
|
@@ -985,7 +1030,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
|
|
|
985
1030
|
}
|
|
986
1031
|
disposeRowBindings(old.bindingDisposers);
|
|
987
1032
|
liveParent.replaceChild(newNode, old.node);
|
|
988
|
-
const fresh = carryOrRewireRowBindings(
|
|
1033
|
+
const fresh = carryOrRewireRowBindings(
|
|
1034
|
+
newNode,
|
|
1035
|
+
void 0,
|
|
1036
|
+
void 0,
|
|
1037
|
+
ni.bindings
|
|
1038
|
+
);
|
|
989
1039
|
return {
|
|
990
1040
|
ref: ni.ref,
|
|
991
1041
|
cacheKey: ni.cacheKey,
|
|
@@ -1070,15 +1120,22 @@ function buildFreshNodes(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
|
1070
1120
|
function findOffendingRow(newRecord, freshIndices, freshHtmls, liveParent) {
|
|
1071
1121
|
for (let i = 0; i < freshHtmls.length; i++) {
|
|
1072
1122
|
if (parseRowTemplate(freshHtmls[i], liveParent).count !== 1) {
|
|
1073
|
-
return rowContractError(
|
|
1123
|
+
return rowContractError(
|
|
1124
|
+
freshIndices[i],
|
|
1125
|
+
newRecord[freshIndices[i]].html,
|
|
1126
|
+
liveParent
|
|
1127
|
+
);
|
|
1074
1128
|
}
|
|
1075
1129
|
}
|
|
1076
|
-
return new Error(
|
|
1130
|
+
return new Error(
|
|
1131
|
+
"each(): bulk-parse mismatch with no per-row offender (kerf bug)."
|
|
1132
|
+
);
|
|
1077
1133
|
}
|
|
1078
1134
|
function removeOldNodes(liveParent, removedItems) {
|
|
1079
1135
|
for (const item of removedItems) {
|
|
1080
1136
|
disposeRowBindings(item.bindingDisposers);
|
|
1081
|
-
if (item.node.parentElement === liveParent)
|
|
1137
|
+
if (item.node.parentElement === liveParent)
|
|
1138
|
+
liveParent.removeChild(item.node);
|
|
1082
1139
|
}
|
|
1083
1140
|
}
|
|
1084
1141
|
function applyMoves(liveParent, newRecord, prevIdx, stable, tailAnchor) {
|
|
@@ -1160,7 +1217,8 @@ function assertNotInsideMountedTree(rootEl) {
|
|
|
1160
1217
|
ancestor = ancestor.parentElement;
|
|
1161
1218
|
}
|
|
1162
1219
|
const stack = [];
|
|
1163
|
-
for (let i = 0; i < rootEl.children.length; i++)
|
|
1220
|
+
for (let i = 0; i < rootEl.children.length; i++)
|
|
1221
|
+
stack.push(rootEl.children[i]);
|
|
1164
1222
|
while (stack.length > 0) {
|
|
1165
1223
|
const cur = stack.pop();
|
|
1166
1224
|
if (isMounted(cur)) throw new Error(NESTED_MOUNT_MSG);
|
|
@@ -1241,7 +1299,8 @@ function mount(rootEl, render) {
|
|
|
1241
1299
|
valueOnlyWarnCtx
|
|
1242
1300
|
);
|
|
1243
1301
|
if (anyRebuiltListIsGranular(segment, renderCtx.rebuiltLists)) {
|
|
1244
|
-
for (const id of renderCtx.rebuiltLists)
|
|
1302
|
+
for (const id of renderCtx.rebuiltLists)
|
|
1303
|
+
renderCtx.bindingCounts.delete(id);
|
|
1245
1304
|
result = runRenderPass();
|
|
1246
1305
|
segment = resultToSegment(result);
|
|
1247
1306
|
nextStaticHtml = runSubsequentRender(
|
|
@@ -1255,7 +1314,8 @@ function mount(rootEl, render) {
|
|
|
1255
1314
|
}
|
|
1256
1315
|
if (nextStaticHtml !== prevStaticHtml) {
|
|
1257
1316
|
bindingDisposers = wireBindings(rootEl, bindingCtx, bindingDisposers);
|
|
1258
|
-
if (devHooks.staleBindingEnabled?.())
|
|
1317
|
+
if (devHooks.staleBindingEnabled?.())
|
|
1318
|
+
prevWiredBindings = bindingCtx.list;
|
|
1259
1319
|
} else {
|
|
1260
1320
|
devHooks.staleBinding?.(prevWiredBindings, bindingCtx.list);
|
|
1261
1321
|
}
|
|
@@ -1307,12 +1367,22 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
|
|
|
1307
1367
|
if (currentStaticHtml === prevStaticHtml) {
|
|
1308
1368
|
return prevStaticHtml;
|
|
1309
1369
|
}
|
|
1310
|
-
devHooks.valueOnlyRerender?.(
|
|
1370
|
+
devHooks.valueOnlyRerender?.(
|
|
1371
|
+
prevStaticHtml,
|
|
1372
|
+
currentStaticHtml,
|
|
1373
|
+
valueOnlyWarnCtx
|
|
1374
|
+
);
|
|
1311
1375
|
cleanupOrphanBindings(segment, bindings, renderCtx);
|
|
1312
1376
|
const template = rootEl.cloneNode(false);
|
|
1313
1377
|
template.innerHTML = currentStaticHtml;
|
|
1314
1378
|
morph(rootEl, template, collectOwnedItems(bindings));
|
|
1315
|
-
bindListsFromMarkers(
|
|
1379
|
+
bindListsFromMarkers(
|
|
1380
|
+
rootEl,
|
|
1381
|
+
segment,
|
|
1382
|
+
bindings,
|
|
1383
|
+
false,
|
|
1384
|
+
renderCtx.rebuiltLists
|
|
1385
|
+
);
|
|
1316
1386
|
return currentStaticHtml;
|
|
1317
1387
|
}
|
|
1318
1388
|
function coerceRenderResult(result) {
|
|
@@ -1339,7 +1409,8 @@ function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLi
|
|
|
1339
1409
|
const id = marker.data.slice(LIST_MARKER_PREFIX.length);
|
|
1340
1410
|
const existing = bindings.get(id);
|
|
1341
1411
|
if (existing) {
|
|
1342
|
-
if (existing.marker === marker && rootEl.contains(existing.marker))
|
|
1412
|
+
if (existing.marker === marker && rootEl.contains(existing.marker))
|
|
1413
|
+
continue;
|
|
1343
1414
|
for (const item of existing.items) {
|
|
1344
1415
|
disposeRowBindings(item.bindingDisposers);
|
|
1345
1416
|
if (rootEl.contains(item.node)) {
|
|
@@ -1385,7 +1456,8 @@ function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
|
|
|
1385
1456
|
const { content, count } = parseRowTemplate(expectedHtml, liveParent);
|
|
1386
1457
|
if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
|
|
1387
1458
|
const expectedTag = content.firstElementChild.tagName;
|
|
1388
|
-
if (boundEl.tagName !== expectedTag)
|
|
1459
|
+
if (boundEl.tagName !== expectedTag)
|
|
1460
|
+
throw rowStructureError(index, boundEl.tagName, expectedTag);
|
|
1389
1461
|
}
|
|
1390
1462
|
function rowStructureError(index, gotTag, wantTag) {
|
|
1391
1463
|
const got = gotTag.toLowerCase();
|
|
@@ -1425,4 +1497,4 @@ function collectComments(node, out) {
|
|
|
1425
1497
|
|
|
1426
1498
|
export { captureFocus, each, morph, mount, moveNode, restoreFocus };
|
|
1427
1499
|
|
|
1428
|
-
//# sourceMappingURL=chunk-
|
|
1500
|
+
//# sourceMappingURL=chunk-DLY444MN.js.map
|