@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.
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/client.js.map +1 -1
- package/dist/compose.d.ts +5 -0
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +9 -1
- package/dist/compose.js.map +1 -1
- package/dist/query.d.ts +89 -6
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +147 -29
- package/dist/query.js.map +1 -1
- package/dist/router.d.ts +108 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +165 -0
- package/dist/router.js.map +1 -0
- package/package.json +10 -5
- package/src/client.ts +4 -0
- package/src/compose.ts +14 -1
- package/src/query.ts +234 -33
- package/src/router.ts +259 -0
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;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,
|
|
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
|
});
|
package/dist/client.js.map
CHANGED
|
@@ -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
|
package/dist/compose.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
package/dist/compose.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
14
|
-
* one in-flight fetch (dedup).
|
|
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
|
|
96
|
+
export declare function HydrationBoundary(props: {
|
|
97
|
+
readonly state: DehydratedState | undefined;
|
|
98
|
+
readonly children?: ReactNode;
|
|
99
|
+
}): ReactNode;
|
|
17
100
|
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 {
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
return
|
|
20
|
+
return undefined;
|
|
21
|
+
if (singleton === undefined)
|
|
22
|
+
singleton = createQueryClient({ now: () => Date.now() });
|
|
23
|
+
return singleton;
|
|
21
24
|
}
|
|
22
|
-
|
|
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
|
-
|
|
46
|
+
const provided = useContext(QueryClientContext);
|
|
47
|
+
return provided ?? getSingleton() ?? NOOP_CLIENT;
|
|
25
48
|
}
|
|
26
|
-
// Stable idle
|
|
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]
|
|
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
|
|
42
|
-
* one in-flight fetch (dedup).
|
|
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
|
|
46
|
-
const
|
|
47
|
-
|
|
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 (
|
|
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
|
-
|
|
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,
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,cAAc,EACd,iBAAiB,GAWlB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,aAAa,EACb,aAAa,EAEb,UAAU,EACV,SAAS,EACT,OAAO,EACP,MAAM,EACN,oBAAoB,GACrB,MAAM,OAAO,CAAA;AAId,uGAAuG;AACvG,yEAAyE;AACzE,IAAI,SAAkC,CAAA;AACtC,SAAS,YAAY;IACnB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAA;IACnD,IAAI,SAAS,KAAK,SAAS;QAAE,SAAS,GAAG,iBAAiB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IACrF,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,uGAAuG;AACvG,+DAA+D;AAC/D,MAAM,WAAW,GAAgB;IAC/B,KAAK,EAAE,GAAG,EAAE,CAAC,WAAiC;IAC9C,aAAa,EAAE,GAAG,EAAE,CAAC,oBAA6B;IAClD,iBAAiB,EAAE,GAAG,EAAE,GAAE,CAAC;IAC3B,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;IAC7B,YAAY,EAAE,GAAG,EAAE,GAAE,CAAC;IACtB,aAAa,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;IAC7B,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAClC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;CAClB,CAAA;AAED,MAAM,kBAAkB,GAAG,aAAa,CAA0B,SAAS,CAAC,CAAA;AAE5E;iFACiF;AACjF,MAAM,UAAU,mBAAmB,CAAC,KAGnC;IACC,OAAO,aAAa,CAAC,kBAAkB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AAC5F,CAAC;AAED;0GAC0G;AAC1G,MAAM,UAAU,cAAc;IAC5B,MAAM,QAAQ,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAA;IAC/C,OAAO,QAAQ,IAAI,YAAY,EAAE,IAAI,WAAW,CAAA;AAClD,CAAC;AAED,0GAA0G;AAC1G,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,GAA2C,IAE7D,CAAA;AACD,MAAM,oBAAoB,GAAG,GAA2C,EAAE,CAAC,aAAa,CAAA;AACxF,MAAM,aAAa,GAAG,GAAiB,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC,CAAA;AAClD,MAAM,SAAS,GAAG,KAAK,IAAoB,EAAE;IAC3C,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAA;AACzF,CAAC,CAAA;AACD,MAAM,WAAW,GAAuB;IACtC,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,aAAa;IACxB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;CACnB,CAAA;AACD,MAAM,oBAAoB,GAAG;IAC3B,QAAQ,EAAE,oBAAoB;IAC9B,SAAS,EAAE,aAAa;IACxB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,aAAa,EAAE,SAAS;IACxB,iBAAiB,EAAE,SAAS;IAC5B,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK;IACxB,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK;CAC7B,CAAA;AAoBD;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CACtB,GAAY,EACZ,EAAoB,EACpB,OAAyB;IAEzB,MAAM,MAAM,GAAG,cAAc,EAAE,CAAA;IAC/B,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,KAAK,KAAK,CAAA;IAC1C,MAAM,SAAS,GACb,OAAO,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACjF,+FAA+F;IAC/F,MAAM,MAAM,GAAmB,MAAM,CAAC,KAAK,CAAI,GAAG,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IAClE,qGAAqG;IACrG,qGAAqG;IACrG,sGAAsG;IACtG,MAAM,KAAK,GAAG,oBAAoB,CAChC,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,QAAQ,EACf,MAAM,CAAC,QAAQ,CAChB,CAAA;IACD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO;YAAE,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAC7C,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACrB,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,OAAO,EAAE,KAAK,CAAC,MAAM,KAAK,OAAO;QACjC,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAA;AACH,CAAC;AAgBD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,EAA6C,EAC7C,YAAkD,EAAE;IAEpD,MAAM,GAAG,GAAG,MAAM,CAAgD,SAAS,CAAC,CAAA;IAC5E,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,cAAc,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;IAC1E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAA;IAC1B,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA,CAAC,0CAA0C;IACvE,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;IACtF,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,CAAC,MAAM,KAAK,MAAM;QAC/B,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,OAAO,EAAE,KAAK,CAAC,MAAM,KAAK,OAAO;QACjC,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE;YACpB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA,CAAC,wDAAwD;QACnG,CAAC;QACD,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAA;AACH,CAAC;AAmBD;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAY,EACZ,EAAgC,EAChC,OAAsC;IAEtC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAA;IAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,KAAK,CAAA;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,aAAa,CAAO,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;IAC3D,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;IACtF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO;YAAE,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IAC7C,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACrB,OAAO;QACL,GAAG,KAAK;QACR,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,OAAO,EAAE,KAAK,CAAC,MAAM,KAAK,OAAO;QACjC,SAAS,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS;QACrC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE;QACjC,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE;QACzC,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAGjC;IACC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAA;IAC/B,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IACvB,OAAO,CAAC,GAAG,EAAE;QACX,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAChD,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IACnB,OAAO,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAA;AAC/B,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -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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"}
|