kerfjs 2.0.1 → 3.0.0
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 +100 -0
- package/ai/cursorrules +11 -1
- package/ai/manifest.json +5 -5
- package/ai/skill.md +11 -1
- package/dist/array-signal.d.ts +7 -1
- package/dist/array-signal.js +11 -2
- package/dist/array-signal.js.map +1 -1
- package/dist/bindings-CYwoJpQb.d.ts +60 -0
- package/dist/chunk-3APBEVHF.js +20 -0
- package/dist/chunk-3APBEVHF.js.map +1 -0
- package/dist/chunk-GY4XV2UV.js +73 -0
- package/dist/chunk-GY4XV2UV.js.map +1 -0
- package/dist/{chunk-GYRZQCSY.js → chunk-JVVU2RQO.js} +11 -79
- package/dist/chunk-JVVU2RQO.js.map +1 -0
- package/dist/chunk-QIP723L4.js +15 -0
- package/dist/chunk-QIP723L4.js.map +1 -0
- package/dist/chunk-SAYPJ6XR.js +43 -0
- package/dist/chunk-SAYPJ6XR.js.map +1 -0
- package/dist/chunk-VVDJLWMP.js +14 -0
- package/dist/chunk-VVDJLWMP.js.map +1 -0
- package/dist/chunk-YHH7OUFA.js +58 -0
- package/dist/chunk-YHH7OUFA.js.map +1 -0
- package/dist/dev.d.ts +339 -0
- package/dist/dev.js +607 -0
- package/dist/dev.js.map +1 -0
- package/dist/html.d.ts +1 -0
- package/dist/html.js +4 -2
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +49 -11
- package/dist/index.js +351 -340
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +16 -60
- package/dist/jsx-runtime.js +4 -2
- package/dist/testing.js +3 -2
- package/llms.txt +2 -2
- package/package.json +22 -11
- package/dist/chunk-GYRZQCSY.js.map +0 -1
- package/dist/chunk-KFUDM3VP.js +0 -131
- package/dist/chunk-KFUDM3VP.js.map +0 -1
- package/dist/chunk-NU7YHYEV.js +0 -90
- package/dist/chunk-NU7YHYEV.js.map +0 -1
package/dist/chunk-NU7YHYEV.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { Signal, signal as signal$1, effect as effect$1 } from '@preact/signals-core';
|
|
2
|
-
export { batch, computed } from '@preact/signals-core';
|
|
3
|
-
|
|
4
|
-
// src/reactive.ts
|
|
5
|
-
|
|
6
|
-
// src/utils/devMode.ts
|
|
7
|
-
function isDevMode() {
|
|
8
|
-
const override = globalThis.KERF_DEV;
|
|
9
|
-
if (typeof override === "boolean") return override;
|
|
10
|
-
const proc = globalThis.process;
|
|
11
|
-
return proc?.env?.NODE_ENV !== "production";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// src/dev-delegate-warn.ts
|
|
15
|
-
var depth = 0;
|
|
16
|
-
var warned = false;
|
|
17
|
-
function isOptedIn() {
|
|
18
|
-
if (!isDevMode()) return false;
|
|
19
|
-
const proc = globalThis.process;
|
|
20
|
-
return proc?.env?.KERF_DEV_WARN_DELEGATE_IN_EFFECT === "1";
|
|
21
|
-
}
|
|
22
|
-
function enterEffect() {
|
|
23
|
-
depth++;
|
|
24
|
-
}
|
|
25
|
-
function exitEffect() {
|
|
26
|
-
depth--;
|
|
27
|
-
}
|
|
28
|
-
function isDevWarnDelegateInEffectEnabled() {
|
|
29
|
-
return isOptedIn();
|
|
30
|
-
}
|
|
31
|
-
function warnIfInsideEffect(fn) {
|
|
32
|
-
if (!isOptedIn()) return;
|
|
33
|
-
if (depth === 0) return;
|
|
34
|
-
if (warned) return;
|
|
35
|
-
warned = true;
|
|
36
|
-
console.warn(
|
|
37
|
-
`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". Set KERF_DEV_WARN_DELEGATE_IN_EFFECT=0 (or unset it) to silence this warning.`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
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. Set KERF_DEV_WARN_UNTRACKED_SIGNALS=0 (or unset it) to silence this warning.";
|
|
41
|
-
var DevSignal = class extends Signal {
|
|
42
|
-
__hasSubscriber = false;
|
|
43
|
-
__warned = false;
|
|
44
|
-
__constructed = false;
|
|
45
|
-
constructor(initial) {
|
|
46
|
-
super(initial, {
|
|
47
|
-
watched() {
|
|
48
|
-
this.__hasSubscriber = true;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
this.__constructed = true;
|
|
52
|
-
}
|
|
53
|
-
get value() {
|
|
54
|
-
return super.value;
|
|
55
|
-
}
|
|
56
|
-
set value(v) {
|
|
57
|
-
super.value = v;
|
|
58
|
-
if (this.__constructed && !this.__hasSubscriber && !this.__warned) {
|
|
59
|
-
this.__warned = true;
|
|
60
|
-
console.warn(WARNING_MESSAGE);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
function isDevWarnUntrackedEnabled() {
|
|
65
|
-
if (!isDevMode()) return false;
|
|
66
|
-
const proc = globalThis.process;
|
|
67
|
-
return proc?.env?.KERF_DEV_WARN_UNTRACKED_SIGNALS === "1";
|
|
68
|
-
}
|
|
69
|
-
function isSignal(value) {
|
|
70
|
-
return value instanceof Signal;
|
|
71
|
-
}
|
|
72
|
-
function signal(value) {
|
|
73
|
-
if (isDevWarnUntrackedEnabled()) return new DevSignal(value);
|
|
74
|
-
return signal$1(value);
|
|
75
|
-
}
|
|
76
|
-
function effect(fn) {
|
|
77
|
-
if (!isDevWarnDelegateInEffectEnabled()) return effect$1(fn);
|
|
78
|
-
return effect$1(() => {
|
|
79
|
-
enterEffect();
|
|
80
|
-
try {
|
|
81
|
-
return fn();
|
|
82
|
-
} finally {
|
|
83
|
-
exitEffect();
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export { effect, isDevMode, isSignal, signal, warnIfInsideEffect };
|
|
89
|
-
//# sourceMappingURL=chunk-NU7YHYEV.js.map
|
|
90
|
-
//# sourceMappingURL=chunk-NU7YHYEV.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/devMode.ts","../src/dev-delegate-warn.ts","../src/dev-signal.ts","../src/reactive.ts"],"names":["Signal","coreSignal","coreEffect"],"mappings":"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","file":"chunk-NU7YHYEV.js","sourcesContent":["/**\n * Shared dev-mode gate. One primary export: `isDevMode()`. Every dev-only\n * behavior in kerf — the `defineStore` `get()` snapshot freeze, the each()\n * row-key warning, and the opt-in `KERF_DEV_WARN_*` warning family — routes\n * its \"is this a development build?\" decision through here.\n *\n * Two inputs, override-wins precedence:\n *\n * 1. `globalThis.KERF_DEV` — explicit runtime override. When set to a boolean\n * it WINS unconditionally: `false` forces production behavior (no store\n * freeze, no dev warnings) even under `NODE_ENV=development`; `true` forces\n * development behavior even under `NODE_ENV=production`. Read lazily (at\n * call time, never memoized at import) so a no-bundler consumer loading\n * kerf from a CDN can set it once before mounting and have it take effect.\n *\n * 2. `process.env.NODE_ENV` — the default when no override is present.\n * Development is ON unless `NODE_ENV === 'production'`. Read through\n * `globalThis.process` so the source runs untouched in a browser that has\n * no `process` binding. Keeping this branch is what lets a bundler that\n * statically substitutes `NODE_ENV` continue to dead-code-eliminate the\n * dev paths for bundled production consumers exactly as before.\n *\n * Why the override matters: a no-bundler consumer (importmap, no build step)\n * has no `process`, so without an override the NODE_ENV branch resolves to\n * development-ON — which is the correct, unchanged default. Previously that\n * consumer had NO way to turn it off, leaving the store freeze and warning\n * machinery permanently active in their production deployment. Setting\n * `globalThis.KERF_DEV = false` before mount is the escape hatch.\n *\n * Perf: the reads are a handful of optional-chained property accesses, as\n * cheap as a boolean read. Hot-path callers that ran a cached boolean before\n * (the store `get()` freeze) keep caching the first result per instance rather\n * than probing on every call.\n */\nexport function isDevMode(): boolean {\n const override = (globalThis as { KERF_DEV?: unknown }).KERF_DEV;\n if (typeof override === 'boolean') return override;\n const proc = (globalThis as { process?: { env?: { NODE_ENV?: string } } }).process;\n return proc?.env?.NODE_ENV !== 'production';\n}\n","/**\n * Dev-mode warning for `delegate()` / `delegateCapture()` calls that run\n * inside an `effect()` body (KERF_DEV_WARN_DELEGATE_IN_EFFECT=1).\n *\n * Why the pattern matters: every effect re-run executes its body fresh, which\n * means a `delegate()` call inside the body installs a NEW root listener on\n * each re-run. The effect's disposer cleans up the reactive subscription but\n * not the side-effects the body produced — so previous listeners stay\n * attached, the per-listener closure pins `rootEl` / `handler` / everything\n * the handler closes over, and listener count grows linearly with signal\n * churn. Structurally identical to the addEventListener-inside-mount foot-gun\n * (Hard Rule 4) but doesn't *look* like it.\n *\n * Static analysis can't reliably detect \"inside an effect\" without flow\n * information (effect() is just a function call), so the canonical defense\n * is this runtime opt-in warning. When enabled, `reactive.ts`'s `effect()`\n * wrapper increments a module-level counter before invoking the user body\n * and decrements after; `delegate.ts` checks the counter and fires the\n * warning once total.\n *\n * Production behavior is unchanged for zero runtime cost — the env-var check\n * short-circuits before any state is touched, and the wrapper in\n * `reactive.ts` only wraps when the gate is on.\n */\n\nimport { isDevMode } from './utils/devMode.js';\n\nlet depth = 0;\nlet warned = false;\n\nfunction isOptedIn(): boolean {\n if (!isDevMode()) return false;\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env?.KERF_DEV_WARN_DELEGATE_IN_EFFECT === '1';\n}\n\n/** Called by the `effect()` wrapper in `reactive.ts` before running the user body. */\nexport function enterEffect(): void {\n depth++;\n}\n\n/** Called by the `effect()` wrapper in `reactive.ts` after the user body returns or throws. */\nexport function exitEffect(): void {\n depth--;\n}\n\n/** Public re-export of the env-var check so `reactive.ts` can decide whether to wrap. */\nexport function isDevWarnDelegateInEffectEnabled(): boolean {\n return isOptedIn();\n}\n\n/**\n * Called at the top of `delegate()` and `delegateCapture()`. If the call is\n * happening inside an `effect()` body (depth > 0) AND the env var is on, fire\n * a one-shot warning. The `fn` argument is the name of the caller for the\n * message (\"delegate\" vs \"delegateCapture\").\n */\nexport function warnIfInsideEffect(fn: 'delegate' | 'delegateCapture'): void {\n if (!isOptedIn()) return;\n if (depth === 0) return;\n if (warned) return;\n warned = true;\n console.warn(\n `kerf: ${fn}() was called inside an effect() body. `\n + 'Every effect re-run installs a fresh root listener; the effect disposer cleans up the '\n + 'reactive subscription but not the listeners, so listener count grows linearly with signal '\n + 'churn and each listener pins its handler closure. Register the delegate once at module '\n + 'or setup scope and gate behavior on the signal *inside the handler* where the read is free. '\n + 'See docs/5-event-delegation.md §5.3 \"When capturing the disposer still isn\\'t enough\". '\n + 'Set KERF_DEV_WARN_DELEGATE_IN_EFFECT=0 (or unset it) to silence this warning.',\n );\n}\n\n/** Test helper — resets the one-shot dedup flag and depth counter for unit tests. */\nexport function _resetWarnedForTests(): void {\n warned = false;\n depth = 0;\n}\n","/**\n * Dev-mode signal subclass with subscriber tracking (KF-176). When the\n * dev-warn opt-in is enabled, `signal()` returns a `DevSignal` that emits a\n * one-shot `console.warn` the first time `.value` is written to an instance\n * that has never had a subscriber attached. This surfaces the canonical\n * Rule 7 violation (read `.value` outside a render fn / effect — the read\n * doesn't subscribe, so subsequent writes silently fail to re-render) at\n * the moment the user makes the wrong write, instead of leaving them to\n * notice that their UI never updates.\n *\n * The gate is `isDevMode()` (NODE_ENV, or a `globalThis.KERF_DEV` override\n * when set) AND `KERF_DEV_WARN_UNTRACKED_SIGNALS === '1'`. Off by default because the\n * heuristic produces false positives for purely imperative signals (used as\n * mutable cells with no UI consumer); opt-in is the right shape until a\n * sharper heuristic is found. Production behavior is unchanged for zero\n * runtime cost.\n *\n * The subclass uses signals-core's `SignalOptions.watched` callback to set a\n * per-instance `__hasSubscriber` flag — fired by signals-core when the first\n * subscriber attaches. We never clear the flag on `unwatched`, so a signal\n * that *was* subscribed at some point won't warn even if its subscribers\n * later detach.\n */\n\nimport { Signal } from '@preact/signals-core';\n\nimport { isDevMode } from './utils/devMode.js';\n\nconst WARNING_MESSAGE\n = 'kerf: signal was written but has no subscribers. '\n + 'Did you read `.value` outside of a render fn / effect()? '\n + 'Hoisted reads do not subscribe, so subsequent writes will not re-render. '\n + 'Move the read inside mount()\\'s render fn or effect() callback. '\n + 'Set KERF_DEV_WARN_UNTRACKED_SIGNALS=0 (or unset it) to silence this warning.';\n\nexport class DevSignal<T> extends Signal<T> {\n private __hasSubscriber = false;\n private __warned = false;\n private __constructed = false;\n\n constructor(initial?: T) {\n super(initial as T, {\n watched(this: Signal<T>) {\n (this as unknown as { __hasSubscriber: boolean }).__hasSubscriber = true;\n },\n });\n this.__constructed = true;\n }\n\n override get value(): T { return super.value; }\n override set value(v: T) {\n super.value = v;\n if (this.__constructed && !this.__hasSubscriber && !this.__warned) {\n this.__warned = true;\n console.warn(WARNING_MESSAGE);\n }\n }\n}\n\nexport function isDevWarnUntrackedEnabled(): boolean {\n if (!isDevMode()) return false;\n const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return proc?.env?.KERF_DEV_WARN_UNTRACKED_SIGNALS === '1';\n}\n","/**\n * Re-exports of `@preact/signals-core`. Lets the rest of the codebase depend\n * on `'./reactive.js'` without naming the underlying lib, so swapping it out\n * later (or fronting it with a hand-rolled implementation) is a one-file\n * change.\n *\n * Two dev-gated wrappers sit in front of the bare re-exports:\n *\n * - `signal()` returns a `DevSignal` when `KERF_DEV_WARN_UNTRACKED_SIGNALS=1`\n * (KF-176) — warns on writes to signals with no subscribers.\n *\n * - `effect()` wraps the user body in `enterEffect()` / `exitEffect()` calls\n * when `KERF_DEV_WARN_DELEGATE_IN_EFFECT=1` so `delegate()` can detect when\n * it's running inside an effect body and fire the appropriate warning.\n *\n * Both gates short-circuit when `isDevMode()` is false (i.e. under\n * `NODE_ENV === 'production'`, or a `globalThis.KERF_DEV = false` override) —\n * production always sees the bare `@preact/signals-core` exports with zero\n * overhead.\n */\n\nimport { effect as coreEffect,Signal,signal as coreSignal } from '@preact/signals-core';\n\nimport { enterEffect, exitEffect, isDevWarnDelegateInEffectEnabled } from './dev-delegate-warn.js';\nimport { DevSignal, isDevWarnUntrackedEnabled } from './dev-signal.js';\n\nexport {\n batch,\n computed,\n type ReadonlySignal,\n Signal,\n} from '@preact/signals-core';\n\n/**\n * Runtime type guard for a `@preact/signals-core` signal (both `signal()`\n * values and `computed()` values are `Signal` instances). Used by the JSX\n * runtime (KF-294) to detect a signal handed straight into an attribute or\n * text hole — the trigger for a fine-grained binding rather than a snapshot\n * stringify.\n */\nexport function isSignal(value: unknown): value is Signal<unknown> {\n return value instanceof Signal;\n}\n\nexport function signal<T>(value?: T): Signal<T> {\n if (isDevWarnUntrackedEnabled()) return new DevSignal<T>(value as T) as Signal<T>;\n return coreSignal(value as T);\n}\n\nexport function effect(fn: () => void | (() => void)): () => void {\n if (!isDevWarnDelegateInEffectEnabled()) return coreEffect(fn);\n return coreEffect(() => {\n enterEffect();\n try {\n return fn();\n } finally {\n exitEffect();\n }\n });\n}\n"]}
|