kerfjs 5.0.0-beta.8 → 5.0.0-beta.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2577 -117
- package/README.md +96 -37
- package/ai/cursorrules +110 -61
- package/ai/manifest.json +16 -7
- package/ai/skill.md +120 -75
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +10 -6
- 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 +1 -66
- package/dist/attach.js.map +1 -1
- package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
- package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
- package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
- package/dist/chunk-4TX6YJDM.js.map +1 -0
- package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
- package/dist/chunk-5WRGJZV6.js.map +1 -0
- package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
- package/dist/chunk-BI4HGICV.js.map +1 -0
- package/dist/chunk-FM6N2B4N.js +70 -0
- package/dist/chunk-FM6N2B4N.js.map +1 -0
- package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
- package/dist/chunk-H7WTRM6F.js.map +1 -0
- package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
- package/dist/chunk-IHMDBBUJ.js.map +1 -0
- package/dist/chunk-JN7EJM3B.js +86 -0
- package/dist/chunk-JN7EJM3B.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-SVATPF5R.js → chunk-TLL3KXT6.js} +249 -160
- package/dist/chunk-TLL3KXT6.js.map +1 -0
- package/dist/{chunk-ZDCJZCNO.js → chunk-UGNPDLNI.js} +78 -45
- package/dist/chunk-UGNPDLNI.js.map +1 -0
- package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
- package/dist/chunk-WKIPLNVO.js.map +1 -0
- package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
- package/dist/dev.d.ts +31 -13
- package/dist/dev.js +101 -35
- 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 +14 -9
- package/dist/index.js +29 -20
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +13 -5
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +7 -3
- package/dist/list.js +126 -28
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +128 -129
- package/dist/overlay.js +675 -366
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +2 -2
- package/dist/router.d.ts +13 -4
- package/dist/router.js +37 -10
- package/dist/router.js.map +1 -1
- package/dist/scope.d.ts +35 -8
- package/dist/scope.js +61 -30
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/timing.d.ts +3 -2
- package/dist/timing.js +1 -1
- package/dist/timing.js.map +1 -1
- package/llms.txt +12 -5
- package/package.json +33 -8
- 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 +0 -54
- 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-U6FK33SG.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
package/dist/dev.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { truncateRowHtml } from './chunk-
|
|
2
|
-
import { LIST_MARKER_PREFIX } from './chunk-
|
|
3
|
-
import { installDevHooks } from './chunk-
|
|
4
|
-
export { clearDevHooks, devHooks, installDevHooks } from './chunk-
|
|
1
|
+
import { truncateRowHtml } from './chunk-WKIPLNVO.js';
|
|
2
|
+
import { LIST_MARKER_PREFIX, OVERLAY_HOST_SELECTOR, isOverlayHost } from './chunk-4TX6YJDM.js';
|
|
3
|
+
import { installDevHooks } from './chunk-3C6F6NM4.js';
|
|
4
|
+
export { clearDevHooks, devHooks, installDevHooks } from './chunk-3C6F6NM4.js';
|
|
5
5
|
import { signal, Signal } from '@preact/signals-core';
|
|
6
6
|
|
|
7
7
|
// src/dev-warn-config.ts
|
|
@@ -24,7 +24,10 @@ function applyWarningOptions(options) {
|
|
|
24
24
|
for (const [key, value] of Object.entries(options)) {
|
|
25
25
|
const envName = ENV_NAME[key];
|
|
26
26
|
if (envName === void 0 || value === void 0) continue;
|
|
27
|
-
overrides.set(
|
|
27
|
+
overrides.set(
|
|
28
|
+
envName,
|
|
29
|
+
value === false ? false : value === true ? "1" : value
|
|
30
|
+
);
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
function devFlag(envName) {
|
|
@@ -33,6 +36,9 @@ function devFlag(envName) {
|
|
|
33
36
|
const proc = globalThis.process;
|
|
34
37
|
return proc?.env?.[envName];
|
|
35
38
|
}
|
|
39
|
+
function silenceHint(key) {
|
|
40
|
+
return `Silence it with enableWarnings({ ${key}: false }) from kerfjs/dev, or set ${ENV_NAME[key]}=0 (or unset it) under Node.`;
|
|
41
|
+
}
|
|
36
42
|
|
|
37
43
|
// src/dev-binding-warn.ts
|
|
38
44
|
var warnedHoles = /* @__PURE__ */ new Set();
|
|
@@ -51,7 +57,7 @@ function maybeWarnStaleBinding(prevWired, current) {
|
|
|
51
57
|
if (warnedHoles.has(cur.id)) continue;
|
|
52
58
|
warnedHoles.add(cur.id);
|
|
53
59
|
console.warn(
|
|
54
|
-
`kerf: fine-grained binding ${describeHole(cur)} switched to a different signal instance on a render whose static-surrounds HTML was byte-for-byte unchanged. On that fast path kerf keeps the original binding effect and does NOT re-bind, so this hole is now stale \u2014 it still tracks the FIRST signal instance and will not reflect the new one. Bind one computed that switches internally (e.g. class={computed(() => cond.value ? sigA.value : sigB.value)}) instead of switching which signal instance you bind (see docs/2-reactivity \xA72.9).
|
|
60
|
+
`kerf: fine-grained binding ${describeHole(cur)} switched to a different signal instance on a render whose static-surrounds HTML was byte-for-byte unchanged. On that fast path kerf keeps the original binding effect and does NOT re-bind, so this hole is now stale \u2014 it still tracks the FIRST signal instance and will not reflect the new one. Bind one computed that switches internally (e.g. class={computed(() => cond.value ? sigA.value : sigB.value)}) instead of switching which signal instance you bind (see docs/2-reactivity \xA72.9). ` + silenceHint("staleBinding")
|
|
55
61
|
);
|
|
56
62
|
}
|
|
57
63
|
}
|
|
@@ -77,7 +83,7 @@ function warnIfInsideEffect(fn) {
|
|
|
77
83
|
if (warned) return;
|
|
78
84
|
warned = true;
|
|
79
85
|
console.warn(
|
|
80
|
-
`kerf: ${fn}() was called inside an effect() body. Every effect re-run installs a fresh root listener; the effect disposer cleans up the reactive subscription but not the listeners, so listener count grows linearly with signal churn and each listener pins its handler closure. Register the delegate once at module or setup scope and gate behavior on the signal *inside the handler* where the read is free. See docs/5-event-delegation.md \xA75.3 "When capturing the disposer still isn't enough".
|
|
86
|
+
`kerf: ${fn}() was called inside an effect() body. Every effect re-run installs a fresh root listener; the effect disposer cleans up the reactive subscription but not the listeners, so listener count grows linearly with signal churn and each listener pins its handler closure. Register the delegate once at module or setup scope and gate behavior on the signal *inside the handler* where the read is free. See docs/5-event-delegation.md \xA75.3 "When capturing the disposer still isn't enough". ` + silenceHint("delegateInEffect")
|
|
81
87
|
);
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -100,7 +106,7 @@ function maybeWarnEachInMorphSkip(id, liveParent, rootEl) {
|
|
|
100
106
|
if (!hasMorphSkipAncestor(liveParent, rootEl)) return;
|
|
101
107
|
warnedIds.add(id);
|
|
102
108
|
console.warn(
|
|
103
|
-
`kerf: each() list '${id}' is inside a data-morph-skip subtree. The keyed reconciler still updates the list rows, but any static signal-reactive JSX inside the same skipped ancestor (e.g. <p>{count.value}</p>) is frozen \u2014 the morph never visits it. Remove data-morph-skip from any element that contains reactive JSX content and reserve it for truly library-owned hosts.
|
|
109
|
+
`kerf: each() list '${id}' is inside a data-morph-skip subtree. The keyed reconciler still updates the list rows, but any static signal-reactive JSX inside the same skipped ancestor (e.g. <p>{count.value}</p>) is frozen \u2014 the morph never visits it. Remove data-morph-skip from any element that contains reactive JSX content and reserve it for truly library-owned hosts. ` + silenceHint("eachInMorphSkip")
|
|
104
110
|
);
|
|
105
111
|
}
|
|
106
112
|
var warnedDupIds = /* @__PURE__ */ new Set();
|
|
@@ -115,7 +121,7 @@ function maybeWarnDuplicateCacheKeys(id, segItems) {
|
|
|
115
121
|
if (seen.has(si.cacheKey)) {
|
|
116
122
|
warnedDupIds.add(id);
|
|
117
123
|
console.warn(
|
|
118
|
-
`kerf: each() list '${id}' has duplicate cacheKey values (duplicate: ${String(si.cacheKey)}). The cacheKey function should return a unique value per row so kerf can tell apart items for memoization \u2014 duplicate values cause some rows to return stale cached HTML when external state that affects their render changes.
|
|
124
|
+
`kerf: each() list '${id}' has duplicate cacheKey values (duplicate: ${String(si.cacheKey)}). The cacheKey function should return a unique value per row so kerf can tell apart items for memoization \u2014 duplicate values cause some rows to return stale cached HTML when external state that affects their render changes. ` + silenceHint("duplicateEachKeys")
|
|
119
125
|
);
|
|
120
126
|
return;
|
|
121
127
|
}
|
|
@@ -157,18 +163,38 @@ function findListInvariantViolations(rootEl, bindings, expectedCounts) {
|
|
|
157
163
|
const { marker, liveParent, items } = binding;
|
|
158
164
|
const expected = expectedCounts?.get(id);
|
|
159
165
|
if (expected !== void 0 && items.length !== expected) {
|
|
160
|
-
problems.push(
|
|
166
|
+
problems.push(
|
|
167
|
+
describe(
|
|
168
|
+
id,
|
|
169
|
+
`holds ${items.length} row(s) but its source has ${expected} \u2014 the reconcile dropped or duplicated rows`
|
|
170
|
+
)
|
|
171
|
+
);
|
|
161
172
|
}
|
|
162
173
|
if (!rootEl.contains(marker)) {
|
|
163
|
-
problems.push(
|
|
174
|
+
problems.push(
|
|
175
|
+
describe(
|
|
176
|
+
id,
|
|
177
|
+
"its marker comment is no longer inside the mount root, so every future reconcile would mutate a detached tree"
|
|
178
|
+
)
|
|
179
|
+
);
|
|
164
180
|
continue;
|
|
165
181
|
}
|
|
166
182
|
if (marker.data !== `${LIST_MARKER_PREFIX}${id}`) {
|
|
167
|
-
problems.push(
|
|
183
|
+
problems.push(
|
|
184
|
+
describe(
|
|
185
|
+
id,
|
|
186
|
+
`its marker reads '${marker.data}' \u2014 the id is carried by a different marker node, so this binding describes another list`
|
|
187
|
+
)
|
|
188
|
+
);
|
|
168
189
|
}
|
|
169
190
|
const markerIndex = indexIn(liveParent, marker);
|
|
170
191
|
if (markerIndex === -1) {
|
|
171
|
-
problems.push(
|
|
192
|
+
problems.push(
|
|
193
|
+
describe(
|
|
194
|
+
id,
|
|
195
|
+
"its marker is not a child of the parent the binding records"
|
|
196
|
+
)
|
|
197
|
+
);
|
|
172
198
|
continue;
|
|
173
199
|
}
|
|
174
200
|
let previousIndex = markerIndex;
|
|
@@ -177,28 +203,46 @@ function findListInvariantViolations(rootEl, bindings, expectedCounts) {
|
|
|
177
203
|
const node = items[i].node;
|
|
178
204
|
const index = indexIn(liveParent, node);
|
|
179
205
|
if (index === -1) {
|
|
180
|
-
problems.push(
|
|
206
|
+
problems.push(
|
|
207
|
+
describe(
|
|
208
|
+
id,
|
|
209
|
+
`bound row ${i} is not a child of the list's parent (<${liveParent.tagName.toLowerCase()}>)${node.isConnected ? " \u2014 it is attached elsewhere in the document" : " \u2014 it is detached"}`
|
|
210
|
+
)
|
|
211
|
+
);
|
|
181
212
|
continue;
|
|
182
213
|
}
|
|
183
214
|
if (index <= previousIndex) {
|
|
184
|
-
problems.push(
|
|
215
|
+
problems.push(
|
|
216
|
+
describe(
|
|
217
|
+
id,
|
|
218
|
+
`bound row ${i} appears at child position ${index}, which is not after the previous one (${previousIndex}) \u2014 the rows are out of order or have crossed the marker`
|
|
219
|
+
)
|
|
220
|
+
);
|
|
185
221
|
}
|
|
186
222
|
previousIndex = index;
|
|
187
223
|
if (first === -1) first = index;
|
|
188
224
|
const owner = owners.get(node);
|
|
189
225
|
if (owner !== void 0) {
|
|
190
|
-
problems.push(
|
|
226
|
+
problems.push(
|
|
227
|
+
describe(id, `bound row ${i} is also claimed by list '${owner}'`)
|
|
228
|
+
);
|
|
191
229
|
} else {
|
|
192
230
|
owners.set(node, id);
|
|
193
231
|
}
|
|
194
232
|
}
|
|
195
|
-
if (first !== -1)
|
|
233
|
+
if (first !== -1)
|
|
234
|
+
spans.push({ id, parent: liveParent, from: first, to: previousIndex });
|
|
196
235
|
}
|
|
197
236
|
for (let a = 0; a < spans.length; a++) {
|
|
198
237
|
for (let b = a + 1; b < spans.length; b++) {
|
|
199
238
|
if (spans[a].parent !== spans[b].parent) continue;
|
|
200
239
|
if (spans[a].from <= spans[b].to && spans[b].from <= spans[a].to) {
|
|
201
|
-
problems.push(
|
|
240
|
+
problems.push(
|
|
241
|
+
describe(
|
|
242
|
+
spans[a].id,
|
|
243
|
+
`its rows (child positions ${spans[a].from}-${spans[a].to}) overlap those of list '${spans[b].id}' (${spans[b].from}-${spans[b].to}) in the same parent`
|
|
244
|
+
)
|
|
245
|
+
);
|
|
202
246
|
}
|
|
203
247
|
}
|
|
204
248
|
}
|
|
@@ -207,7 +251,11 @@ function findListInvariantViolations(rootEl, bindings, expectedCounts) {
|
|
|
207
251
|
function maybeCheckListInvariants(rootEl, bindings, expectedCounts) {
|
|
208
252
|
const level = mode();
|
|
209
253
|
if (level === "off") return;
|
|
210
|
-
const problems = findListInvariantViolations(
|
|
254
|
+
const problems = findListInvariantViolations(
|
|
255
|
+
rootEl,
|
|
256
|
+
bindings,
|
|
257
|
+
expectedCounts
|
|
258
|
+
);
|
|
211
259
|
if (problems.length === 0) return;
|
|
212
260
|
const report = `${problems.join("\n")}
|
|
213
261
|
This is a kerf bug, not an application one \u2014 please report it with the markup that produced it. Set KERF_DEV_INVARIANTS=1 to warn instead of throw, or unset it to disable these checks.`;
|
|
@@ -224,7 +272,7 @@ function maybeWarnStaleIndex(id) {
|
|
|
224
272
|
if (warnedIds2.has(id)) return;
|
|
225
273
|
warnedIds2.add(id);
|
|
226
274
|
console.warn(
|
|
227
|
-
`kerf: each() list '${id}' reused a memoized row at a new index this render, but its render function takes an \`index\` argument. Rows are memoized by object identity, not by position, so a reorder or an insert/remove ahead of a surviving row serves that row's cached HTML \u2014 computed at its OLD index. A numbered list, zebra striping, or an "N of M" label will silently show the wrong value. If the row output depends on the index, fold it into the memo key: each(items, render, { cacheKey: (_, i) => i }) (combine with your own key if you have one). If the index is not used in the output, ignore this.
|
|
275
|
+
`kerf: each() list '${id}' reused a memoized row at a new index this render, but its render function takes an \`index\` argument. Rows are memoized by object identity, not by position, so a reorder or an insert/remove ahead of a surviving row serves that row's cached HTML \u2014 computed at its OLD index. A numbered list, zebra striping, or an "N of M" label will silently show the wrong value. If the row output depends on the index, fold it into the memo key: each(items, render, { cacheKey: (_, i) => i }) (combine with your own key if you have one). If the index is not used in the output, ignore this. ` + silenceHint("staleIndex")
|
|
228
276
|
);
|
|
229
277
|
}
|
|
230
278
|
|
|
@@ -245,14 +293,13 @@ function maybeWarnListRebind(id, liveParent) {
|
|
|
245
293
|
if (warnedIds3.has(id)) return;
|
|
246
294
|
warnedIds3.add(id);
|
|
247
295
|
console.warn(
|
|
248
|
-
`kerf: each() list '${id}' had its container (<${liveParent.tagName.toLowerCase()}>) rebuilt by the morph this render \u2014 an ancestor's tag changed so the subtree was replaced, or a same-tag sibling positionally took the container's place. The list re-binds and repopulates automatically, but its rows were re-created from scratch: focus, scroll positions, in-progress IME composition, and any imperative listeners on the old row nodes are lost. If the rows should survive, give the LIST'S OWN container a stable id/data-key (keying the conditional sibling instead only helps when it is removed, not when it reappears) and keep the tags of the list's ancestors stable across renders.
|
|
296
|
+
`kerf: each() list '${id}' had its container (<${liveParent.tagName.toLowerCase()}>) rebuilt by the morph this render \u2014 an ancestor's tag changed so the subtree was replaced, or a same-tag sibling positionally took the container's place. The list re-binds and repopulates automatically, but its rows were re-created from scratch: focus, scroll positions, in-progress IME composition, and any imperative listeners on the old row nodes are lost. If the rows should survive, give the LIST'S OWN container a stable id/data-key (keying the conditional sibling instead only helps when it is removed, not when it reappears) and keep the tags of the list's ancestors stable across renders. ` + silenceHint("listRebind")
|
|
249
297
|
);
|
|
250
298
|
}
|
|
251
299
|
|
|
252
300
|
// src/dev-listener-warn.ts
|
|
253
301
|
var LISTENER_MARKER = /* @__PURE__ */ Symbol.for("kerfjs.devListener");
|
|
254
302
|
var patched = false;
|
|
255
|
-
var warned2 = false;
|
|
256
303
|
function isOptedIn5() {
|
|
257
304
|
return devFlag("KERF_DEV_WARN_REBUILT_LISTENERS") === "1";
|
|
258
305
|
}
|
|
@@ -277,33 +324,38 @@ function patchAddEventListenerOnce() {
|
|
|
277
324
|
};
|
|
278
325
|
}
|
|
279
326
|
function hasMarkedListener(el) {
|
|
280
|
-
|
|
281
|
-
const stack = [];
|
|
282
|
-
for (let i = 0; i < el.children.length; i++) stack.push(el.children[i]);
|
|
327
|
+
const stack = [el];
|
|
283
328
|
while (stack.length > 0) {
|
|
284
329
|
const cur = stack.pop();
|
|
285
|
-
if (cur[LISTENER_MARKER] === true)
|
|
330
|
+
if (cur[LISTENER_MARKER] === true)
|
|
331
|
+
return true;
|
|
332
|
+
if (isOverlayHost(cur)) continue;
|
|
286
333
|
for (let i = 0; i < cur.children.length; i++) stack.push(cur.children[i]);
|
|
287
334
|
}
|
|
288
335
|
return false;
|
|
289
336
|
}
|
|
337
|
+
function insideHostSlot(target, rootEl) {
|
|
338
|
+
const host = target.closest(OVERLAY_HOST_SELECTOR);
|
|
339
|
+
return host !== null && host !== rootEl && rootEl.contains(host);
|
|
340
|
+
}
|
|
290
341
|
function emitWarning() {
|
|
291
|
-
if (warned2) return;
|
|
292
|
-
warned2 = true;
|
|
293
342
|
console.warn(
|
|
294
|
-
"kerf: a node inside a mount()-managed tree was removed/rebuilt while carrying an imperative addEventListener listener. The listener is gone with the old node. Use `delegate(rootEl, 'click', '[data-action=\"...\"]', handler)` so the listener lives on a stable ancestor and survives re-renders, or wrap the host in `data-morph-skip` if the subtree is library-owned (Monaco, xterm, D3 charts).
|
|
343
|
+
"kerf: a node inside a mount()-managed tree was removed/rebuilt while carrying an imperative addEventListener listener. The listener is gone with the old node. Use `delegate(rootEl, 'click', '[data-action=\"...\"]', handler)` so the listener lives on a stable ancestor and survives re-renders, or wrap the host in `data-morph-skip` if the subtree is library-owned (Monaco, xterm, D3 charts). " + silenceHint("rebuiltListeners")
|
|
295
344
|
);
|
|
296
345
|
}
|
|
297
346
|
function installListenerRebuildWarn(rootEl) {
|
|
298
347
|
if (!isOptedIn5()) return null;
|
|
299
348
|
patchAddEventListenerOnce();
|
|
349
|
+
let warned3 = false;
|
|
300
350
|
const observer = new MutationObserver((mutations) => {
|
|
301
|
-
if (
|
|
351
|
+
if (warned3) return;
|
|
302
352
|
for (const m of mutations) {
|
|
353
|
+
if (insideHostSlot(m.target, rootEl)) continue;
|
|
303
354
|
for (let i = 0; i < m.removedNodes.length; i++) {
|
|
304
355
|
const removed = m.removedNodes[i];
|
|
305
356
|
if (!(removed instanceof Element)) continue;
|
|
306
357
|
if (hasMarkedListener(removed)) {
|
|
358
|
+
warned3 = true;
|
|
307
359
|
emitWarning();
|
|
308
360
|
return;
|
|
309
361
|
}
|
|
@@ -314,6 +366,18 @@ function installListenerRebuildWarn(rootEl) {
|
|
|
314
366
|
return observer;
|
|
315
367
|
}
|
|
316
368
|
|
|
369
|
+
// src/dev-overlay-warn.ts
|
|
370
|
+
var warned2 = /* @__PURE__ */ new Set();
|
|
371
|
+
var MESSAGES = {
|
|
372
|
+
hidden: "kerf overlay(): a surface was opened while a modal <dialog> is open, and this engine lacks the <dialog>.showModal() / Popover API needed to lift it into the top layer. It renders beneath the dialog and is inert, so the user can neither see nor use it. Render the content inside the dialog's own markup, or open the dialog without `native: true`.",
|
|
373
|
+
inert: "kerf overlay(): a non-modal surface opened while a modal <dialog> is open was lifted into the top layer so it is visible, but browsers keep everything outside a modal dialog inert \u2014 its focusable controls cannot be focused or clicked. Anchor the popover inside the dialog and give the dialog a `data-kerf-overlay-host` element (dialogs kerf opens get one automatically), or pass a `container` inside the dialog."
|
|
374
|
+
};
|
|
375
|
+
function maybeWarnOverlayBlockedByModal(reason) {
|
|
376
|
+
if (warned2.has(reason)) return;
|
|
377
|
+
warned2.add(reason);
|
|
378
|
+
console.warn(MESSAGES[reason]);
|
|
379
|
+
}
|
|
380
|
+
|
|
317
381
|
// src/dev-parser-repair-warn.ts
|
|
318
382
|
var BLOCK_TAGS = [
|
|
319
383
|
"address",
|
|
@@ -378,7 +442,7 @@ function maybeWarnParserRepair(html) {
|
|
|
378
442
|
if (warnedPairs.has(pair)) return;
|
|
379
443
|
warnedPairs.add(pair);
|
|
380
444
|
console.warn(
|
|
381
|
-
`kerf: a <${tag}> inside a <p> will not survive parsing. <p> may contain only phrasing content, so the HTML parser closes it before a block-level child \u2014 your <p> ends up EMPTY and the <${tag}> (plus everything after it) becomes its sibling instead of its child. kerf then reconciles that repaired tree correctly, so updates still work; what you lose is the structure you wrote, along with any CSS or querySelector that assumed it. Use a <div> (or a phrasing element like <span>) in place of the <p>, or move the block content outside it.
|
|
445
|
+
`kerf: a <${tag}> inside a <p> will not survive parsing. <p> may contain only phrasing content, so the HTML parser closes it before a block-level child \u2014 your <p> ends up EMPTY and the <${tag}> (plus everything after it) becomes its sibling instead of its child. kerf then reconciles that repaired tree correctly, so updates still work; what you lose is the structure you wrote, along with any CSS or querySelector that assumed it. Use a <div> (or a phrasing element like <span>) in place of the <p>, or move the block content outside it. ` + silenceHint("parserRepair")
|
|
382
446
|
);
|
|
383
447
|
}
|
|
384
448
|
|
|
@@ -417,7 +481,7 @@ function maybeWarnValueOnlyRerender(prevHtml, nextHtml, ctx) {
|
|
|
417
481
|
if (!_isValueOnlyDiff(a.content, b.content)) return;
|
|
418
482
|
ctx.warned = true;
|
|
419
483
|
console.warn(
|
|
420
|
-
"kerf: this re-render changed only text content and attribute values \u2014 no structural change \u2014 so every changed hole could be a fine-grained binding instead. Values bind, structure re-renders: pass the signal/computed itself ({count}, class={sig}) rather than reading .value in the hole, and each change updates just that node with no render re-run (a mount whose render reads no .value never re-renders at all). See docs/2-reactivity \xA72.9.
|
|
484
|
+
"kerf: this re-render changed only text content and attribute values \u2014 no structural change \u2014 so every changed hole could be a fine-grained binding instead. Values bind, structure re-renders: pass the signal/computed itself ({count}, class={sig}) rather than reading .value in the hole, and each change updates just that node with no render re-run (a mount whose render reads no .value never re-renders at all). See docs/2-reactivity \xA72.9. " + silenceHint("valueOnlyRerender")
|
|
421
485
|
);
|
|
422
486
|
}
|
|
423
487
|
|
|
@@ -430,7 +494,7 @@ function maybeWarnMissingRowKey(rowEl, rowHtml, binding) {
|
|
|
430
494
|
`kerf each(): the first row has no \`id\` or \`data-key\` attribute. Without one, rows match positionally \u2014 an insert/remove at the head shifts every row's identity, so focused inputs jump to the wrong row, mid-edit textareas swap content with their neighbor, and any per-row state silently follows the wrong item. Add \`data-key={item.id}\` (or set \`id\`) to the top-level element returned by the row render. Row HTML: ${JSON.stringify(truncateRowHtml(rowHtml))}`
|
|
431
495
|
);
|
|
432
496
|
}
|
|
433
|
-
var WARNING_MESSAGE = "kerf: signal was written but has no subscribers. Did you read `.value` outside of a render fn / effect()? Hoisted reads do not subscribe, so subsequent writes will not re-render. Move the read inside mount()'s render fn or effect() callback.
|
|
497
|
+
var WARNING_MESSAGE = "kerf: signal was written but has no subscribers. Did you read `.value` outside of a render fn / effect()? Hoisted reads do not subscribe, so subsequent writes will not re-render. Move the read inside mount()'s render fn or effect() callback. " + silenceHint("untrackedSignals");
|
|
434
498
|
var DevSignal = class extends Signal {
|
|
435
499
|
__hasSubscriber = false;
|
|
436
500
|
__warned = false;
|
|
@@ -462,13 +526,13 @@ function noteUntrackedCoverage() {
|
|
|
462
526
|
if (coverageNoticeShown) return;
|
|
463
527
|
coverageNoticeShown = true;
|
|
464
528
|
console.warn(
|
|
465
|
-
"kerf: KERF_DEV_WARN_UNTRACKED_SIGNALS only covers signals created AFTER kerfjs/dev is installed. Static imports are hoisted above `await import('kerfjs/dev')`, so module-scope signals in the modules you import are created first and this warning cannot see them \u2014 you may get no warnings even where the bug exists. To cover them, make `import 'kerfjs/dev'` the FIRST STATIC import of a dev-only entry file (static imports evaluate in order), then load the rest of your app.
|
|
529
|
+
"kerf: KERF_DEV_WARN_UNTRACKED_SIGNALS only covers signals created AFTER kerfjs/dev is installed. Static imports are hoisted above `await import('kerfjs/dev')`, so module-scope signals in the modules you import are created first and this warning cannot see them \u2014 you may get no warnings even where the bug exists. To cover them, make `import 'kerfjs/dev'` the FIRST STATIC import of a dev-only entry file (static imports evaluate in order), then load the rest of your app. " + silenceHint("untrackedSignals")
|
|
466
530
|
);
|
|
467
531
|
}
|
|
468
532
|
|
|
469
533
|
// src/dev-store-warn.ts
|
|
470
534
|
var WARNING_PREFIX = "kerf: defineStore.set() called with keys missing from the current state \u2014 ";
|
|
471
|
-
var WARNING_SUFFIX = ". set() REPLACES state; the missing keys will be undefined after this call. Use `set({ ...get(), ...next })` to merge instead, or update each call site to pass the full state.
|
|
535
|
+
var WARNING_SUFFIX = ". set() REPLACES state; the missing keys will be undefined after this call. Use `set({ ...get(), ...next })` to merge instead, or update each call site to pass the full state. " + silenceHint("narrowSet");
|
|
472
536
|
function isOptedIn8() {
|
|
473
537
|
return devFlag("KERF_DEV_WARN_NARROW_SET") === "1";
|
|
474
538
|
}
|
|
@@ -594,6 +658,8 @@ var DEV_HOOKS = {
|
|
|
594
658
|
staleIndexEnabled: isOptedInStaleIndex,
|
|
595
659
|
staleIndex: maybeWarnStaleIndex,
|
|
596
660
|
duplicateCacheKeys: maybeWarnDuplicateCacheKeys,
|
|
661
|
+
// --- overlay ----------------------------------------------------------
|
|
662
|
+
overlayBlockedByModal: maybeWarnOverlayBlockedByModal,
|
|
597
663
|
// --- urlScreen --------------------------------------------------------
|
|
598
664
|
urlScreenThrow: (message) => {
|
|
599
665
|
throw new Error(message);
|