@nifrajs/web-react 1.6.0 → 1.9.1

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.
@@ -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;AAcnE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,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,CAc7D"}
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;AAcnE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,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,CAkB7D"}
package/dist/client.js CHANGED
@@ -30,6 +30,10 @@ export function mountRouter(options) {
30
30
  data: state.data,
31
31
  actionData: state.actionData,
32
32
  pending: state.pending,
33
+ // `params`/`path` feed the routing hooks (useParams/useLocation) via compose's RouterContext —
34
+ // sourced from router state here, matching the SSR render's request-derived values on hydration.
35
+ params: state.params,
36
+ path: state.path,
33
37
  // The in-flight submission (for optimistic UI) — spread only when present.
34
38
  ...(state.submission ? { submission: state.submission } : {}),
35
39
  });
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAA0B,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,wGAAwG;AACxG,+FAA+F;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,WAAW,CAAC,SAAoB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAC1D,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,MAAM,MAAM,GAAsB,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtF,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;YAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAA;IACJ,CAAC,CAAA;IACD,WAAW,CAAC,SAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAC1D,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAA0B,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,wGAAwG;AACxG,+FAA+F;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,WAAW,CAAC,SAAoB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAC1D,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,MAAM,MAAM,GAAsB,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtF,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;YAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,+FAA+F;YAC/F,iGAAiG;YACjG,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAA;IACJ,CAAC,CAAA;IACD,WAAW,CAAC,SAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAC1D,CAAC"}
package/dist/compose.d.ts CHANGED
@@ -4,6 +4,11 @@ import { type ReactNode } from "react";
4
4
  * Fold a layout chain (outermost layout → page) into a single React tree: the page
5
5
  * (innermost) receives `props` (the loader data); each layout wraps the child via its
6
6
  * `children`. Shared by the server adapter (renderToString) and the client (hydrateRoot).
7
+ *
8
+ * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params` + the
9
+ * current `path` (both threaded through `RenderProps` identically on SSR and client), so the routing
10
+ * hooks (`useParams`/`useLocation`/`useSearchParams`) read the same value on both sides — no hydration
11
+ * mismatch. A render with no routing fields (a non-router adapter usage) provides the empty default.
7
12
  */
8
13
  export declare function compose(chain: readonly unknown[], props: RenderProps): ReactNode;
9
14
  //# sourceMappingURL=compose.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE7E;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,CAQhF"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAM7E;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,CAYhF"}
package/dist/compose.js CHANGED
@@ -1,8 +1,16 @@
1
1
  import { createElement } from "react";
2
+ import { RouterContext } from "./router.js";
3
+ // Stable empty params so the provider value's `params` has a fixed reference when a route has none.
4
+ const EMPTY_PARAMS = Object.freeze({});
2
5
  /**
3
6
  * Fold a layout chain (outermost layout → page) into a single React tree: the page
4
7
  * (innermost) receives `props` (the loader data); each layout wraps the child via its
5
8
  * `children`. Shared by the server adapter (renderToString) and the client (hydrateRoot).
9
+ *
10
+ * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params` + the
11
+ * current `path` (both threaded through `RenderProps` identically on SSR and client), so the routing
12
+ * hooks (`useParams`/`useLocation`/`useSearchParams`) read the same value on both sides — no hydration
13
+ * mismatch. A render with no routing fields (a non-router adapter usage) provides the empty default.
6
14
  */
7
15
  export function compose(chain, props) {
8
16
  const last = chain.length - 1;
@@ -11,6 +19,6 @@ export function compose(chain, props) {
11
19
  // children passed as the 3rd arg (not a `children` prop) — React's canonical form.
12
20
  node = createElement(chain[i], null, node);
13
21
  }
14
- return node;
22
+ return createElement(RouterContext.Provider, { value: { params: props.params ?? EMPTY_PARAMS, path: props.path ?? "" } }, node);
15
23
  }
16
24
  //# sourceMappingURL=compose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;AAE7E;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAc,aAAa,CAAC,KAAK,CAAC,IAAI,CAAmC,EAAE,KAAK,CAAC,CAAA;IACzF,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,mFAAmF;QACnF,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAsB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,oGAAoG;AACpG,MAAM,YAAY,GAAqC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExE;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAc,aAAa,CAAC,KAAK,CAAC,IAAI,CAAmC,EAAE,KAAK,CAAC,CAAA;IACzF,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,mFAAmF;QACnF,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAsB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,aAAa,CAClB,aAAa,CAAC,QAAQ,EACtB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,EAC3E,IAAI,CACL,CAAA;AACH,CAAC"}
package/dist/query.d.ts CHANGED
@@ -1,17 +1,100 @@
1
- import { type QueryClient, type QueryState } from "@nifrajs/web";
2
- /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
3
- export declare function useQueryClient(): Pick<QueryClient, "invalidateQueries">;
1
+ /**
2
+ * `@nifrajs/web-react/query` — React bindings for the keyed query-cache + mutations: `useQuery`,
3
+ * `useInfiniteQuery`, `useMutation`, `useQueryClient`, `QueryClientProvider`, and the SSR
4
+ * `HydrationBoundary` (+ `dehydrate` re-exported). A drop-in for the TanStack Query surface,
5
+ * backed by `@nifrajs/web`'s agnostic engine. Imports only `react` + `@nifrajs/web` (never `react-dom/*`),
6
+ * so route components use it on the server *and* client. No JSX (the package builds with plain `tsc`).
7
+ *
8
+ * Resolution order for the client a hook uses: a `QueryClientProvider` in the tree (required for SSR
9
+ * dehydrate/hydrate and for tests), else a lazily-created **client-side** module singleton (the simple
10
+ * client-only app — the `typeof window` guard means the server has none, so hooks render idle/pending
11
+ * and the first client render matches for a clean hydration).
12
+ */
13
+ import { type DehydratedState, type InfiniteData, type InfiniteQueryOptions, type MutationCallbacks, type MutationState, type QueryClient, type QueryOptions, type QueryState } from "@nifrajs/web";
14
+ import { type ReactNode } from "react";
15
+ export type { DehydratedState } from "@nifrajs/web";
16
+ /** Provide a {@link QueryClient} to the tree — required for SSR dehydrate/hydrate and for tests; a
17
+ * client-only app can omit it and rely on the built-in client-side singleton. */
18
+ export declare function QueryClientProvider(props: {
19
+ readonly client: QueryClient;
20
+ readonly children?: ReactNode;
21
+ }): ReactNode;
22
+ /** The active {@link QueryClient}: a `QueryClientProvider`'s client, else the client-side singleton,
23
+ * else a no-op (server / pre-hydration). Use it to `invalidateQueries`/`setQueryData`/`prefetchQuery`. */
24
+ export declare function useQueryClient(): QueryClient;
25
+ /** Options for {@link useQuery}. */
26
+ export interface UseQueryOptions extends QueryOptions {
27
+ /** When `false`, don't fetch (the query stays idle) — for dependent queries. Default `true`. */
28
+ readonly enabled?: boolean;
29
+ }
4
30
  /** A query's reactive {@link QueryState} plus `isPending` + `refetch`. */
5
31
  export interface UseQueryResult<T> extends QueryState<T> {
6
32
  /** `status === "pending"` — no data yet (initial load). */
7
33
  readonly isPending: boolean;
34
+ /** `status === "error"`. */
35
+ readonly isError: boolean;
36
+ /** `status === "success"`. */
37
+ readonly isSuccess: boolean;
8
38
  /** Force a refetch (ignores `staleTime`). */
9
39
  readonly refetch: () => Promise<T>;
10
40
  }
11
41
  /**
12
42
  * Subscribe to the keyed query for `key`, fetched via `fn`. Returns `{ status, data, error, isFetching,
13
- * updatedAt, isPending, refetch }`. Concurrent `useQuery`s with the same key share one cache entry +
14
- * one in-flight fetch (dedup). Refetches on mount and when the key changes; SSR-idle.
43
+ * updatedAt, isPending, isError, isSuccess, refetch }`. Concurrent `useQuery`s with the same key share
44
+ * one cache entry + one in-flight fetch (dedup). Fetches on mount and when the key changes; `enabled:
45
+ * false` keeps it idle (dependent queries). SSR-idle unless a `QueryClientProvider` supplies a hydrated
46
+ * client.
47
+ */
48
+ export declare function useQuery<T>(key: unknown, fn: () => Promise<T>, options?: UseQueryOptions): UseQueryResult<T>;
49
+ /** A mutation's reactive state + imperative controls (the TanStack `useMutation` shape). */
50
+ export interface UseMutationResult<TData, TVariables> extends MutationState<TData, TVariables> {
51
+ readonly isIdle: boolean;
52
+ readonly isPending: boolean;
53
+ readonly isError: boolean;
54
+ readonly isSuccess: boolean;
55
+ /** Fire-and-forget: runs the mutation and swallows rejection (read `error`/`isError` for failures). */
56
+ readonly mutate: (variables: TVariables) => void;
57
+ /** Run the mutation and return the promise (rejects on failure) — for `await`. */
58
+ readonly mutateAsync: (variables: TVariables) => Promise<TData>;
59
+ /** Reset back to idle. */
60
+ readonly reset: () => void;
61
+ }
62
+ /**
63
+ * A mutation hook (create/update/delete). Returns `{ mutate, mutateAsync, data, error, variables, isIdle,
64
+ * isPending, isError, isSuccess, reset }`. Invalidate affected queries from `onSuccess` via
65
+ * `useQueryClient().invalidateQueries(...)`. The handle is stable across renders; the latest `fn`/
66
+ * callbacks re-bind each render.
67
+ */
68
+ export declare function useMutation<TData, TVariables = void>(fn: (variables: TVariables) => Promise<TData>, callbacks?: MutationCallbacks<TData, TVariables>): UseMutationResult<TData, TVariables>;
69
+ /** Options for {@link useInfiniteQuery} — the engine's {@link InfiniteQueryOptions} plus `enabled`. */
70
+ export interface UseInfiniteQueryOptions<T, P> extends InfiniteQueryOptions<T, P> {
71
+ readonly enabled?: boolean;
72
+ }
73
+ /** An infinite query's reactive state + paging controls. */
74
+ export interface UseInfiniteQueryResult<T, P> extends QueryState<InfiniteData<T, P>> {
75
+ readonly isPending: boolean;
76
+ readonly isError: boolean;
77
+ readonly isSuccess: boolean;
78
+ readonly fetchNextPage: () => Promise<InfiniteData<T, P>>;
79
+ readonly fetchPreviousPage: () => Promise<InfiniteData<T, P>>;
80
+ readonly hasNextPage: boolean;
81
+ readonly hasPreviousPage: boolean;
82
+ readonly refetch: () => Promise<InfiniteData<T, P>>;
83
+ }
84
+ /**
85
+ * Subscribe to a paged (infinite-scroll) query. Returns the accumulated `data.pages` plus
86
+ * `fetchNextPage`/`fetchPreviousPage`/`hasNextPage`/`hasPreviousPage`. Fetches the first page on mount.
87
+ * SSR-idle unless a `QueryClientProvider` supplies a hydrated client.
88
+ */
89
+ export declare function useInfiniteQuery<T, P>(key: unknown, fn: (pageParam: P) => Promise<T>, options: UseInfiniteQueryOptions<T, P>): UseInfiniteQueryResult<T, P>;
90
+ /**
91
+ * Seed the context's {@link QueryClient} from a server {@link dehydrate} snapshot — the SSR data bridge.
92
+ * Wrap the app (inside `QueryClientProvider`) so server-prefetched queries are in the cache before the
93
+ * first client render, avoiding a loading flash. Hydration runs during render (idempotent, fresher-wins),
94
+ * so the data is available synchronously to child `useQuery`s.
15
95
  */
16
- export declare function useQuery<T>(key: unknown, fn: () => Promise<T>): UseQueryResult<T>;
96
+ export declare function HydrationBoundary(props: {
97
+ readonly state: DehydratedState | undefined;
98
+ readonly children?: ReactNode;
99
+ }): ReactNode;
17
100
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAA;AAqBrB,wGAAwG;AACxG,wBAAgB,cAAc,IAAI,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAEvE;AAgBD,0EAA0E;AAC1E,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACtD,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CACnC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAgBjF"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -1,29 +1,52 @@
1
- import { createQueryClient, } from "@nifrajs/web";
2
1
  /**
3
- * `@nifrajs/web-react/query` — React bindings for the keyed query-cache. `useQuery(key, fn)` subscribes a
4
- * component to a query (via `useSyncExternalStore`) and fetches on mount / key change; `useQueryClient`
5
- * exposes `invalidateQueries` for after a mutation. Imports only `react` + `@nifrajs/web` (never
6
- * `react-dom/*`), so route components use it on the server *and* client. No JSX (the package builds
7
- * with plain `tsc`).
2
+ * `@nifrajs/web-react/query` — React bindings for the keyed query-cache + mutations: `useQuery`,
3
+ * `useInfiniteQuery`, `useMutation`, `useQueryClient`, `QueryClientProvider`, and the SSR
4
+ * `HydrationBoundary` (+ `dehydrate` re-exported). A drop-in for the TanStack Query surface,
5
+ * backed by `@nifrajs/web`'s agnostic engine. Imports only `react` + `@nifrajs/web` (never `react-dom/*`),
6
+ * so route components use it on the server *and* client. No JSX (the package builds with plain `tsc`).
8
7
  *
9
- * The query client is created lazily **client-side only** (the `typeof window` guard) — on the server
10
- * there is none, so `useQuery` renders the idle/pending state (queries are client-first; loaders are
11
- * the SSR data source). The first client render uses the same idle server-snapshot, so no mismatch.
8
+ * Resolution order for the client a hook uses: a `QueryClientProvider` in the tree (required for SSR
9
+ * dehydrate/hydrate and for tests), else a lazily-created **client-side** module singleton (the simple
10
+ * client-only app — the `typeof window` guard means the server has none, so hooks render idle/pending
11
+ * and the first client render matches for a clean hydration).
12
12
  */
13
- import { useEffect, useSyncExternalStore } from "react";
14
- let client;
15
- function getClient() {
13
+ import { createMutation, createQueryClient, } from "@nifrajs/web";
14
+ import { createContext, createElement, useContext, useEffect, useMemo, useRef, useSyncExternalStore, } from "react";
15
+ // The lazily-created client-side singleton (used when no QueryClientProvider is present). SSR-guarded:
16
+ // the server has no singleton, so provider-less hooks render idle there.
17
+ let singleton;
18
+ function getSingleton() {
16
19
  if (typeof window === "undefined")
17
- return undefined; // SSR → no client (queries render idle)
18
- if (client === undefined)
19
- client = createQueryClient({ now: () => Date.now() });
20
- return client;
20
+ return undefined;
21
+ if (singleton === undefined)
22
+ singleton = createQueryClient({ now: () => Date.now() });
23
+ return singleton;
21
24
  }
22
- /** Access the query client to imperatively `invalidateQueries(keyOrPrefix)` (e.g. after a mutation). */
25
+ // A no-op client for the server / pre-hydration (all reads empty, all writes ignored). Stable ref so a
26
+ // hook's `useSyncExternalStore` server snapshot is consistent.
27
+ const NOOP_CLIENT = {
28
+ query: () => IDLE_HANDLE,
29
+ infiniteQuery: () => IDLE_INFINITE_HANDLE,
30
+ invalidateQueries: () => { },
31
+ getQueryData: () => undefined,
32
+ setQueryData: () => { },
33
+ prefetchQuery: async () => { },
34
+ dehydrate: () => ({ queries: [] }),
35
+ hydrate: () => { },
36
+ };
37
+ const QueryClientContext = createContext(undefined);
38
+ /** Provide a {@link QueryClient} to the tree — required for SSR dehydrate/hydrate and for tests; a
39
+ * client-only app can omit it and rely on the built-in client-side singleton. */
40
+ export function QueryClientProvider(props) {
41
+ return createElement(QueryClientContext.Provider, { value: props.client }, props.children);
42
+ }
43
+ /** The active {@link QueryClient}: a `QueryClientProvider`'s client, else the client-side singleton,
44
+ * else a no-op (server / pre-hydration). Use it to `invalidateQueries`/`setQueryData`/`prefetchQuery`. */
23
45
  export function useQueryClient() {
24
- return getClient() ?? { invalidateQueries: () => { } };
46
+ const provided = useContext(QueryClientContext);
47
+ return provided ?? getSingleton() ?? NOOP_CLIENT;
25
48
  }
26
- // Stable idle snapshot for the server / pre-fetch render (stable ref → no loop, no hydration mismatch).
49
+ // Stable idle snapshots + handles for the server / pre-fetch render (stable refs → no loop, no mismatch).
27
50
  const IDLE = Object.freeze({
28
51
  status: "pending",
29
52
  data: undefined,
@@ -32,27 +55,122 @@ const IDLE = Object.freeze({
32
55
  updatedAt: Number.NEGATIVE_INFINITY,
33
56
  });
34
57
  const idleSnapshot = () => IDLE;
58
+ const IDLE_INFINITE = IDLE;
59
+ const idleInfiniteSnapshot = () => IDLE_INFINITE;
35
60
  const noopSubscribe = () => () => { };
36
61
  const noopAsync = async () => {
37
- throw new Error("[nifra/web-react] useQuery.refetch called with no query client (server?)");
62
+ throw new Error("[nifra/web-react] query action called with no query client (server?)");
63
+ };
64
+ const IDLE_HANDLE = {
65
+ snapshot: idleSnapshot,
66
+ subscribe: noopSubscribe,
67
+ fetch: noopAsync,
68
+ refetch: noopAsync,
69
+ };
70
+ const IDLE_INFINITE_HANDLE = {
71
+ snapshot: idleInfiniteSnapshot,
72
+ subscribe: noopSubscribe,
73
+ fetch: noopAsync,
74
+ refetch: noopAsync,
75
+ fetchNextPage: noopAsync,
76
+ fetchPreviousPage: noopAsync,
77
+ hasNextPage: () => false,
78
+ hasPreviousPage: () => false,
38
79
  };
39
80
  /**
40
81
  * Subscribe to the keyed query for `key`, fetched via `fn`. Returns `{ status, data, error, isFetching,
41
- * updatedAt, isPending, refetch }`. Concurrent `useQuery`s with the same key share one cache entry +
42
- * one in-flight fetch (dedup). Refetches on mount and when the key changes; SSR-idle.
82
+ * updatedAt, isPending, isError, isSuccess, refetch }`. Concurrent `useQuery`s with the same key share
83
+ * one cache entry + one in-flight fetch (dedup). Fetches on mount and when the key changes; `enabled:
84
+ * false` keeps it idle (dependent queries). SSR-idle unless a `QueryClientProvider` supplies a hydrated
85
+ * client.
43
86
  */
44
- export function useQuery(key, fn) {
45
- const handle = getClient()?.query(key, fn);
46
- const state = useSyncExternalStore(handle?.subscribe ?? noopSubscribe, handle ? handle.snapshot : idleSnapshot, idleSnapshot);
47
- // Fetch on mount + whenever the key changes (the handle is stable per key, so it changes with the key).
87
+ export function useQuery(key, fn, options) {
88
+ const client = useQueryClient();
89
+ const enabled = options?.enabled !== false;
90
+ const queryOpts = options?.staleTime !== undefined ? { staleTime: options.staleTime } : undefined;
91
+ // A disabled query still binds a handle (so it re-renders when re-enabled), but never fetches.
92
+ const handle = client.query(key, fn, queryOpts);
93
+ // Server snapshot = the handle's own snapshot: without a provider the client is the NOOP one (idle),
94
+ // but WITH a hydrated provider client it returns the server-seeded data — so a HydrationBoundary-fed
95
+ // query renders its data during SSR and the first client render matches (no loading flash, no drift).
96
+ const state = useSyncExternalStore(handle.subscribe, handle.snapshot, handle.snapshot);
48
97
  useEffect(() => {
49
- if (handle !== undefined)
98
+ if (enabled)
50
99
  handle.fetch().catch(() => { });
51
- }, [handle]);
100
+ }, [handle, enabled]);
101
+ return {
102
+ ...state,
103
+ isPending: state.status === "pending",
104
+ isError: state.status === "error",
105
+ isSuccess: state.status === "success",
106
+ refetch: handle.refetch,
107
+ };
108
+ }
109
+ /**
110
+ * A mutation hook (create/update/delete). Returns `{ mutate, mutateAsync, data, error, variables, isIdle,
111
+ * isPending, isError, isSuccess, reset }`. Invalidate affected queries from `onSuccess` via
112
+ * `useQueryClient().invalidateQueries(...)`. The handle is stable across renders; the latest `fn`/
113
+ * callbacks re-bind each render.
114
+ */
115
+ export function useMutation(fn, callbacks = {}) {
116
+ const ref = useRef(undefined);
117
+ if (ref.current === undefined)
118
+ ref.current = createMutation(fn, callbacks);
119
+ const handle = ref.current;
120
+ handle.rebind(fn, callbacks); // pick up the latest closures each render
121
+ const state = useSyncExternalStore(handle.subscribe, handle.snapshot, handle.snapshot);
52
122
  return {
53
123
  ...state,
124
+ isIdle: state.status === "idle",
54
125
  isPending: state.status === "pending",
55
- refetch: handle ? handle.refetch : noopAsync,
126
+ isError: state.status === "error",
127
+ isSuccess: state.status === "success",
128
+ mutate: (variables) => {
129
+ handle.mutate(variables).catch(() => { }); // fire-and-forget: don't surface an unhandled rejection
130
+ },
131
+ mutateAsync: handle.mutate,
132
+ reset: handle.reset,
56
133
  };
57
134
  }
135
+ /**
136
+ * Subscribe to a paged (infinite-scroll) query. Returns the accumulated `data.pages` plus
137
+ * `fetchNextPage`/`fetchPreviousPage`/`hasNextPage`/`hasPreviousPage`. Fetches the first page on mount.
138
+ * SSR-idle unless a `QueryClientProvider` supplies a hydrated client.
139
+ */
140
+ export function useInfiniteQuery(key, fn, options) {
141
+ const client = useQueryClient();
142
+ const enabled = options.enabled !== false;
143
+ const handle = client.infiniteQuery(key, fn, options);
144
+ const state = useSyncExternalStore(handle.subscribe, handle.snapshot, handle.snapshot);
145
+ useEffect(() => {
146
+ if (enabled)
147
+ handle.fetch().catch(() => { });
148
+ }, [handle, enabled]);
149
+ return {
150
+ ...state,
151
+ isPending: state.status === "pending",
152
+ isError: state.status === "error",
153
+ isSuccess: state.status === "success",
154
+ fetchNextPage: handle.fetchNextPage,
155
+ fetchPreviousPage: handle.fetchPreviousPage,
156
+ hasNextPage: handle.hasNextPage(),
157
+ hasPreviousPage: handle.hasPreviousPage(),
158
+ refetch: handle.refetch,
159
+ };
160
+ }
161
+ /**
162
+ * Seed the context's {@link QueryClient} from a server {@link dehydrate} snapshot — the SSR data bridge.
163
+ * Wrap the app (inside `QueryClientProvider`) so server-prefetched queries are in the cache before the
164
+ * first client render, avoiding a loading flash. Hydration runs during render (idempotent, fresher-wins),
165
+ * so the data is available synchronously to child `useQuery`s.
166
+ */
167
+ export function HydrationBoundary(props) {
168
+ const client = useQueryClient();
169
+ const { state } = props;
170
+ useMemo(() => {
171
+ if (state !== undefined)
172
+ client.hydrate(state);
173
+ }, [client, state]);
174
+ return props.children ?? null;
175
+ }
58
176
  //# sourceMappingURL=query.js.map
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAIlB,MAAM,cAAc,CAAA;AACrB;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEvD,IAAI,MAA+B,CAAA;AACnC,SAAS,SAAS;IAChB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAA,CAAC,wCAAwC;IAC5F,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,GAAG,iBAAiB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC/E,OAAO,MAAM,CAAA;AACf,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,cAAc;IAC5B,OAAO,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAA;AACvD,CAAC;AAED,wGAAwG;AACxG,MAAM,IAAI,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS;IACjB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,UAAU,EAAE,KAAK;IACjB,SAAS,EAAE,MAAM,CAAC,iBAAiB;CACpC,CAAC,CAAA;AACF,MAAM,YAAY,GAAG,GAAsB,EAAE,CAAC,IAAI,CAAA;AAClD,MAAM,aAAa,GAAG,GAAiB,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAA;AAClD,MAAM,SAAS,GAAG,KAAK,IAAoB,EAAE;IAC3C,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAA;AAC7F,CAAC,CAAA;AAUD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAI,GAAY,EAAE,EAAoB;IAC5D,MAAM,MAAM,GAA+B,SAAS,EAAE,EAAE,KAAK,CAAI,GAAG,EAAE,EAAE,CAAC,CAAA;IACzE,MAAM,KAAK,GAAG,oBAAoB,CAChC,MAAM,EAAE,SAAS,IAAI,aAAa,EAClC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,EACvC,YAAY,CACb,CAAA;IACD,wGAAwG;IACxG,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAC1D,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACZ,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,SAA8B;KACnE,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * `@nifrajs/web-react/router` — React routing bindings over the agnostic `@nifrajs/web` router:
3
+ * `<Link>`/`<NavLink>`, `useNavigate`, `useParams`, `useLocation`, `useSearchParams`, and `<Navigate>`.
4
+ *
5
+ * These read the current route from a {@link RouterContext} that `compose` provides on BOTH the SSR
6
+ * render and the client mount (seeded from the request match / router state respectively), so the read
7
+ * hooks (`useParams`/`useLocation`/`useSearchParams`) are SSR-correct and hydrate with no mismatch.
8
+ * Navigation goes through `@nifrajs/web`'s DOM-free navigate bridge (`getBrowserNavigate`, populated by
9
+ * `installHistory`) — so this module imports only `react` (never `react-dom/*`), and a route component
10
+ * can use these on the server and the client without dragging a DOM build into the wrong bundle. No JSX
11
+ * (the package builds with plain `tsc`), so everything is `createElement`.
12
+ */
13
+ import { type NavigateOptions } from "@nifrajs/web";
14
+ import { type AnchorHTMLAttributes, type CSSProperties, type ReactNode } from "react";
15
+ /** The current route the routing hooks read. Provided by `compose` on SSR + client mount alike. */
16
+ export interface RouterContextValue {
17
+ /** The matched route's decoded path params (`/users/:id` → `{ id: "7" }`). */
18
+ readonly params: Readonly<Record<string, string>>;
19
+ /** The current URL's `pathname + search` (no hash — the router never carries one). */
20
+ readonly path: string;
21
+ }
22
+ /** Router context. The default ({} params, "" path) is what a component sees when rendered outside a
23
+ * nifra route tree — the hooks stay defined (no throw) so a stray `useParams` degrades gracefully. */
24
+ export declare const RouterContext: import("react").Context<RouterContextValue>;
25
+ /**
26
+ * The matched route's decoded path params — `/users/:id` on `/users/7` → `{ id: "7" }`. SSR-correct:
27
+ * `compose` provides the same value server-side (from the request match) and client-side (from router
28
+ * state), so a param rendered into markup doesn't flash on hydration.
29
+ */
30
+ export declare function useParams<T extends Record<string, string | undefined> = Record<string, string>>(): Readonly<T>;
31
+ /** The parsed current location. `hash` is always `""` — the fragment is client-only and never reaches
32
+ * the router state / server, so exposing a live hash would hydration-mismatch; read `window.location.hash`
33
+ * directly (in an effect) if you truly need it. */
34
+ export interface Location {
35
+ readonly pathname: string;
36
+ readonly search: string;
37
+ readonly hash: string;
38
+ }
39
+ /** The current {@link Location} (`pathname`/`search`/`hash`), derived from the router context. */
40
+ export declare function useLocation(): Location;
41
+ /** A programmatic navigate: a string path (push, or replace via `{ replace: true }`) or a history delta
42
+ * (`-1`/`1`). A no-op on the server / before hydration (a render-time navigate isn't valid — use
43
+ * {@link Navigate}, which navigates in an effect). */
44
+ export type NavigateFunction = (to: string | number, options?: NavigateOptions) => void;
45
+ /** Get the {@link NavigateFunction}. Stable across renders; resolves the browser navigate at call time
46
+ * (so it works as soon as `installHistory` has run, and no-ops before then / on the server). */
47
+ export declare function useNavigate(): NavigateFunction;
48
+ /** The value forms `setSearchParams` accepts. */
49
+ export type SearchParamsInit = URLSearchParams | Record<string, string | readonly string[]> | string;
50
+ /** Set the query string. Accepts a `URLSearchParams`, a record, a raw string, or an updater of the
51
+ * current params; navigates to the same pathname with the new query (push, or replace via options). */
52
+ export type SetSearchParams = (next: SearchParamsInit | ((prev: URLSearchParams) => SearchParamsInit), options?: NavigateOptions) => void;
53
+ /**
54
+ * The current query as a `URLSearchParams` (SSR-correct via the router context) plus a setter that
55
+ * navigates to the new query. Mirrors react-router's `useSearchParams` tuple.
56
+ */
57
+ export declare function useSearchParams(): readonly [URLSearchParams, SetSearchParams];
58
+ /** {@link Link} props: every `<a>` attribute except `href` (set from `to`), plus `to` + `replace`. */
59
+ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
60
+ /** Same-origin destination path (e.g. `/users/7?tab=posts`). Rendered as the `<a href>`. */
61
+ readonly to: string;
62
+ /** Replace the current history entry instead of pushing. */
63
+ readonly replace?: boolean;
64
+ }
65
+ /**
66
+ * A client-navigating anchor. Renders a real `<a href={to}>` (so it's a working link before hydration
67
+ * and for right-click / open-in-new-tab), and on a plain left-click navigates through the router
68
+ * instead of a full reload. Calling `navigate` + `preventDefault` here means `installHistory`'s
69
+ * document-level click handler sees `defaultPrevented` and stands down — exactly one navigation.
70
+ */
71
+ export declare const Link: import("react").ForwardRefExoticComponent<LinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
72
+ /** The state a {@link NavLink}'s function-form `className`/`style`/`children` receive. */
73
+ export interface NavLinkRenderProps {
74
+ /** True when the current location matches this link's `to` (prefix match, or exact when `end`). */
75
+ readonly isActive: boolean;
76
+ /** Reserved for a future pending-navigation signal; currently always `false`. */
77
+ readonly isPending: boolean;
78
+ }
79
+ /** {@link NavLink} props — like {@link LinkProps}, but `className`/`style`/`children` may be functions
80
+ * of the active state, and `end`/`caseSensitive` tune matching. */
81
+ export interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "children"> {
82
+ /** Match the full path exactly instead of as a prefix (use for `to="/"` so it isn't always active). */
83
+ readonly end?: boolean;
84
+ /** Match case-sensitively (default: case-insensitive, like the DOM). */
85
+ readonly caseSensitive?: boolean;
86
+ readonly className?: string | ((props: NavLinkRenderProps) => string | undefined);
87
+ readonly style?: CSSProperties | ((props: NavLinkRenderProps) => CSSProperties | undefined);
88
+ readonly children?: ReactNode | ((props: NavLinkRenderProps) => ReactNode);
89
+ }
90
+ /**
91
+ * A {@link Link} that knows whether it points at the current location. Adds `aria-current="page"` when
92
+ * active and resolves function-form `className`/`style`/`children` with `{ isActive, isPending }`.
93
+ * Default matching is prefix-on-segment-boundary (so `/users` is active on `/users/7`); pass `end` for
94
+ * an exact match.
95
+ */
96
+ export declare const NavLink: import("react").ForwardRefExoticComponent<NavLinkProps & import("react").RefAttributes<HTMLAnchorElement>>;
97
+ /** {@link Navigate} props: the destination `to` and whether to `replace` the history entry. */
98
+ export interface NavigateProps {
99
+ readonly to: string;
100
+ readonly replace?: boolean;
101
+ }
102
+ /**
103
+ * Declaratively navigate on mount — the component analogue of `useNavigate` (e.g. a guard that renders
104
+ * `<Navigate to="/login" replace />`). Navigates in an effect, so it's a safe no-op during SSR (renders
105
+ * `null`); the redirect happens once on the client after hydration.
106
+ */
107
+ export declare function Navigate({ to, replace }: NavigateProps): null;
108
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}