@nifrajs/web-solid 5.8.0 → 5.10.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 CHANGED
@@ -1,5 +1,109 @@
1
1
  # @nifrajs/web-solid
2
2
 
3
+ ## 5.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 611a3be: Expose mutation and infinite-query bindings across the framework adapters, with request-scoped query-client providers and SSR cache hydration. Mutation functions and callbacks receive a cooperative abort signal, and identity changes cancel pending work across tabs. `dehydrate` can filter regular and infinite query values before they are serialized to the browser.
8
+ - d2066e0: Add opt-in local `?image` metadata imports and content-hashed build-time responsive variants using the existing injectable image backends. Emit images with client, SSR and static builds, and infer imported dimensions in all five UI adapters. String image URLs retain their existing dimension requirements.
9
+
10
+ Add explicit static translated-path mappings to i18n routing, with reciprocal URL/SEO helpers, availability-aware redirects and static-path enumeration. These mappings do not implicitly create filesystem route aliases.
11
+
12
+ Add `vercelRuntime: "node"` for ordinary SSR Vercel builds through both bundlers, using a shared streaming Node request bridge. Vercel Edge remains the default.
13
+ - 824a75e: Partial prerendering is no longer experimental. `createWebApp` takes it as `ppr`, where it was `experimental.ppr`, and the `experimental` option is gone: passing it throws a `TypeError` that names `ppr`, and the startup errors name `ppr.origin`, `ppr.deployment` and `ppr.store`. `nifra upgrade` moves `experimental: { ppr }` to `ppr` in a `createWebApp` options literal and lists any other `experimental` shape it finds there by line (options spread in, or built in another file, are yours to check). `nifra scaffold` is stable.
14
+ - 1faf86c: `href({ to, params, search, hash })` builds an in-app URL from a route pattern and returns an `Href`; `rawHref(path)` returns one for a same-origin path built elsewhere (a CMS link, `/llms.txt`). `to` is a key of the generated `RouteTable`, so a misspelt route, a missing, misspelt or extra param, and a `search` that breaks the route's schema are compile errors. A route without a `searchSchema` takes any `search`. Each param is encoded as one path segment, so a value such as `a/b` or `//evil.com` cannot change where the URL goes; a param that is missing, empty, `.`, `..` or an unpaired surrogate throws a `TypeError` naming it and the pattern. `rawHref` throws a `TypeError` unless `isSameOriginPath` accepts the path. Both are exported by `@nifrajs/web` and by the `/router` entry of every adapter.
15
+
16
+ `navigate` takes an `Href` or a history delta. A string, a template literal and the `{ to, search }` object form are compile errors, and a value forged past the types is still held to the same-origin check the browser navigate makes. `<Navigate to>` and the `useSearchParams` setter in `@nifrajs/web-react` pass their path through `rawHref`, so an off-origin `to` throws instead of doing nothing. `isSameOriginPath` is exported from `@nifrajs/core/pattern` as well as `@nifrajs/core/server`.
17
+
18
+ Upgrade: `navigate("/users/7")` becomes `navigate(href({ to: "/users/:id", params: { id: "7" } }))`, `navigate({ to, search, replace: true })` becomes `navigate(href({ to, search }), { replace: true })`, and a path built elsewhere is `navigate(rawHref(path))`. `resolveNavigate`, `NavigateTargetInput` and `NavigateSearchOf` are removed from `@nifrajs/web`.
19
+
20
+ ### Patch Changes
21
+
22
+ - 2c361be: Opening a modal route on a Solid page no longer rebuilds the page. A layout was handed the `boundaries` value rather than a getter over it, so an intercept boundary taking its target re-ran the whole chain: a typed input lost its text and every component remounted. The page now stays mounted underneath, as the other adapters keep it.
23
+ - Updated dependencies [535c4e5]
24
+ - Updated dependencies [87e7fd9]
25
+ - Updated dependencies [611a3be]
26
+ - Updated dependencies [7f32bb2]
27
+ - Updated dependencies [0aa10d0]
28
+ - Updated dependencies [ab69ee6]
29
+ - Updated dependencies [548bade]
30
+ - Updated dependencies [d2066e0]
31
+ - Updated dependencies [7e292dd]
32
+ - Updated dependencies [a6af4c9]
33
+ - Updated dependencies [e465ed5]
34
+ - Updated dependencies [683c3d6]
35
+ - Updated dependencies [824a75e]
36
+ - Updated dependencies [d2066e0]
37
+ - Updated dependencies [5845e1c]
38
+ - Updated dependencies [5d1be78]
39
+ - Updated dependencies [1faf86c]
40
+ - Updated dependencies [950996b]
41
+ - @nifrajs/web@5.10.0
42
+ - @nifrajs/core@5.10.0
43
+
44
+ ## 5.9.0
45
+
46
+ ### Patch Changes
47
+
48
+ - 2ffa7c1: The React, Preact, Solid, Svelte and Vue `useServerFn` bindings take their function through one shared helper in `@nifrajs/web/internal/client-server-fn`.
49
+
50
+ The React, Svelte and Vue client and keep-alive mounts read a hydration verification runner's hooks through one reader in `@nifrajs/web/internal/hydration-assurance`. Recoverable errors and warnings reach the runner as before.
51
+ - 71dea89: Every binding's `useNavigate` returns one navigate from `@nifrajs/web/internal/navigate-runtime`, the same function on every call. The React, Preact, Solid and Vue `Hole` components read their boundary state through the `HoleState` type in `@nifrajs/web/internal/hole-runtime`.
52
+ - a0fe5bf: The keep-alive mounts of the React, Preact, Svelte, Solid and Vue adapters take their framework-free half from `@nifrajs/web/internal/keep-alive-mount`. This covers the slot tree of each router state, the parking each committed tree goes to, render props made once per state, and the slots under each layout. Each adapter still renders the slots its own way. Kept pages behave as before.
53
+ - Updated dependencies [2ffa7c1]
54
+ - Updated dependencies [100a0aa]
55
+ - Updated dependencies [71dea89]
56
+ - Updated dependencies [9c20e05]
57
+ - Updated dependencies [9942748]
58
+ - Updated dependencies [8cdb138]
59
+ - Updated dependencies [be1c677]
60
+ - Updated dependencies [f399e06]
61
+ - Updated dependencies [93e7078]
62
+ - Updated dependencies [2c2f58b]
63
+ - Updated dependencies [123409c]
64
+ - Updated dependencies [7ee17d0]
65
+ - Updated dependencies [68d9f31]
66
+ - Updated dependencies [fa7d102]
67
+ - Updated dependencies [ddeb735]
68
+ - Updated dependencies [a3fb195]
69
+ - Updated dependencies [47a864f]
70
+ - Updated dependencies [9942748]
71
+ - Updated dependencies [ffcf41a]
72
+ - Updated dependencies [7a15327]
73
+ - Updated dependencies [866c8ec]
74
+ - Updated dependencies [bc42fd2]
75
+ - Updated dependencies [a29d1cc]
76
+ - Updated dependencies [9942748]
77
+ - Updated dependencies [77b98ea]
78
+ - Updated dependencies [a3caf9b]
79
+ - Updated dependencies [a0fe5bf]
80
+ - Updated dependencies [7ba5c9b]
81
+ - Updated dependencies [0445564]
82
+ - Updated dependencies [180bf9d]
83
+ - Updated dependencies [ff9bc11]
84
+ - Updated dependencies [8094874]
85
+ - Updated dependencies [a7b89ee]
86
+ - Updated dependencies [34b780e]
87
+ - Updated dependencies [111c3be]
88
+ - Updated dependencies [5bd225e]
89
+ - Updated dependencies [d6e6617]
90
+ - Updated dependencies [5bc60b7]
91
+ - Updated dependencies [159ae97]
92
+ - Updated dependencies [6ba9740]
93
+ - Updated dependencies [5f4416a]
94
+ - Updated dependencies [bc05ec5]
95
+ - Updated dependencies [d3c050e]
96
+ - Updated dependencies [c4ed3e9]
97
+ - Updated dependencies [9942748]
98
+ - Updated dependencies [5bc60b7]
99
+ - Updated dependencies [3299474]
100
+ - Updated dependencies [543d00a]
101
+ - Updated dependencies [020eb7b]
102
+ - Updated dependencies [9942748]
103
+ - Updated dependencies [9785cd4]
104
+ - @nifrajs/web@5.9.0
105
+ - @nifrajs/core@5.9.0
106
+
3
107
  ## 5.8.0
4
108
 
5
109
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAmC,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAMpE;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,CAAC,OAAO,CAmCxF"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAmC,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAMpE;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,GAAG,CAAC,OAAO,CAqCxF"}
package/dist/compose.js CHANGED
@@ -25,7 +25,9 @@ export function compose(chain, props) {
25
25
  get data() {
26
26
  return props.layoutData?.[i] ?? null;
27
27
  },
28
- ...(props.boundaries !== undefined ? { boundaries: props.boundaries } : {}),
28
+ get boundaries() {
29
+ return props.boundaries;
30
+ },
29
31
  children: child(),
30
32
  });
31
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,eAAe,EAAY,MAAM,UAAU,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE/D,sFAAsF;AACtF,MAAM,YAAY,GAAsC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAEzE;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAsB,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAA2B,EAAE,KAAK,CAAC,CAAA;IACjG,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAyC,CAAA;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAA;QAClB,6FAA6F;QAC7F,mGAAmG;QACnG,kEAAkE;QAClE,0FAA0F;QAC1F,6EAA6E;QAC7E,IAAI,GAAG,GAAG,EAAE,CACV,eAAe,CAAC,MAAM,EAAE;YACtB,IAAI,IAAI;gBACN,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;YACtC,CAAC;YACD,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3E,QAAQ,EAAE,KAAK,EAAE;SAClB,CAAC,CAAA;IACN,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAA;IAClB,qGAAqG;IACrG,sFAAsF;IACtF,OAAO,GAAG,EAAE,CACV,eAAe,CAAC,aAAa,CAAC,QAAQ,EAAE;QACtC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,YAAY,CAA4B;QACtE,IAAI,QAAQ;YACV,OAAO,eAAe,CAAC,kBAAkB,CAAC,QAAQ,EAAE;gBAClD,KAAK,EAAE,KAAK;gBACZ,IAAI,QAAQ;oBACV,OAAO,KAAK,EAAE,CAAA;gBAChB,CAAC;aACF,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACN,CAAC"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,eAAe,EAAY,MAAM,UAAU,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE/D,sFAAsF;AACtF,MAAM,YAAY,GAAsC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAEzE;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAsB,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAA2B,EAAE,KAAK,CAAC,CAAA;IACjG,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAyC,CAAA;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAA;QAClB,6FAA6F;QAC7F,mGAAmG;QACnG,kEAAkE;QAClE,0FAA0F;QAC1F,6EAA6E;QAC7E,IAAI,GAAG,GAAG,EAAE,CACV,eAAe,CAAC,MAAM,EAAE;YACtB,IAAI,IAAI;gBACN,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;YACtC,CAAC;YACD,IAAI,UAAU;gBACZ,OAAO,KAAK,CAAC,UAAU,CAAA;YACzB,CAAC;YACD,QAAQ,EAAE,KAAK,EAAE;SAClB,CAAC,CAAA;IACN,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAA;IAClB,qGAAqG;IACrG,sFAAsF;IACtF,OAAO,GAAG,EAAE,CACV,eAAe,CAAC,aAAa,CAAC,QAAQ,EAAE;QACtC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,YAAY,CAA4B;QACtE,IAAI,QAAQ;YACV,OAAO,eAAe,CAAC,kBAAkB,CAAC,QAAQ,EAAE;gBAClD,KAAK,EAAE,KAAK;gBACZ,IAAI,QAAQ;oBACV,OAAO,KAAK,EAAE,CAAA;gBAChB,CAAC;aACF,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,CAAA;AACN,CAAC"}
package/dist/fn.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fn.d.ts","sourceRoot":"","sources":["../src/fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAkB,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,KAAK,QAAQ,EAA2B,MAAM,UAAU,CAAA;AAEjE,+DAA+D;AAC/D,MAAM,WAAW,cAAc,CAAC,KAAK,EAAE,MAAM;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAChD,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,EAAE,EAAE,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,GACnC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAS/B"}
1
+ {"version":3,"file":"fn.d.ts","sourceRoot":"","sources":["../src/fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE/E,OAAO,EAAE,KAAK,QAAQ,EAA2B,MAAM,UAAU,CAAA;AAEjE,+DAA+D;AAC/D,MAAM,WAAW,cAAc,CAAC,KAAK,EAAE,MAAM;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAChD,oBAAoB;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,EAAE,EAAE,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,GACnC,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAS/B"}
package/dist/fn.js CHANGED
@@ -8,6 +8,7 @@
8
8
  * "is it pending" has one answer rather than five that drift. This file contributes only a signal fed by the store.
9
9
  */
10
10
  import { createServerFnStore } from "@nifrajs/web/fn-state";
11
+ import { clientServerFn } from "@nifrajs/web/internal/client-server-fn";
11
12
  import { createSignal, onCleanup } from "solid-js";
12
13
  /**
13
14
  * Track one server function's call state.
@@ -16,7 +17,7 @@ import { createSignal, onCleanup } from "solid-js";
16
17
  * <button disabled={addTodo.state().pending} onClick={() => addTodo.call({ text }).catch(() => {})}>
17
18
  */
18
19
  export function useServerFn(fn) {
19
- const store = createServerFnStore(fn);
20
+ const store = createServerFnStore(clientServerFn(fn));
20
21
  // Seeded from the store, not from the idle constant. They are the same value here - the store is
21
22
  // created one line up and cannot have moved - so this is correctness that does not depend on that
22
23
  // staying true. Svelte had exactly this line reading the constant, and when its subscription turned
package/dist/fn.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fn.js","sourceRoot":"","sources":["../src/fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,mBAAmB,EAAsB,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAYjE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,EAAoC;IAEpC,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAmC,CAAC,CAAA;IACtE,iGAAiG;IACjG,kGAAkG;IAClG,oGAAoG;IACpG,oFAAoF;IACpF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAwB,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC/E,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;IAClE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAA;AACxD,CAAC"}
1
+ {"version":3,"file":"fn.js","sourceRoot":"","sources":["../src/fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,mBAAmB,EAAsB,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAA;AACvE,OAAO,EAAiB,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAYjE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,EAAoC;IAEpC,MAAM,KAAK,GAAG,mBAAmB,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAA;IACrD,iGAAiG;IACjG,kGAAkG;IAClG,oGAAoG;IACpG,oFAAoF;IACpF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAwB,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC/E,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;IAClE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAA;AACxD,CAAC"}
package/dist/image.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * reactively (`splitProps`/`mergeProps`). Resizing is delegated to the `loader`; nifra bundles no codec.
7
7
  * No JSX (`createComponent`/`Dynamic`), so the package builds with plain `tsc`.
8
8
  */
9
- import { type ImageLoader, type ImageProps } from "@nifrajs/image";
9
+ import { type ImageLoader, type ImageProps, type StaticImageProps } from "@nifrajs/image";
10
10
  import { type JSX } from "solid-js";
11
11
  /** Attributes `resolveImage` computes - callers can't override them via DOM passthrough. */
12
12
  type ComputedAttrs = "src" | "srcset" | "sizes" | "width" | "height" | "alt" | "loading" | "decoding" | "fetchpriority";
@@ -14,10 +14,11 @@ export interface ImageComponentProps extends ImageProps, Omit<JSX.ImgHTMLAttribu
14
14
  /** CDN URL builder. Defaults to the identity loader (no transform - still CLS-safe + lazy). */
15
15
  readonly loader?: ImageLoader;
16
16
  }
17
+ export type StaticImageComponentProps = StaticImageProps & Omit<ImageComponentProps, "src" | "width" | "height">;
17
18
  /**
18
- * Render a responsive, CLS-safe `<img>`. `width`+`height` are required (reserve layout space);
19
+ * Render a responsive, CLS-safe `<img>`. String URLs require dimensions; asset imports infer them;
19
20
  * `priority` marks the LCP image (`eager` + `fetchpriority="high"`). Extra DOM props pass through.
20
21
  */
21
- export declare function Image(props: ImageComponentProps): JSX.Element;
22
+ export declare function Image(props: ImageComponentProps | StaticImageComponentProps): JSX.Element;
22
23
  export {};
23
24
  //# sourceMappingURL=image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAA6B,MAAM,gBAAgB,CAAA;AAC7F,OAAO,EAAmB,KAAK,GAAG,EAA0B,MAAM,UAAU,CAAA;AAG5E,4FAA4F;AAC5F,KAAK,aAAa,GACd,KAAK,GACL,QAAQ,GACR,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,SAAS,GACT,UAAU,GACV,eAAe,CAAA;AAEnB,MAAM,WAAW,mBACf,SAAQ,UAAU,EAChB,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,aAAa,GAAG,MAAM,UAAU,CAAC;IACjF,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,GAAG,CAAC,OAAO,CAgC7D"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EAEf,KAAK,gBAAgB,EAEtB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAmB,KAAK,GAAG,EAA0B,MAAM,UAAU,CAAA;AAG5E,4FAA4F;AAC5F,KAAK,aAAa,GACd,KAAK,GACL,QAAQ,GACR,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,SAAS,GACT,UAAU,GACV,eAAe,CAAA;AAEnB,MAAM,WAAW,mBACf,SAAQ,UAAU,EAChB,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,aAAa,GAAG,MAAM,UAAU,CAAC;IACjF,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED,MAAM,MAAM,yBAAyB,GAAG,gBAAgB,GACtD,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAA;AAEvD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,yBAAyB,GAAG,GAAG,CAAC,OAAO,CA2CzF"}
package/dist/image.js CHANGED
@@ -6,11 +6,11 @@
6
6
  * reactively (`splitProps`/`mergeProps`). Resizing is delegated to the `loader`; nifra bundles no codec.
7
7
  * No JSX (`createComponent`/`Dynamic`), so the package builds with plain `tsc`.
8
8
  */
9
- import { resolveImage, toHtmlAttrs } from "@nifrajs/image";
9
+ import { resolveImage, toHtmlAttrs, } from "@nifrajs/image";
10
10
  import { createComponent, mergeProps, splitProps } from "solid-js";
11
11
  import { Dynamic } from "solid-js/web";
12
12
  /**
13
- * Render a responsive, CLS-safe `<img>`. `width`+`height` are required (reserve layout space);
13
+ * Render a responsive, CLS-safe `<img>`. String URLs require dimensions; asset imports infer them;
14
14
  * `priority` marks the LCP image (`eager` + `fetchpriority="high"`). Extra DOM props pass through.
15
15
  */
16
16
  export function Image(props) {
@@ -31,10 +31,7 @@ export function Image(props) {
31
31
  const attrs = () => {
32
32
  // Build ImageProps via conditional spreads (exactOptionalPropertyTypes: an unset optional is
33
33
  // absent, not `undefined`). resolveImage validates width/height > 0 (CLS contract).
34
- const input = {
35
- src: props.src,
36
- width: props.width,
37
- height: props.height,
34
+ const common = {
38
35
  alt: props.alt,
39
36
  ...(props.sizes !== undefined ? { sizes: props.sizes } : {}),
40
37
  ...(props.widths !== undefined ? { widths: props.widths } : {}),
@@ -42,6 +39,19 @@ export function Image(props) {
42
39
  ...(props.loading !== undefined ? { loading: props.loading } : {}),
43
40
  ...(props.priority !== undefined ? { priority: props.priority } : {}),
44
41
  };
42
+ const input = typeof props.src === "string"
43
+ ? {
44
+ ...common,
45
+ src: props.src,
46
+ width: props.width ?? Number.NaN,
47
+ height: props.height ?? Number.NaN,
48
+ }
49
+ : {
50
+ ...common,
51
+ src: props.src,
52
+ ...(props.width !== undefined ? { width: props.width } : {}),
53
+ ...(props.height !== undefined ? { height: props.height } : {}),
54
+ };
45
55
  return toHtmlAttrs(resolveImage(input, props.loader));
46
56
  };
47
57
  return createComponent(Dynamic, mergeProps(rest, attrs, { component: "img" }));
package/dist/image.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAqC,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7F,OAAO,EAAE,eAAe,EAAY,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAqBtC;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,KAA0B;IAC9C,sGAAsG;IACtG,iGAAiG;IACjG,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE;QACjC,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,UAAU;QACV,KAAK;QACL,OAAO;QACP,QAAQ;QACR,KAAK;QACL,OAAO;QACP,SAAS;KACV,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,GAAmC,EAAE;QACjD,6FAA6F;QAC7F,oFAAoF;QACpF,MAAM,KAAK,GAAe;YACxB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtE,CAAA;QACD,OAAO,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,CAAC,CAAA;IACD,OAAO,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;AAChF,CAAC"}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAIL,YAAY,EAEZ,WAAW,GACZ,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,eAAe,EAAY,UAAU,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAwBtC;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,KAAsD;IAC1E,sGAAsG;IACtG,iGAAiG;IACjG,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE;QACjC,QAAQ;QACR,QAAQ;QACR,SAAS;QACT,UAAU;QACV,KAAK;QACL,OAAO;QACP,QAAQ;QACR,KAAK;QACL,OAAO;QACP,SAAS;KACV,CAAC,CAAA;IACF,MAAM,KAAK,GAAG,GAAmC,EAAE;QACjD,6FAA6F;QAC7F,oFAAoF;QACpF,MAAM,MAAM,GAAG;YACb,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtE,CAAA;QACD,MAAM,KAAK,GACT,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;YAC3B,CAAC,CAAC;gBACE,GAAG,MAAM;gBACT,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,GAAG;gBAChC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG;aACnC;YACH,CAAC,CAAC;gBACE,GAAG,MAAM;gBACT,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChE,CAAA;QACP,OAAO,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IACvD,CAAC,CAAA;IACD,OAAO,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;AAChF,CAAC"}
@@ -9,7 +9,7 @@
9
9
  * in place, synchronously, so the DOM half runs right after each publish.
10
10
  */
11
11
  import type { MountRouterOptions } from "@nifrajs/web";
12
- import { type KeepAliveRouter, type KeepAliveTable } from "@nifrajs/web/keep-alive";
12
+ import type { KeepAliveRouter, KeepAliveTable } from "@nifrajs/web/keep-alive";
13
13
  export * from "./client.js";
14
14
  export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js";
15
15
  /** `mountRouter`'s options in a keep-alive app: the generated entry adds where the wrappers go. */
@@ -1 +1 @@
1
- {"version":3,"file":"keep-alive.d.ts","sourceRoot":"","sources":["../src/keep-alive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,cAAc,CAAA;AACnE,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,cAAc,EAGpB,MAAM,yBAAyB,CAAA;AAOhC,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAEhF,mGAAmG;AACnG,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CACnC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAsChE"}
1
+ {"version":3,"file":"keep-alive.d.ts","sourceRoot":"","sources":["../src/keep-alive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,cAAc,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAY,MAAM,yBAAyB,CAAA;AAOxF,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAEhF,mGAAmG;AACnG,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CACnC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI,CA8BhE"}
@@ -1,4 +1,4 @@
1
- import { createSlotParking, slotTree, } from "@nifrajs/web/keep-alive";
1
+ import { keptMount } from "@nifrajs/web/internal/keep-alive-mount";
2
2
  import { createMemo, createSignal } from "solid-js";
3
3
  import { hydrate as solidHydrate } from "solid-js/web";
4
4
  import { setMountedRouter } from "./fetcher.js";
@@ -11,37 +11,29 @@ export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js"
11
11
  * wrappers the server rendered), so hydration claims the document as it is.
12
12
  */
13
13
  export function mountRouter(options) {
14
- const { router, routes, searchSchemas, container, keepAlive: table } = options;
14
+ const { router, searchSchemas, container } = options;
15
15
  const matchChains = options.matchChains ?? {};
16
16
  setMountedRouter(router);
17
- const parking = createSlotParking();
18
- let committed;
19
- router.beforePublish((next) => parking.before(slotTree(next, matchChains, table, committed)));
17
+ const kept = keptMount(options);
20
18
  let current = [];
21
19
  let publish;
22
- // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the generated entry passes the element the server rendered the app into
23
20
  solidHydrate(() => {
24
21
  const [state, setState] = createSignal(router.snapshot());
25
22
  publish = (next) => setState(() => next);
26
23
  const tree = createMemo(() => {
27
- current = slotTree(state(), matchChains, table, current);
24
+ current = kept.tree(state(), current);
28
25
  return current;
29
26
  });
30
27
  const mount = {
28
+ ...kept,
31
29
  propsOf: (of) => routeProps(of, searchSchemas, matchChains, (read) => createMemo(read)),
32
- chainOf: (of) => routes[of.routeId] ?? [],
33
- entry: parking.entry,
34
- page: parking.page,
35
30
  };
36
31
  return KeepAliveView({ tree, shown: mount.propsOf(state), mount });
37
32
  // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the generated entry passes the element the server rendered the app into
38
33
  }, container);
39
34
  // Solid has updated the document by the time a publish returns: park what it hid, restore what it
40
35
  // showed again.
41
- const after = () => {
42
- committed = current;
43
- parking.after(current);
44
- };
36
+ const after = () => kept.after(current);
45
37
  after();
46
38
  router.subscribe(() => {
47
39
  publish?.(router.snapshot());
@@ -1 +1 @@
1
- {"version":3,"file":"keep-alive.js","sourceRoot":"","sources":["../src/keep-alive.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,iBAAiB,EAIjB,QAAQ,GACT,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAA2B,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAQhF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAA8B;IACxD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,OAAO,CAAA;IAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAA;IAC7C,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACxB,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAA;IACnC,IAAI,SAA0C,CAAA;IAC9C,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IAE7F,IAAI,OAAO,GAAwB,EAAE,CAAA;IACrC,IAAI,OAAmD,CAAA;IACvD,yIAAyI;IACzI,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QACzD,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;YACxD,OAAO,OAAO,CAAA;QAChB,CAAC,CAAC,CAAA;QACF,MAAM,KAAK,GAAuB;YAChC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACvF,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE;YACzC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAA;QACD,OAAO,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAClE,yIAAyI;IAC3I,CAAC,EAAE,SAAoB,CAAC,CAAA;IACxB,kGAAkG;IAClG,gBAAgB;IAChB,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,SAAS,GAAG,OAAO,CAAA;QACnB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxB,CAAC,CAAA;IACD,KAAK,EAAE,CAAA;IACP,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE;QACpB,OAAO,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5B,KAAK,EAAE,CAAA;IACT,CAAC,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"keep-alive.js","sourceRoot":"","sources":["../src/keep-alive.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AAElE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,aAAa,EAA2B,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAQhF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAA8B;IACxD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IACpD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAA;IAC7C,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACxB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IAE/B,IAAI,OAAO,GAAwB,EAAE,CAAA;IACrC,IAAI,OAAmD,CAAA;IACvD,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QACzD,OAAO,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAA;YACrC,OAAO,OAAO,CAAA;QAChB,CAAC,CAAC,CAAA;QACF,MAAM,KAAK,GAAuB;YAChC,GAAG,IAAI;YACP,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SACxF,CAAA;QACD,OAAO,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QAClE,yIAAyI;IAC3I,CAAC,EAAE,SAAoB,CAAC,CAAA;IACxB,kGAAkG;IAClG,gBAAgB;IAChB,MAAM,KAAK,GAAG,GAAS,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7C,KAAK,EAAE,CAAA;IACP,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE;QACpB,OAAO,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5B,KAAK,EAAE,CAAA;IACT,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/dist/ppr.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type HoleState } from "@nifrajs/web/internal/hole-runtime";
1
2
  import { type JSX } from "solid-js";
2
3
  /**
3
4
  * The fields of a route's boundary declaration a hole renders from. `fallback` is a function: JSX
@@ -9,14 +10,6 @@ export interface HoleDefinition<Data> {
9
10
  readonly fallback?: () => JSX.Element;
10
11
  readonly interactive?: boolean;
11
12
  }
12
- /** The slice of a boundary state a hole reads: `ppr` marks a hole the shared shell left open, and
13
- * `fill` that host's refill when the page reloads. */
14
- interface HoleState {
15
- readonly status: "unresolved" | "pending" | "ready" | "error";
16
- readonly data?: unknown;
17
- readonly ppr?: true;
18
- readonly fill?: unknown;
19
- }
20
13
  export interface HoleProps<Data> {
21
14
  readonly of: HoleDefinition<Data>;
22
15
  readonly state: HoleState | undefined;
@@ -24,7 +17,7 @@ export interface HoleProps<Data> {
24
17
  readonly error?: JSX.Element;
25
18
  }
26
19
  /**
27
- * Experimental: one `dynamic` or `shared` boundary of a partially prerendered page.
20
+ * One `dynamic` or `shared` boundary of a partially prerendered page.
28
21
  *
29
22
  * In a shared shell the hole is an empty host Solid never fills: the PPR runtime puts the server's
30
23
  * HTML into it, and an interactive hole then hydrates as its own root from the frame's props. When the
@@ -33,5 +26,4 @@ export interface HoleProps<Data> {
33
26
  * navigation the hole renders like any streamed boundary.
34
27
  */
35
28
  export declare function Hole<Data>(props: HoleProps<Data>): JSX.Element;
36
- export {};
37
29
  //# sourceMappingURL=ppr.d.ts.map
package/dist/ppr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ppr.d.ts","sourceRoot":"","sources":["../src/ppr.ts"],"names":[],"mappings":"AACA,OAAO,EAA+B,KAAK,GAAG,EAAW,MAAM,UAAU,CAAA;AAKzE;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,IAAI;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG,CAAC,OAAO,CAAA;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,OAAO,CAAA;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED;sDACsD;AACtD,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,SAAS,CAAC,IAAI;IAC7B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CAC7B;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,CA4B9D"}
1
+ {"version":3,"file":"ppr.d.ts","sourceRoot":"","sources":["../src/ppr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,oCAAoC,CAAA;AAChF,OAAO,EAA+B,KAAK,GAAG,EAAW,MAAM,UAAU,CAAA;AAKzE;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,IAAI;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG,CAAC,OAAO,CAAA;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,OAAO,CAAA;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,SAAS,CAAC,IAAI;IAC7B,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC,IAAI,CAAC,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CAC7B;AAiBD;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,CA4B9D"}
package/dist/ppr.js CHANGED
@@ -18,7 +18,7 @@ function openHost(hole, fill) {
18
18
  return clientHost(hole.name, hole.render, hole.interactive === true, attributes, fill);
19
19
  }
20
20
  /**
21
- * Experimental: one `dynamic` or `shared` boundary of a partially prerendered page.
21
+ * One `dynamic` or `shared` boundary of a partially prerendered page.
22
22
  *
23
23
  * In a shared shell the hole is an empty host Solid never fills: the PPR runtime puts the server's
24
24
  * HTML into it, and an interactive hole then hydrates as its own root from the frame's props. When the
package/dist/ppr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ppr.js","sourceRoot":"","sources":["../src/ppr.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ppr.js","sourceRoot":"","sources":["../src/ppr.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -1,15 +1,22 @@
1
- import type { QueryClient, QueryOptions, QueryState } from "@nifrajs/web";
1
+ import type { DehydratedState, InfiniteData, InfiniteQueryOptions, MutationCallbacks, MutationContext, MutationState, QueryClient, QueryOptions, QueryState } from "@nifrajs/web";
2
2
  /**
3
3
  * `@nifrajs/web-solid/query` - Solid bindings for the keyed query-cache. `createQuery(key, fn)` bridges a
4
4
  * query's `subscribe`/`snapshot` store into a Solid signal and fetches on mount; `useQueryClient`
5
5
  * exposes `invalidateQueries` for after a mutation. Imports only `solid-js` + `@nifrajs/web` (never
6
6
  * `solid-js/web`), so route components use it on the server *and* client. No JSX.
7
7
  *
8
- * The query client is created lazily **client-side only** (`typeof window`) - on the server the
9
- * accessor returns the idle/pending state (queries are client-first; loaders are the SSR data source).
8
+ * Without a provider, the query client is created lazily **client-side only** (`typeof window`) - on
9
+ * the server the accessor returns idle state and route loaders remain the data source. A
10
+ * `QueryClientProvider` can supply a request-scoped hydrated client.
10
11
  * The `key` is captured once (Solid setup runs once); a reactive key is a documented future enhancement.
11
12
  */
12
- import { type Accessor } from "solid-js";
13
+ import { type Accessor, type JSX } from "solid-js";
14
+ /** Provide a request-scoped client and optionally seed it before descendants run. */
15
+ export declare function QueryClientProvider(props: {
16
+ readonly client: QueryClient;
17
+ readonly state?: DehydratedState;
18
+ readonly children?: JSX.Element;
19
+ }): JSX.Element;
13
20
  /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
14
21
  export declare function useQueryClient(): Pick<QueryClient, "invalidateQueries">;
15
22
  /** A query's reactive state accessor plus `refetch`. */
@@ -26,4 +33,36 @@ export interface CreateQueryResult<T> {
26
33
  * the live keys that refetch it.
27
34
  */
28
35
  export declare function createQuery<T>(key: unknown, fn: () => Promise<T>, options?: QueryOptions): CreateQueryResult<T>;
36
+ /** A mutation result expressed as Solid accessors and imperative controls. */
37
+ export interface CreateMutationResult<TData, TVariables> {
38
+ readonly state: Accessor<MutationState<TData, TVariables>>;
39
+ readonly isIdle: Accessor<boolean>;
40
+ readonly isPending: Accessor<boolean>;
41
+ readonly isError: Accessor<boolean>;
42
+ readonly isSuccess: Accessor<boolean>;
43
+ readonly mutate: (variables: TVariables) => void;
44
+ readonly mutateAsync: (variables: TVariables) => Promise<TData>;
45
+ readonly reset: () => void;
46
+ readonly cancel: () => void;
47
+ }
48
+ /** Bind a mutation to a Solid owner. Identity changes discard late results and callbacks. */
49
+ export declare function createMutation<TData, TVariables = void>(fn: (variables: TVariables, context: MutationContext) => Promise<TData>, callbacks?: MutationCallbacks<TData, TVariables>): CreateMutationResult<TData, TVariables>;
50
+ /** Options for a Solid infinite query. */
51
+ export interface CreateInfiniteQueryOptions<T, P> extends InfiniteQueryOptions<T, P> {
52
+ readonly enabled?: boolean;
53
+ }
54
+ /** Infinite-query state and page controls expressed as Solid accessors. */
55
+ export interface CreateInfiniteQueryResult<T, P> {
56
+ readonly state: Accessor<QueryState<InfiniteData<T, P>>>;
57
+ readonly isPending: Accessor<boolean>;
58
+ readonly isError: Accessor<boolean>;
59
+ readonly isSuccess: Accessor<boolean>;
60
+ readonly hasNextPage: Accessor<boolean>;
61
+ readonly hasPreviousPage: Accessor<boolean>;
62
+ readonly fetchNextPage: () => Promise<InfiniteData<T, P>>;
63
+ readonly fetchPreviousPage: () => Promise<InfiniteData<T, P>>;
64
+ readonly refetch: () => Promise<InfiniteData<T, P>>;
65
+ }
66
+ /** Bind a paged query to a Solid owner and fetch its first page on mount. */
67
+ export declare function createInfiniteQuery<T, P>(key: unknown, fn: (pageParam: P) => Promise<T>, options: CreateInfiniteQueryOptions<T, P>): CreateInfiniteQueryResult<T, P>;
29
68
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAQzE;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAoC,MAAM,UAAU,CAAA;AAE1E,wGAAwG;AACxG,wBAAgB,cAAc,IAAI,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAEvE;AAED,wDAAwD;AACxD,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,uGAAuG;IACvG,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IACvC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,YAAY,GACrB,iBAAiB,CAAC,CAAC,CAAC,CActB"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createMutation as createMutationHandle } from "@nifrajs/web/client";
1
2
  import { createNoClientRefetch, fetchOnMount, getQueryClientSingleton, IDLE_QUERY_STATE, noop, } from "@nifrajs/web/internal/query-runtime";
2
3
  /**
3
4
  * `@nifrajs/web-solid/query` - Solid bindings for the keyed query-cache. `createQuery(key, fn)` bridges a
@@ -5,14 +6,30 @@ import { createNoClientRefetch, fetchOnMount, getQueryClientSingleton, IDLE_QUER
5
6
  * exposes `invalidateQueries` for after a mutation. Imports only `solid-js` + `@nifrajs/web` (never
6
7
  * `solid-js/web`), so route components use it on the server *and* client. No JSX.
7
8
  *
8
- * The query client is created lazily **client-side only** (`typeof window`) - on the server the
9
- * accessor returns the idle/pending state (queries are client-first; loaders are the SSR data source).
9
+ * Without a provider, the query client is created lazily **client-side only** (`typeof window`) - on
10
+ * the server the accessor returns idle state and route loaders remain the data source. A
11
+ * `QueryClientProvider` can supply a request-scoped hydrated client.
10
12
  * The `key` is captured once (Solid setup runs once); a reactive key is a documented future enhancement.
11
13
  */
12
- import { createSignal, onCleanup, onMount } from "solid-js";
14
+ import { createComponent, createContext, createSignal, onCleanup, onMount, useContext, } from "solid-js";
15
+ const QueryClientContext = createContext();
16
+ function queryClientForCurrentTree() {
17
+ return useContext(QueryClientContext) ?? getQueryClientSingleton();
18
+ }
19
+ /** Provide a request-scoped client and optionally seed it before descendants run. */
20
+ export function QueryClientProvider(props) {
21
+ if (props.state !== undefined)
22
+ props.client.hydrate(props.state);
23
+ return createComponent(QueryClientContext.Provider, {
24
+ value: props.client,
25
+ get children() {
26
+ return props.children;
27
+ },
28
+ });
29
+ }
13
30
  /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
14
31
  export function useQueryClient() {
15
- return getQueryClientSingleton() ?? { invalidateQueries: noop };
32
+ return queryClientForCurrentTree() ?? { invalidateQueries: noop };
16
33
  }
17
34
  /**
18
35
  * Bind the keyed query for `key`, fetched via `fn`. Returns a reactive `state()` accessor + `refetch`.
@@ -21,7 +38,7 @@ export function useQueryClient() {
21
38
  * the live keys that refetch it.
22
39
  */
23
40
  export function createQuery(key, fn, options) {
24
- const handle = getQueryClientSingleton()?.query(key, fn, options);
41
+ const handle = queryClientForCurrentTree()?.query(key, fn, options);
25
42
  const [state, setState] = createSignal(handle ? handle.snapshot() : IDLE_QUERY_STATE);
26
43
  if (handle !== undefined) {
27
44
  onCleanup(handle.subscribe(() => setState(() => handle.snapshot())));
@@ -33,4 +50,55 @@ export function createQuery(key, fn, options) {
33
50
  refetch: createNoClientRefetch("[nifra/web-solid]", "createQuery.refetch"),
34
51
  };
35
52
  }
53
+ /** Bind a mutation to a Solid owner. Identity changes discard late results and callbacks. */
54
+ export function createMutation(fn, callbacks = {}) {
55
+ const handle = createMutationHandle(fn, callbacks);
56
+ const [state, setState] = createSignal(handle.snapshot());
57
+ onCleanup(handle.subscribe(() => setState(() => handle.snapshot())));
58
+ return {
59
+ state,
60
+ isIdle: () => state().status === "idle",
61
+ isPending: () => state().status === "pending",
62
+ isError: () => state().status === "error",
63
+ isSuccess: () => state().status === "success",
64
+ mutate: (variables) => {
65
+ handle.mutate(variables).catch(noop);
66
+ },
67
+ mutateAsync: handle.mutate,
68
+ reset: handle.reset,
69
+ cancel: handle.cancel,
70
+ };
71
+ }
72
+ /** Bind a paged query to a Solid owner and fetch its first page on mount. */
73
+ export function createInfiniteQuery(key, fn, options) {
74
+ const client = queryClientForCurrentTree();
75
+ const enabled = options.enabled !== false;
76
+ const handle = client?.infiniteQuery(key, fn, options);
77
+ const [state, setState] = createSignal(handle
78
+ ? handle.snapshot()
79
+ : // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: An idle query has no generic page data; the provider supplies the typed state before use.
80
+ IDLE_QUERY_STATE);
81
+ if (handle) {
82
+ onCleanup(handle.subscribe(() => setState(() => handle.snapshot())));
83
+ onMount(fetchOnMount(enabled ? handle : undefined));
84
+ }
85
+ const noClientAction = createNoClientRefetch("[nifra/web-solid]", "infinite query action");
86
+ return {
87
+ state,
88
+ isPending: () => state().status === "pending",
89
+ isError: () => state().status === "error",
90
+ isSuccess: () => state().status === "success",
91
+ hasNextPage: () => {
92
+ state();
93
+ return handle?.hasNextPage() ?? false;
94
+ },
95
+ hasPreviousPage: () => {
96
+ state();
97
+ return handle?.hasPreviousPage() ?? false;
98
+ },
99
+ fetchNextPage: handle?.fetchNextPage ?? noClientAction,
100
+ fetchPreviousPage: handle?.fetchPreviousPage ?? noClientAction,
101
+ refetch: handle?.refetch ?? noClientAction,
102
+ };
103
+ }
36
104
  //# sourceMappingURL=query.js.map
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,uBAAuB,EACvB,gBAAgB,EAChB,IAAI,GACL,MAAM,qCAAqC,CAAA;AAC5C;;;;;;;;;GASG;AACH,OAAO,EAAiB,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAE1E,wGAAwG;AACxG,MAAM,UAAU,cAAc;IAC5B,OAAO,uBAAuB,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAA;AACjE,CAAC;AAUD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,GAAY,EACZ,EAAoB,EACpB,OAAsB;IAEtB,MAAM,MAAM,GAAG,uBAAuB,EAAE,EAAE,KAAK,CAAI,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;IACpE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,YAAY,CACpC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAC9C,CAAA;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAA;QACpE,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAA;QAC7B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;IAC3C,CAAC;IACD,OAAO;QACL,KAAK;QACL,OAAO,EAAE,qBAAqB,CAAI,mBAAmB,EAAE,qBAAqB,CAAC;KAC9E,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/router.d.ts CHANGED
@@ -8,6 +8,7 @@ import type { Blocker, BlockerFunction, BlockerState, NavigateFunction, RenderPr
8
8
  * reads the accessor `compose` provides on SSR + client mount alike. Imports only `solid-js`. No JSX.
9
9
  */
10
10
  import { type Accessor, type Context } from "solid-js";
11
+ export { href, rawHref } from "@nifrajs/web/client";
11
12
  export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js";
12
13
  export type { Blocker, BlockerFunction, BlockerState, NavigateFunction, UIMatch };
13
14
  export declare const SearchContext: Context<Accessor<Record<string, unknown>> | undefined>;
@@ -38,7 +39,7 @@ export declare function useMatches(): Accessor<readonly UIMatch[]>;
38
39
  * ```
39
40
  */
40
41
  export declare function useSearch<Schema extends StandardSchemaV1 | undefined = undefined>(): Accessor<Schema extends StandardSchemaV1 ? InferOutput<Schema> : Record<string, unknown>>;
41
- /** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object).
42
+ /** Get the {@link NavigateFunction} (an `Href` from `href()` or `rawHref()`, or a history delta).
42
43
  * Resolves the browser navigate at call time, so it works as soon as `installHistory` has run and no-ops
43
44
  * before then / on the server. */
44
45
  export declare function useNavigate(): NavigateFunction;
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAIzE,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,YAAY,EACZ,gBAAgB,EAGhB,WAAW,EACX,OAAO,EACR,MAAM,cAAc,CAAA;AAWrB;;;;;;GAMG;AACH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EAMb,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAChF,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAA;AAiBjF,eAAO,MAAM,aAAa,wDACoE,CAAA;AAO9F,eAAO,MAAM,kBAAkB,kCACuC,CAAA;AAKtE;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,IAAI,QAAQ,CAAC,SAAS,OAAO,EAAE,CAAC,CAIzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,MAAM,SAAS,gBAAgB,GAAG,SAAS,GAAG,SAAS,KAAK,QAAQ,CAC5F,MAAM,SAAS,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAChF,CAIA;AAED;;kCAEkC;AAClC,wBAAgB,WAAW,IAAI,gBAAgB,CAO9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,CAYpF"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAIzE,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,OAAO,EACR,MAAM,cAAc,CAAA;AAOrB;;;;;;GAMG;AACH,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EAMb,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAChF,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAA;AAiBjF,eAAO,MAAM,aAAa,wDACoE,CAAA;AAO9F,eAAO,MAAM,kBAAkB,kCACuC,CAAA;AAKtE;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,IAAI,QAAQ,CAAC,SAAS,OAAO,EAAE,CAAC,CAIzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,MAAM,SAAS,gBAAgB,GAAG,SAAS,GAAG,SAAS,KAAK,QAAQ,CAC5F,MAAM,SAAS,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAChF,CAIA;AAED;;kCAEkC;AAClC,wBAAgB,WAAW,IAAI,gBAAgB,CAE9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,CAYpF"}
package/dist/router.js CHANGED
@@ -1,8 +1,9 @@
1
1
  // `/client`, not the root - these are DOM values, and the root's graph carries the
2
2
  // server, which Vite's dev server evaluates rather than tree-shakes.
3
- import { getBrowserNavigate, IDLE_BLOCKER, registerBlocker, resolveNavigate, } from "@nifrajs/web/client";
3
+ import { IDLE_BLOCKER, registerBlocker } from "@nifrajs/web/client";
4
4
  // Its own subpath, so an app that never calls `useMatches` never bundles it.
5
5
  import { chainMatches } from "@nifrajs/web/internal/matches-runtime";
6
+ import { navigateNow } from "@nifrajs/web/internal/navigate-runtime";
6
7
  /**
7
8
  * `@nifrajs/web-solid/router` - Solid routing bindings over the agnostic `@nifrajs/web` history layer:
8
9
  * `useNavigate` (programmatic navigation), `useBlocker` (the unsaved-changes guard), and `useSearch`
@@ -12,6 +13,7 @@ import { chainMatches } from "@nifrajs/web/internal/matches-runtime";
12
13
  */
13
14
  import { createContext, createMemo, createSignal, onCleanup, useContext, } from "solid-js";
14
15
  import { PageContext } from "./page-visibility.js";
16
+ export { href, rawHref } from "@nifrajs/web/client";
15
17
  export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js";
16
18
  // Frozen empty search + a stable accessor for a `useSearch` used outside a nifra route tree.
17
19
  const EMPTY_SEARCH = Object.freeze({});
@@ -67,17 +69,11 @@ export function useMatches() {
67
69
  export function useSearch() {
68
70
  return (useContext(SearchContext) ?? EMPTY_SEARCH_ACCESSOR);
69
71
  }
70
- /** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object).
72
+ /** Get the {@link NavigateFunction} (an `Href` from `href()` or `rawHref()`, or a history delta).
71
73
  * Resolves the browser navigate at call time, so it works as soon as `installHistory` has run and no-ops
72
74
  * before then / on the server. */
73
75
  export function useNavigate() {
74
- return ((to, options) => {
75
- const navigate = getBrowserNavigate();
76
- if (navigate === undefined)
77
- return;
78
- const resolved = resolveNavigate(to, options);
79
- navigate(resolved.to, resolved.options);
80
- });
76
+ return navigateNow;
81
77
  }
82
78
  /**
83
79
  * Guard navigation away from a page with unsaved work, confirming with your OWN async UI. Mirrors
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAYA,mFAAmF;AACnF,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACnE,6EAA6E;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AACpE;;;;;;GAMG;AACH,OAAO,EAGL,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAAU,GACX,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAGhF,6FAA6F;AAC7F,MAAM,YAAY,GAAsC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AACzE,MAAM,qBAAqB,GAAsC,GAAG,EAAE,CAAC,YAAY,CAAA;AAEnF;;;;;;2BAM2B;AAC3B,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAA;AACxE,MAAM,iBAAiB,GAAG,UAEzB,CAAA;AACD,MAAM,CAAC,MAAM,aAAa,GACxB,iBAAiB,CAAC,mBAAmB,CAAC,IAAI,aAAa,EAAqC,CAAA;AAC9F,iBAAiB,CAAC,mBAAmB,CAAC,GAAG,aAAa,CAAA;AAEtD;sGACsG;AACtG,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;AACtE,MAAM,gBAAgB,GAAG,UAAyE,CAAA;AAClG,MAAM,CAAC,MAAM,kBAAkB,GAC7B,gBAAgB,CAAC,kBAAkB,CAAC,IAAI,aAAa,EAAe,CAAA;AACtE,gBAAgB,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAAA;AAEzD,MAAM,UAAU,GAAuB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAA;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,EAAE,CAAC,UAAU,CAAA;IAChD,OAAO,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;AAC9C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS;IAGvB,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,qBAAqB,CAEzD,CAAA;AACH,CAAC;AAED;;kCAEkC;AAClC,MAAM,UAAU,WAAW;IACzB,OAAO,WAAW,CAAA;AACpB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,WAAsC;IAC/D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,YAAY,CAAU,YAAY,CAAC,CAAA;IACjE,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACpC,SAAS,CACP,eAAe,CACb,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI;QACvB,CAAC,OAAO,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,EACvE,UAAU,CACX,CACF,CAAA;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/web-solid",
3
- "version": "5.8.0",
3
+ "version": "5.10.0",
4
4
  "description": "Solid render adapter for @nifrajs/web - SSR + hydration, with the Solid Babel build plugin (@nifrajs/web-solid/plugin).",
5
5
  "keywords": [
6
6
  "bun",
@@ -132,16 +132,16 @@
132
132
  "dependencies": {
133
133
  "@babel/core": "^7.26.0",
134
134
  "@babel/preset-typescript": "^7.26.0",
135
- "@nifrajs/core": "5.8.0",
136
- "@nifrajs/web": "5.8.0",
135
+ "@nifrajs/core": "5.10.0",
136
+ "@nifrajs/web": "5.10.0",
137
137
  "babel-preset-solid": "^1.9.3",
138
138
  "solid-refresh": "^0.7.8"
139
139
  },
140
140
  "peerDependencies": {
141
141
  "@mdx-js/mdx": "^3",
142
142
  "solid-js": "^1.9.0",
143
- "@nifrajs/i18n": "^5.8.0",
144
- "@nifrajs/image": "^5.8.0"
143
+ "@nifrajs/i18n": "^5.10.0",
144
+ "@nifrajs/image": "^5.10.0"
145
145
  },
146
146
  "peerDependenciesMeta": {
147
147
  "@mdx-js/mdx": {
package/src/compose.ts CHANGED
@@ -29,7 +29,9 @@ export function compose(chain: readonly unknown[], props: RenderProps): () => JS
29
29
  get data() {
30
30
  return props.layoutData?.[i] ?? null
31
31
  },
32
- ...(props.boundaries !== undefined ? { boundaries: props.boundaries } : {}),
32
+ get boundaries() {
33
+ return props.boundaries
34
+ },
33
35
  children: child(),
34
36
  })
35
37
  }
package/src/fn.ts CHANGED
@@ -8,8 +8,9 @@
8
8
  * "is it pending" has one answer rather than five that drift. This file contributes only a signal fed by the store.
9
9
  */
10
10
 
11
- import type { ClientServerFn, ServerFnReference } from "@nifrajs/web/fn"
11
+ import type { ServerFnReference } from "@nifrajs/web/fn"
12
12
  import { createServerFnStore, type ServerFnState } from "@nifrajs/web/fn-state"
13
+ import { clientServerFn } from "@nifrajs/web/internal/client-server-fn"
13
14
  import { type Accessor, createSignal, onCleanup } from "solid-js"
14
15
 
15
16
  /** A server function's state accessor plus the call itself. */
@@ -31,7 +32,7 @@ export interface ServerFnHandle<Input, Output> {
31
32
  export function useServerFn<Input, Output>(
32
33
  fn: ServerFnReference<Input, Output>,
33
34
  ): ServerFnHandle<Input, Output> {
34
- const store = createServerFnStore(fn as ClientServerFn<Input, Output>)
35
+ const store = createServerFnStore(clientServerFn(fn))
35
36
  // Seeded from the store, not from the idle constant. They are the same value here - the store is
36
37
  // created one line up and cannot have moved - so this is correctness that does not depend on that
37
38
  // staying true. Svelte had exactly this line reading the constant, and when its subscription turned
package/src/image.ts CHANGED
@@ -6,7 +6,14 @@
6
6
  * reactively (`splitProps`/`mergeProps`). Resizing is delegated to the `loader`; nifra bundles no codec.
7
7
  * No JSX (`createComponent`/`Dynamic`), so the package builds with plain `tsc`.
8
8
  */
9
- import { type ImageLoader, type ImageProps, resolveImage, toHtmlAttrs } from "@nifrajs/image"
9
+ import {
10
+ type ImageInputProps,
11
+ type ImageLoader,
12
+ type ImageProps,
13
+ resolveImage,
14
+ type StaticImageProps,
15
+ toHtmlAttrs,
16
+ } from "@nifrajs/image"
10
17
  import { createComponent, type JSX, mergeProps, splitProps } from "solid-js"
11
18
  import { Dynamic } from "solid-js/web"
12
19
 
@@ -29,11 +36,14 @@ export interface ImageComponentProps
29
36
  readonly loader?: ImageLoader
30
37
  }
31
38
 
39
+ export type StaticImageComponentProps = StaticImageProps &
40
+ Omit<ImageComponentProps, "src" | "width" | "height">
41
+
32
42
  /**
33
- * Render a responsive, CLS-safe `<img>`. `width`+`height` are required (reserve layout space);
43
+ * Render a responsive, CLS-safe `<img>`. String URLs require dimensions; asset imports infer them;
34
44
  * `priority` marks the LCP image (`eager` + `fetchpriority="high"`). Extra DOM props pass through.
35
45
  */
36
- export function Image(props: ImageComponentProps): JSX.Element {
46
+ export function Image(props: ImageComponentProps | StaticImageComponentProps): JSX.Element {
37
47
  // Separate the nifra-only props (not valid DOM attributes) from the DOM passthrough (`rest`), keeping
38
48
  // both reactive. `rest` carries class/style/id/handlers; the computed attrs are layered over it.
39
49
  const [, rest] = splitProps(props, [
@@ -51,10 +61,7 @@ export function Image(props: ImageComponentProps): JSX.Element {
51
61
  const attrs = (): ReturnType<typeof toHtmlAttrs> => {
52
62
  // Build ImageProps via conditional spreads (exactOptionalPropertyTypes: an unset optional is
53
63
  // absent, not `undefined`). resolveImage validates width/height > 0 (CLS contract).
54
- const input: ImageProps = {
55
- src: props.src,
56
- width: props.width,
57
- height: props.height,
64
+ const common = {
58
65
  alt: props.alt,
59
66
  ...(props.sizes !== undefined ? { sizes: props.sizes } : {}),
60
67
  ...(props.widths !== undefined ? { widths: props.widths } : {}),
@@ -62,6 +69,20 @@ export function Image(props: ImageComponentProps): JSX.Element {
62
69
  ...(props.loading !== undefined ? { loading: props.loading } : {}),
63
70
  ...(props.priority !== undefined ? { priority: props.priority } : {}),
64
71
  }
72
+ const input: ImageInputProps =
73
+ typeof props.src === "string"
74
+ ? {
75
+ ...common,
76
+ src: props.src,
77
+ width: props.width ?? Number.NaN,
78
+ height: props.height ?? Number.NaN,
79
+ }
80
+ : {
81
+ ...common,
82
+ src: props.src,
83
+ ...(props.width !== undefined ? { width: props.width } : {}),
84
+ ...(props.height !== undefined ? { height: props.height } : {}),
85
+ }
65
86
  return toHtmlAttrs(resolveImage(input, props.loader))
66
87
  }
67
88
  return createComponent(Dynamic, mergeProps(rest, attrs, { component: "img" }))
package/src/keep-alive.ts CHANGED
@@ -9,13 +9,8 @@
9
9
  * in place, synchronously, so the DOM half runs right after each publish.
10
10
  */
11
11
  import type { MountRouterOptions, RouterState } from "@nifrajs/web"
12
- import {
13
- createSlotParking,
14
- type KeepAliveRouter,
15
- type KeepAliveTable,
16
- type SlotNode,
17
- slotTree,
18
- } from "@nifrajs/web/keep-alive"
12
+ import { keptMount } from "@nifrajs/web/internal/keep-alive-mount"
13
+ import type { KeepAliveRouter, KeepAliveTable, SlotNode } from "@nifrajs/web/keep-alive"
19
14
  import { createMemo, createSignal } from "solid-js"
20
15
  import { hydrate as solidHydrate } from "solid-js/web"
21
16
  import { setMountedRouter } from "./fetcher.ts"
@@ -36,38 +31,30 @@ export interface KeepAliveMountOptions extends MountRouterOptions {
36
31
  * wrappers the server rendered), so hydration claims the document as it is.
37
32
  */
38
33
  export function mountRouter(options: KeepAliveMountOptions): void {
39
- const { router, routes, searchSchemas, container, keepAlive: table } = options
34
+ const { router, searchSchemas, container } = options
40
35
  const matchChains = options.matchChains ?? {}
41
36
  setMountedRouter(router)
42
- const parking = createSlotParking()
43
- let committed: readonly SlotNode[] | undefined
44
- router.beforePublish((next) => parking.before(slotTree(next, matchChains, table, committed)))
37
+ const kept = keptMount(options)
45
38
 
46
39
  let current: readonly SlotNode[] = []
47
40
  let publish: ((state: RouterState) => void) | undefined
48
- // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the generated entry passes the element the server rendered the app into
49
41
  solidHydrate(() => {
50
42
  const [state, setState] = createSignal(router.snapshot())
51
43
  publish = (next) => setState(() => next)
52
44
  const tree = createMemo(() => {
53
- current = slotTree(state(), matchChains, table, current)
45
+ current = kept.tree(state(), current)
54
46
  return current
55
47
  })
56
48
  const mount: KeepAliveViewMount = {
49
+ ...kept,
57
50
  propsOf: (of) => routeProps(of, searchSchemas, matchChains, (read) => createMemo(read)),
58
- chainOf: (of) => routes[of.routeId] ?? [],
59
- entry: parking.entry,
60
- page: parking.page,
61
51
  }
62
52
  return KeepAliveView({ tree, shown: mount.propsOf(state), mount })
63
53
  // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the generated entry passes the element the server rendered the app into
64
54
  }, container as Element)
65
55
  // Solid has updated the document by the time a publish returns: park what it hid, restore what it
66
56
  // showed again.
67
- const after = (): void => {
68
- committed = current
69
- parking.after(current)
70
- }
57
+ const after = (): void => kept.after(current)
71
58
  after()
72
59
  router.subscribe(() => {
73
60
  publish?.(router.snapshot())
package/src/ppr.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { initialHtml } from "@nifrajs/web/internal/hole-runtime"
1
+ import { type HoleState, initialHtml } from "@nifrajs/web/internal/hole-runtime"
2
2
  import { createComponent, createMemo, type JSX, untrack } from "solid-js"
3
3
  import { Dynamic, isServer } from "solid-js/web"
4
4
  import { Await } from "./await.ts"
@@ -15,15 +15,6 @@ export interface HoleDefinition<Data> {
15
15
  readonly interactive?: boolean
16
16
  }
17
17
 
18
- /** The slice of a boundary state a hole reads: `ppr` marks a hole the shared shell left open, and
19
- * `fill` that host's refill when the page reloads. */
20
- interface HoleState {
21
- readonly status: "unresolved" | "pending" | "ready" | "error"
22
- readonly data?: unknown
23
- readonly ppr?: true
24
- readonly fill?: unknown
25
- }
26
-
27
18
  export interface HoleProps<Data> {
28
19
  readonly of: HoleDefinition<Data>
29
20
  readonly state: HoleState | undefined
@@ -47,7 +38,7 @@ function openHost<Data>(hole: HoleDefinition<Data>, fill: () => unknown): JSX.El
47
38
  }
48
39
 
49
40
  /**
50
- * Experimental: one `dynamic` or `shared` boundary of a partially prerendered page.
41
+ * One `dynamic` or `shared` boundary of a partially prerendered page.
51
42
  *
52
43
  * In a shared shell the hole is an empty host Solid never fills: the PPR runtime puts the server's
53
44
  * HTML into it, and an interactive hole then hydrates as its own root from the frame's props. When the
package/src/query.ts CHANGED
@@ -1,4 +1,16 @@
1
- import type { QueryClient, QueryOptions, QueryState } from "@nifrajs/web"
1
+ import type {
2
+ DehydratedState,
3
+ InfiniteData,
4
+ InfiniteQueryOptions,
5
+ MutationCallbacks,
6
+ MutationContext,
7
+ MutationHandle,
8
+ MutationState,
9
+ QueryClient,
10
+ QueryOptions,
11
+ QueryState,
12
+ } from "@nifrajs/web"
13
+ import { createMutation as createMutationHandle } from "@nifrajs/web/client"
2
14
  import {
3
15
  createNoClientRefetch,
4
16
  fetchOnMount,
@@ -12,15 +24,46 @@ import {
12
24
  * exposes `invalidateQueries` for after a mutation. Imports only `solid-js` + `@nifrajs/web` (never
13
25
  * `solid-js/web`), so route components use it on the server *and* client. No JSX.
14
26
  *
15
- * The query client is created lazily **client-side only** (`typeof window`) - on the server the
16
- * accessor returns the idle/pending state (queries are client-first; loaders are the SSR data source).
27
+ * Without a provider, the query client is created lazily **client-side only** (`typeof window`) - on
28
+ * the server the accessor returns idle state and route loaders remain the data source. A
29
+ * `QueryClientProvider` can supply a request-scoped hydrated client.
17
30
  * The `key` is captured once (Solid setup runs once); a reactive key is a documented future enhancement.
18
31
  */
19
- import { type Accessor, createSignal, onCleanup, onMount } from "solid-js"
32
+ import {
33
+ type Accessor,
34
+ createComponent,
35
+ createContext,
36
+ createSignal,
37
+ type JSX,
38
+ onCleanup,
39
+ onMount,
40
+ useContext,
41
+ } from "solid-js"
42
+
43
+ const QueryClientContext = createContext<QueryClient>()
44
+
45
+ function queryClientForCurrentTree(): QueryClient | undefined {
46
+ return useContext(QueryClientContext) ?? getQueryClientSingleton()
47
+ }
48
+
49
+ /** Provide a request-scoped client and optionally seed it before descendants run. */
50
+ export function QueryClientProvider(props: {
51
+ readonly client: QueryClient
52
+ readonly state?: DehydratedState
53
+ readonly children?: JSX.Element
54
+ }): JSX.Element {
55
+ if (props.state !== undefined) props.client.hydrate(props.state)
56
+ return createComponent(QueryClientContext.Provider, {
57
+ value: props.client,
58
+ get children() {
59
+ return props.children
60
+ },
61
+ })
62
+ }
20
63
 
21
64
  /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
22
65
  export function useQueryClient(): Pick<QueryClient, "invalidateQueries"> {
23
- return getQueryClientSingleton() ?? { invalidateQueries: noop }
66
+ return queryClientForCurrentTree() ?? { invalidateQueries: noop }
24
67
  }
25
68
 
26
69
  /** A query's reactive state accessor plus `refetch`. */
@@ -42,7 +85,7 @@ export function createQuery<T>(
42
85
  fn: () => Promise<T>,
43
86
  options?: QueryOptions,
44
87
  ): CreateQueryResult<T> {
45
- const handle = getQueryClientSingleton()?.query<T>(key, fn, options)
88
+ const handle = queryClientForCurrentTree()?.query<T>(key, fn, options)
46
89
  const [state, setState] = createSignal<QueryState<T>>(
47
90
  handle ? handle.snapshot() : IDLE_QUERY_STATE,
48
91
  )
@@ -56,3 +99,99 @@ export function createQuery<T>(
56
99
  refetch: createNoClientRefetch<T>("[nifra/web-solid]", "createQuery.refetch"),
57
100
  }
58
101
  }
102
+
103
+ /** A mutation result expressed as Solid accessors and imperative controls. */
104
+ export interface CreateMutationResult<TData, TVariables> {
105
+ readonly state: Accessor<MutationState<TData, TVariables>>
106
+ readonly isIdle: Accessor<boolean>
107
+ readonly isPending: Accessor<boolean>
108
+ readonly isError: Accessor<boolean>
109
+ readonly isSuccess: Accessor<boolean>
110
+ readonly mutate: (variables: TVariables) => void
111
+ readonly mutateAsync: (variables: TVariables) => Promise<TData>
112
+ readonly reset: () => void
113
+ readonly cancel: () => void
114
+ }
115
+
116
+ /** Bind a mutation to a Solid owner. Identity changes discard late results and callbacks. */
117
+ export function createMutation<TData, TVariables = void>(
118
+ fn: (variables: TVariables, context: MutationContext) => Promise<TData>,
119
+ callbacks: MutationCallbacks<TData, TVariables> = {},
120
+ ): CreateMutationResult<TData, TVariables> {
121
+ const handle: MutationHandle<TData, TVariables> = createMutationHandle(fn, callbacks)
122
+ const [state, setState] = createSignal(handle.snapshot())
123
+ onCleanup(handle.subscribe(() => setState(() => handle.snapshot())))
124
+ return {
125
+ state,
126
+ isIdle: () => state().status === "idle",
127
+ isPending: () => state().status === "pending",
128
+ isError: () => state().status === "error",
129
+ isSuccess: () => state().status === "success",
130
+ mutate: (variables) => {
131
+ handle.mutate(variables).catch(noop)
132
+ },
133
+ mutateAsync: handle.mutate,
134
+ reset: handle.reset,
135
+ cancel: handle.cancel,
136
+ }
137
+ }
138
+
139
+ /** Options for a Solid infinite query. */
140
+ export interface CreateInfiniteQueryOptions<T, P> extends InfiniteQueryOptions<T, P> {
141
+ readonly enabled?: boolean
142
+ }
143
+
144
+ /** Infinite-query state and page controls expressed as Solid accessors. */
145
+ export interface CreateInfiniteQueryResult<T, P> {
146
+ readonly state: Accessor<QueryState<InfiniteData<T, P>>>
147
+ readonly isPending: Accessor<boolean>
148
+ readonly isError: Accessor<boolean>
149
+ readonly isSuccess: Accessor<boolean>
150
+ readonly hasNextPage: Accessor<boolean>
151
+ readonly hasPreviousPage: Accessor<boolean>
152
+ readonly fetchNextPage: () => Promise<InfiniteData<T, P>>
153
+ readonly fetchPreviousPage: () => Promise<InfiniteData<T, P>>
154
+ readonly refetch: () => Promise<InfiniteData<T, P>>
155
+ }
156
+
157
+ /** Bind a paged query to a Solid owner and fetch its first page on mount. */
158
+ export function createInfiniteQuery<T, P>(
159
+ key: unknown,
160
+ fn: (pageParam: P) => Promise<T>,
161
+ options: CreateInfiniteQueryOptions<T, P>,
162
+ ): CreateInfiniteQueryResult<T, P> {
163
+ const client = queryClientForCurrentTree()
164
+ const enabled = options.enabled !== false
165
+ const handle = client?.infiniteQuery<T, P>(key, fn, options)
166
+ const [state, setState] = createSignal<QueryState<InfiniteData<T, P>>>(
167
+ handle
168
+ ? handle.snapshot()
169
+ : // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: An idle query has no generic page data; the provider supplies the typed state before use.
170
+ (IDLE_QUERY_STATE as QueryState<InfiniteData<T, P>>),
171
+ )
172
+ if (handle) {
173
+ onCleanup(handle.subscribe(() => setState(() => handle.snapshot())))
174
+ onMount(fetchOnMount(enabled ? handle : undefined))
175
+ }
176
+ const noClientAction = createNoClientRefetch<InfiniteData<T, P>>(
177
+ "[nifra/web-solid]",
178
+ "infinite query action",
179
+ )
180
+ return {
181
+ state,
182
+ isPending: () => state().status === "pending",
183
+ isError: () => state().status === "error",
184
+ isSuccess: () => state().status === "success",
185
+ hasNextPage: () => {
186
+ state()
187
+ return handle?.hasNextPage() ?? false
188
+ },
189
+ hasPreviousPage: () => {
190
+ state()
191
+ return handle?.hasPreviousPage() ?? false
192
+ },
193
+ fetchNextPage: handle?.fetchNextPage ?? noClientAction,
194
+ fetchPreviousPage: handle?.fetchPreviousPage ?? noClientAction,
195
+ refetch: handle?.refetch ?? noClientAction,
196
+ }
197
+ }
package/src/router.ts CHANGED
@@ -1,27 +1,21 @@
1
1
  import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server"
2
2
  // `import type` + a local re-export, NOT `export type { … } from "@nifrajs/web"`: that form leaves a
3
3
  // bare `import "@nifrajs/web"` in the output, which pulls the server graph into the browser under
4
- // Vite's dev server. Sourced from the ROOT so the generated `RouteSearch` augmentation applies.
4
+ // Vite's dev server. Sourced from the ROOT so the generated `RouteTable` augmentation applies.
5
5
  import type {
6
6
  Blocker,
7
7
  BlockerFunction,
8
8
  BlockerState,
9
9
  NavigateFunction,
10
- NavigateOptions,
11
- NavigateTargetInput,
12
10
  RenderProps,
13
11
  UIMatch,
14
12
  } from "@nifrajs/web"
15
13
  // `/client`, not the root - these are DOM values, and the root's graph carries the
16
14
  // server, which Vite's dev server evaluates rather than tree-shakes.
17
- import {
18
- getBrowserNavigate,
19
- IDLE_BLOCKER,
20
- registerBlocker,
21
- resolveNavigate,
22
- } from "@nifrajs/web/client"
15
+ import { IDLE_BLOCKER, registerBlocker } from "@nifrajs/web/client"
23
16
  // Its own subpath, so an app that never calls `useMatches` never bundles it.
24
17
  import { chainMatches } from "@nifrajs/web/internal/matches-runtime"
18
+ import { navigateNow } from "@nifrajs/web/internal/navigate-runtime"
25
19
  /**
26
20
  * `@nifrajs/web-solid/router` - Solid routing bindings over the agnostic `@nifrajs/web` history layer:
27
21
  * `useNavigate` (programmatic navigation), `useBlocker` (the unsaved-changes guard), and `useSearch`
@@ -40,6 +34,7 @@ import {
40
34
  } from "solid-js"
41
35
  import { PageContext } from "./page-visibility.ts"
42
36
 
37
+ export { href, rawHref } from "@nifrajs/web/client"
43
38
  export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.ts"
44
39
  export type { Blocker, BlockerFunction, BlockerState, NavigateFunction, UIMatch }
45
40
 
@@ -110,16 +105,11 @@ export function useSearch<Schema extends StandardSchemaV1 | undefined = undefine
110
105
  >
111
106
  }
112
107
 
113
- /** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object).
108
+ /** Get the {@link NavigateFunction} (an `Href` from `href()` or `rawHref()`, or a history delta).
114
109
  * Resolves the browser navigate at call time, so it works as soon as `installHistory` has run and no-ops
115
110
  * before then / on the server. */
116
111
  export function useNavigate(): NavigateFunction {
117
- return ((to: string | number | NavigateTargetInput, options?: NavigateOptions) => {
118
- const navigate = getBrowserNavigate()
119
- if (navigate === undefined) return
120
- const resolved = resolveNavigate(to, options)
121
- navigate(resolved.to, resolved.options)
122
- }) as NavigateFunction
112
+ return navigateNow
123
113
  }
124
114
 
125
115
  /**