@nifrajs/web-svelte 2.1.0 → 2.3.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.
Files changed (49) hide show
  1. package/README.md +3 -3
  2. package/dist/Await.svelte +2 -2
  3. package/dist/Chain.svelte +16 -6
  4. package/dist/Content.svelte +2 -2
  5. package/dist/I18nProvider.svelte +1 -1
  6. package/dist/Image.svelte +3 -3
  7. package/dist/Image.svelte.d.ts +1 -1
  8. package/dist/Router.svelte +15 -8
  9. package/dist/client.d.ts +1 -1
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +9 -6
  12. package/dist/client.js.map +1 -1
  13. package/dist/error.d.ts +1 -1
  14. package/dist/fetcher.d.ts +7 -7
  15. package/dist/fetcher.js +7 -7
  16. package/dist/fn.d.ts +29 -0
  17. package/dist/fn.d.ts.map +1 -0
  18. package/dist/fn.js +31 -0
  19. package/dist/fn.js.map +1 -0
  20. package/dist/i18n.d.ts +1 -1
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +7 -7
  23. package/dist/index.js.map +1 -1
  24. package/dist/mdx.js +5 -5
  25. package/dist/plugin.js +4 -4
  26. package/dist/query.d.ts +3 -3
  27. package/dist/query.js +3 -3
  28. package/dist/router.d.ts +45 -0
  29. package/dist/router.d.ts.map +1 -0
  30. package/dist/router.js +62 -0
  31. package/dist/router.js.map +1 -0
  32. package/package.json +16 -5
  33. package/src/Await.svelte +2 -2
  34. package/src/Chain.svelte +16 -6
  35. package/src/Content.svelte +2 -2
  36. package/src/I18nProvider.svelte +1 -1
  37. package/src/Image.svelte +3 -3
  38. package/src/Image.svelte.d.ts +1 -1
  39. package/src/Router.svelte +15 -8
  40. package/src/client.ts +9 -6
  41. package/src/error.ts +1 -1
  42. package/src/fetcher.ts +7 -7
  43. package/src/fn.ts +45 -0
  44. package/src/i18n.ts +1 -1
  45. package/src/index.ts +9 -9
  46. package/src/mdx.ts +5 -5
  47. package/src/plugin.ts +4 -4
  48. package/src/query.ts +3 -3
  49. package/src/router.ts +86 -0
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @nifrajs/web-svelte
2
2
 
3
- Svelte 5 render adapter for @nifrajs/web — SSR + hydration + the .svelte compiler Bun-plugin.
3
+ Svelte 5 render adapter for @nifrajs/web - SSR + hydration + the .svelte compiler Bun-plugin.
4
4
 
5
- Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework — one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
5
+ Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework - one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
6
6
 
7
7
  ## Install
8
8
 
@@ -19,7 +19,7 @@ MIT
19
19
 
20
20
  ## For AI agents
21
21
 
22
- Start with [`LLM.md`](./LLM.md) — this package's contract card (the exports you call + its footguns),
22
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
23
23
  one cheap read instead of the whole corpus. For the wider framework: the repo's
24
24
  [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
25
25
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
package/dist/Await.svelte CHANGED
@@ -1,9 +1,9 @@
1
1
  <!--
2
- Await.svelte — the `<Await>` primitive for deferred loader/action data (`defer()`), built on Svelte's
2
+ Await.svelte - the `<Await>` primitive for deferred loader/action data (`defer()`), built on Svelte's
3
3
  native `{#await}` block + scoped-snippet props: `children(value)` (resolved), `pending`, `error(err)`.
4
4
 
5
5
  Svelte SSR renders the PENDING branch of `{#await}` (it does not block on the promise), so the server
6
- emits the fallback and the client resolves it (hydration / soft-nav) — matching nifra's non-blocking
6
+ emits the fallback and the client resolves it (hydration / soft-nav) - matching nifra's non-blocking
7
7
  `defer()` (like the Vue adapter; contrast React/Preact, which stream the resolved content into the SSR
8
8
  HTML). An already-resolved `resolve` (a plain value, or a client nav that awaited it) renders directly.
9
9
  Plain-JS script (no TS) so it needs no svelte TS preprocessor.
package/dist/Chain.svelte CHANGED
@@ -1,13 +1,23 @@
1
1
  <!--
2
- Chain.svelte — folds a nifra layout chain (outermost layout → page) into nested Svelte components:
2
+ Chain.svelte - folds a nifra layout chain (outermost layout → page) into nested Svelte components:
3
3
  the page (innermost, chain[last]) receives `props` (the loader data); each layout wraps the rest via
4
4
  its `children` snippet ({@render children()} in the layout). Recurses on itself. This is the Svelte
5
- analogue of the React/Vue `compose` — Svelte components are compiled (not callable), so the fold is a
5
+ analogue of the React/Vue `compose` - Svelte components are compiled (not callable), so the fold is a
6
6
  component, not a function returning a node.
7
+
8
+ `layoutData` is the per-layout loader data, aligned with the chain's LEADING layout prefix. It is
9
+ sliced in lockstep with `chain` as each layout is consumed, so a layout always reads its own entry.
10
+ The `_error` boundary marker deliberately does NOT consume from it: the marker sits after every
11
+ layout, so the array is already empty by then, and leaving it alone keeps that true if it ever moves.
7
12
  -->
8
13
  <script>
14
+ import { setContext } from "svelte"
9
15
  import Self from "./Chain.svelte"
10
- let { chain, props } = $props()
16
+ let { chain, props, layoutData } = $props()
17
+ // Provide the validated search as a getter (so `useSearch` reads the current value reactively) - the
18
+ // same value the server put in RenderProps.search, so a query-reading page hydrates with no drift. The
19
+ // key string is kept in sync with router.ts's `useSearch`.
20
+ setContext("@nifrajs/web-svelte:search", () => props.search ?? {})
11
21
  </script>
12
22
 
13
23
  {#if chain.length <= 1}
@@ -20,16 +30,16 @@
20
30
  `failed` snippet renders the route's _error component with the serialized error. -->
21
31
  {@const Fallback = chain[0].__nifraErrorBoundary}
22
32
  <svelte:boundary>
23
- <Self chain={chain.slice(1)} {props} />
33
+ <Self chain={chain.slice(1)} {props} {layoutData} />
24
34
  {#snippet failed(error)}
25
35
  <Fallback data={{ name: error?.name ?? "Error", message: String(error?.message ?? error) }} />
26
36
  {/snippet}
27
37
  </svelte:boundary>
28
38
  {:else}
29
39
  {@const Layout = chain[0]}
30
- <Layout>
40
+ <Layout data={layoutData?.[0] ?? null}>
31
41
  {#snippet children()}
32
- <Self chain={chain.slice(1)} {props} />
42
+ <Self chain={chain.slice(1)} {props} layoutData={layoutData?.slice(1)} />
33
43
  {/snippet}
34
44
  </Layout>
35
45
  {/if}
@@ -1,10 +1,10 @@
1
1
  <!--
2
- Content.svelte — render trusted pre-rendered HTML (e.g. a @nifrajs/content collection entry's `.html`)
2
+ Content.svelte - render trusted pre-rendered HTML (e.g. a @nifrajs/content collection entry's `.html`)
3
3
  into the DOM via Svelte's {@html}. Pairs with content collections: <Content html={entry.html} />.
4
4
  `as` picks the wrapper element; extra attributes in `...rest` (class/style/id/data-*) pass through.
5
5
 
6
6
  Security: {@html} injects raw HTML, so `html` MUST be trusted (your own Markdown/content, rendered at
7
- build/server time) — never pass user-supplied HTML without sanitizing it first. Plain-JS script.
7
+ build/server time) - never pass user-supplied HTML without sanitizing it first. Plain-JS script.
8
8
  -->
9
9
  <script>
10
10
  let { html, as = "div", ...rest } = $props()
@@ -1,5 +1,5 @@
1
1
  <!--
2
- I18nProvider.svelte — provides a nifra i18n `Formatter` (built from `locale` + `messages`) to the
2
+ I18nProvider.svelte - provides a nifra i18n `Formatter` (built from `locale` + `messages`) to the
3
3
  subtree via Svelte context; consumers read it with `useT()` from `@nifrajs/web-svelte/i18n`. The
4
4
  formatter is `$derived`, so switching locale rebuilds it. `locale` + `messages` are serializable, so
5
5
  SSR renders the negotiated catalog and the client rebuilds the same formatter on hydrate (no
package/dist/Image.svelte CHANGED
@@ -1,9 +1,9 @@
1
1
  <!--
2
- Image.svelte — a CLS-safe responsive <Image> for nifra + Svelte. A thin wrapper over `resolveImage` +
2
+ Image.svelte - a CLS-safe responsive <Image> for nifra + Svelte. A thin wrapper over `resolveImage` +
3
3
  `toHtmlAttrs` from @nifrajs/image: it computes the responsive <img> attributes (src/srcset/sizes/width/
4
- height/loading/decoding/fetchpriority — lowercase HTML names) and renders an <img>. Any extra
4
+ height/loading/decoding/fetchpriority - lowercase HTML names) and renders an <img>. Any extra
5
5
  attributes (class/style/id/data-*/handlers in `...rest`) pass through. `width`+`height` are required
6
- (reserve layout space — no CLS); `priority` marks the LCP image. Resizing is delegated to the
6
+ (reserve layout space - no CLS); `priority` marks the LCP image. Resizing is delegated to the
7
7
  `loader` (an image CDN); nifra bundles no codec. Plain-JS script (no TS preprocessor needed).
8
8
  -->
9
9
  <script>
@@ -21,7 +21,7 @@ type ComputedAttrs =
21
21
  export interface ImageComponentProps
22
22
  extends ImageProps,
23
23
  Omit<HTMLImgAttributes, ComputedAttrs | keyof ImageProps> {
24
- /** CDN URL builder. Defaults to the identity loader (no transform — still CLS-safe + lazy). */
24
+ /** CDN URL builder. Defaults to the identity loader (no transform - still CLS-safe + lazy). */
25
25
  loader?: ImageLoader
26
26
  }
27
27
 
@@ -1,26 +1,33 @@
1
1
  <!--
2
- Router.svelte — the reactive root for client navigation. Holds the agnostic store's snapshot in
2
+ Router.svelte - the reactive root for client navigation. Holds the agnostic store's snapshot in
3
3
  `$state`, subscribes via `$effect` (cleaned up on unmount), and renders the matched layout chain
4
4
  through `Chain.svelte`. `$derived` recomputes the chain + props on every store change, so client
5
5
  navigations swap routes without a full reload. The initial snapshot matches the SSR markup (the
6
6
  server rendered `Chain` for the same matched route), so `hydrate` reconciles cleanly.
7
7
 
8
- Runes (`$state`/`$effect`/`$derived`) only work in compiled `.svelte` files — which is why this
8
+ Runes (`$state`/`$effect`/`$derived`) only work in compiled `.svelte` files - which is why this
9
9
  reactive root is a component, not a function in `client.ts`.
10
10
  -->
11
11
  <script>
12
+ import { searchOfChain } from "@nifrajs/web"
12
13
  import Chain from "./Chain.svelte"
13
- let { router, routes } = $props()
14
+ let { router, routes, searchSchemas } = $props()
14
15
 
15
16
  let snapshot = $state(router.snapshot())
16
17
  $effect(() => router.subscribe(() => { snapshot = router.snapshot() }))
17
18
 
18
19
  let chain = $derived(routes[snapshot.routeId] ?? [])
19
- let props = $derived({
20
- data: snapshot.data,
21
- actionData: snapshot.actionData,
22
- pending: snapshot.pending,
23
- ...(snapshot.submission ? { submission: snapshot.submission } : {}),
20
+ let props = $derived.by(() => {
21
+ // This route's typed search from the URL + schema chain (the SAME searchOfChain the server ran), so
22
+ // useSearch stays reactive across navigation and matches the SSR value on hydration.
23
+ const q = snapshot.path.indexOf("?")
24
+ return {
25
+ data: snapshot.data,
26
+ actionData: snapshot.actionData,
27
+ pending: snapshot.pending,
28
+ search: searchOfChain(searchSchemas?.[snapshot.routeId] ?? [], q === -1 ? "" : snapshot.path.slice(q)),
29
+ ...(snapshot.submission ? { submission: snapshot.submission } : {}),
30
+ }
24
31
  })
25
32
  </script>
26
33
 
package/dist/client.d.ts CHANGED
@@ -4,7 +4,7 @@ export { errorBoundary } from "./error.js";
4
4
  export declare function hydrate(chain: readonly unknown[], props: RenderProps, container: unknown): void;
5
5
  /**
6
6
  * Hydrate a stateful Svelte Router. The `Router` component holds the store snapshot in `$state` and
7
- * re-renders the matched layout chain on each store change — so client navigations swap routes without
7
+ * re-renders the matched layout chain on each store change - so client navigations swap routes without
8
8
  * a full reload. Its initial render matches the SSR markup (the server rendered `Chain` for the same
9
9
  * matched route), so hydration reconciles cleanly.
10
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAenE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAE/F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAI7D"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAenE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAK/F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAI7D"}
package/dist/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @nifrajs/web-svelte/client — Svelte 5 client runtime. `hydrate` hydrates a single SSR'd route via the
2
+ * @nifrajs/web-svelte/client - Svelte 5 client runtime. `hydrate` hydrates a single SSR'd route via the
3
3
  * recursive `Chain`; `mountRouter` hydrates the reactive `Router` component, which subscribes to the
4
4
  * agnostic store and re-renders the matched chain on every client navigation (no full reload). Both
5
5
  * use Svelte's `hydrate` (reconcile against the SSR markup). Kept in its own entry so server code
@@ -9,22 +9,25 @@ import { hydrate as svelteHydrate } from "svelte";
9
9
  import Chain from "./Chain.svelte";
10
10
  import { setMountedRouter } from "./fetcher.js";
11
11
  import Router from "./Router.svelte";
12
- // The `_error` boundary marker — Chain.svelte renders it as a <svelte:boundary>. Re-exported here so
12
+ // The `_error` boundary marker - Chain.svelte renders it as a <svelte:boundary>. Re-exported here so
13
13
  // nifra's client codegen resolves it from `@nifrajs/web-svelte/client` alongside `mountRouter`.
14
14
  export { errorBoundary } from "./error.js";
15
15
  /** Hydrate a server-rendered Svelte layout `chain` (with the loader `props`) inside `container`. */
16
16
  export function hydrate(chain, props, container) {
17
- svelteHydrate(Chain, { target: container, props: { chain, props } });
17
+ svelteHydrate(Chain, {
18
+ target: container,
19
+ props: { chain, props, layoutData: props.layoutData },
20
+ });
18
21
  }
19
22
  /**
20
23
  * Hydrate a stateful Svelte Router. The `Router` component holds the store snapshot in `$state` and
21
- * re-renders the matched layout chain on each store change — so client navigations swap routes without
24
+ * re-renders the matched layout chain on each store change - so client navigations swap routes without
22
25
  * a full reload. Its initial render matches the SSR markup (the server rendered `Chain` for the same
23
26
  * matched route), so hydration reconciles cleanly.
24
27
  */
25
28
  export function mountRouter(options) {
26
- const { router, routes, container } = options;
29
+ const { router, routes, searchSchemas, container } = options;
27
30
  setMountedRouter(router); // expose it to useFetcher/useFetchers (same page, client-only)
28
- svelteHydrate(Router, { target: container, props: { router, routes } });
31
+ svelteHydrate(Router, { target: container, props: { router, routes, searchSchemas } });
29
32
  }
30
33
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAEpC,qGAAqG;AACrG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAoB,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;AACjF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC7C,gBAAgB,CAAC,MAAM,CAAC,CAAA,CAAC,+DAA+D;IACxF,aAAa,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAoB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;AACpF,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,MAAM,MAAM,iBAAiB,CAAA;AAEpC,qGAAqG;AACrG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,oGAAoG;AACpG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,aAAa,CAAC,KAAK,EAAE;QACnB,MAAM,EAAE,SAAoB;QAC5B,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE;KACtD,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC5D,gBAAgB,CAAC,MAAM,CAAC,CAAA,CAAC,+DAA+D;IACxF,aAAa,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,SAAoB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,CAAC,CAAA;AACnG,CAAC"}
package/dist/error.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `errorBoundary` — the Svelte `_error.tsx` boundary chain element. Svelte components are compiled (not
2
+ * `errorBoundary` - the Svelte `_error.tsx` boundary chain element. Svelte components are compiled (not
3
3
  * JS closures), so unlike the other adapters this can't return a component bound to `fallback`. Instead
4
4
  * it returns a **marker** that `Chain.svelte` recognises and renders as a `<svelte:boundary>` (Svelte
5
5
  * 5.3+) with `fallback` as the `failed` snippet. Re-exported from `@nifrajs/web-svelte/client` so nifra's
package/dist/fetcher.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import type { ClientRouter, Fetcher, FetcherState } from "@nifrajs/web";
2
2
  /**
3
- * `@nifrajs/web-svelte/fetcher` — Svelte bindings for concurrent fetchers, as **Svelte stores** (plain
3
+ * `@nifrajs/web-svelte/fetcher` - Svelte bindings for concurrent fetchers, as **Svelte stores** (plain
4
4
  * `.ts`, no runes/compiler needed). `useFetcher(key)` returns a `Readable<FetcherState>` augmented with
5
- * `load`/`submit` — read it reactively in a component with `$fetcher` (auto-subscription), call
5
+ * `load`/`submit` - read it reactively in a component with `$fetcher` (auto-subscription), call
6
6
  * `fetcher.load(...)` / `fetcher.submit(...)` imperatively. `useFetchers()` returns the live collection
7
7
  * as a store. The store's start/stop notifier subscribes to the agnostic store lazily (on the first
8
- * `$`-subscription) and unsubscribes on the last — so lifecycle is tied to the component automatically.
8
+ * `$`-subscription) and unsubscribes on the last - so lifecycle is tied to the component automatically.
9
9
  *
10
- * The router that owns the fetchers is the one `mountRouter` hydrated — it registers itself here via
10
+ * The router that owns the fetchers is the one `mountRouter` hydrated - it registers itself here via
11
11
  * `setMountedRouter`. On the server (no mount) there is no router, so the stores hold an idle value
12
- * (fetchers are client-only); the first client render starts from the same idle snapshot — no mismatch.
12
+ * (fetchers are client-only); the first client render starts from the same idle snapshot - no mismatch.
13
13
  */
14
14
  import { type Readable } from "svelte/store";
15
- /** Register (or clear) the router that owns fetchers — called by `mountRouter`. Not for app use. */
15
+ /** Register (or clear) the router that owns fetchers - called by `mountRouter`. Not for app use. */
16
16
  export declare function setMountedRouter(router: ClientRouter | undefined): void;
17
17
  /** A fetcher store: a `Readable<FetcherState>` (read via `$`) plus imperative `load`/`submit`. */
18
18
  export type FetcherStore = Readable<FetcherState> & {
@@ -28,7 +28,7 @@ export type FetcherStore = Readable<FetcherState> & {
28
28
  */
29
29
  export declare function useFetcher(key: string): FetcherStore;
30
30
  /**
31
- * Subscribe to the whole live fetcher collection — for a global busy view (e.g. "3 saving…"). Read
31
+ * Subscribe to the whole live fetcher collection - for a global busy view (e.g. "3 saving…"). Read
32
32
  * each entry's `.snapshot()` for its state. The store updates whenever any fetcher transitions or a
33
33
  * new one is created.
34
34
  */
package/dist/fetcher.js CHANGED
@@ -1,20 +1,20 @@
1
1
  /**
2
- * `@nifrajs/web-svelte/fetcher` — Svelte bindings for concurrent fetchers, as **Svelte stores** (plain
2
+ * `@nifrajs/web-svelte/fetcher` - Svelte bindings for concurrent fetchers, as **Svelte stores** (plain
3
3
  * `.ts`, no runes/compiler needed). `useFetcher(key)` returns a `Readable<FetcherState>` augmented with
4
- * `load`/`submit` — read it reactively in a component with `$fetcher` (auto-subscription), call
4
+ * `load`/`submit` - read it reactively in a component with `$fetcher` (auto-subscription), call
5
5
  * `fetcher.load(...)` / `fetcher.submit(...)` imperatively. `useFetchers()` returns the live collection
6
6
  * as a store. The store's start/stop notifier subscribes to the agnostic store lazily (on the first
7
- * `$`-subscription) and unsubscribes on the last — so lifecycle is tied to the component automatically.
7
+ * `$`-subscription) and unsubscribes on the last - so lifecycle is tied to the component automatically.
8
8
  *
9
- * The router that owns the fetchers is the one `mountRouter` hydrated — it registers itself here via
9
+ * The router that owns the fetchers is the one `mountRouter` hydrated - it registers itself here via
10
10
  * `setMountedRouter`. On the server (no mount) there is no router, so the stores hold an idle value
11
- * (fetchers are client-only); the first client render starts from the same idle snapshot — no mismatch.
11
+ * (fetchers are client-only); the first client render starts from the same idle snapshot - no mismatch.
12
12
  */
13
13
  import { readable } from "svelte/store";
14
14
  // The active client router (set by `mountRouter`). Module-scoped: the browser mounts one app per
15
15
  // page, and fetchers never exist on the server. Shared with `client.ts`'s `mountRouter`.
16
16
  let mountedRouter;
17
- /** Register (or clear) the router that owns fetchers — called by `mountRouter`. Not for app use. */
17
+ /** Register (or clear) the router that owns fetchers - called by `mountRouter`. Not for app use. */
18
18
  export function setMountedRouter(router) {
19
19
  mountedRouter = router;
20
20
  }
@@ -39,7 +39,7 @@ export function useFetcher(key) {
39
39
  });
40
40
  }
41
41
  /**
42
- * Subscribe to the whole live fetcher collection — for a global busy view (e.g. "3 saving…"). Read
42
+ * Subscribe to the whole live fetcher collection - for a global busy view (e.g. "3 saving…"). Read
43
43
  * each entry's `.snapshot()` for its state. The store updates whenever any fetcher transitions or a
44
44
  * new one is created.
45
45
  */
package/dist/fn.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `@nifrajs/web-svelte/fn` - Svelte binding for a server function's pending/error state.
3
+ *
4
+ * Calling a server function needs no hook: the client stub is `(input) => Promise<Output>`. This adds
5
+ * the part a component usually wants around it - in flight, last result, last error - and nothing more.
6
+ *
7
+ * The state machine is `@nifrajs/web`'s `createServerFnStore`, shared with every other adapter, so
8
+ * "is it pending" has one answer rather than five that drift. This file contributes only a `readable` fed by the store.
9
+ */
10
+ import type { ServerFnReference } from "@nifrajs/web/fn";
11
+ import { type ServerFnState } from "@nifrajs/web/fn-state";
12
+ import { type Readable } from "svelte/store";
13
+ /** A server function's readable state plus the call itself. */
14
+ export interface ServerFnHandle<Input, Output> {
15
+ /** Reactive state; subscribe with `$state` in a component. */
16
+ readonly state: Readable<ServerFnState<Output>>;
17
+ /** Invoke it. Records state, and rejects on failure - attach `.catch` if you only render state. */
18
+ readonly call: (input: Input) => Promise<Output>;
19
+ /** Back to idle. */
20
+ readonly reset: () => void;
21
+ }
22
+ /**
23
+ * Track one server function's call state.
24
+ *
25
+ * The `readable` subscribes on first use and unsubscribes when the last subscriber leaves, so a
26
+ * component that never reads the state never attaches a listener.
27
+ */
28
+ export declare function useServerFn<Input, Output>(fn: ServerFnReference<Input, Output>): ServerFnHandle<Input, Output>;
29
+ //# sourceMappingURL=fn.d.ts.map
@@ -0,0 +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,EAA0C,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAClG,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAA;AAEtD,+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,CAY/B"}
package/dist/fn.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `@nifrajs/web-svelte/fn` - Svelte binding for a server function's pending/error state.
3
+ *
4
+ * Calling a server function needs no hook: the client stub is `(input) => Promise<Output>`. This adds
5
+ * the part a component usually wants around it - in flight, last result, last error - and nothing more.
6
+ *
7
+ * The state machine is `@nifrajs/web`'s `createServerFnStore`, shared with every other adapter, so
8
+ * "is it pending" has one answer rather than five that drift. This file contributes only a `readable` fed by the store.
9
+ */
10
+ import { createServerFnStore, idleServerFnState } from "@nifrajs/web/fn-state";
11
+ import { readable } from "svelte/store";
12
+ /**
13
+ * Track one server function's call state.
14
+ *
15
+ * The `readable` subscribes on first use and unsubscribes when the last subscriber leaves, so a
16
+ * component that never reads the state never attaches a listener.
17
+ */
18
+ export function useServerFn(fn) {
19
+ const store = createServerFnStore(fn);
20
+ const state = readable(idleServerFnState(), (set) => {
21
+ // Prime before subscribing, exactly as `useFetcher` does in this package. `readable` runs this
22
+ // start function only on the FIRST subscription, so without the snapshot a store attached after a
23
+ // call has already run reports its initial value - idle - for a call that finished. The four
24
+ // sibling adapters read a snapshot on mount, so skipping it here made Svelte the one place where
25
+ // "is it pending" disagreed with the shared state machine it exists to report.
26
+ set(store.snapshot());
27
+ return store.subscribe(() => set(store.snapshot()));
28
+ });
29
+ return { state, call: store.call, reset: store.reset };
30
+ }
31
+ //# sourceMappingURL=fn.js.map
package/dist/fn.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fn.js","sourceRoot":"","sources":["../src/fn.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAsB,MAAM,uBAAuB,CAAA;AAClG,OAAO,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAA;AAYtD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,EAAoC;IAEpC,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAmC,CAAC,CAAA;IACtE,MAAM,KAAK,GAAG,QAAQ,CAAwB,iBAAiB,EAAU,EAAE,CAAC,GAAG,EAAE,EAAE;QACjF,+FAA+F;QAC/F,kGAAkG;QAClG,6FAA6F;QAC7F,iGAAiG;QACjG,+EAA+E;QAC/E,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;QACrB,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAA;AACxD,CAAC"}
package/dist/i18n.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `@nifrajs/web-svelte/i18n` — Svelte bindings for `@nifrajs/i18n`: the `<I18nProvider>` component (re-exported
2
+ * `@nifrajs/web-svelte/i18n` - Svelte bindings for `@nifrajs/i18n`: the `<I18nProvider>` component (re-exported
3
3
  * from `I18nProvider.svelte`) plus `useT()`, which reads the provided `Formatter` from Svelte context.
4
4
  * Call `useT()` during a component's initialization (like any `getContext`).
5
5
  */
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * @nifrajs/web-svelte — the Svelte 5 render adapter for @nifrajs/web (server side) + the `.svelte` compiler
2
+ * @nifrajs/web-svelte - the Svelte 5 render adapter for @nifrajs/web (server side) + the `.svelte` compiler
3
3
  * Bun-plugin. SSR via Svelte's `render` (svelte/server), which returns HTML strings (Svelte SSR is
4
4
  * string-based, not a stream), wrapped in a one-chunk Web `ReadableStream` (the seam's shape). The
5
5
  * layout-chain fold is the recursive `Chain.svelte`. Client hydration lives in `@nifrajs/web-svelte/client`.
6
6
  * Svelte components compile from `.svelte` files, so this adapter ships a build plugin (like
7
- * `@nifrajs/web-solid`'s Babel plugin) — there is no callable-component runtime.
7
+ * `@nifrajs/web-solid`'s Babel plugin) - there is no callable-component runtime.
8
8
  */
9
9
  import type { RenderAdapter } from "@nifrajs/web";
10
10
  export { svelteBunPlugin } from "./plugin.js";
11
- /** The Svelte server render adapter — pass to @nifrajs/web's `renderPage`. */
11
+ /** The Svelte server render adapter - pass to @nifrajs/web's `renderPage`. */
12
12
  export declare const svelteAdapter: RenderAdapter;
13
13
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { render } from "svelte/server";
2
2
  import Chain from "./Chain.svelte";
3
3
  // Re-export the compiler plugin for convenience. NOTE: the SSR preload must import it from
4
- // `@nifrajs/web-svelte/plugin` (not from here) — importing this module eagerly loads `Chain.svelte`,
4
+ // `@nifrajs/web-svelte/plugin` (not from here) - importing this module eagerly loads `Chain.svelte`,
5
5
  // which must be compiled by the already-registered plugin.
6
6
  export { svelteBunPlugin } from "./plugin.js";
7
7
  // Svelte SSR yields a complete HTML string; the seam wants a stream, so emit it as one chunk.
@@ -14,25 +14,25 @@ function oneChunk(html) {
14
14
  },
15
15
  });
16
16
  }
17
- /** The Svelte server render adapter — pass to @nifrajs/web's `renderPage`. */
17
+ /** The Svelte server render adapter - pass to @nifrajs/web's `renderPage`. */
18
18
  export const svelteAdapter = {
19
19
  // Svelte SSR is already synchronous + string-based, so this is the native shape: `renderToString`
20
20
  // returns the body directly (renderPage buffers it on the non-deferred fast path), and
21
21
  // `renderToStream` wraps the same string in a one-chunk stream for the deferred path. No streaming
22
- // renderer to skip here — but going straight to a string avoids the per-request stream allocation.
22
+ // renderer to skip here - but going straight to a string avoids the per-request stream allocation.
23
23
  renderToString(chain, props) {
24
- return render(Chain, { props: { chain, props } }).body;
24
+ return render(Chain, { props: { chain, props, layoutData: props.layoutData } }).body;
25
25
  },
26
26
  renderToStream(chain, props) {
27
27
  // `Chain` folds the layout chain (page innermost gets `props`; layouts wrap via their `children`
28
28
  // snippet). Svelte's `render` returns { head, body }; the body goes into #root. (Svelte's dynamic
29
- // `head` — from <svelte:head> — isn't surfaced through the seam's static `hydrationHead`; nifra's
29
+ // `head` - from <svelte:head> - isn't surfaced through the seam's static `hydrationHead`; nifra's
30
30
  // own meta/head API manages the document head instead.)
31
- const { body } = render(Chain, { props: { chain, props } });
31
+ const { body } = render(Chain, { props: { chain, props, layoutData: props.layoutData } });
32
32
  return oneChunk(body);
33
33
  },
34
34
  // Svelte's client `hydrate` reconciles against the existing DOM; no per-document bootstrap script is
35
- // needed (contrast Solid's generateHydrationScript) — the seam allows the empty string.
35
+ // needed (contrast Solid's generateHydrationScript) - the seam allows the empty string.
36
36
  hydrationHead() {
37
37
  return "";
38
38
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAElC,2FAA2F;AAC3F,qGAAqG;AACrG,2DAA2D;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,8FAA8F;AAC9F,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5C,OAAO,IAAI,cAAc,CAAC;QACxB,KAAK,CAAC,UAAU;YACd,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACzB,UAAU,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,kGAAkG;IAClG,uFAAuF;IACvF,mGAAmG;IACnG,mGAAmG;IACnG,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,OAAO,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAA;IACxD,CAAC;IACD,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,iGAAiG;QACjG,kGAAkG;QAClG,kGAAkG;QAClG,wDAAwD;QACxD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QAC3D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IACD,qGAAqG;IACrG,wFAAwF;IACxF,aAAa;QACX,OAAO,EAAE,CAAA;IACX,CAAC;CACF,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,KAAK,MAAM,gBAAgB,CAAA;AAElC,2FAA2F;AAC3F,qGAAqG;AACrG,2DAA2D;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,8FAA8F;AAC9F,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5C,OAAO,IAAI,cAAc,CAAC;QACxB,KAAK,CAAC,UAAU;YACd,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACzB,UAAU,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,kGAAkG;IAClG,uFAAuF;IACvF,mGAAmG;IACnG,mGAAmG;IACnG,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,OAAO,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,CAAA;IACtF,CAAC;IACD,cAAc,CAAC,KAAK,EAAE,KAAK;QACzB,iGAAiG;QACjG,kGAAkG;QAClG,kGAAkG;QAClG,wDAAwD;QACxD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;QACzF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IACD,qGAAqG;IACrG,wFAAwF;IACxF,aAAa;QACX,OAAO,EAAE,CAAA;IACX,CAAC;CACF,CAAA"}
package/dist/mdx.js CHANGED
@@ -8,9 +8,9 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
8
8
  };
9
9
  import { compile } from "svelte/compiler";
10
10
  /**
11
- * `@nifrajs/web-svelte/mdx` — compile `.mdx` routes/files to **Svelte** components. Svelte has no JSX
12
- * runtime, so (unlike the JSX-family `mdxBunPlugin`) MDX-for-Svelte goes through `mdsvex` — Svelte's
13
- * own Markdown compiler — which produces a `.svelte` module, then the standard `svelte/compiler` runs
11
+ * `@nifrajs/web-svelte/mdx` - compile `.mdx` routes/files to **Svelte** components. Svelte has no JSX
12
+ * runtime, so (unlike the JSX-family `mdxBunPlugin`) MDX-for-Svelte goes through `mdsvex` - Svelte's
13
+ * own Markdown compiler - which produces a `.svelte` module, then the standard `svelte/compiler` runs
14
14
  * (`generate: "server"` for SSR, `"client"` for the browser). Pass it to `buildClient`/`buildServer`'s
15
15
  * `plugins` alongside `svelteBunPlugin` (one handles `.svelte`, this one `.mdx`). `mdsvex` is an
16
16
  * optional peer, lazy-loaded at build time.
@@ -33,12 +33,12 @@ export function svelteMdxBunPlugin(generate) {
33
33
  mdsvexCompile = (await import(__rewriteRelativeImportExtension(MDSVEX_MODULE))).compile;
34
34
  }
35
35
  catch {
36
- throw new Error("@nifrajs/web-svelte/mdx: compiling `.mdx` for Svelte needs the `mdsvex` package — install it (`bun add mdsvex`).");
36
+ throw new Error("@nifrajs/web-svelte/mdx: compiling `.mdx` for Svelte needs the `mdsvex` package - install it (`bun add mdsvex`).");
37
37
  }
38
38
  build.onLoad({ filter: /\.mdx(\?|$)/ }, async (args) => {
39
39
  const path = args.path.split("?")[0] ?? args.path;
40
40
  // 1. Markdown/MDX → Svelte source (mdsvex). `extensions` must include `.mdx` or mdsvex skips the
41
- // file (returns undefined — it only processes its configured extensions, default `.svx`).
41
+ // file (returns undefined - it only processes its configured extensions, default `.svx`).
42
42
  const pre = await mdsvexCompile(await Bun.file(path).text(), {
43
43
  filename: path,
44
44
  extensions: [".mdx", ".svx", ".md"],
package/dist/plugin.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { compile } from "svelte/compiler";
2
2
  /**
3
- * `@nifrajs/web-svelte/plugin` — the `.svelte` compiler Bun-plugin, in its OWN module (no `.svelte`
3
+ * `@nifrajs/web-svelte/plugin` - the `.svelte` compiler Bun-plugin, in its OWN module (no `.svelte`
4
4
  * imports). This matters for the SSR preload: `bun --preload` must register the plugin BEFORE any
5
5
  * `.svelte` file is loaded. Importing it from the package root (`./index`) would eagerly load
6
- * `Chain.svelte` (the adapter's fold) before the plugin is registered — so it lives here instead.
6
+ * `Chain.svelte` (the adapter's fold) before the plugin is registered - so it lives here instead.
7
7
  *
8
8
  * `generate: "server"` for SSR, `"client"` for the browser (Svelte 5's client output is hydratable).
9
9
  * Preload it for SSR (`bun --preload`) and pass it to `buildClient({ plugins: [...] })` for the client
@@ -13,12 +13,12 @@ import { compile } from "svelte/compiler";
13
13
  * the markup (both client + server output), so SSR HTML is already scoped. The **client** build emits
14
14
  * the scoped stylesheet as a virtual `?svelte-css` module that `Bun.build`'s CSS bundler folds into the
15
15
  * app stylesheet (served as a `<link>`); the SSR build drops it (the stylesheet ships from the client
16
- * build — no double-emit). With `nifra dev`, `@sveltejs/vite-plugin-svelte` injects the CSS instead.
16
+ * build - no double-emit). With `nifra dev`, `@sveltejs/vite-plugin-svelte` injects the CSS instead.
17
17
  */
18
18
  const STYLE_SUFFIX = "?svelte-css";
19
19
  const STYLE_NS = "nifra-svelte-css";
20
20
  export function svelteBunPlugin(generate) {
21
- // Compiled scoped CSS per `.svelte` file (client build only) — read back by the virtual-module loader.
21
+ // Compiled scoped CSS per `.svelte` file (client build only) - read back by the virtual-module loader.
22
22
  const cssByPath = new Map();
23
23
  return {
24
24
  name: `nifra-svelte-${generate}`,
package/dist/query.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { type QueryClient, type QueryState } from "@nifrajs/web";
2
2
  /**
3
- * `@nifrajs/web-svelte/query` — Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
4
- * `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` — read it
3
+ * `@nifrajs/web-svelte/query` - Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
4
+ * `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` - read it
5
5
  * reactively with `$query`. The store fetches on mount (the start notifier fires on the first
6
6
  * `$`-subscription) and subscribes to the cache entry; concurrent `useQuery`s with the same key share
7
7
  * one entry + one in-flight fetch (dedup). `useQueryClient` exposes `invalidateQueries`.
8
8
  *
9
- * The query client is created lazily **client-side only** (the `typeof window` guard) — on the server
9
+ * The query client is created lazily **client-side only** (the `typeof window` guard) - on the server
10
10
  * there is none, so `useQuery` holds the idle/pending state (queries are client-first; loaders are the
11
11
  * SSR data source). The first client render uses the same idle snapshot, so no mismatch.
12
12
  */
package/dist/query.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { createQueryClient, } from "@nifrajs/web";
2
2
  /**
3
- * `@nifrajs/web-svelte/query` — Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
4
- * `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` — read it
3
+ * `@nifrajs/web-svelte/query` - Svelte bindings for the keyed query-cache, as **Svelte stores** (plain
4
+ * `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch` - read it
5
5
  * reactively with `$query`. The store fetches on mount (the start notifier fires on the first
6
6
  * `$`-subscription) and subscribes to the cache entry; concurrent `useQuery`s with the same key share
7
7
  * one entry + one in-flight fetch (dedup). `useQueryClient` exposes `invalidateQueries`.
8
8
  *
9
- * The query client is created lazily **client-side only** (the `typeof window` guard) — on the server
9
+ * The query client is created lazily **client-side only** (the `typeof window` guard) - on the server
10
10
  * there is none, so `useQuery` holds the idle/pending state (queries are client-first; loaders are the
11
11
  * SSR data source). The first client render uses the same idle snapshot, so no mismatch.
12
12
  */
@@ -0,0 +1,45 @@
1
+ import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server";
2
+ import { type Blocker, type BlockerFunction, type NavigateFunction } from "@nifrajs/web";
3
+ /**
4
+ * `@nifrajs/web-svelte/router` - Svelte routing bindings over the agnostic `@nifrajs/web` history layer,
5
+ * as plain `.ts` (no runes/compiler needed): `useNavigate` (programmatic navigation), `useBlocker` (the
6
+ * unsaved-changes guard), and `useSearch` (the route's typed, validated search, as a reactive accessor).
7
+ * Navigation goes through `@nifrajs/web`'s DOM-free bridges (`getBrowserNavigate` / `registerBlocker`,
8
+ * populated by `installHistory`); `useSearch` reads the accessor `Chain.svelte` provides via Svelte
9
+ * context on SSR + client alike. Idle before hydration (native `<a>` navigation still works). SSR-safe.
10
+ */
11
+ import { type Readable } from "svelte/store";
12
+ export type { Blocker, BlockerFunction, BlockerState, NavigateFunction } from "@nifrajs/web";
13
+ /**
14
+ * The route's typed, validated search params as a reactive accessor - the SAME value the loader received
15
+ * as `ctx.search`. SSR-correct: `Chain.svelte` provides it from the URL server-side and from the
16
+ * identical client derivation, so a value rendered from it doesn't flash on hydration. Call it (in a
17
+ * `$derived` or the template) to read the current search - it updates on navigation. Pass the route's
18
+ * `searchSchema` as the type argument for its output type.
19
+ *
20
+ * ```svelte
21
+ * <script>
22
+ * const search = useSearch() // () => { page: number, ... }
23
+ * </script>
24
+ * <span>{search().page}</span>
25
+ * ```
26
+ */
27
+ export declare function useSearch<Schema extends StandardSchemaV1 | undefined = undefined>(): () => Schema extends StandardSchemaV1 ? InferOutput<Schema> : Record<string, unknown>;
28
+ /** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object).
29
+ * Resolves the browser navigate at call time, so it works as soon as `installHistory` has run and no-ops
30
+ * before then / on the server. */
31
+ export declare function useNavigate(): NavigateFunction;
32
+ /**
33
+ * Guard navigation away from a page with unsaved work, confirming with your OWN async UI. Mirrors
34
+ * react-router's `useBlocker`: pass a boolean or a `({ currentLocation, nextLocation }) => boolean`
35
+ * predicate, and get back a {@link Blocker} store (read with `$blocker`). When a navigation (an anchor
36
+ * click, `useNavigate`, or a browser back/forward) is intercepted, `$blocker.state` becomes `"blocked"`
37
+ * and `proceed`/`reset` go live - show a dialog and call `proceed()` to continue or `reset()` to stay.
38
+ * It also arms the browser's native "Leave site?" prompt on tab close / reload. Idle on the server and
39
+ * before hydration.
40
+ *
41
+ * The store is created once, so to track a CHANGING flag pass a function - `useBlocker(() => dirty)` -
42
+ * re-evaluated at navigation time. A constant boolean is fine as-is.
43
+ */
44
+ export declare function useBlocker(shouldBlock: boolean | BlockerFunction): Readable<Blocker>;
45
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +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;AACzE,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EAGpB,KAAK,gBAAgB,EAKtB,MAAM,cAAc,CAAA;AAErB;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAA;AAEtD,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAQ5F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,gBAAgB,GAAG,SAAS,GAAG,SAAS,KACpD,MAAM,MAAM,SAAS,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzF;AAED;;kCAEkC;AAClC,wBAAgB,WAAW,IAAI,gBAAgB,CAO9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,OAAO,GAAG,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC,CAOpF"}