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/dev.d.ts
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { B as Binding } from './bindings-CYwoJpQb.js';
|
|
2
|
+
import { Signal } from '@preact/signals-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-list binding shape kept by `mount()` and consumed by both reconcile
|
|
6
|
+
* paths in `list-reconcile-snapshot.ts` and `list-reconcile-granular.ts`.
|
|
7
|
+
*
|
|
8
|
+
* Living in its own file (not re-exported through `list-reconcile.ts`) so
|
|
9
|
+
* the snapshot + granular paths can import it without creating a circular
|
|
10
|
+
* dependency on `list-reconcile.ts` itself. ESM handles cycles via function
|
|
11
|
+
* hoisting, but a cycle involving a non-function helper would be a temporal
|
|
12
|
+
* dead zone — extracting the binding shape + `endAnchor` here keeps the
|
|
13
|
+
* dependency graph acyclic.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface BoundItem {
|
|
17
|
+
ref: object;
|
|
18
|
+
cacheKey: unknown;
|
|
19
|
+
html: string;
|
|
20
|
+
node: Element;
|
|
21
|
+
/** KF-294: this row's fine-grained binding specs (undefined/empty if none). */
|
|
22
|
+
bindings?: Binding[];
|
|
23
|
+
/**
|
|
24
|
+
* KF-294: live disposers for this row's bound effects. Set when the row node
|
|
25
|
+
* is wired (first-render inline in `mount()`, or `buildFreshNodes` during a
|
|
26
|
+
* reconcile) and called when the node is removed / the mount is torn down.
|
|
27
|
+
*/
|
|
28
|
+
bindingDisposers?: Array<() => void>;
|
|
29
|
+
}
|
|
30
|
+
interface ListBinding {
|
|
31
|
+
liveParent: Element;
|
|
32
|
+
/**
|
|
33
|
+
* One entry per item currently mounted under `liveParent`, in order.
|
|
34
|
+
* Mirrors the segment's `items` length after each reconcile.
|
|
35
|
+
*/
|
|
36
|
+
items: BoundItem[];
|
|
37
|
+
/**
|
|
38
|
+
* The list's `<!--kf-list:N-->` start marker, kept in the live DOM as
|
|
39
|
+
* a permanent anchor (KF-102 round 2). The marker stays put across
|
|
40
|
+
* static-surrounds diffs (it morphs as a comment node), so it gives
|
|
41
|
+
* the list reconciler a stable "begin" position even when surrounding
|
|
42
|
+
* siblings get inserted, removed, or reordered around the list.
|
|
43
|
+
*
|
|
44
|
+
* `endAnchor(binding)` derives the "insert at end of list" anchor from
|
|
45
|
+
* `marker.nextSibling` (empty list) or `items[last].node.nextSibling`
|
|
46
|
+
* (non-empty list) — picking up whatever the diff placed between the
|
|
47
|
+
* list and the parent's tail.
|
|
48
|
+
*/
|
|
49
|
+
marker: Comment;
|
|
50
|
+
/**
|
|
51
|
+
* KF-173: once we've emitted the missing-key dev warning for this list,
|
|
52
|
+
* we suppress further warnings for the same binding. The flag is set
|
|
53
|
+
* inside `maybeWarnMissingRowKey()` and never cleared — the list is
|
|
54
|
+
* considered "decided" after the first row check.
|
|
55
|
+
*/
|
|
56
|
+
warnedMissingKey?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The dev-hook registry — kerf's single seam between production code and the
|
|
61
|
+
* opt-in development diagnostics.
|
|
62
|
+
*
|
|
63
|
+
* ## Why this exists
|
|
64
|
+
*
|
|
65
|
+
* kerf used to INFER whether it was running in development, by reading
|
|
66
|
+
* `globalThis.process?.env?.NODE_ENV` through `utils/devMode.ts`. That
|
|
67
|
+
* inference cannot be made correct, and it was wrong in the most common case:
|
|
68
|
+
* bundlers substitute the BARE `process.env.NODE_ENV` token and never create a
|
|
69
|
+
* `globalThis.process` object for browser targets, so the read returned
|
|
70
|
+
* `undefined` and `undefined !== 'production'` resolved to DEVELOPMENT inside
|
|
71
|
+
* production browser bundles.
|
|
72
|
+
*
|
|
73
|
+
* It also could not be fixed by rewriting the expression. Only the
|
|
74
|
+
* *production* answer can be made static: `X && false` folds to a constant for
|
|
75
|
+
* any side-effect-free `X`, while `X && true` does not. So any form that a
|
|
76
|
+
* bundler can eliminate is also a form that treats "no `process` binding" as
|
|
77
|
+
* production — which silently disables every warning in the no-build/CDN path
|
|
78
|
+
* and in browser dev bundles.
|
|
79
|
+
*
|
|
80
|
+
* The fix is to stop guessing. Every environment already has a correct,
|
|
81
|
+
* statically-foldable dev flag; what none of them offers is a way to hand that
|
|
82
|
+
* flag to a *library*. So the consumer writes the conditional, in their own
|
|
83
|
+
* code, with their own flag:
|
|
84
|
+
*
|
|
85
|
+
* ```js
|
|
86
|
+
* if (import.meta.env.DEV) await import('kerfjs/dev'); // Vite
|
|
87
|
+
* if (process.env.NODE_ENV !== 'production') await import('kerfjs/dev');
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* Because that condition folds to `false` in the consumer's production build,
|
|
91
|
+
* the entire statement is eliminated and the dev chunk is never emitted or
|
|
92
|
+
* fetched. Installation IS the development signal — there is nothing left to
|
|
93
|
+
* detect, and no environment kerf can be wrong about.
|
|
94
|
+
*
|
|
95
|
+
* ## The contract
|
|
96
|
+
*
|
|
97
|
+
* Core modules never import a `dev-*` module. They read a nullable slot off
|
|
98
|
+
* `devHooks` and call through it:
|
|
99
|
+
*
|
|
100
|
+
* ```ts
|
|
101
|
+
* devHooks.listRebind?.(id, marker.parentElement as Element);
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* When nothing is installed every slot is `undefined`, so the cost is one
|
|
105
|
+
* property read per call site and the `dev-*` modules are unreachable from the
|
|
106
|
+
* main entry — which is what lets a bundler drop them. This is why the gate
|
|
107
|
+
* lives at the CALL SITE rather than inside each warner: an unconditional call
|
|
108
|
+
* into a self-gating warner keeps the module reachable no matter how the gate
|
|
109
|
+
* is written, so no amount of dead-code elimination can reclaim it.
|
|
110
|
+
*
|
|
111
|
+
* Slots ending in `Enabled` are predicates rather than warnings. They exist for
|
|
112
|
+
* the handful of call sites that must decide whether to do *expensive
|
|
113
|
+
* preparatory work* — capturing the previous render's binding list, allocating
|
|
114
|
+
* a per-render `Map` — before there is anything to warn about. Core must check
|
|
115
|
+
* those before paying the cost, exactly as it checked the old `isOptedIn()`
|
|
116
|
+
* exports.
|
|
117
|
+
*
|
|
118
|
+
* Each warner keeps its own internal opt-in check (the `KERF_DEV_WARN_*` env
|
|
119
|
+
* reads). Installation decides whether the diagnostics are *present*; the
|
|
120
|
+
* individual warner still decides whether it is *switched on*.
|
|
121
|
+
*
|
|
122
|
+
* @see docs/11-dev-warnings.md
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/** Per-mount / per-store one-shot dedup flag, owned by the caller in core. */
|
|
126
|
+
interface WarnOnceContext {
|
|
127
|
+
warned: boolean;
|
|
128
|
+
}
|
|
129
|
+
interface DevHooks {
|
|
130
|
+
/**
|
|
131
|
+
* Replaces `signal()`'s constructor so writes to never-subscribed signals can
|
|
132
|
+
* warn. Resolved at signal-CREATION time, so signals created before the dev
|
|
133
|
+
* entry is installed stay plain — see `signalsCreatedBeforeInstall`.
|
|
134
|
+
*/
|
|
135
|
+
signalFactory?: <T>(value: T) => Signal<T>;
|
|
136
|
+
/**
|
|
137
|
+
* Wraps an `effect()` body so `delegate()` can detect that it is running
|
|
138
|
+
* inside one. Returns the body to actually run.
|
|
139
|
+
*/
|
|
140
|
+
wrapEffect?: (fn: () => void | (() => void)) => () => void | (() => void);
|
|
141
|
+
delegateInEffect?: (fn: 'delegate' | 'delegateCapture') => void;
|
|
142
|
+
narrowSet?: (prev: unknown, next: unknown, ctx: WarnOnceContext) => void;
|
|
143
|
+
/** Deep read-only proxy for the `get()` snapshot, so stray writes throw. */
|
|
144
|
+
storeReadonly?: <T extends object>(state: T) => T;
|
|
145
|
+
/** Unwraps a proxy handed back through `set({ ...get() })`. */
|
|
146
|
+
storeToRaw?: <T>(next: T) => T;
|
|
147
|
+
listenerRebuild?: (rootEl: Element) => MutationObserver | null;
|
|
148
|
+
listIdShift?: (id: string) => void;
|
|
149
|
+
parserRepair?: (html: string) => void;
|
|
150
|
+
staleBindingEnabled?: () => boolean;
|
|
151
|
+
staleBinding?: (prevWired: readonly Binding[], current: readonly Binding[]) => void;
|
|
152
|
+
listInvariantsEnabled?: () => boolean;
|
|
153
|
+
listInvariants?: (rootEl: Element, bindings: ReadonlyMap<string, ListBinding>, expectedCounts?: ReadonlyMap<string, number>) => void;
|
|
154
|
+
valueOnlyRerender?: (prevHtml: string, nextHtml: string, ctx: WarnOnceContext) => void;
|
|
155
|
+
listRebind?: (id: string, liveParent: Element) => void;
|
|
156
|
+
eachInMorphSkip?: (id: string, liveParent: Element, rootEl: Element) => void;
|
|
157
|
+
missingRowKey?: (rowEl: Element, rowHtml: string, binding: {
|
|
158
|
+
warnedMissingKey?: boolean;
|
|
159
|
+
}) => void;
|
|
160
|
+
staleIndexEnabled?: () => boolean;
|
|
161
|
+
staleIndex?: (id: string) => void;
|
|
162
|
+
duplicateCacheKeys?: (id: string, segItems: readonly {
|
|
163
|
+
cacheKey: unknown;
|
|
164
|
+
}[]) => void;
|
|
165
|
+
/**
|
|
166
|
+
* When installed, a screened URL throws instead of warning-and-dropping.
|
|
167
|
+
* A slot rather than a boolean so the check stays uniform with the rest.
|
|
168
|
+
*/
|
|
169
|
+
urlScreenThrow?: (message: string) => never;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The live slot table. Mutable by design — this is the fourth sanctioned
|
|
173
|
+
* module-level mutable location (Design rule 5), and like `store.ts:REGISTRY`
|
|
174
|
+
* it depends on there being exactly ONE copy at runtime. `tsup`'s
|
|
175
|
+
* `splitting: true` guarantees that: shared modules are promoted into a single
|
|
176
|
+
* chunk that both the main entry and the `kerfjs/dev` entry import.
|
|
177
|
+
*/
|
|
178
|
+
declare const devHooks: DevHooks;
|
|
179
|
+
/**
|
|
180
|
+
* Install (or extend) the dev hooks. Called by the `kerfjs/dev` entry; not part
|
|
181
|
+
* of the public API surface.
|
|
182
|
+
*
|
|
183
|
+
* Merges rather than replaces, so a consumer can install the standard bundle
|
|
184
|
+
* and then override a single slot in a test.
|
|
185
|
+
*/
|
|
186
|
+
declare function installDevHooks(hooks: DevHooks): void;
|
|
187
|
+
/**
|
|
188
|
+
* Remove every installed hook. Exists for test isolation — a suite that asserts
|
|
189
|
+
* the not-installed (production-shaped) path needs to get back to a clean slate
|
|
190
|
+
* without reloading modules.
|
|
191
|
+
*/
|
|
192
|
+
declare function clearDevHooks(): void;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The switch layer for the opt-in diagnostics — the second of the two gates
|
|
196
|
+
* (§11.3.1). The first gate is installation: reaching this module at all means
|
|
197
|
+
* the consumer imported `kerfjs/dev`.
|
|
198
|
+
*
|
|
199
|
+
* Two sources feed one lookup, explicit-call-wins:
|
|
200
|
+
*
|
|
201
|
+
* 1. `enableWarnings({...})` — an in-memory map, set by the consumer through
|
|
202
|
+
* the `kerfjs/dev` entry. Explicit wins in BOTH directions, so
|
|
203
|
+
* `{ narrowSet: false }` silences a warning an ambient env var switched on.
|
|
204
|
+
* 2. `globalThis.process?.env?.KERF_DEV_*` — the environment, kept for Node,
|
|
205
|
+
* SSR, and CI, where exporting a variable is the natural way to turn a
|
|
206
|
+
* diagnostic on for one run.
|
|
207
|
+
*
|
|
208
|
+
* ## Why the env var could not be the only switch
|
|
209
|
+
*
|
|
210
|
+
* It is unreachable in the majority case. kerf is a browser framework, and a
|
|
211
|
+
* browser realm has no `process` object at all — so every one of these
|
|
212
|
+
* warnings was permanently off in exactly the environment (a Vite/webpack dev
|
|
213
|
+
* server) where a developer most wants them. A bundler `define` does not fix
|
|
214
|
+
* it either: the read goes through `globalThis.process` into a local binding,
|
|
215
|
+
* so nothing substitutes the `process.env.X` token. That indirection is not
|
|
216
|
+
* incidental — reading the bare token is what made kerf infer DEVELOPMENT
|
|
217
|
+
* inside production browser bundles.
|
|
218
|
+
*
|
|
219
|
+
* The consumer already holds the module at the moment they opt in
|
|
220
|
+
* (`const dev = await import('kerfjs/dev')`), so handing them a typed function
|
|
221
|
+
* there is both the most reachable and the most discoverable switch. The env
|
|
222
|
+
* vars stay because kerf's own suites and any CI run use them.
|
|
223
|
+
*/
|
|
224
|
+
/**
|
|
225
|
+
* Which diagnostics to switch on. Every key is off unless you name it; passing
|
|
226
|
+
* `false` explicitly forces a warning off even when its env var is set.
|
|
227
|
+
*
|
|
228
|
+
* `invariants` is not a warning but the structural audit of kerf's own list
|
|
229
|
+
* bookkeeping: `true` reports violations with `console.warn`, `'throw'` raises
|
|
230
|
+
* them (what you want in a test suite, where a warning inside a passing test
|
|
231
|
+
* is invisible).
|
|
232
|
+
*/
|
|
233
|
+
interface DevWarningOptions {
|
|
234
|
+
/** Imperative `addEventListener` on a node the morph later rebuilds. */
|
|
235
|
+
rebuiltListeners?: boolean;
|
|
236
|
+
/** A `.value` write to a signal that never had a subscriber. */
|
|
237
|
+
untrackedSignals?: boolean;
|
|
238
|
+
/** `defineStore` `set()` called with keys missing from the current state. */
|
|
239
|
+
narrowSet?: boolean;
|
|
240
|
+
/** `delegate()` called inside an `effect()` body — listeners stack up. */
|
|
241
|
+
delegateInEffect?: boolean;
|
|
242
|
+
/** An `each()` list under a `data-morph-skip` subtree. */
|
|
243
|
+
eachInMorphSkip?: boolean;
|
|
244
|
+
/** Two rows in one `each()` producing the same `cacheKey`. */
|
|
245
|
+
duplicateEachKeys?: boolean;
|
|
246
|
+
/** A fine-grained binding switching signal instance on the fast path. */
|
|
247
|
+
staleBinding?: boolean;
|
|
248
|
+
/** A re-render whose whole diff was values — candidates for bindings. */
|
|
249
|
+
valueOnlyRerender?: boolean;
|
|
250
|
+
/** An `each()` container rebuilt by the morph and self-healed. */
|
|
251
|
+
listRebind?: boolean;
|
|
252
|
+
/** A memoized row reused at a different index than it rendered at. */
|
|
253
|
+
staleIndex?: boolean;
|
|
254
|
+
/** Markup the HTML parser repaired (a block element inside a `<p>`). */
|
|
255
|
+
parserRepair?: boolean;
|
|
256
|
+
/** Structural audit of kerf's list bookkeeping: warn, or `'throw'`. */
|
|
257
|
+
invariants?: boolean | 'throw';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* `kerfjs/dev` — the development diagnostics bundle.
|
|
262
|
+
*
|
|
263
|
+
* Importing this module installs kerf's dev-only behavior: the whole
|
|
264
|
+
* `KERF_DEV_WARN_*` warning family, the structural list invariants, the
|
|
265
|
+
* read-only store snapshot, and the throw-on-dangerous-URL screen. Importing
|
|
266
|
+
* nothing leaves every hook slot `undefined` and kerf runs in production shape.
|
|
267
|
+
*
|
|
268
|
+
* Put the import behind YOUR environment's dev flag, in YOUR code. That
|
|
269
|
+
* condition folds to `false` in your production build, so the whole statement
|
|
270
|
+
* is eliminated and this chunk is never emitted or fetched:
|
|
271
|
+
*
|
|
272
|
+
* ```js
|
|
273
|
+
* if (import.meta.env.DEV) await import('kerfjs/dev'); // Vite
|
|
274
|
+
* if (process.env.NODE_ENV !== 'production') await import('kerfjs/dev'); // webpack / Node
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* No-build / CDN consumers import it unconditionally from their dev page and
|
|
278
|
+
* simply leave it out of the production page — there is no bundler to fold the
|
|
279
|
+
* condition, and nothing for kerf to detect.
|
|
280
|
+
*
|
|
281
|
+
* ## Install ordering
|
|
282
|
+
*
|
|
283
|
+
* Every hook except one is read at CALL time (render, reconcile, `set()`,
|
|
284
|
+
* `delegate()`), so installing any time before your first `mount()` is enough.
|
|
285
|
+
*
|
|
286
|
+
* The exception is `signalFactory`: `signal()` picks its constructor when the
|
|
287
|
+
* signal is CREATED. Static imports are hoisted above a top-level
|
|
288
|
+
* `await import()`, so module-scope signals in imported modules are created
|
|
289
|
+
* before this module runs and the untracked-signal warning will not see them.
|
|
290
|
+
* To cover those, make `import 'kerfjs/dev'` the FIRST STATIC import of a
|
|
291
|
+
* dev-only entry file, then load the rest of your app.
|
|
292
|
+
*
|
|
293
|
+
* Opting into that warning prints this boundary once, so the gap is loud
|
|
294
|
+
* rather than silent. It cannot be closed by retro-fitting existing signals:
|
|
295
|
+
* `Signal.prototype`'s `value` accessor is non-configurable, and reaching live
|
|
296
|
+
* instances would require a per-signal registry that production would pay for.
|
|
297
|
+
*
|
|
298
|
+
* Installation decides whether the diagnostics are PRESENT; each individual
|
|
299
|
+
* warner still reads its own `KERF_DEV_WARN_*` env var to decide whether it is
|
|
300
|
+
* switched ON. That keeps the existing opt-in contract intact.
|
|
301
|
+
*
|
|
302
|
+
* @see docs/11-dev-warnings.md
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Switch individual diagnostics on:
|
|
307
|
+
*
|
|
308
|
+
* ```js
|
|
309
|
+
* if (import.meta.env.DEV) {
|
|
310
|
+
* const dev = await import('kerfjs/dev');
|
|
311
|
+
* dev.enableWarnings({ staleBinding: true, narrowSet: true });
|
|
312
|
+
* }
|
|
313
|
+
* ```
|
|
314
|
+
*
|
|
315
|
+
* Installing this module makes the diagnostics PRESENT; this decides which are
|
|
316
|
+
* SWITCHED ON, so the console isn't flooded by warnings you didn't ask for. The
|
|
317
|
+
* `KERF_DEV_WARN_*` environment variables do the same thing for Node, SSR, and
|
|
318
|
+
* CI; an explicit call here wins over the environment in both directions, so
|
|
319
|
+
* `{ narrowSet: false }` silences an ambient var.
|
|
320
|
+
*
|
|
321
|
+
* **In a browser this function is the only switch that works.** A browser realm
|
|
322
|
+
* has no `process` object, and a bundler `define` cannot reach the read (it
|
|
323
|
+
* goes through `globalThis.process` into a local binding), so the environment
|
|
324
|
+
* variables are unreachable there.
|
|
325
|
+
*
|
|
326
|
+
* Call it as early as you can. Every diagnostic reads its switch at call time
|
|
327
|
+
* except the untracked-signal warning, which is chosen when a signal is
|
|
328
|
+
* CREATED — enabling that one prints its coverage boundary once.
|
|
329
|
+
*/
|
|
330
|
+
declare function enableWarnings(options: DevWarningOptions): void;
|
|
331
|
+
/**
|
|
332
|
+
* The standard bundle of hooks this entry installs. Exported so kerf's own
|
|
333
|
+
* suites can drop back to the production shape (`clearDevHooks()`) and restore
|
|
334
|
+
* afterwards — re-importing this module would not re-run the install, since
|
|
335
|
+
* module evaluation happens once.
|
|
336
|
+
*/
|
|
337
|
+
declare const DEV_HOOKS: DevHooks;
|
|
338
|
+
|
|
339
|
+
export { DEV_HOOKS, type DevHooks, type DevWarningOptions, clearDevHooks, devHooks, enableWarnings, installDevHooks };
|