@nifrajs/web-solid 5.5.1 → 5.6.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 (59) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/client.js +2 -2
  3. package/dist/client.js.map +1 -1
  4. package/dist/content.d.ts +1 -1
  5. package/dist/content.js +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +40 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/keep-alive-view.d.ts +35 -0
  10. package/dist/keep-alive-view.d.ts.map +1 -0
  11. package/dist/keep-alive-view.js +135 -0
  12. package/dist/keep-alive-view.js.map +1 -0
  13. package/dist/keep-alive.d.ts +25 -0
  14. package/dist/keep-alive.d.ts.map +1 -0
  15. package/dist/keep-alive.js +51 -0
  16. package/dist/keep-alive.js.map +1 -0
  17. package/dist/mdx.d.ts.map +1 -1
  18. package/dist/mdx.js +5 -16
  19. package/dist/mdx.js.map +1 -1
  20. package/dist/page-visibility.d.ts +28 -0
  21. package/dist/page-visibility.d.ts.map +1 -0
  22. package/dist/page-visibility.js +42 -0
  23. package/dist/page-visibility.js.map +1 -0
  24. package/dist/ppr-client.d.ts +9 -0
  25. package/dist/ppr-client.d.ts.map +1 -0
  26. package/dist/ppr-client.js +63 -0
  27. package/dist/ppr-client.js.map +1 -0
  28. package/dist/ppr.d.ts +37 -0
  29. package/dist/ppr.d.ts.map +1 -0
  30. package/dist/ppr.js +61 -0
  31. package/dist/ppr.js.map +1 -0
  32. package/dist/query.d.ts +4 -3
  33. package/dist/query.d.ts.map +1 -1
  34. package/dist/query.js +7 -8
  35. package/dist/query.js.map +1 -1
  36. package/dist/route-props.d.ts +5 -2
  37. package/dist/route-props.d.ts.map +1 -1
  38. package/dist/route-props.js +16 -7
  39. package/dist/route-props.js.map +1 -1
  40. package/dist/router.d.ts +3 -2
  41. package/dist/router.d.ts.map +1 -1
  42. package/dist/router.js +7 -3
  43. package/dist/router.js.map +1 -1
  44. package/dist/server-components/Outlet.jsx +1 -1
  45. package/dist/server-components/Outlet.jsx.map +1 -1
  46. package/package.json +16 -5
  47. package/src/client.ts +2 -2
  48. package/src/content.ts +1 -1
  49. package/src/index.ts +48 -4
  50. package/src/keep-alive-view.ts +202 -0
  51. package/src/keep-alive.ts +76 -0
  52. package/src/mdx.ts +5 -9
  53. package/src/page-visibility.ts +53 -0
  54. package/src/ppr-client.ts +77 -0
  55. package/src/ppr.ts +86 -0
  56. package/src/query.ts +13 -8
  57. package/src/route-props.ts +16 -6
  58. package/src/router.ts +8 -3
  59. package/src/server-components/Outlet.tsx +1 -1
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Whether a page is on screen, for a `keepAlive` page that stays mounted while it is hidden. Pages read
3
+ * it through `@nifrajs/web-solid/router`; the keep-alive view provides it per kept slot.
4
+ */
5
+ import { createContext, onCleanup, useContext } from "solid-js";
6
+ // A `globalThis` singleton for the same reason as the router's contexts (see router.ts).
7
+ const PAGE_CONTEXT_SLOT = Symbol.for("nifra.web-solid.page-context");
8
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: only this module writes the registry key, and it writes a page context
9
+ const pageContextSlot = globalThis;
10
+ export const PageContext = pageContextSlot[PAGE_CONTEXT_SLOT] ?? createContext();
11
+ pageContextSlot[PAGE_CONTEXT_SLOT] = PageContext;
12
+ const ALWAYS = () => true;
13
+ /**
14
+ * Whether the page this component renders in is on screen, as an accessor: `false` while it is kept
15
+ * hidden behind another page, where it keeps running. Always `true` on the server and on a page that
16
+ * does not keep alive.
17
+ */
18
+ export function usePageVisibility() {
19
+ const slot = useContext(PageContext);
20
+ return slot === undefined ? ALWAYS : () => !slot.hidden();
21
+ }
22
+ const onVisibility = (kind, callback) => {
23
+ const slot = useContext(PageContext);
24
+ if (slot === undefined)
25
+ return;
26
+ slot[kind].add(callback);
27
+ onCleanup(() => slot[kind].delete(callback));
28
+ };
29
+ /**
30
+ * Run `callback` each time the `keepAlive` page this component renders in is hidden behind another
31
+ * page, before it leaves the document. The page keeps running while it is hidden: pause its timers,
32
+ * sockets and media here. Call it while the component sets up; it is removed with the component. A
33
+ * no-op outside a kept page.
34
+ */
35
+ export function onPageHide(callback) {
36
+ onVisibility("hide", callback);
37
+ }
38
+ /** Run `callback` each time the `keepAlive` page this component renders in is shown again. */
39
+ export function onPageShow(callback) {
40
+ onVisibility("show", callback);
41
+ }
42
+ //# sourceMappingURL=page-visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-visibility.js","sourceRoot":"","sources":["../src/page-visibility.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA+B,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAS5F,yFAAyF;AACzF,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAA;AACpE,wIAAwI;AACxI,MAAM,eAAe,GAAG,UAAqE,CAAA;AAC7F,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAC,iBAAiB,CAAC,IAAI,aAAa,EAAY,CAAA;AAC1F,eAAe,CAAC,iBAAiB,CAAC,GAAG,WAAW,CAAA;AAEhD,MAAM,MAAM,GAAsB,GAAG,EAAE,CAAC,IAAI,CAAA;AAE5C;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACpC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;AAC3D,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,IAAqB,EAAE,QAAoB,EAAQ,EAAE;IACzE,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAM;IAC9B,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACxB,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;AAC9C,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,QAAoB;IAC7C,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAChC,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,UAAU,CAAC,QAAoB;IAC7C,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAChC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type JSX } from "solid-js";
2
+ /**
3
+ * A hole's host in the browser: once mounted, the PPR runtime's fill goes in, an interactive hole
4
+ * hydrates that server HTML as its own root from the frame's props, and each refill `fill` brings is
5
+ * applied in place. Object props reach the root as a store, so a refill updates it and keeps its
6
+ * state; any other value makes the root again.
7
+ */
8
+ export declare function clientHost<Data>(name: string, render: (data: Data) => JSX.Element, interactive: boolean, attributes: Readonly<Record<string, string>>, fill: () => unknown): JSX.Element;
9
+ //# sourceMappingURL=ppr-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ppr-client.d.ts","sourceRoot":"","sources":["../src/ppr-client.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,GAAG,EAKT,MAAM,UAAU,CAAA;AAMjB;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,GAAG,CAAC,OAAO,EACnC,WAAW,EAAE,OAAO,EACpB,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC5C,IAAI,EAAE,MAAM,OAAO,GAClB,GAAG,CAAC,OAAO,CAiDb"}
@@ -0,0 +1,63 @@
1
+ import { holeHost } from "@nifrajs/web/internal/hole-runtime";
2
+ import { createComponent, createEffect, on, onCleanup, onMount, sharedConfig, } from "solid-js";
3
+ import { createStore, reconcile } from "solid-js/store";
4
+ import { Dynamic, hydrate } from "solid-js/web";
5
+ const isObject = (value) => typeof value === "object" && value !== null;
6
+ /**
7
+ * A hole's host in the browser: once mounted, the PPR runtime's fill goes in, an interactive hole
8
+ * hydrates that server HTML as its own root from the frame's props, and each refill `fill` brings is
9
+ * applied in place. Object props reach the root as a store, so a refill updates it and keeps its
10
+ * state; any other value makes the root again.
11
+ */
12
+ export function clientHost(name, render, interactive, attributes, fill) {
13
+ let host;
14
+ const element = createComponent(Dynamic, {
15
+ component: "div",
16
+ ref: (el) => {
17
+ host = el;
18
+ },
19
+ ...attributes,
20
+ });
21
+ onMount(() => {
22
+ const target = host;
23
+ if (target === undefined)
24
+ return;
25
+ const root = (into, data) => {
26
+ const [props, setProps] = createStore({ value: data });
27
+ let dispose;
28
+ let left = false;
29
+ const start = () => {
30
+ if (left)
31
+ return;
32
+ dispose = hydrate(
33
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: the server framed these props from this hole's own loader, through its output guard.
34
+ () => render(props.value), into, { renderId: `nifra-${name}-` });
35
+ };
36
+ // Solid keeps one hydration context: a fill met while the page hydrates waits for it to end.
37
+ if (sharedConfig.context)
38
+ queueMicrotask(start);
39
+ else
40
+ start();
41
+ return {
42
+ update: (next) => {
43
+ if (!isObject(next) || !isObject(props.value))
44
+ return false;
45
+ setProps("value", reconcile(next));
46
+ return true;
47
+ },
48
+ dispose: () => {
49
+ left = true;
50
+ dispose?.();
51
+ },
52
+ };
53
+ };
54
+ const driven = holeHost(name, target, interactive ? root : undefined);
55
+ createEffect(on(fill, (next) => {
56
+ if (next !== undefined)
57
+ driven.refill(next);
58
+ }));
59
+ onCleanup(() => driven.dispose());
60
+ });
61
+ return element;
62
+ }
63
+ //# sourceMappingURL=ppr-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ppr-client.js","sourceRoot":"","sources":["../src/ppr-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAC5E,OAAO,EACL,eAAe,EACf,YAAY,EAEZ,EAAE,EACF,SAAS,EACT,OAAO,EACP,YAAY,GACb,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE/C,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAA;AAEjG;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,MAAmC,EACnC,WAAoB,EACpB,UAA4C,EAC5C,IAAmB;IAEnB,IAAI,IAA6B,CAAA;IACjC,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE;QACvC,SAAS,EAAE,KAAK;QAChB,GAAG,EAAE,CAAC,EAAe,EAAE,EAAE;YACvB,IAAI,GAAG,EAAE,CAAA;QACX,CAAC;QACD,GAAG,UAAU;KACd,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,EAAE;QACX,MAAM,MAAM,GAAG,IAAI,CAAA;QACnB,IAAI,MAAM,KAAK,SAAS;YAAE,OAAM;QAChC,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,IAAa,EAAY,EAAE;YACtD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACtD,IAAI,OAAiC,CAAA;YACrC,IAAI,IAAI,GAAG,KAAK,CAAA;YAChB,MAAM,KAAK,GAAG,GAAS,EAAE;gBACvB,IAAI,IAAI;oBAAE,OAAM;gBAChB,OAAO,GAAG,OAAO;gBACf,sJAAsJ;gBACtJ,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAa,CAAC,EACjC,IAAI,EACJ,EAAE,QAAQ,EAAE,SAAS,IAAI,GAAG,EAAE,CAC/B,CAAA;YACH,CAAC,CAAA;YACD,6FAA6F;YAC7F,IAAI,YAAY,CAAC,OAAO;gBAAE,cAAc,CAAC,KAAK,CAAC,CAAA;;gBAC1C,KAAK,EAAE,CAAA;YACZ,OAAO;gBACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;oBACf,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC;wBAAE,OAAO,KAAK,CAAA;oBAC3D,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;oBAClC,OAAO,IAAI,CAAA;gBACb,CAAC;gBACD,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,GAAG,IAAI,CAAA;oBACX,OAAO,EAAE,EAAE,CAAA;gBACb,CAAC;aACF,CAAA;QACH,CAAC,CAAA;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACrE,YAAY,CACV,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YAChB,IAAI,IAAI,KAAK,SAAS;gBAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC7C,CAAC,CAAC,CACH,CAAA;QACD,SAAS,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC"}
package/dist/ppr.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type JSX } from "solid-js";
2
+ /**
3
+ * The fields of a route's boundary declaration a hole renders from. `fallback` is a function: JSX
4
+ * evaluated at import is one DOM node, so every place that shows it would take it from the last.
5
+ */
6
+ export interface HoleDefinition<Data> {
7
+ readonly name: string;
8
+ readonly render: (data: Data) => JSX.Element;
9
+ readonly fallback?: () => JSX.Element;
10
+ readonly interactive?: boolean;
11
+ }
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
+ export interface HoleProps<Data> {
21
+ readonly of: HoleDefinition<Data>;
22
+ readonly state: HoleState | undefined;
23
+ /** Shown when the hole's loader failed. Default: nothing. */
24
+ readonly error?: JSX.Element;
25
+ }
26
+ /**
27
+ * Experimental: one `dynamic` or `shared` boundary of a partially prerendered page.
28
+ *
29
+ * In a shared shell the hole is an empty host Solid never fills: the PPR runtime puts the server's
30
+ * HTML into it, and an interactive hole then hydrates as its own root from the frame's props. When the
31
+ * page reloads, after an action or a live change, the same host takes the new fill in place: an
32
+ * interactive root gets the new props through its store and keeps its state. After a client
33
+ * navigation the hole renders like any streamed boundary.
34
+ */
35
+ export declare function Hole<Data>(props: HoleProps<Data>): JSX.Element;
36
+ export {};
37
+ //# sourceMappingURL=ppr.d.ts.map
@@ -0,0 +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"}
package/dist/ppr.js ADDED
@@ -0,0 +1,61 @@
1
+ import { initialHtml } from "@nifrajs/web/internal/hole-runtime";
2
+ import { createComponent, createMemo, untrack } from "solid-js";
3
+ import { Dynamic, isServer } from "solid-js/web";
4
+ import { Await } from "./await.js";
5
+ import { clientHost } from "./ppr-client.js";
6
+ /** An open hole's host: claimed by its hydration key like any element, never given children in the
7
+ * browser. On the server it holds a blocking render's HTML. */
8
+ function openHost(hole, fill) {
9
+ const attributes = { "data-nifra-hole": hole.name, "aria-busy": "true" };
10
+ if (isServer) {
11
+ const initial = initialHtml(fill());
12
+ return createComponent(Dynamic, {
13
+ component: "div",
14
+ ...attributes,
15
+ ...(initial === "" ? {} : { innerHTML: initial }),
16
+ });
17
+ }
18
+ return clientHost(hole.name, hole.render, hole.interactive === true, attributes, fill);
19
+ }
20
+ /**
21
+ * Experimental: one `dynamic` or `shared` boundary of a partially prerendered page.
22
+ *
23
+ * In a shared shell the hole is an empty host Solid never fills: the PPR runtime puts the server's
24
+ * HTML into it, and an interactive hole then hydrates as its own root from the frame's props. When the
25
+ * page reloads, after an action or a live change, the same host takes the new fill in place: an
26
+ * interactive root gets the new props through its store and keeps its state. After a client
27
+ * navigation the hole renders like any streamed boundary.
28
+ */
29
+ export function Hole(props) {
30
+ const open = createMemo(() => props.state?.ppr === true);
31
+ const fill = createMemo(() => (open() ? props.state?.fill : undefined));
32
+ const view = createMemo(() => {
33
+ // Only `open` is read for a host, so a refill keeps it.
34
+ if (open())
35
+ return untrack(() => openHost(props.of, fill));
36
+ const hole = props.of;
37
+ const state = props.state;
38
+ if (state === undefined)
39
+ return untrack(() => hole.fallback?.());
40
+ if (state.status === "error")
41
+ return props.error;
42
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: a ready state carries this boundary's loader output, through its output guard.
43
+ if (state.status === "ready")
44
+ return untrack(() => hole.render(state.data));
45
+ // A streamed hole's data is its deferred loader output, whatever status the payload names.
46
+ if (state.data === undefined)
47
+ return untrack(() => hole.fallback?.());
48
+ return untrack(() => createComponent((Await), {
49
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: a pending streamed state carries this boundary's deferred loader output.
50
+ resolve: state.data,
51
+ get fallback() {
52
+ return hole.fallback?.();
53
+ },
54
+ errorFallback: () => props.error,
55
+ children: hole.render,
56
+ }));
57
+ });
58
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion lint/plugin/noChainedTypeAssertions: Solid inserts an accessor reactively, as its own Show returns one; its JSX.Element type leaves functions out.
59
+ return view;
60
+ }
61
+ //# sourceMappingURL=ppr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ppr.js","sourceRoot":"","sources":["../src/ppr.ts"],"names":[],"mappings":"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"}
package/dist/query.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { QueryClient, QueryState } from "@nifrajs/web";
1
+ import type { 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`
@@ -22,7 +22,8 @@ export interface CreateQueryResult<T> {
22
22
  /**
23
23
  * Bind the keyed query for `key`, fetched via `fn`. Returns a reactive `state()` accessor + `refetch`.
24
24
  * Concurrent `createQuery`s with the same key share one cache entry + one in-flight fetch (dedup).
25
- * Fetches on mount; SSR-idle. Call inside a component (owns the subscription).
25
+ * Fetches on mount; SSR-idle. Call inside a component (owns the subscription). `options.live` names
26
+ * the live keys that refetch it.
26
27
  */
27
- export declare function createQuery<T>(key: unknown, fn: () => Promise<T>): CreateQueryResult<T>;
28
+ export declare function createQuery<T>(key: unknown, fn: () => Promise<T>, options?: QueryOptions): CreateQueryResult<T>;
28
29
  //# 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,UAAU,EAAE,MAAM,cAAc,CAAA;AAM3D;;;;;;;;;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;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAgBvF"}
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"}
package/dist/query.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createNoClientRefetch, getQueryClientSingleton, IDLE_QUERY_STATE, } from "@nifrajs/web/internal/query-runtime";
1
+ import { createNoClientRefetch, fetchOnMount, getQueryClientSingleton, IDLE_QUERY_STATE, noop, } from "@nifrajs/web/internal/query-runtime";
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`
@@ -12,21 +12,20 @@ import { createNoClientRefetch, getQueryClientSingleton, IDLE_QUERY_STATE, } fro
12
12
  import { createSignal, onCleanup, onMount } from "solid-js";
13
13
  /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
14
14
  export function useQueryClient() {
15
- return getQueryClientSingleton() ?? { invalidateQueries: () => { } };
15
+ return getQueryClientSingleton() ?? { invalidateQueries: noop };
16
16
  }
17
17
  /**
18
18
  * Bind the keyed query for `key`, fetched via `fn`. Returns a reactive `state()` accessor + `refetch`.
19
19
  * Concurrent `createQuery`s with the same key share one cache entry + one in-flight fetch (dedup).
20
- * Fetches on mount; SSR-idle. Call inside a component (owns the subscription).
20
+ * Fetches on mount; SSR-idle. Call inside a component (owns the subscription). `options.live` names
21
+ * the live keys that refetch it.
21
22
  */
22
- export function createQuery(key, fn) {
23
- const handle = getQueryClientSingleton()?.query(key, fn);
23
+ export function createQuery(key, fn, options) {
24
+ const handle = getQueryClientSingleton()?.query(key, fn, options);
24
25
  const [state, setState] = createSignal(handle ? handle.snapshot() : IDLE_QUERY_STATE);
25
26
  if (handle !== undefined) {
26
27
  onCleanup(handle.subscribe(() => setState(() => handle.snapshot())));
27
- onMount(() => {
28
- handle.fetch().catch(() => { });
29
- });
28
+ onMount(fetchOnMount(handle));
30
29
  return { state, refetch: handle.refetch };
31
30
  }
32
31
  return {
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,uBAAuB,EACvB,gBAAgB,GACjB,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,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;AACrE,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAI,GAAY,EAAE,EAAoB;IAC/D,MAAM,MAAM,GAAG,uBAAuB,EAAE,EAAE,KAAK,CAAI,GAAG,EAAE,EAAE,CAAC,CAAA;IAC3D,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,GAAG,EAAE;YACX,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAChC,CAAC,CAAC,CAAA;QACF,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":"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,7 +1,10 @@
1
1
  import type { MountRouterOptions, RenderProps, RouterState } from "@nifrajs/web";
2
2
  /**
3
3
  * The client half of the server's `RenderProps`, as getters so a settle re-renders only what reads the
4
- * changed field. Cast: a getter always exists where `exactOptionalPropertyTypes` expects absence.
4
+ * changed field. `derive` is `createMemo` on the client mount: `data` and `layoutData` then notify only
5
+ * when they change identity, so a reload that kept them (the router shares what it did not change)
6
+ * re-runs nothing that reads them - a `<For>` keeps its rows. Cast: a getter always exists where
7
+ * `exactOptionalPropertyTypes` expects absence.
5
8
  */
6
- export declare function routeProps(snapshot: () => RouterState, searchSchemas: MountRouterOptions["searchSchemas"], matchChains?: MountRouterOptions["matchChains"]): RenderProps;
9
+ export declare function routeProps(snapshot: () => RouterState, searchSchemas: MountRouterOptions["searchSchemas"], matchChains?: MountRouterOptions["matchChains"], derive?: <T>(read: () => T) => () => T): RenderProps;
7
10
  //# sourceMappingURL=route-props.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-props.d.ts","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAKhF;;;GAGG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,WAAW,EAC3B,aAAa,EAAE,kBAAkB,CAAC,eAAe,CAAC,EAClD,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,GAC9C,WAAW,CA4Cb"}
1
+ {"version":3,"file":"route-props.d.ts","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAKhF;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,WAAW,EAC3B,aAAa,EAAE,kBAAkB,CAAC,eAAe,CAAC,EAClD,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,EAC/C,MAAM,GAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,MAAM,CAAkB,GACrD,WAAW,CAkDb"}
@@ -3,12 +3,17 @@
3
3
  import { searchOfChain } from "@nifrajs/web/client";
4
4
  /**
5
5
  * The client half of the server's `RenderProps`, as getters so a settle re-renders only what reads the
6
- * changed field. Cast: a getter always exists where `exactOptionalPropertyTypes` expects absence.
6
+ * changed field. `derive` is `createMemo` on the client mount: `data` and `layoutData` then notify only
7
+ * when they change identity, so a reload that kept them (the router shares what it did not change)
8
+ * re-runs nothing that reads them - a `<For>` keeps its rows. Cast: a getter always exists where
9
+ * `exactOptionalPropertyTypes` expects absence.
7
10
  */
8
- export function routeProps(snapshot, searchSchemas, matchChains) {
11
+ export function routeProps(snapshot, searchSchemas, matchChains, derive = (read) => read) {
12
+ const data = derive(() => snapshot().data);
13
+ const layoutData = derive(() => snapshot().layoutData);
9
14
  return {
10
15
  get data() {
11
- return snapshot().data;
16
+ return data();
12
17
  },
13
18
  // Each layout's own loader data. Without it a layout renders `data: null` on the client while the
14
19
  // server rendered it with its data.
@@ -16,7 +21,7 @@ export function routeProps(snapshot, searchSchemas, matchChains) {
16
21
  return snapshot().serverComponents;
17
22
  },
18
23
  get layoutData() {
19
- return snapshot().layoutData;
24
+ return layoutData();
20
25
  },
21
26
  get actionData() {
22
27
  return snapshot().actionData;
@@ -28,15 +33,19 @@ export function routeProps(snapshot, searchSchemas, matchChains) {
28
33
  get params() {
29
34
  return snapshot().params;
30
35
  },
36
+ // While an intercept boundary holds a target the URL is the target's; the route's props describe
37
+ // the page underneath, so they read the page's own path.
31
38
  get path() {
32
- return snapshot().path;
39
+ const s = snapshot();
40
+ return s.intercepted?.pagePath ?? s.path;
33
41
  },
34
42
  get search() {
35
43
  // The SAME searchOfChain the server ran, over this snapshot's URL - a same-route search change
36
44
  // updates in place (fine-grained), matching the SSR value on hydration.
37
45
  const s = snapshot();
38
- const idx = s.path.indexOf("?");
39
- return searchOfChain(searchSchemas?.[s.routeId] ?? [], idx === -1 ? "" : s.path.slice(idx));
46
+ const path = s.intercepted?.pagePath ?? s.path;
47
+ const idx = path.indexOf("?");
48
+ return searchOfChain(searchSchemas?.[s.routeId] ?? [], idx === -1 ? "" : path.slice(idx));
40
49
  },
41
50
  get submission() {
42
51
  return snapshot().submission;
@@ -1 +1 @@
1
- {"version":3,"file":"route-props.js","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AACA,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,QAA2B,EAC3B,aAAkD,EAClD,WAA+C;IAE/C,OAAO;QACL,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,kGAAkG;QAClG,oCAAoC;QACpC,IAAI,gBAAgB;YAClB,OAAO,QAAQ,EAAE,CAAC,gBAAgB,CAAA;QACpC,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO;YACT,OAAO,QAAQ,EAAE,CAAC,OAAO,CAAA;QAC3B,CAAC;QACD,+FAA+F;QAC/F,IAAI,MAAM;YACR,OAAO,QAAQ,EAAE,CAAC,MAAM,CAAA;QAC1B,CAAC;QACD,IAAI,IAAI;YACN,OAAO,QAAQ,EAAE,CAAC,IAAI,CAAA;QACxB,CAAC;QACD,IAAI,MAAM;YACR,+FAA+F;YAC/F,wEAAwE;YACxE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAA;YACpB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC/B,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC7F,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,qFAAqF;QACrF,IAAI,UAAU;YACZ,OAAO,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1C,CAAC;KACa,CAAA;AAClB,CAAC"}
1
+ {"version":3,"file":"route-props.js","sourceRoot":"","sources":["../src/route-props.ts"],"names":[],"mappings":"AACA,mGAAmG;AACnG,gGAAgG;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,QAA2B,EAC3B,aAAkD,EAClD,WAA+C,EAC/C,SAAwC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI;IAEtD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;IACtD,OAAO;QACL,IAAI,IAAI;YACN,OAAO,IAAI,EAAE,CAAA;QACf,CAAC;QACD,kGAAkG;QAClG,oCAAoC;QACpC,IAAI,gBAAgB;YAClB,OAAO,QAAQ,EAAE,CAAC,gBAAgB,CAAA;QACpC,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,UAAU,EAAE,CAAA;QACrB,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,OAAO;YACT,OAAO,QAAQ,EAAE,CAAC,OAAO,CAAA;QAC3B,CAAC;QACD,+FAA+F;QAC/F,IAAI,MAAM;YACR,OAAO,QAAQ,EAAE,CAAC,MAAM,CAAA;QAC1B,CAAC;QACD,iGAAiG;QACjG,yDAAyD;QACzD,IAAI,IAAI;YACN,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAA;YACpB,OAAO,CAAC,CAAC,WAAW,EAAE,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAA;QAC1C,CAAC;QACD,IAAI,MAAM;YACR,+FAA+F;YAC/F,wEAAwE;YACxE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAA;YACpB,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAA;YAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC7B,OAAO,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3F,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,QAAQ,EAAE,CAAC,UAAU,CAAA;QAC9B,CAAC;QACD,qFAAqF;QACrF,IAAI,UAAU;YACZ,OAAO,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAA;QAC1C,CAAC;KACa,CAAA;AAClB,CAAC"}
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 { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js";
11
12
  export type { Blocker, BlockerFunction, BlockerState, NavigateFunction, UIMatch };
12
13
  export declare const SearchContext: Context<Accessor<Record<string, unknown>> | undefined>;
13
14
  export declare const RenderPropsContext: Context<RenderProps | undefined>;
@@ -47,8 +48,8 @@ export declare function useNavigate(): NavigateFunction;
47
48
  * predicate, and get back a reactive {@link Blocker} accessor. When a navigation (an anchor click,
48
49
  * `useNavigate`, or a browser back/forward) is intercepted, `blocker().state` becomes `"blocked"` and
49
50
  * `proceed`/`reset` go live - show a dialog and call `proceed()` to continue or `reset()` to stay put.
50
- * It also arms the browser's native "Leave site?" prompt on tab close / reload. Idle on the server and
51
- * before hydration.
51
+ * It also arms the browser's native "Leave site?" prompt on tab close / reload. Idle on the server,
52
+ * before hydration, and while a `keepAlive` page is hidden.
52
53
  *
53
54
  * The component body runs once, so to track a CHANGING flag pass a function -
54
55
  * `useBlocker(() => isDirty())` - not a bare signal accessor's current value. A constant boolean is fine.
@@ -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;AAEjB,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,CASpF"}
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"}
package/dist/router.js CHANGED
@@ -11,6 +11,8 @@ import { chainMatches } from "@nifrajs/web/internal/matches-runtime";
11
11
  * reads the accessor `compose` provides on SSR + client mount alike. Imports only `solid-js`. No JSX.
12
12
  */
13
13
  import { createContext, createMemo, createSignal, onCleanup, useContext, } from "solid-js";
14
+ import { PageContext } from "./page-visibility.js";
15
+ export { onPageHide, onPageShow, usePageVisibility } from "./page-visibility.js";
14
16
  // Frozen empty search + a stable accessor for a `useSearch` used outside a nifra route tree.
15
17
  const EMPTY_SEARCH = Object.freeze({});
16
18
  const EMPTY_SEARCH_ACCESSOR = () => EMPTY_SEARCH;
@@ -83,15 +85,17 @@ export function useNavigate() {
83
85
  * predicate, and get back a reactive {@link Blocker} accessor. When a navigation (an anchor click,
84
86
  * `useNavigate`, or a browser back/forward) is intercepted, `blocker().state` becomes `"blocked"` and
85
87
  * `proceed`/`reset` go live - show a dialog and call `proceed()` to continue or `reset()` to stay put.
86
- * It also arms the browser's native "Leave site?" prompt on tab close / reload. Idle on the server and
87
- * before hydration.
88
+ * It also arms the browser's native "Leave site?" prompt on tab close / reload. Idle on the server,
89
+ * before hydration, and while a `keepAlive` page is hidden.
88
90
  *
89
91
  * The component body runs once, so to track a CHANGING flag pass a function -
90
92
  * `useBlocker(() => isDirty())` - not a bare signal accessor's current value. A constant boolean is fine.
91
93
  */
92
94
  export function useBlocker(shouldBlock) {
93
95
  const [blocker, setBlocker] = createSignal(IDLE_BLOCKER);
94
- onCleanup(registerBlocker((args) => (typeof shouldBlock === "function" ? shouldBlock(args) : shouldBlock), setBlocker));
96
+ const slot = useContext(PageContext);
97
+ onCleanup(registerBlocker((args) => slot?.hidden() !== true &&
98
+ (typeof shouldBlock === "function" ? shouldBlock(args) : shouldBlock), setBlocker));
95
99
  return blocker;
96
100
  }
97
101
  //# sourceMappingURL=router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAcA,mFAAmF;AACnF,qEAAqE;AACrE,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAC5B,6EAA6E;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAA;AACpE;;;;;;GAMG;AACH,OAAO,EAGL,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAAU,GACX,MAAM,UAAU,CAAA;AAIjB,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,CAAC,CAAC,EAAyC,EAAE,OAAyB,EAAE,EAAE;QAC/E,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAM;QAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;QAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;IACzC,CAAC,CAAqB,CAAA;AACxB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,WAAsC;IAC/D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,YAAY,CAAU,YAAY,CAAC,CAAA;IACjE,SAAS,CACP,eAAe,CACb,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,EAC/E,UAAU,CACX,CACF,CAAA;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,7 @@ function Range(props) {
24
24
  if (controller !== undefined)
25
25
  void (props.controller === undefined
26
26
  ? controller.dispose()
27
- : controller.unmount(record.instance));
27
+ : controller.unmount(record.instance, host));
28
28
  });
29
29
  createEffect(() => {
30
30
  const state = props.state;
@@ -1 +1 @@
1
- {"version":3,"file":"Outlet.jsx","sourceRoot":"","sources":["../../src/server-components/Outlet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EAInB,sBAAsB,GACvB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAY,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACvF,MAAM,UAAU,eAAe,CAAqB,KAQnD;IACC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAC7B,sBAAsB,CACpB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,IAAI,IAAI,CACrB,CACF,CAAA;IACD,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAClC;MAAA,CAAC,CAAC,SAAiB,EAAE,EAAE,CAAC,CACtB,CAAC,KAAK,CACJ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CACnB,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CACjB,UAAU,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAC7B,aAAa,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EACnC,CACH,CACH;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AACD,SAAS,KAAK,CAAC,KAKd;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,IAAI,IAAqB,CAAA;IACzB,IAAI,UAA2C,CAAA;IAC/C,OAAO,CAAC,GAAG,EAAE;QACX,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;YACrE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;QACvC,UAAU;YACR,KAAK,CAAC,UAAU;gBAChB,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,aAAc,EAAE,CAAC,CAAA;QAChF,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACtD,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS;gBAClC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE;gBACtB,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IACF,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QACzB,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,CAAA;AAC1F,CAAC"}
1
+ {"version":3,"file":"Outlet.jsx","sourceRoot":"","sources":["../../src/server-components/Outlet.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EAInB,sBAAsB,GACvB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAY,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACvF,MAAM,UAAU,eAAe,CAAqB,KAQnD;IACC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAC7B,sBAAsB,CACpB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,WAAW,EACjB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,IAAI,IAAI,CACrB,CACF,CAAA;IACD,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAClC;MAAA,CAAC,CAAC,SAAiB,EAAE,EAAE,CAAC,CACtB,CAAC,KAAK,CACJ,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CACnB,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CACjB,UAAU,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAC7B,aAAa,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EACnC,CACH,CACH;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AACD,SAAS,KAAK,CAAC,KAKd;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC3B,IAAI,IAAqB,CAAA;IACzB,IAAI,UAA2C,CAAA;IAC/C,OAAO,CAAC,GAAG,EAAE;QACX,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;YACrE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;QACvC,UAAU;YACR,KAAK,CAAC,UAAU;gBAChB,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,aAAc,EAAE,CAAC,CAAA;QAChF,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACtD,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACrC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS;gBAClC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE;gBACtB,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;IAClD,CAAC,CAAC,CAAA;IACF,YAAY,CAAC,GAAG,EAAE;QAChB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QACzB,IAAI,UAAU,KAAK,SAAS;YAC1B,KAAK,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAG,CAAA;AAC1F,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/web-solid",
3
- "version": "5.5.1",
3
+ "version": "5.6.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",
@@ -31,6 +31,11 @@
31
31
  "types": "./dist/client.d.ts",
32
32
  "default": "./dist/client.js"
33
33
  },
34
+ "./keep-alive": {
35
+ "bun": "./src/keep-alive.ts",
36
+ "types": "./dist/keep-alive.d.ts",
37
+ "default": "./dist/keep-alive.js"
38
+ },
34
39
  "./plugin": {
35
40
  "bun": "./src/plugin.ts",
36
41
  "types": "./dist/plugin.d.ts",
@@ -41,6 +46,11 @@
41
46
  "types": "./dist/await.d.ts",
42
47
  "default": "./dist/await.js"
43
48
  },
49
+ "./ppr": {
50
+ "bun": "./src/ppr.ts",
51
+ "types": "./dist/ppr.d.ts",
52
+ "default": "./dist/ppr.js"
53
+ },
44
54
  "./fetcher": {
45
55
  "bun": "./src/fetcher.ts",
46
56
  "types": "./dist/fetcher.d.ts",
@@ -122,10 +132,10 @@
122
132
  "dependencies": {
123
133
  "@babel/core": "^7.26.0",
124
134
  "@babel/preset-typescript": "^7.26.0",
125
- "@nifrajs/core": "5.5.1",
126
- "@nifrajs/i18n": "5.5.1",
127
- "@nifrajs/image": "5.5.1",
128
- "@nifrajs/web": "5.5.1",
135
+ "@nifrajs/core": "5.6.0",
136
+ "@nifrajs/i18n": "5.6.0",
137
+ "@nifrajs/image": "5.6.0",
138
+ "@nifrajs/web": "5.6.0",
129
139
  "babel-preset-solid": "^1.9.3",
130
140
  "solid-refresh": "^0.7.8"
131
141
  },
@@ -141,6 +151,7 @@
141
151
  "devDependencies": {
142
152
  "@babel/types": "7.29.7",
143
153
  "@types/babel__core": "^7.20.5",
154
+ "happy-dom": "21.0.4",
144
155
  "solid-js": "^1.9.3"
145
156
  },
146
157
  "repository": {
package/src/client.ts CHANGED
@@ -7,7 +7,7 @@ import type { MountRouterOptions, RenderProps } from "@nifrajs/web"
7
7
  * build; kept in its own entry so SSR code never ships to the client bundle. No JSX - Solid's package
8
8
  * build is plain `tsc`, so this uses Solid's runtime functions directly, like `compose`.
9
9
  */
10
- import { createSignal } from "solid-js"
10
+ import { createMemo, createSignal } from "solid-js"
11
11
  import { hydrate as solidHydrate, render as solidRender } from "solid-js/web"
12
12
  import { compose } from "./compose.ts"
13
13
  import { setMountedRouter } from "./fetcher.ts"
@@ -58,7 +58,7 @@ export function mountRouter(options: MountRouterOptions): void {
58
58
  // and re-mounts below, so the root render fn reads no signal and never re-runs on its own.
59
59
  return compose(
60
60
  routes[initial.routeId] ?? [],
61
- routeProps(snapshot, searchSchemas, matchChains),
61
+ routeProps(snapshot, searchSchemas, matchChains, (read) => createMemo(read)),
62
62
  )()
63
63
  }, el)
64
64
  }
package/src/content.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `@nifrajs/web-solid/content` - render pre-rendered HTML (e.g. a `@nifrajs/content` collection entry's
3
3
  * `.html`) into the DOM. Solid sets `innerHTML` reactively on a `<Dynamic>` element. Pairs with content
4
- * collections: `<Content html={entry.html} />`. No JSX (`createComponent`/`Dynamic`), so it builds with
4
+ * collections: `<Content html={trustHtml(entry.html)} />`, with `trustHtml` from `@nifrajs/web`. No JSX (`createComponent`/`Dynamic`), so it builds with
5
5
  * plain `tsc`.
6
6
  *
7
7
  * Security: injects raw HTML, so `html` MUST be trusted (your own Markdown/content, rendered at