@nisli/core 0.54.0 → 0.55.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/README.md +93 -3
- package/dist/component.js +146 -5
- package/dist/component.js.map +1 -1
- package/dist/diagnostics.d.ts +48 -0
- package/dist/diagnostics.js +118 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/disposable.d.ts +14 -0
- package/dist/disposable.js +8 -0
- package/dist/disposable.js.map +1 -0
- package/dist/emitter.d.ts +2 -1
- package/dist/emitter.js +2 -1
- package/dist/emitter.js.map +1 -1
- package/dist/hmr/registry.js +8 -1
- package/dist/hmr/registry.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/injector.d.ts +9 -2
- package/dist/injector.js +18 -0
- package/dist/injector.js.map +1 -1
- package/dist/lifecycle.d.ts +9 -0
- package/dist/lifecycle.js +17 -7
- package/dist/lifecycle.js.map +1 -1
- package/dist/projection.js +27 -10
- package/dist/projection.js.map +1 -1
- package/dist/query.d.ts +134 -55
- package/dist/query.js +379 -188
- package/dist/query.js.map +1 -1
- package/dist/resource.d.ts +2 -1
- package/dist/resource.js +21 -4
- package/dist/resource.js.map +1 -1
- package/dist/settle.d.ts +61 -0
- package/dist/settle.js +106 -0
- package/dist/settle.js.map +1 -0
- package/dist/signal.d.ts +42 -7
- package/dist/signal.js +248 -68
- package/dist/signal.js.map +1 -1
- package/dist/template-audit.d.ts +64 -0
- package/dist/template-audit.js +246 -0
- package/dist/template-audit.js.map +1 -0
- package/dist/template.d.ts +70 -1
- package/dist/template.js +477 -218
- package/dist/template.js.map +1 -1
- package/dist/view-transition.d.ts +70 -0
- package/dist/view-transition.js +98 -0
- package/dist/view-transition.js.map +1 -0
- package/package.json +1 -1
package/dist/emitter.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
import { type Signal } from './signal.js';
|
|
21
|
+
import { type Disposer } from './disposable.js';
|
|
21
22
|
type EventMap = Record<string, unknown>;
|
|
22
23
|
type Listener<T> = (payload: T) => void;
|
|
23
24
|
export declare class Emitter<Events extends EventMap> {
|
|
@@ -33,7 +34,7 @@ export declare class Emitter<Events extends EventMap> {
|
|
|
33
34
|
* If called during component setup, the subscription is automatically
|
|
34
35
|
* disposed on disconnectedCallback.
|
|
35
36
|
*/
|
|
36
|
-
on<K extends keyof Events>(event: K, fn: Listener<Events[K]>):
|
|
37
|
+
on<K extends keyof Events>(event: K, fn: Listener<Events[K]>): Disposer;
|
|
37
38
|
/**
|
|
38
39
|
* Bridge an event into the signal system.
|
|
39
40
|
* Returns a signal that updates whenever the event fires.
|
package/dist/emitter.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { signal } from './signal.js';
|
|
21
21
|
import { hasContext, getCurrentComponent } from './context.js';
|
|
22
|
+
import { disposable } from './disposable.js';
|
|
22
23
|
// ── Emitter base class ──────────────────────────────────────────────
|
|
23
24
|
export class Emitter {
|
|
24
25
|
listeners = new Map();
|
|
@@ -63,7 +64,7 @@ export class Emitter {
|
|
|
63
64
|
if (hasContext()) {
|
|
64
65
|
getCurrentComponent().addDisposer(unsubscribe);
|
|
65
66
|
}
|
|
66
|
-
return unsubscribe;
|
|
67
|
+
return disposable(unsubscribe, unsubscribe);
|
|
67
68
|
}
|
|
68
69
|
/**
|
|
69
70
|
* Bridge an event into the signal system.
|
package/dist/emitter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../src/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAO5D,uEAAuE;AAEvE,MAAM,OAAO,OAAO;IACV,SAAS,GAAG,IAAI,GAAG,EAAoC,CAAC;IAEhE;;;OAGG;IACH,IAAI,CAAyB,KAAQ,EAAE,OAAkB;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,sDAAsD;QACtD,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,EAAE,CAAC,OAAO,CAAC,CAAC;YACd,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,EAAE,CAAyB,KAAQ,EAAE,EAAuB;QAC1D,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEZ,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,GAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChB,IAAI,GAAI,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC;QAEF,iDAAiD;QACjD,IAAI,UAAU,EAAE,EAAE,CAAC;YACjB,mBAAmB,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,UAAU,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CACN,KAAQ,EACR,QAAmC,EACnC,OAAU;QAEV,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE;YACzB,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,CAAC;IACX,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
|
package/dist/hmr/registry.js
CHANGED
|
@@ -57,12 +57,19 @@ export function __register(tag, setup) {
|
|
|
57
57
|
if (!suppressAutoDrain)
|
|
58
58
|
scheduleRemount();
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
const thunk = (props, host) => {
|
|
61
61
|
const current = registry.get(tag);
|
|
62
62
|
if (!current)
|
|
63
63
|
throw new Error(`[nisli-hmr] no setup registered for <${tag}>`);
|
|
64
64
|
return current(props, host);
|
|
65
65
|
};
|
|
66
|
+
// N201 exemption marker (ADR 0030.2 §3/§8): component() throws a coded dev
|
|
67
|
+
// error on duplicate `define`, EXCEPT when the incoming setup is a registry
|
|
68
|
+
// thunk — an HMR re-evaluation of the same module re-calling component() is
|
|
69
|
+
// the swap mechanism working, not a second definer. The marker rides the
|
|
70
|
+
// thunk itself so component.ts needs no import from this dev-only module.
|
|
71
|
+
thunk.__nisliHmr = true;
|
|
72
|
+
return thunk;
|
|
66
73
|
}
|
|
67
74
|
/** Number of tags awaiting a re-mount (for orchestration/tests). */
|
|
68
75
|
export function pendingRemountCount() {
|
package/dist/hmr/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hmr/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,uEAAuE;AAEvE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE7C,0EAA0E;AAC1E,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;AAC1C,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;GAKG;AACH,IAAI,iBAAiB,GAAG,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,iBAAiB,GAAG,IAAI,CAAC;IACzB,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc;IAC5B,iBAAiB,GAAG,KAAK,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,KAAe;IACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACzB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACzC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,iBAAiB;YAAE,eAAe,EAAE,CAAC;IAC5C,CAAC;IACD,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hmr/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,15 +13,17 @@
|
|
|
13
13
|
* } from './framework';
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
-
export { signal, computed, effect, flush, tick, isSignal, flushEffects, untrack, SIGNAL_BRAND, type Signal, type ReadonlySignal, } from './signal.js';
|
|
16
|
+
export { signal, computed, effect, flush, tick, isSignal, flushEffects, untrack, SIGNAL_BRAND, type Signal, type ReadonlySignal, type Disposer, } from './signal.js';
|
|
17
17
|
export { runWithContext, getCurrentComponent, hasContext, type ComponentHost, } from './context.js';
|
|
18
18
|
export { Emitter } from './emitter.js';
|
|
19
19
|
export { inject, provide, createToken, resetInjector, type Constructor, type InjectionToken, } from './injector.js';
|
|
20
20
|
export { createContext, type Context, type Inject, } from './element-context.js';
|
|
21
21
|
export { component, type ReactiveProps, type AttrValueType, type SetupFunction, type ComponentFactory, type ComponentOptions, type AttrDecl, type ComponentAttrs, type PropInput, type HostAttrs, } from './component.js';
|
|
22
|
-
export { html, el, when, each, type TemplateResult, type TypedEventHandler, type ElProps, type ElChild, } from './template.js';
|
|
22
|
+
export { html, el, when, each, raw, sanitized, setSanitizerFallback, type RawHtml, type SanitizedHtml, type SanitizerFallback, type TemplateResult, type TypedEventHandler, type ElProps, type ElChild, } from './template.js';
|
|
23
23
|
export { ref, isRef, type Ref, } from './ref.js';
|
|
24
24
|
export { onMount, onCleanup, useHostEvent, } from './lifecycle.js';
|
|
25
25
|
export { children, } from './projection.js';
|
|
26
|
-
export { query, QueryClient, type QueryResult, type QueryOptions, } from './query.js';
|
|
26
|
+
export { query, QueryClient, type QueryResult, type QueryOptions, type QueryKey, type QueryKeyElement, type QueryStatus, type QueryFetcher, } from './query.js';
|
|
27
27
|
export { resource, type ResourceResult, } from './resource.js';
|
|
28
|
+
export { settle } from './settle.js';
|
|
29
|
+
export { viewTransition, type ViewTransitionOptions, } from './view-transition.js';
|
package/dist/index.js
CHANGED
|
@@ -26,15 +26,19 @@ export { createContext, } from './element-context.js';
|
|
|
26
26
|
// ── Component model ─────────────────────────────────────────────────
|
|
27
27
|
export { component, } from './component.js';
|
|
28
28
|
// ── Template engine ─────────────────────────────────────────────────
|
|
29
|
-
export { html, el, when, each, } from './template.js';
|
|
29
|
+
export { html, el, when, each, raw, sanitized, setSanitizerFallback, } from './template.js';
|
|
30
30
|
// ── Element refs ────────────────────────────────────────────────────
|
|
31
31
|
export { ref, isRef, } from './ref.js';
|
|
32
32
|
// ── Lifecycle hooks ─────────────────────────────────────────────────
|
|
33
33
|
export { onMount, onCleanup, useHostEvent, } from './lifecycle.js';
|
|
34
34
|
// ── Content projection (ADR 0025 item 1) ────────────────
|
|
35
35
|
export { children, } from './projection.js';
|
|
36
|
-
// ── Declarative data loading
|
|
36
|
+
// ── Declarative data loading (keyed logical-request store) ──────────
|
|
37
37
|
export { query, QueryClient, } from './query.js';
|
|
38
38
|
// ── Local async derivations ─────────────────────────────────────────
|
|
39
39
|
export { resource, } from './resource.js';
|
|
40
|
+
// ── Async quiescence (test/verify + SSG pre-snapshot barrier) ───────
|
|
41
|
+
export { settle } from './settle.js';
|
|
42
|
+
// ── View Transitions (opt-in wrapper over the synchronous flush) ────
|
|
43
|
+
export { viewTransition, } from './view-transition.js';
|
|
40
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,uEAAuE;AACvE,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,YAAY,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,uEAAuE;AACvE,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,YAAY,GAIb,MAAM,aAAa,CAAC;AAErB,uEAAuE;AACvE,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,UAAU,GAEX,MAAM,cAAc,CAAC;AAEtB,uEAAuE;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,uEAAuE;AACvE,OAAO,EACL,MAAM,EACN,OAAO,EACP,WAAW,EACX,aAAa,GAGd,MAAM,eAAe,CAAC;AAEvB,uEAAuE;AACvE,OAAO,EACL,aAAa,GAGd,MAAM,sBAAsB,CAAC;AAE9B,uEAAuE;AACvE,OAAO,EACL,SAAS,GAUV,MAAM,gBAAgB,CAAC;AAExB,uEAAuE;AACvE,OAAO,EACL,IAAI,EACJ,EAAE,EACF,IAAI,EACJ,IAAI,EACJ,GAAG,EACH,SAAS,EACT,oBAAoB,GAQrB,MAAM,eAAe,CAAC;AAEvB,uEAAuE;AACvE,OAAO,EACL,GAAG,EACH,KAAK,GAEN,MAAM,UAAU,CAAC;AAElB,uEAAuE;AACvE,OAAO,EACL,OAAO,EACP,SAAS,EACT,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,2DAA2D;AAC3D,OAAO,EACL,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAEzB,uEAAuE;AACvE,OAAO,EACL,KAAK,EACL,WAAW,GAOZ,MAAM,YAAY,CAAC;AAEpB,uEAAuE;AACvE,OAAO,EACL,QAAQ,GAET,MAAM,eAAe,CAAC;AAEvB,uEAAuE;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,uEAAuE;AACvE,OAAO,EACL,cAAc,GAEf,MAAM,sBAAsB,CAAC"}
|
package/dist/injector.d.ts
CHANGED
|
@@ -42,8 +42,13 @@ export declare function inject<T>(token: InjectionToken<T>): T;
|
|
|
42
42
|
* Register a factory override for a class or token.
|
|
43
43
|
* The factory will be called on the next inject() if no cached instance exists.
|
|
44
44
|
*
|
|
45
|
-
* Call provide() before the first inject() of that token.
|
|
46
|
-
*
|
|
45
|
+
* Call provide() before the first inject() of that token. Once a token has
|
|
46
|
+
* been instantiated the injector is FROZEN for it (dev, coded error N501):
|
|
47
|
+
* everything that already injected holds the old instance while later
|
|
48
|
+
* inject() calls would get the new one — a silent mixed-instance split
|
|
49
|
+
* (ADR 0030.2 §3). `resetInjector()` is the escape hatch (tests use it).
|
|
50
|
+
* Production keeps the historical silent replace so a live page never breaks
|
|
51
|
+
* over an ordering issue that dev already surfaced.
|
|
47
52
|
*/
|
|
48
53
|
export declare function provide<T>(token: Constructor<T>, factory: () => T): void;
|
|
49
54
|
export declare function provide<T>(token: InjectionToken<T>, factory: () => T): void;
|
|
@@ -58,6 +63,8 @@ export declare function provide<T>(token: InjectionToken<T>, factory: () => T):
|
|
|
58
63
|
* ```
|
|
59
64
|
*/
|
|
60
65
|
export declare function createToken<T>(name: string, defaultFactory?: () => T): InjectionToken<T>;
|
|
66
|
+
/** @internal Register a hook run by every resetInjector() call. */
|
|
67
|
+
export declare function registerResetHook(hook: () => void): void;
|
|
61
68
|
/**
|
|
62
69
|
* Reset the injector — clears all singletons and factory overrides.
|
|
63
70
|
* Used in tests to ensure isolation between test cases.
|
package/dist/injector.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
import { hasContext, getCurrentComponent } from './context.js';
|
|
21
|
+
import { formatDiag, isDev } from './diagnostics.js';
|
|
21
22
|
// ── Global singleton registry ───────────────────────────────────────
|
|
22
23
|
/** Stores singleton instances keyed by constructor or token. */
|
|
23
24
|
const singletonCache = new Map();
|
|
@@ -67,6 +68,13 @@ export function inject(token) {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
export function provide(token, factory) {
|
|
71
|
+
if (isDev() && singletonCache.has(token)) {
|
|
72
|
+
const name = typeof token === 'function' ? token.name : token.name;
|
|
73
|
+
throw new Error(formatDiag('N501', `provide(${name}) after '${name}' was already instantiated — existing `
|
|
74
|
+
+ `consumers keep the old instance while new inject() calls would get the `
|
|
75
|
+
+ `new one (mixed-instance split). Call resetInjector() first (tests), or `
|
|
76
|
+
+ `provide() before the first inject().`));
|
|
77
|
+
}
|
|
70
78
|
factoryOverrides.set(token, factory);
|
|
71
79
|
// Remove cached instance so next inject() uses the new factory
|
|
72
80
|
singletonCache.delete(token);
|
|
@@ -88,6 +96,14 @@ export function createToken(name, defaultFactory) {
|
|
|
88
96
|
_factory: defaultFactory,
|
|
89
97
|
};
|
|
90
98
|
}
|
|
99
|
+
// Hooks run by resetInjector() — module-level caches elsewhere (settle's
|
|
100
|
+
// pending registry) stay test-isolated without upside-down imports: those
|
|
101
|
+
// modules import the injector and self-register, never the reverse.
|
|
102
|
+
const resetHooks = new Set();
|
|
103
|
+
/** @internal Register a hook run by every resetInjector() call. */
|
|
104
|
+
export function registerResetHook(hook) {
|
|
105
|
+
resetHooks.add(hook);
|
|
106
|
+
}
|
|
91
107
|
/**
|
|
92
108
|
* Reset the injector — clears all singletons and factory overrides.
|
|
93
109
|
* Used in tests to ensure isolation between test cases.
|
|
@@ -96,5 +112,7 @@ export function resetInjector() {
|
|
|
96
112
|
singletonCache.clear();
|
|
97
113
|
factoryOverrides.clear();
|
|
98
114
|
instantiating.clear();
|
|
115
|
+
for (const hook of resetHooks)
|
|
116
|
+
hook();
|
|
99
117
|
}
|
|
100
118
|
//# sourceMappingURL=injector.js.map
|
package/dist/injector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"injector.js","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"injector.js","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"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"}
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -52,6 +52,15 @@ export declare function useHostEvent<E extends Event = Event>(target: EventTarge
|
|
|
52
52
|
/**
|
|
53
53
|
* Run all registered mount callbacks for a component host.
|
|
54
54
|
* Called by component.ts after the template is mounted to the DOM.
|
|
55
|
+
*
|
|
56
|
+
* A throw here PROPAGATES to component.ts's containment boundary — the SAME
|
|
57
|
+
* boundary as a setup throw (ADR 0030.2 T6, code N402): remaining callbacks
|
|
58
|
+
* are skipped, the partial scope (including cleanups already registered by
|
|
59
|
+
* earlier callbacks) is disposed, the error fallback renders, and the host is
|
|
60
|
+
* stamped `data-nisli-error`. The pre-T6 behavior (console-and-limp per
|
|
61
|
+
* callback) left a half-mounted component alive with no machine-readable
|
|
62
|
+
* trace and no teardown — the one containment semantics setup already had
|
|
63
|
+
* (issue 0010) now covers mount too.
|
|
55
64
|
* @internal
|
|
56
65
|
*/
|
|
57
66
|
export declare function runMountCallbacks(comp: object): void;
|
package/dist/lifecycle.js
CHANGED
|
@@ -83,25 +83,35 @@ export function useHostEvent(target, eventName, handler) {
|
|
|
83
83
|
/**
|
|
84
84
|
* Run all registered mount callbacks for a component host.
|
|
85
85
|
* Called by component.ts after the template is mounted to the DOM.
|
|
86
|
+
*
|
|
87
|
+
* A throw here PROPAGATES to component.ts's containment boundary — the SAME
|
|
88
|
+
* boundary as a setup throw (ADR 0030.2 T6, code N402): remaining callbacks
|
|
89
|
+
* are skipped, the partial scope (including cleanups already registered by
|
|
90
|
+
* earlier callbacks) is disposed, the error fallback renders, and the host is
|
|
91
|
+
* stamped `data-nisli-error`. The pre-T6 behavior (console-and-limp per
|
|
92
|
+
* callback) left a half-mounted component alive with no machine-readable
|
|
93
|
+
* trace and no teardown — the one containment semantics setup already had
|
|
94
|
+
* (issue 0010) now covers mount too.
|
|
86
95
|
* @internal
|
|
87
96
|
*/
|
|
88
97
|
export function runMountCallbacks(comp) {
|
|
89
98
|
const callbacks = mountCallbacks.get(comp);
|
|
90
99
|
if (!callbacks)
|
|
91
100
|
return;
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
try {
|
|
102
|
+
for (const cb of callbacks) {
|
|
94
103
|
const cleanup = cb();
|
|
95
104
|
if (typeof cleanup === 'function') {
|
|
96
105
|
// Register cleanup as a disposer on the component
|
|
97
106
|
comp.addDisposer(cleanup);
|
|
98
107
|
}
|
|
99
108
|
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
console.error('onMount callback error:', err);
|
|
102
|
-
}
|
|
103
109
|
}
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
finally {
|
|
111
|
+
// Clear — mount callbacks run once (also when a throw contains the scope:
|
|
112
|
+
// the boundary re-runs setup from scratch on the next mount, which
|
|
113
|
+
// re-registers a fresh callback list).
|
|
114
|
+
mountCallbacks.delete(comp);
|
|
115
|
+
}
|
|
106
116
|
}
|
|
107
117
|
//# sourceMappingURL=lifecycle.js.map
|
package/dist/lifecycle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE/D,uEAAuE;AAEvE,wFAAwF;AACxF,MAAM,cAAc,GAAG,IAAI,OAAO,EAAyC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAmC;IACzD,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,oCAAoC;YACpC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,IAAI,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,CAAC;QACf,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAoB;IAC5C,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,sCAAsC;YACtC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,mBAAmB,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,SAAiB,EACjB,OAA2B;IAE3B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,yCAAyC;YACzC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAC7D,mBAAmB,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QACrC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE/D,uEAAuE;AAEvE,wFAAwF;AACxF,MAAM,cAAc,GAAG,IAAI,OAAO,EAAyC,CAAC;AAE5E;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAmC;IACzD,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,oCAAoC;YACpC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,IAAI,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,CAAC;QACf,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAoB;IAC5C,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,sCAAsC;YACtC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,mBAAmB,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,SAAiB,EACjB,OAA2B;IAE3B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,yCAAyC;YACzC,wDAAwD,CACzD,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAC7D,mBAAmB,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QACrC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAwB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS;QAAE,OAAO;IAEvB,IAAI,CAAC;QACH,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,EAAE,EAAE,CAAC;YACrB,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;gBAClC,kDAAkD;gBACjD,IAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,0EAA0E;QAC1E,mEAAmE;QACnE,uCAAuC;QACvC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
package/dist/projection.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* has any, and only children() consumes them.
|
|
35
35
|
*/
|
|
36
36
|
import { getCurrentComponent } from './context.js';
|
|
37
|
-
import {
|
|
37
|
+
import { onCleanup } from './lifecycle.js';
|
|
38
38
|
import { signal, computed, flush } from './signal.js';
|
|
39
39
|
/** True if the nodes carry meaningful content (element or non-blank text). */
|
|
40
40
|
function isMeaningful(nodes) {
|
|
@@ -63,11 +63,9 @@ function textTemplate(text) {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Normalize a slot value so the template ALWAYS sees a TemplateResult / factory
|
|
66
|
-
* / array / null — never a bare primitive.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* "[object Object]"). Wrapping primitives keeps the slot a reactive-template
|
|
70
|
-
* slot for its whole life.
|
|
66
|
+
* / array / null — never a bare primitive. Projection values may be detached
|
|
67
|
+
* and reinserted as fallback/light-DOM ownership changes, so wrapping primitive
|
|
68
|
+
* text keeps that projection value explicitly re-mountable.
|
|
71
69
|
*/
|
|
72
70
|
function asSlot(value) {
|
|
73
71
|
if (value == null || value === '')
|
|
@@ -100,18 +98,37 @@ export function children(fallback) {
|
|
|
100
98
|
const childrenProp = host._propSignal ? host._propSignal('children') : undefined;
|
|
101
99
|
// Projected light-DOM nodes; grows when the late-parser sweep finds more.
|
|
102
100
|
const projectedNodes = signal(captured);
|
|
101
|
+
// True once this setup scope has been disposed (true disconnect, HMR
|
|
102
|
+
// teardown, OR the containment boundary tearing down a failed mount). The
|
|
103
|
+
// queued sweep below must then no-op: it would otherwise misread the
|
|
104
|
+
// post-teardown host children (restored light DOM, or the error fallback)
|
|
105
|
+
// as "late" and relocate them into a dead slot.
|
|
106
|
+
let disposed = false;
|
|
103
107
|
// A true disconnect/HMR teardown clears the component-owned rendered tree
|
|
104
108
|
// before setup runs again. Hand the plain light-DOM nodes back to component.ts
|
|
105
109
|
// so it can restore them as direct host children for the next capture.
|
|
106
110
|
onCleanup(() => {
|
|
111
|
+
disposed = true;
|
|
107
112
|
const nodes = projectedNodes.value;
|
|
108
113
|
host.__capturedChildren = nodes.length ? nodes : undefined;
|
|
109
114
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
// LATE-CHILDREN SWEEP — scheduled by component.ts's dedicated projection
|
|
116
|
+
// phase (ADR 0030.2 §3), which runs after the template is committed and
|
|
117
|
+
// BEFORE any onMount callback. Pre-0030.2 this was registered via
|
|
118
|
+
// children()'s own onMount, so the rendered-roots snapshot depended on
|
|
119
|
+
// where children() sat relative to other onMount() registrations (the 0025
|
|
120
|
+
// batch-3B ordering trap). Hoisting the SCHEDULING makes the rule
|
|
121
|
+
// universal: the snapshot is exactly the rendered template roots, and
|
|
122
|
+
// anything appearing after it — late parser/innerHTML children AND onMount
|
|
123
|
+
// host-appends — classifies as projected content. Only scheduling moved;
|
|
124
|
+
// the prop READ path (the computed below) is unchanged.
|
|
125
|
+
host.__nisliProjectionSweep = () => {
|
|
126
|
+
// The host's direct children at phase time are the rendered template
|
|
127
|
+
// roots. Anything that appears later is late projected content.
|
|
113
128
|
const renderedRoots = new Set(Array.from(host.childNodes));
|
|
114
129
|
queueMicrotask(() => {
|
|
130
|
+
if (disposed)
|
|
131
|
+
return; // scope torn down before the sweep ran
|
|
115
132
|
const late = Array.from(host.childNodes).filter((n) => !renderedRoots.has(n));
|
|
116
133
|
if (late.length && isMeaningful(late)) {
|
|
117
134
|
for (const node of late)
|
|
@@ -123,7 +140,7 @@ export function children(fallback) {
|
|
|
123
140
|
flush();
|
|
124
141
|
}
|
|
125
142
|
});
|
|
126
|
-
}
|
|
143
|
+
};
|
|
127
144
|
// Stable, RE-MOUNTABLE fallback slot: the same slot object is returned on
|
|
128
145
|
// every empty render, and it can be mounted → unmounted → mounted again
|
|
129
146
|
// (empty → filled → empty) without crashing. A string wraps to a fresh text
|
package/dist/projection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,85 +1,164 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* query.ts —
|
|
2
|
+
* query.ts — Keyed logical-request store (ADR 0030.2 T1).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The cache unit is a per-key RECORD living in `QueryClient`:
|
|
5
|
+
* `{data, error, status, fetchedAt}` as signals, plus exactly one `run()`
|
|
6
|
+
* owning the retry loop, a per-run `AbortController` handed to the fetcher,
|
|
7
|
+
* and the cache commit. `query()` is a thin OBSERVER: an effect keeps it
|
|
8
|
+
* registered on the record its current key points at, and its outputs are
|
|
9
|
+
* `computed()` views over that record. Dedup unit = logical request, by
|
|
10
|
+
* construction (issues 0005–0009 close as a class).
|
|
8
11
|
*
|
|
9
12
|
* ```ts
|
|
10
13
|
* const tasks = query(
|
|
11
14
|
* () => ['tasks', scopeId.value],
|
|
12
|
-
* () => api.getTasks(scopeId.value),
|
|
15
|
+
* (signal) => api.getTasks(scopeId.value, { signal }),
|
|
13
16
|
* );
|
|
14
|
-
* tasks.data //
|
|
15
|
-
* tasks.loading //
|
|
16
|
-
* tasks.error //
|
|
17
|
-
* tasks.
|
|
17
|
+
* tasks.data // ReadonlySignal<Task[] | undefined>
|
|
18
|
+
* tasks.loading // ReadonlySignal<boolean>
|
|
19
|
+
* tasks.error // ReadonlySignal<Error | null>
|
|
20
|
+
* tasks.status // ReadonlySignal<'idle' | 'loading' | 'success' | 'error'>
|
|
21
|
+
* tasks.refetch() // force a refetch (joins an in-flight run)
|
|
22
|
+
* tasks.dispose() // standalone callers; component setup auto-disposes
|
|
18
23
|
* ```
|
|
24
|
+
*
|
|
25
|
+
* Ownership rules (ADR 0030.2 §8, binding):
|
|
26
|
+
* - The most-recently-mounted enabled observer's fetcher/options own the
|
|
27
|
+
* next run for a shared key; `staleTime` freshness is per-observer POLICY
|
|
28
|
+
* (it decides whether *that* observer triggers a run), while the record
|
|
29
|
+
* state is shared truth.
|
|
30
|
+
* - `invalidate()` marks records stale and reruns only records with ≥1
|
|
31
|
+
* enabled observer; disabled observers revalidate on re-enable.
|
|
32
|
+
* - A zero-observer record's in-flight run completes and commits.
|
|
33
|
+
* - Records are NEVER garbage-collected: the cache is unbounded for the
|
|
34
|
+
* life of the client (restates today's unbounded-cache bound). Bound it
|
|
35
|
+
* operationally with `clear()` or a fresh client per app lifetime.
|
|
36
|
+
*
|
|
37
|
+
* Key contract: keys are FLAT — `readonly (string|number|boolean|null)[]`.
|
|
38
|
+
* Objects and `undefined` are rejected with coded error N602 (`null` is
|
|
39
|
+
* the optional sentinel). Flat keys are order-sensitive: prefer
|
|
40
|
+
* per-endpoint key-builder helpers — a reordered key is a silent cache
|
|
41
|
+
* *miss*, benign relative to a collision, but a miss.
|
|
42
|
+
*
|
|
43
|
+
* SSG stance (documented, not built): under core-render, records fetch
|
|
44
|
+
* into the build-lifetime client and must TERMINATE before snapshot —
|
|
45
|
+
* `await settle()` is the barrier. Otherwise queries must be statically
|
|
46
|
+
* disabled for the build.
|
|
19
47
|
*/
|
|
20
48
|
import { type Signal, type ReadonlySignal } from './signal.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
49
|
+
import { type DisposableResource } from './disposable.js';
|
|
50
|
+
/** A single query-key element. `null` is the optional sentinel. */
|
|
51
|
+
export type QueryKeyElement = string | number | boolean | null;
|
|
52
|
+
/** The supported key contract: a flat, order-sensitive tuple. */
|
|
53
|
+
export type QueryKey = readonly QueryKeyElement[];
|
|
54
|
+
export type QueryStatus = 'idle' | 'loading' | 'success' | 'error';
|
|
55
|
+
/** Fetcher contract: receives the run's AbortSignal. Zero-arg fetchers
|
|
56
|
+
* remain assignable. */
|
|
57
|
+
export type QueryFetcher<T> = (signal: AbortSignal) => Promise<T>;
|
|
58
|
+
export interface QueryResult<T> extends DisposableResource {
|
|
59
|
+
/** The record's data, or `initialData`/undefined before first commit. */
|
|
60
|
+
data: ReadonlySignal<T | undefined>;
|
|
61
|
+
/** True while this observer is enabled and its record is fetching. */
|
|
25
62
|
loading: ReadonlySignal<boolean>;
|
|
26
|
-
/** The error
|
|
27
|
-
error:
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
*
|
|
63
|
+
/** The record's terminal error, or null. Cleared when a run starts. */
|
|
64
|
+
error: ReadonlySignal<Error | null>;
|
|
65
|
+
/** The record's lifecycle status. */
|
|
66
|
+
status: ReadonlySignal<QueryStatus>;
|
|
67
|
+
/** Force a refetch: bypasses staleTime freshness, but JOINS an in-flight
|
|
68
|
+
* logical request rather than starting a second one (issue 0005). No-op
|
|
69
|
+
* while disabled. */
|
|
33
70
|
refetch(): void;
|
|
71
|
+
/** Unregister this observer. Component setup calls this automatically at
|
|
72
|
+
* teardown; standalone callers (tests, scripts) call it explicitly. The
|
|
73
|
+
* record — and any in-flight run — survives: zero-observer runs complete
|
|
74
|
+
* and commit (ADR 0030.2 §8). */
|
|
75
|
+
dispose(): void;
|
|
34
76
|
}
|
|
35
77
|
export interface QueryOptions<T> {
|
|
36
|
-
/** Cache freshness duration in ms. Default: 0 (
|
|
78
|
+
/** Cache freshness duration in ms for THIS observer. Default: 0 (a
|
|
79
|
+
* registration/re-enable always revalidates). */
|
|
37
80
|
staleTime?: number;
|
|
38
|
-
/** Number of retries
|
|
81
|
+
/** Number of retries the run performs when this observer owns it. Default: 0 */
|
|
39
82
|
retry?: number;
|
|
40
|
-
/** Skip
|
|
83
|
+
/** Skip fetching while this returns false. Signal reads are tracked. */
|
|
41
84
|
enabled?: () => boolean;
|
|
42
|
-
/** Synchronous
|
|
85
|
+
/** Synchronous seed committed into an untouched (idle) record. */
|
|
43
86
|
initialData?: T;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
}
|
|
88
|
+
interface QueryRecord {
|
|
89
|
+
readonly key: QueryKey;
|
|
90
|
+
readonly serialized: string;
|
|
91
|
+
readonly data: Signal<unknown>;
|
|
92
|
+
readonly error: Signal<Error | null>;
|
|
93
|
+
readonly status: Signal<QueryStatus>;
|
|
94
|
+
/** Epoch ms of the last successful commit; 0 = never. */
|
|
95
|
+
readonly fetchedAt: Signal<number>;
|
|
96
|
+
/** Marked by invalidate(); cleared when a run starts. */
|
|
97
|
+
stale: boolean;
|
|
98
|
+
/** True while a run is in flight (status mirrors it reactively). */
|
|
99
|
+
running: boolean;
|
|
100
|
+
/** Increments per run start; a superseded run may never commit. */
|
|
101
|
+
generation: number;
|
|
102
|
+
controller: AbortController | null;
|
|
103
|
+
/** The in-flight run (joinable by prefetch). */
|
|
104
|
+
runPromise: Promise<void> | null;
|
|
105
|
+
/** settle() terminator for the in-flight run. */
|
|
106
|
+
terminate: (() => void) | null;
|
|
107
|
+
readonly observers: Set<QueryObserver>;
|
|
108
|
+
}
|
|
109
|
+
interface QueryObserver {
|
|
110
|
+
readonly mountIndex: number;
|
|
111
|
+
readonly fetcher: QueryFetcher<unknown>;
|
|
112
|
+
readonly staleTime: number;
|
|
113
|
+
readonly retry: number;
|
|
114
|
+
enabled: boolean;
|
|
115
|
+
record: QueryRecord | null;
|
|
48
116
|
}
|
|
49
117
|
/**
|
|
50
|
-
* Global
|
|
51
|
-
*
|
|
118
|
+
* Global keyed logical-request store. One per app: resolved via
|
|
119
|
+
* `inject(QueryClient)` (auto-singleton; override with
|
|
120
|
+
* `provide(QueryClient, ...)` BEFORE the first query() call).
|
|
121
|
+
*
|
|
122
|
+
* Records are never GC'd — see the module doc for the bound.
|
|
52
123
|
*/
|
|
53
124
|
export declare class QueryClient {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
getCached<T>(key: string): T | undefined;
|
|
60
|
-
/** Store data in cache */
|
|
61
|
-
set(key: string, data: unknown, staleTime: number): void;
|
|
62
|
-
/** Get in-flight promise for deduplication */
|
|
63
|
-
getInFlight(key: string): Promise<unknown> | undefined;
|
|
64
|
-
/** Register an in-flight request */
|
|
65
|
-
setInFlight(key: string, promise: Promise<unknown>): void;
|
|
125
|
+
/** @internal */ _records: Map<string, QueryRecord>;
|
|
126
|
+
/** @internal clear() bumps this; observer effects track it and re-resolve. */
|
|
127
|
+
_version: Signal<number>;
|
|
128
|
+
/** @internal live observer count (N601 mixed-client diagnostic). */
|
|
129
|
+
_observers: number;
|
|
66
130
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
131
|
+
* Mark records matching the key prefix stale and rerun those with ≥1
|
|
132
|
+
* enabled observer (using the owning observer's fetcher/options).
|
|
133
|
+
* Disabled observers revalidate on re-enable; a record with only
|
|
134
|
+
* disabled/no observers stays stale until one arrives. Returns the
|
|
135
|
+
* number of records invalidated.
|
|
69
136
|
*
|
|
70
|
-
* invalidate(['tasks']) matches ['tasks'], ['tasks', '1'], ['tasks', '2', 'x']
|
|
137
|
+
* invalidate(['tasks']) matches ['tasks'], ['tasks', '1'], ['tasks', '2', 'x'].
|
|
138
|
+
*/
|
|
139
|
+
invalidate(keyPrefix: QueryKey): number;
|
|
140
|
+
/**
|
|
141
|
+
* Drop every record. In-flight runs are aborted (a settle() termination).
|
|
142
|
+
* Live observers re-resolve fresh records on the version bump — enabled
|
|
143
|
+
* ones revalidate immediately.
|
|
71
144
|
*/
|
|
72
|
-
invalidate(keyPrefix: unknown[]): number;
|
|
73
|
-
/** Prefetch and cache data */
|
|
74
|
-
prefetch(key: unknown[], fetcher: () => Promise<unknown>, staleTime?: number): Promise<void>;
|
|
75
|
-
/** Clear all cache */
|
|
76
145
|
clear(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Imperatively warm a record. Joins an in-flight run; a fresh record
|
|
148
|
+
* (per the given staleTime) is a no-op. Otherwise starts a run with THE
|
|
149
|
+
* GIVEN fetcher — an explicit imperative is its own owner. The run
|
|
150
|
+
* commits even with zero observers. Resolves at run termination.
|
|
151
|
+
*/
|
|
152
|
+
prefetch<T>(key: QueryKey, fetcher: QueryFetcher<T>, staleTime?: number): Promise<void>;
|
|
77
153
|
}
|
|
78
154
|
/**
|
|
79
|
-
*
|
|
155
|
+
* Observe the record the reactive key points at.
|
|
80
156
|
*
|
|
81
|
-
* @param keyFn - Returns
|
|
82
|
-
*
|
|
83
|
-
* @param
|
|
157
|
+
* @param keyFn - Returns the flat cache key. Signal reads are tracked; a
|
|
158
|
+
* key change re-registers the observer on the new record.
|
|
159
|
+
* @param fetcher - Fetches the data; receives the run's AbortSignal.
|
|
160
|
+
* @param options - staleTime / retry / enabled / initialData.
|
|
161
|
+
* @throws N602 synchronously when the initial key violates the contract.
|
|
84
162
|
*/
|
|
85
|
-
export declare function query<T>(keyFn: () =>
|
|
163
|
+
export declare function query<T>(keyFn: () => QueryKey, fetcher: QueryFetcher<T>, options?: QueryOptions<T>): QueryResult<T>;
|
|
164
|
+
export {};
|