@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.
- package/README.md +3 -3
- package/dist/Await.svelte +2 -2
- package/dist/Chain.svelte +16 -6
- package/dist/Content.svelte +2 -2
- package/dist/I18nProvider.svelte +1 -1
- package/dist/Image.svelte +3 -3
- package/dist/Image.svelte.d.ts +1 -1
- package/dist/Router.svelte +15 -8
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -6
- package/dist/client.js.map +1 -1
- package/dist/error.d.ts +1 -1
- package/dist/fetcher.d.ts +7 -7
- package/dist/fetcher.js +7 -7
- package/dist/fn.d.ts +29 -0
- package/dist/fn.d.ts.map +1 -0
- package/dist/fn.js +31 -0
- package/dist/fn.js.map +1 -0
- package/dist/i18n.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/mdx.js +5 -5
- package/dist/plugin.js +4 -4
- package/dist/query.d.ts +3 -3
- package/dist/query.js +3 -3
- package/dist/router.d.ts +45 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +62 -0
- package/dist/router.js.map +1 -0
- package/package.json +16 -5
- package/src/Await.svelte +2 -2
- package/src/Chain.svelte +16 -6
- package/src/Content.svelte +2 -2
- package/src/I18nProvider.svelte +1 -1
- package/src/Image.svelte +3 -3
- package/src/Image.svelte.d.ts +1 -1
- package/src/Router.svelte +15 -8
- package/src/client.ts +9 -6
- package/src/error.ts +1 -1
- package/src/fetcher.ts +7 -7
- package/src/fn.ts +45 -0
- package/src/i18n.ts +1 -1
- package/src/index.ts +9 -9
- package/src/mdx.ts +5 -5
- package/src/plugin.ts +4 -4
- package/src/query.ts +3 -3
- 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
|
|
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
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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`
|
|
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}
|
package/dist/Content.svelte
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
Content.svelte
|
|
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)
|
|
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()
|
package/dist/I18nProvider.svelte
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
I18nProvider.svelte
|
|
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
|
|
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
|
|
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
|
|
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>
|
package/dist/Image.svelte.d.ts
CHANGED
|
@@ -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
|
|
24
|
+
/** CDN URL builder. Defaults to the identity loader (no transform - still CLS-safe + lazy). */
|
|
25
25
|
loader?: ImageLoader
|
|
26
26
|
}
|
|
27
27
|
|
package/dist/Router.svelte
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
Router.svelte
|
|
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
*/
|
package/dist/client.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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, {
|
|
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
|
|
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
|
package/dist/client.js.map
CHANGED
|
@@ -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,
|
|
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`
|
|
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`
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
package/dist/fn.d.ts.map
ADDED
|
@@ -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`
|
|
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
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
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`
|
|
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)
|
|
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;
|
|
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`
|
|
12
|
-
* runtime, so (unlike the JSX-family `mdxBunPlugin`) MDX-for-Svelte goes through `mdsvex`
|
|
13
|
-
* own Markdown compiler
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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)
|
|
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`
|
|
4
|
-
* `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch`
|
|
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)
|
|
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`
|
|
4
|
-
* `.ts`). `useQuery(key, fn)` returns a `Readable<QueryState<T>>` augmented with `refetch`
|
|
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)
|
|
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/router.d.ts
ADDED
|
@@ -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"}
|