@ttt-productions/query-core 0.14.21 → 0.14.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/defaults.d.ts +0 -22
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +0 -35
- package/dist/defaults.js.map +1 -1
- package/dist/firestore/types.d.ts +1 -191
- package/dist/firestore/types.d.ts.map +1 -1
- package/dist/firestore/types.js +1 -6
- package/dist/firestore/types.js.map +1 -1
- package/dist/index.d.ts +2 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -8
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +0 -22
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +0 -4
- package/dist/keys.js.map +1 -1
- package/dist/react/cache-helpers.d.ts +16 -0
- package/dist/react/cache-helpers.d.ts.map +1 -0
- package/dist/{cache-helpers.js → react/cache-helpers.js} +0 -28
- package/dist/react/cache-helpers.js.map +1 -0
- package/dist/react/defaults.d.ts +4 -0
- package/dist/react/defaults.d.ts.map +1 -0
- package/dist/react/defaults.js +17 -0
- package/dist/react/defaults.js.map +1 -0
- package/dist/react/domain-events/cache-invalidation.d.ts +33 -0
- package/dist/react/domain-events/cache-invalidation.d.ts.map +1 -0
- package/dist/react/domain-events/cache-invalidation.js +74 -0
- package/dist/react/domain-events/cache-invalidation.js.map +1 -0
- package/dist/react/domain-events/create-domain-event-invalidator.d.ts +28 -0
- package/dist/react/domain-events/create-domain-event-invalidator.d.ts.map +1 -0
- package/dist/{domain-events → react/domain-events}/create-domain-event-invalidator.js +1 -6
- package/dist/react/domain-events/create-domain-event-invalidator.js.map +1 -0
- package/dist/react/firestore/absence-scheduler.d.ts +0 -28
- package/dist/react/firestore/absence-scheduler.d.ts.map +1 -1
- package/dist/react/firestore/absence-scheduler.js +1 -39
- package/dist/react/firestore/absence-scheduler.js.map +1 -1
- package/dist/react/firestore/batch-doc-loader.d.ts +0 -30
- package/dist/react/firestore/batch-doc-loader.d.ts.map +1 -1
- package/dist/react/firestore/batch-doc-loader.js +0 -34
- package/dist/react/firestore/batch-doc-loader.js.map +1 -1
- package/dist/react/firestore/context.d.ts +0 -19
- package/dist/react/firestore/context.d.ts.map +1 -1
- package/dist/react/firestore/context.js +1 -20
- package/dist/react/firestore/context.js.map +1 -1
- package/dist/react/firestore/doc-subscription-registry.d.ts +0 -23
- package/dist/react/firestore/doc-subscription-registry.d.ts.map +1 -1
- package/dist/react/firestore/doc-subscription-registry.js +5 -26
- package/dist/react/firestore/doc-subscription-registry.js.map +1 -1
- package/dist/react/firestore/infinite-helpers.d.ts +9 -0
- package/dist/react/firestore/infinite-helpers.d.ts.map +1 -0
- package/dist/react/firestore/infinite-helpers.js +11 -0
- package/dist/react/firestore/infinite-helpers.js.map +1 -0
- package/dist/react/firestore/resubscribe.d.ts +0 -20
- package/dist/react/firestore/resubscribe.d.ts.map +1 -1
- package/dist/react/firestore/resubscribe.js +0 -20
- package/dist/react/firestore/resubscribe.js.map +1 -1
- package/dist/react/firestore/subscription-status.d.ts +0 -29
- package/dist/react/firestore/subscription-status.d.ts.map +1 -1
- package/dist/react/firestore/subscription-status.js +1 -34
- package/dist/react/firestore/subscription-status.js.map +1 -1
- package/dist/react/firestore/types.d.ts +80 -0
- package/dist/react/firestore/types.d.ts.map +1 -0
- package/dist/react/firestore/types.js +4 -0
- package/dist/react/firestore/types.js.map +1 -0
- package/dist/react/firestore/use-firestore-collection.d.ts +2 -21
- package/dist/react/firestore/use-firestore-collection.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-collection.js +23 -44
- package/dist/react/firestore/use-firestore-collection.js.map +1 -1
- package/dist/react/firestore/use-firestore-count.d.ts +1 -16
- package/dist/react/firestore/use-firestore-count.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-count.js +0 -15
- package/dist/react/firestore/use-firestore-count.js.map +1 -1
- package/dist/react/firestore/use-firestore-doc.d.ts +2 -24
- package/dist/react/firestore/use-firestore-doc.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-doc.js +21 -42
- package/dist/react/firestore/use-firestore-doc.js.map +1 -1
- package/dist/react/firestore/use-firestore-infinite.d.ts +1 -30
- package/dist/react/firestore/use-firestore-infinite.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-infinite.js +0 -32
- package/dist/react/firestore/use-firestore-infinite.js.map +1 -1
- package/dist/react/firestore/use-firestore-live-infinite.d.ts +2 -35
- package/dist/react/firestore/use-firestore-live-infinite.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-live-infinite.js +10 -36
- package/dist/react/firestore/use-firestore-live-infinite.js.map +1 -1
- package/dist/react/firestore/use-firestore-mutations.d.ts +2 -1
- package/dist/react/firestore/use-firestore-mutations.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-mutations.js +3 -3
- package/dist/react/firestore/use-firestore-mutations.js.map +1 -1
- package/dist/react/firestore/use-firestore-paginated.d.ts +2 -31
- package/dist/react/firestore/use-firestore-paginated.d.ts.map +1 -1
- package/dist/react/firestore/use-firestore-paginated.js +8 -36
- package/dist/react/firestore/use-firestore-paginated.js.map +1 -1
- package/dist/react/firestore/useBatchFirestoreDocs.d.ts +1 -123
- package/dist/react/firestore/useBatchFirestoreDocs.d.ts.map +1 -1
- package/dist/react/firestore/useBatchFirestoreDocs.js +12 -61
- package/dist/react/firestore/useBatchFirestoreDocs.js.map +1 -1
- package/dist/react/index.d.ts +13 -4
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -13
- package/dist/react/index.js.map +1 -1
- package/dist/react/pending-mutations.d.ts +2 -22
- package/dist/react/pending-mutations.d.ts.map +1 -1
- package/dist/react/pending-mutations.js +7 -38
- package/dist/react/pending-mutations.js.map +1 -1
- package/dist/react/provider.d.ts +1 -6
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/provider.js +1 -5
- package/dist/react/provider.js.map +1 -1
- package/dist/{query-client.d.ts → react/query-client.d.ts} +0 -4
- package/dist/react/query-client.d.ts.map +1 -0
- package/dist/{query-client.js → react/query-client.js} +0 -4
- package/dist/react/query-client.js.map +1 -0
- package/dist/react/search/types.d.ts +16 -0
- package/dist/react/search/types.d.ts.map +1 -0
- package/dist/react/search/types.js +2 -0
- package/dist/react/search/types.js.map +1 -0
- package/dist/react/search/use-firestore-search.d.ts +1 -30
- package/dist/react/search/use-firestore-search.d.ts.map +1 -1
- package/dist/react/search/use-firestore-search.js +3 -41
- package/dist/react/search/use-firestore-search.js.map +1 -1
- package/dist/react/use-read-retry-state.d.ts +1 -21
- package/dist/react/use-read-retry-state.d.ts.map +1 -1
- package/dist/react/use-read-retry-state.js +3 -22
- package/dist/react/use-read-retry-state.js.map +1 -1
- package/dist/search/rule.d.ts +0 -6
- package/dist/search/rule.d.ts.map +1 -1
- package/dist/search/rule.js +0 -6
- package/dist/search/rule.js.map +1 -1
- package/dist/search/types.d.ts +1 -39
- package/dist/search/types.d.ts.map +1 -1
- package/dist/search/types.js.map +1 -1
- package/package.json +2 -2
- package/dist/cache-helpers.d.ts +0 -48
- package/dist/cache-helpers.d.ts.map +0 -1
- package/dist/cache-helpers.js.map +0 -1
- package/dist/domain-events/cache-invalidation.d.ts +0 -70
- package/dist/domain-events/cache-invalidation.d.ts.map +0 -1
- package/dist/domain-events/cache-invalidation.js +0 -123
- package/dist/domain-events/cache-invalidation.js.map +0 -1
- package/dist/domain-events/create-domain-event-invalidator.d.ts +0 -46
- package/dist/domain-events/create-domain-event-invalidator.d.ts.map +0 -1
- package/dist/domain-events/create-domain-event-invalidator.js.map +0 -1
- package/dist/domain-events/index.d.ts +0 -5
- package/dist/domain-events/index.d.ts.map +0 -1
- package/dist/domain-events/index.js +0 -3
- package/dist/domain-events/index.js.map +0 -1
- package/dist/firestore/infinite-helpers.d.ts +0 -20
- package/dist/firestore/infinite-helpers.d.ts.map +0 -1
- package/dist/firestore/infinite-helpers.js +0 -23
- package/dist/firestore/infinite-helpers.js.map +0 -1
- package/dist/query-client.d.ts.map +0 -1
- package/dist/query-client.js.map +0 -1
|
@@ -1,36 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// travels in the mutation's variables. Pending is read from that cache, never from one mounted
|
|
5
|
-
// control's `isPending`, which gives three guarantees:
|
|
6
|
-
//
|
|
7
|
-
// 1. an in-flight request survives its originating control unmounting;
|
|
8
|
-
// 2. a remounted control still renders pending for that target;
|
|
9
|
-
// 3. a second submission for the same target is refused while the first is pending.
|
|
10
|
-
//
|
|
11
|
-
// The app owns its keys, its variable shapes and what counts as the same target; this module owns
|
|
12
|
-
// only the mechanics.
|
|
13
|
-
import { useCallback } from "react";
|
|
14
|
-
import { useMutationState, useQueryClient, } from "@tanstack/react-query";
|
|
15
|
-
/** The variables of every pending mutation under `mutationKey` (prefix-matched), live. */
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import { useMutationState, useQueryClient, } from '@tanstack/react-query';
|
|
16
4
|
export function usePendingMutationVariables(mutationKey) {
|
|
17
5
|
return useMutationState({
|
|
18
|
-
filters: { mutationKey, status:
|
|
6
|
+
filters: { mutationKey, status: 'pending' },
|
|
19
7
|
select: (mutation) => mutation.state.variables,
|
|
20
8
|
});
|
|
21
9
|
}
|
|
22
|
-
/** The variables of every pending mutation under `mutationKey`, read once — for a duplicate guard. */
|
|
23
10
|
export function pendingMutationVariables(queryClient, mutationKey) {
|
|
24
11
|
return queryClient
|
|
25
12
|
.getMutationCache()
|
|
26
|
-
.findAll({ mutationKey, status:
|
|
13
|
+
.findAll({ mutationKey, status: 'pending' })
|
|
27
14
|
.map((mutation) => mutation.state.variables);
|
|
28
15
|
}
|
|
29
|
-
/**
|
|
30
|
-
* The targets of every pending mutation under `mutationKey`, live — for a list whose rows each
|
|
31
|
-
* ask "is mine pending?" through one hook call. `targetOf` names a variables' target, or returns
|
|
32
|
-
* nothing for variables without one.
|
|
33
|
-
*/
|
|
34
16
|
export function usePendingMutationTargets(mutationKey, targetOf) {
|
|
35
17
|
const targets = new Set();
|
|
36
18
|
for (const variables of usePendingMutationVariables(mutationKey)) {
|
|
@@ -42,31 +24,18 @@ export function usePendingMutationTargets(mutationKey, targetOf) {
|
|
|
42
24
|
}
|
|
43
25
|
return targets;
|
|
44
26
|
}
|
|
45
|
-
/**
|
|
46
|
-
* True when a mutation under `mutationKey` is pending — any one, or with `matches`, one whose
|
|
47
|
-
* variables are for the same target.
|
|
48
|
-
*/
|
|
49
27
|
export function hasPendingMutation(queryClient, mutationKey, matches) {
|
|
50
28
|
const pending = pendingMutationVariables(queryClient, mutationKey);
|
|
51
29
|
if (!matches)
|
|
52
30
|
return pending.length > 0;
|
|
53
31
|
return pending.some((variables) => variables !== undefined && matches(variables));
|
|
54
32
|
}
|
|
55
|
-
/**
|
|
56
|
-
* The rejection a guarded `mutateAsync` throws for a duplicate submission: an `AbortError`, the
|
|
57
|
-
* shape a caller already treats as "nothing happened" rather than a failure to report.
|
|
58
|
-
*/
|
|
59
33
|
export function duplicateSubmissionError(what) {
|
|
60
34
|
const duplicate = new Error(`${what} is already in flight.`);
|
|
61
|
-
duplicate.name =
|
|
35
|
+
duplicate.name = 'AbortError';
|
|
62
36
|
return duplicate;
|
|
63
37
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Wrap a keyed mutation's `mutate` / `mutateAsync` in the cache-consulting duplicate guard:
|
|
66
|
-
* `mutate` drops a duplicate silently, and `mutateAsync` rejects it with
|
|
67
|
-
* {@link duplicateSubmissionError}. `isDuplicate` must have a stable identity (declare it at module
|
|
68
|
-
* level) so the guarded callbacks keep theirs.
|
|
69
|
-
*/
|
|
38
|
+
/** `isDuplicate` must have a stable identity (declare it at module level) so the guarded callbacks keep theirs. */
|
|
70
39
|
export function useDuplicateGuardedMutation(mutation, isDuplicate, what) {
|
|
71
40
|
const queryClient = useQueryClient();
|
|
72
41
|
const { mutate: rawMutate, mutateAsync: rawMutateAsync } = mutation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-mutations.js","sourceRoot":"","sources":["../../src/react/pending-mutations.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,
|
|
1
|
+
{"version":3,"file":"pending-mutations.js","sourceRoot":"","sources":["../../src/react/pending-mutations.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\nimport {\n useMutationState,\n useQueryClient,\n type MutateOptions,\n type MutationKey,\n type QueryClient,\n type UseMutationResult,\n} from '@tanstack/react-query';\n\nexport function usePendingMutationVariables<V>(\n mutationKey: MutationKey,\n): ReadonlyArray<V | undefined> {\n return useMutationState({\n filters: { mutationKey, status: 'pending' },\n select: (mutation) => mutation.state.variables as V | undefined,\n });\n}\n\nexport function pendingMutationVariables<V>(\n queryClient: QueryClient,\n mutationKey: MutationKey,\n): Array<V | undefined> {\n return queryClient\n .getMutationCache()\n .findAll({ mutationKey, status: 'pending' })\n .map((mutation) => mutation.state.variables as V | undefined);\n}\n\nexport function usePendingMutationTargets<V>(\n mutationKey: MutationKey,\n targetOf: (variables: V) => string | null | undefined,\n): ReadonlySet<string> {\n const targets = new Set<string>();\n for (const variables of usePendingMutationVariables<V>(mutationKey)) {\n if (variables === undefined) continue;\n const target = targetOf(variables);\n if (target) targets.add(target);\n }\n return targets;\n}\n\nexport function hasPendingMutation<V>(\n queryClient: QueryClient,\n mutationKey: MutationKey,\n matches?: (variables: V) => boolean,\n): boolean {\n const pending = pendingMutationVariables<V>(queryClient, mutationKey);\n if (!matches) return pending.length > 0;\n return pending.some((variables) => variables !== undefined && matches(variables));\n}\n\nexport function duplicateSubmissionError(what: string): Error {\n const duplicate = new Error(`${what} is already in flight.`);\n duplicate.name = 'AbortError';\n return duplicate;\n}\n\n/** `isDuplicate` must have a stable identity (declare it at module level) so the guarded callbacks keep theirs. */\nexport function useDuplicateGuardedMutation<TData, TError, TVariables, TContext>(\n mutation: UseMutationResult<TData, TError, TVariables, TContext>,\n isDuplicate: (queryClient: QueryClient, variables: TVariables) => boolean,\n what: string,\n): UseMutationResult<TData, TError, TVariables, TContext> {\n const queryClient = useQueryClient();\n const { mutate: rawMutate, mutateAsync: rawMutateAsync } = mutation;\n const mutate = useCallback(\n (variables: TVariables, options?: MutateOptions<TData, TError, TVariables, TContext>) => {\n if (isDuplicate(queryClient, variables)) return;\n rawMutate(variables, options);\n },\n [queryClient, rawMutate, isDuplicate],\n );\n const mutateAsync = useCallback(\n async (variables: TVariables, options?: MutateOptions<TData, TError, TVariables, TContext>) => {\n if (isDuplicate(queryClient, variables)) throw duplicateSubmissionError(what);\n return rawMutateAsync(variables, options);\n },\n [queryClient, rawMutateAsync, isDuplicate, what],\n );\n return { ...mutation, mutate, mutateAsync } as UseMutationResult<\n TData,\n TError,\n TVariables,\n TContext\n >;\n}\n"]}
|
package/dist/react/provider.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type CreateQueryClientOptions } from '
|
|
2
|
+
import { type CreateQueryClientOptions } from './query-client.js';
|
|
3
3
|
export type QueryProviderProps = React.PropsWithChildren<{
|
|
4
|
-
/** Optional overrides applied when the client is first created. */
|
|
5
4
|
clientOptions?: CreateQueryClientOptions;
|
|
6
5
|
}>;
|
|
7
|
-
/**
|
|
8
|
-
* Client component wrapper for Next.js / React apps.
|
|
9
|
-
* Creates a single QueryClient per browser session.
|
|
10
|
-
*/
|
|
11
6
|
export declare function QueryProvider({ children, clientOptions }: QueryProviderProps): React.JSX.Element;
|
|
12
7
|
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAErF,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,iBAAiB,CAAC;IACvD,aAAa,CAAC,EAAE,wBAAwB,CAAC;CAC1C,CAAC,CAAC;AAEH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,kBAAkB,qBAG5E"}
|
package/dist/react/provider.js
CHANGED
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
-
import { createQueryClient } from '
|
|
6
|
-
/**
|
|
7
|
-
* Client component wrapper for Next.js / React apps.
|
|
8
|
-
* Creates a single QueryClient per browser session.
|
|
9
|
-
*/
|
|
5
|
+
import { createQueryClient } from './query-client.js';
|
|
10
6
|
export function QueryProvider({ children, clientOptions }) {
|
|
11
7
|
const [client] = React.useState(() => createQueryClient(clientOptions));
|
|
12
8
|
return _jsx(QueryClientProvider, { client: client, children: children });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAiC,MAAM,
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAiC,MAAM,mBAAmB,CAAC;AAMrF,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAsB;IAC3E,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC;IACxE,OAAO,KAAC,mBAAmB,IAAC,MAAM,EAAE,MAAM,YAAG,QAAQ,GAAuB,CAAC;AAC/E,CAAC","sourcesContent":["'use client';\n\nimport React from 'react';\nimport { QueryClientProvider } from '@tanstack/react-query';\nimport { createQueryClient, type CreateQueryClientOptions } from './query-client.js';\n\nexport type QueryProviderProps = React.PropsWithChildren<{\n clientOptions?: CreateQueryClientOptions;\n}>;\n\nexport function QueryProvider({ children, clientOptions }: QueryProviderProps) {\n const [client] = React.useState(() => createQueryClient(clientOptions));\n return <QueryClientProvider client={client}>{children}</QueryClientProvider>;\n}\n"]}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { QueryClient, type QueryClientConfig } from '@tanstack/react-query';
|
|
2
2
|
export type CreateQueryClientOptions = QueryClientConfig;
|
|
3
|
-
/**
|
|
4
|
-
* Factory so every app uses the same baseline behavior.
|
|
5
|
-
* Pass overrides to adjust defaults without forking.
|
|
6
|
-
*/
|
|
7
3
|
export declare function createQueryClient(overrides?: CreateQueryClientOptions): QueryClient;
|
|
8
4
|
//# sourceMappingURL=query-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-client.d.ts","sourceRoot":"","sources":["../../src/react/query-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG5E,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAEzD,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,wBAA6B,eAiBzE"}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { QueryClient } from '@tanstack/react-query';
|
|
2
2
|
import { defaultQueryClientConfig } from './defaults.js';
|
|
3
|
-
/**
|
|
4
|
-
* Factory so every app uses the same baseline behavior.
|
|
5
|
-
* Pass overrides to adjust defaults without forking.
|
|
6
|
-
*/
|
|
7
3
|
export function createQueryClient(overrides = {}) {
|
|
8
4
|
return new QueryClient({
|
|
9
5
|
...defaultQueryClientConfig,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-client.js","sourceRoot":"","sources":["../../src/react/query-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA0B,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAIzD,MAAM,UAAU,iBAAiB,CAAC,YAAsC,EAAE;IACxE,OAAO,IAAI,WAAW,CAAC;QACrB,GAAG,wBAAwB;QAC3B,GAAG,SAAS;QACZ,cAAc,EAAE;YACd,GAAG,wBAAwB,CAAC,cAAc;YAC1C,GAAG,SAAS,CAAC,cAAc;YAC3B,OAAO,EAAE;gBACP,GAAG,wBAAwB,CAAC,cAAc,EAAE,OAAO;gBACnD,GAAG,SAAS,CAAC,cAAc,EAAE,OAAO;aACrC;YACD,SAAS,EAAE;gBACT,GAAG,wBAAwB,CAAC,cAAc,EAAE,SAAS;gBACrD,GAAG,SAAS,CAAC,cAAc,EAAE,SAAS;aACvC;SACF;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { QueryClient, type QueryClientConfig } from '@tanstack/react-query';\nimport { defaultQueryClientConfig } from './defaults.js';\n\nexport type CreateQueryClientOptions = QueryClientConfig;\n\nexport function createQueryClient(overrides: CreateQueryClientOptions = {}) {\n return new QueryClient({\n ...defaultQueryClientConfig,\n ...overrides,\n defaultOptions: {\n ...defaultQueryClientConfig.defaultOptions,\n ...overrides.defaultOptions,\n queries: {\n ...defaultQueryClientConfig.defaultOptions?.queries,\n ...overrides.defaultOptions?.queries,\n },\n mutations: {\n ...defaultQueryClientConfig.defaultOptions?.mutations,\n ...overrides.defaultOptions?.mutations,\n },\n },\n });\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DocumentData } from 'firebase/firestore';
|
|
2
|
+
import type { SearchEqualityFilter } from '../../search/types.js';
|
|
3
|
+
export interface FirestoreSearchOptions<T extends DocumentData = DocumentData> {
|
|
4
|
+
collectionPath: string;
|
|
5
|
+
searchField: string;
|
|
6
|
+
queryText: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
select?: (data: DocumentData & {
|
|
10
|
+
id: string;
|
|
11
|
+
}) => T;
|
|
12
|
+
debounceMs?: number;
|
|
13
|
+
staleTime?: number;
|
|
14
|
+
equalityFilters?: SearchEqualityFilter[];
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/search/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAElE,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,oBAAoB,EAAE,CAAC;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/react/search/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { DocumentData } from 'firebase/firestore';\nimport type { SearchEqualityFilter } from '../../search/types.js';\n\nexport interface FirestoreSearchOptions<T extends DocumentData = DocumentData> {\n collectionPath: string;\n searchField: string;\n queryText: string;\n limit?: number;\n enabled?: boolean;\n select?: (data: DocumentData & { id: string }) => T;\n debounceMs?: number;\n staleTime?: number;\n equalityFilters?: SearchEqualityFilter[];\n}\n"]}
|
|
@@ -1,35 +1,6 @@
|
|
|
1
1
|
import { type UseQueryResult } from '@tanstack/react-query';
|
|
2
2
|
import { type DocumentData } from 'firebase/firestore';
|
|
3
|
-
import type { FirestoreSearchOptions } from '
|
|
3
|
+
import type { FirestoreSearchOptions } from './types.js';
|
|
4
4
|
import type { WithId } from '../../firestore/types.js';
|
|
5
|
-
/**
|
|
6
|
-
* Generic Firestore search hook with debouncing and case-insensitive prefix matching.
|
|
7
|
-
* Automatically normalizes search text to lowercase and uses Firestore range queries.
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - Debounced input (default 300ms)
|
|
11
|
-
* - Case-insensitive search
|
|
12
|
-
* - Searches only a text `isSearchableText` accepts (`FIRESTORE_SEARCH_MIN_LENGTH`, trimmed)
|
|
13
|
-
* - No answer while the debounce lags the typed text: `data` is undefined and `isLoading` is true
|
|
14
|
-
* (idle instead when the typed text is too short or the search is disabled)
|
|
15
|
-
* - Prefix matching with '\uf8ff' suffix
|
|
16
|
-
* - Configurable result limit
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* const { data, isLoading } = useFirestoreSearch<UserProfile>({
|
|
21
|
-
* collectionPath: 'userProfiles',
|
|
22
|
-
* searchField: 'username',
|
|
23
|
-
* queryText: searchValue,
|
|
24
|
-
* });
|
|
25
|
-
*
|
|
26
|
-
* const { data } = useFirestoreSearch<Team>({
|
|
27
|
-
* collectionPath: 'teams',
|
|
28
|
-
* searchField: 'name_lowercase',
|
|
29
|
-
* queryText: searchValue,
|
|
30
|
-
* limit: 10,
|
|
31
|
-
* });
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
5
|
export declare function useFirestoreSearch<T extends DocumentData = DocumentData>({ collectionPath, searchField, queryText, limit, enabled, select, debounceMs, staleTime, equalityFilters, }: FirestoreSearchOptions<T>): UseQueryResult<WithId<T>[], Error>;
|
|
35
6
|
//# sourceMappingURL=use-firestore-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-firestore-search.d.ts","sourceRoot":"","sources":["../../../src/react/search/use-firestore-search.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"use-firestore-search.d.ts","sourceRoot":"","sources":["../../../src/react/search/use-firestore-search.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAOL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAOvD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EACxE,cAAc,EACd,WAAW,EACX,SAAS,EACT,KAAqB,EACrB,OAAc,EACd,MAAM,EACN,UAAgC,EAChC,SAA8B,EAC9B,eAAoB,GACrB,EAAE,sBAAsB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAyDhE"}
|
|
@@ -7,40 +7,10 @@ import { keys } from '../../keys.js';
|
|
|
7
7
|
import { isSearchableText, normalizeSearchText } from '../../search/rule.js';
|
|
8
8
|
const DEFAULT_LIMIT = 5;
|
|
9
9
|
const DEFAULT_DEBOUNCE_MS = 300;
|
|
10
|
-
const DEFAULT_STALE_TIME = 60 * 1000;
|
|
11
|
-
/**
|
|
12
|
-
* Generic Firestore search hook with debouncing and case-insensitive prefix matching.
|
|
13
|
-
* Automatically normalizes search text to lowercase and uses Firestore range queries.
|
|
14
|
-
*
|
|
15
|
-
* Features:
|
|
16
|
-
* - Debounced input (default 300ms)
|
|
17
|
-
* - Case-insensitive search
|
|
18
|
-
* - Searches only a text `isSearchableText` accepts (`FIRESTORE_SEARCH_MIN_LENGTH`, trimmed)
|
|
19
|
-
* - No answer while the debounce lags the typed text: `data` is undefined and `isLoading` is true
|
|
20
|
-
* (idle instead when the typed text is too short or the search is disabled)
|
|
21
|
-
* - Prefix matching with '\uf8ff' suffix
|
|
22
|
-
* - Configurable result limit
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* ```typescript
|
|
26
|
-
* const { data, isLoading } = useFirestoreSearch<UserProfile>({
|
|
27
|
-
* collectionPath: 'userProfiles',
|
|
28
|
-
* searchField: 'username',
|
|
29
|
-
* queryText: searchValue,
|
|
30
|
-
* });
|
|
31
|
-
*
|
|
32
|
-
* const { data } = useFirestoreSearch<Team>({
|
|
33
|
-
* collectionPath: 'teams',
|
|
34
|
-
* searchField: 'name_lowercase',
|
|
35
|
-
* queryText: searchValue,
|
|
36
|
-
* limit: 10,
|
|
37
|
-
* });
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
10
|
+
const DEFAULT_STALE_TIME = 60 * 1000;
|
|
40
11
|
export function useFirestoreSearch({ collectionPath, searchField, queryText, limit = DEFAULT_LIMIT, enabled = true, select, debounceMs = DEFAULT_DEBOUNCE_MS, staleTime = DEFAULT_STALE_TIME, equalityFilters = [], }) {
|
|
41
12
|
const db = useFirestoreDb();
|
|
42
13
|
const [debouncedQuery, setDebouncedQuery] = useState(queryText);
|
|
43
|
-
// Debounce the search query
|
|
44
14
|
useEffect(() => {
|
|
45
15
|
const timer = setTimeout(() => {
|
|
46
16
|
setDebouncedQuery(queryText);
|
|
@@ -56,8 +26,7 @@ export function useFirestoreSearch({ collectionPath, searchField, queryText, lim
|
|
|
56
26
|
return [];
|
|
57
27
|
}
|
|
58
28
|
const collectionRef = collection(db, collectionPath);
|
|
59
|
-
//
|
|
60
|
-
// Using '\uf8ff' as upper bound creates a range that matches all strings starting with the query
|
|
29
|
+
// '\uf8ff' is the highest code point Firestore orders, so the range covers every string with the prefix.
|
|
61
30
|
const constraints = [
|
|
62
31
|
...equalityFilters.map((f) => where(f.field, '==', f.value)),
|
|
63
32
|
where(searchField, '>=', normalizedQuery),
|
|
@@ -77,20 +46,13 @@ export function useFirestoreSearch({ collectionPath, searchField, queryText, lim
|
|
|
77
46
|
enabled: shouldSearch,
|
|
78
47
|
staleTime,
|
|
79
48
|
});
|
|
80
|
-
// Until the debounce catches up, the query's key holds an earlier text, so its result (or its
|
|
81
|
-
// absence) is not an answer for the text typed now (FRONTEND-206): report no answer instead —
|
|
82
|
-
// still searching when the typed text will be searched, idle when it will not.
|
|
83
49
|
const typedQuery = normalizeSearchText(queryText);
|
|
84
50
|
if (typedQuery === normalizedQuery)
|
|
85
51
|
return searchQuery;
|
|
86
52
|
const willSearch = enabled && isSearchableText(queryText);
|
|
87
53
|
return withoutAnswer(searchQuery, willSearch);
|
|
88
54
|
}
|
|
89
|
-
/**
|
|
90
|
-
* The query result with every answer field replaced. The other fields stay getters onto the
|
|
91
|
-
* original result: TanStack tracks which fields a component reads to decide when it re-renders,
|
|
92
|
-
* and reading them all here (a spread) would opt every consumer out of that.
|
|
93
|
-
*/
|
|
55
|
+
/** Other fields stay getters onto the original result: TanStack re-renders on the fields a component reads, and a spread reads them all. */
|
|
94
56
|
function withoutAnswer(result, searching) {
|
|
95
57
|
const answerFields = {
|
|
96
58
|
data: undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-firestore-search.js","sourceRoot":"","sources":["../../../src/react/search/use-firestore-search.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AACtE,OAAO,EACL,UAAU,EACV,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,KAAK,IAAI,cAAc,GAGxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC
|
|
1
|
+
{"version":3,"file":"use-firestore-search.js","sourceRoot":"","sources":["../../../src/react/search/use-firestore-search.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useState, useEffect } from 'react';\nimport { useQuery, type UseQueryResult } from '@tanstack/react-query';\nimport {\n collection,\n getDocs,\n query,\n where,\n orderBy,\n limit as firestoreLimit,\n type DocumentData,\n type QueryConstraint,\n} from 'firebase/firestore';\nimport { useFirestoreDb } from '../firestore/context.js';\nimport { keys } from '../../keys.js';\nimport type { FirestoreSearchOptions } from './types.js';\nimport type { WithId } from '../../firestore/types.js';\nimport { isSearchableText, normalizeSearchText } from '../../search/rule.js';\n\nconst DEFAULT_LIMIT = 5;\nconst DEFAULT_DEBOUNCE_MS = 300;\nconst DEFAULT_STALE_TIME = 60 * 1000;\n\nexport function useFirestoreSearch<T extends DocumentData = DocumentData>({\n collectionPath,\n searchField,\n queryText,\n limit = DEFAULT_LIMIT,\n enabled = true,\n select,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n staleTime = DEFAULT_STALE_TIME,\n equalityFilters = [],\n}: FirestoreSearchOptions<T>): UseQueryResult<WithId<T>[], Error> {\n const db = useFirestoreDb();\n const [debouncedQuery, setDebouncedQuery] = useState(queryText);\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setDebouncedQuery(queryText);\n }, debounceMs);\n\n return () => clearTimeout(timer);\n }, [queryText, debounceMs]);\n\n const normalizedQuery = normalizeSearchText(debouncedQuery);\n const shouldSearch = enabled && isSearchableText(debouncedQuery);\n\n const searchQuery = useQuery({\n queryKey: keys.custom(\n 'search',\n collectionPath,\n searchField,\n normalizedQuery,\n JSON.stringify(equalityFilters),\n ),\n queryFn: async (): Promise<WithId<T>[]> => {\n if (!shouldSearch) {\n return [];\n }\n\n const collectionRef = collection(db, collectionPath);\n\n // '\\uf8ff' is the highest code point Firestore orders, so the range covers every string with the prefix.\n const constraints: QueryConstraint[] = [\n ...equalityFilters.map((f) => where(f.field, '==', f.value)),\n where(searchField, '>=', normalizedQuery),\n where(searchField, '<=', normalizedQuery + '\\uf8ff'),\n orderBy(searchField, 'asc'),\n firestoreLimit(limit),\n ];\n\n const q = query(collectionRef, ...constraints);\n const snapshot = await getDocs(q);\n\n return snapshot.docs.map((docSnap) => {\n const rawData = docSnap.data();\n const dataWithId = { id: docSnap.id, ...rawData };\n const data = select ? select(dataWithId) : dataWithId;\n return data as WithId<T>;\n });\n },\n enabled: shouldSearch,\n staleTime,\n });\n\n const typedQuery = normalizeSearchText(queryText);\n if (typedQuery === normalizedQuery) return searchQuery;\n const willSearch = enabled && isSearchableText(queryText);\n return withoutAnswer(searchQuery, willSearch);\n}\n\n/** Other fields stay getters onto the original result: TanStack re-renders on the fields a component reads, and a spread reads them all. */\nfunction withoutAnswer<R extends UseQueryResult<unknown, Error>>(result: R, searching: boolean): R {\n const answerFields: Record<string, unknown> = {\n data: undefined,\n error: null,\n isError: false,\n isLoadingError: false,\n isRefetchError: false,\n isSuccess: false,\n isPending: true,\n isPlaceholderData: false,\n isFetched: false,\n isFetchedAfterMount: false,\n isRefetching: false,\n isLoading: searching,\n isFetching: searching,\n status: 'pending',\n fetchStatus: searching ? 'fetching' : 'idle',\n dataUpdatedAt: 0,\n errorUpdatedAt: 0,\n };\n const view = {} as R;\n for (const key of Object.keys(result)) {\n Object.defineProperty(view, key, {\n enumerable: true,\n configurable: true,\n get: key in answerFields ? () => answerFields[key] : () => result[key as keyof R],\n });\n }\n return view;\n}\n"]}
|
|
@@ -1,40 +1,20 @@
|
|
|
1
1
|
import { type QueryKey } from '@tanstack/react-query';
|
|
2
|
-
/** The parts of a read's result the retry state is decided from. */
|
|
3
2
|
export interface ReadRetrySource<TError = Error> {
|
|
4
3
|
error: TError | null | undefined;
|
|
5
4
|
isFetching: boolean;
|
|
6
5
|
isLoading: boolean;
|
|
7
|
-
/** A fetch waiting for the network counts as still running. */
|
|
8
6
|
isPaused?: boolean;
|
|
9
7
|
}
|
|
10
|
-
/** A read's failure as its surface shows it, held on screen while a retry of it runs. */
|
|
11
8
|
export interface ReadRetryState<TError = Error> {
|
|
12
|
-
/** The read's failure: its current one, or the last one while a retry of it is running. */
|
|
13
9
|
error: TError | null;
|
|
14
|
-
/** A retry of the failed read is running. */
|
|
15
10
|
isRetrying: boolean;
|
|
16
|
-
/** The read has not answered yet and is not a retry of a failure. */
|
|
17
11
|
isLoading: boolean;
|
|
18
12
|
}
|
|
19
|
-
/** One read a keyed retry state follows: its result and its identity. */
|
|
20
13
|
export interface ReadRetryEntry<TError = Error> {
|
|
21
14
|
source: ReadRetrySource<TError>;
|
|
22
15
|
queryKey: QueryKey;
|
|
23
16
|
}
|
|
24
|
-
/**
|
|
25
|
-
* The failure and retry state of each of several reads, in entry order — the keyed form of
|
|
26
|
-
* `useReadRetryState` for a set whose size changes between renders (a `useQueries` list, a batch
|
|
27
|
-
* of per-id reads). Each read's failure is held under its own `queryKey`, so one read's retry
|
|
28
|
-
* never shows another's failure, and a read that leaves the set drops what it held.
|
|
29
|
-
*/
|
|
30
17
|
export declare function useReadRetryStates<TError = Error>(entries: readonly ReadRetryEntry<TError>[]): ReadRetryState<TError>[];
|
|
31
|
-
/**
|
|
32
|
-
* The failure and retry state of one read. TanStack Query clears the `error` of a query that never
|
|
33
|
-
* succeeded and puts it back to `pending` the moment a refetch of it starts, so a retry state built
|
|
34
|
-
* from `isFetching && error` is never true for a failed first read, and its failure panel turns into
|
|
35
|
-
* a loading state under the retry the person pressed. This holds the read's last failure until the
|
|
36
|
-
* retry settles — an answer clears it, a new failure replaces it — and reports the retry as running
|
|
37
|
-
* meanwhile. The failure belongs to `queryKey`: a read under another key never shows it.
|
|
38
|
-
*/
|
|
18
|
+
/** TanStack clears a never-succeeded query's `error` as its refetch starts, so the failure is held until the retry settles. */
|
|
39
19
|
export declare function useReadRetryState<TError = Error>(source: ReadRetrySource<TError>, queryKey: QueryKey): ReadRetryState<TError>;
|
|
40
20
|
//# sourceMappingURL=use-read-retry-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-read-retry-state.d.ts","sourceRoot":"","sources":["../../src/react/use-read-retry-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE/D,
|
|
1
|
+
{"version":3,"file":"use-read-retry-state.d.ts","sourceRoot":"","sources":["../../src/react/use-read-retry-state.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,WAAW,eAAe,CAAC,MAAM,GAAG,KAAK;IAC7C,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,KAAK;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAeD,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,KAAK;IAC5C,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAyBD,wBAAgB,kBAAkB,CAAC,MAAM,GAAG,KAAK,EAC/C,OAAO,EAAE,SAAS,cAAc,CAAC,MAAM,CAAC,EAAE,GACzC,cAAc,CAAC,MAAM,CAAC,EAAE,CAY1B;AAUD,+HAA+H;AAC/H,wBAAgB,iBAAiB,CAAC,MAAM,GAAG,KAAK,EAC9C,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,EAC/B,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,MAAM,CAAC,CAExB"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { hashKey } from '@tanstack/react-query';
|
|
4
|
-
/**
|
|
5
|
-
* One failure is its name, message, and `code`, not its object: a composite source that builds its
|
|
6
|
-
* error in render hands back a new object for the same failure every render, and holding each one
|
|
7
|
-
* as new would set state on every render without end.
|
|
8
|
-
*/
|
|
4
|
+
/** A composite source builds a new error object every render, so a failure is compared by name, message, and `code`. */
|
|
9
5
|
function sameFailure(a, b) {
|
|
10
6
|
if (a === b)
|
|
11
7
|
return true;
|
|
@@ -15,7 +11,6 @@ function sameFailure(a, b) {
|
|
|
15
11
|
const y = b;
|
|
16
12
|
return x.name === y.name && x.message === y.message && x.code === y.code;
|
|
17
13
|
}
|
|
18
|
-
/** The failure a read holds next, given what it held: its live failure, or the held one while it runs. */
|
|
19
14
|
function nextHeldFailure(held, source) {
|
|
20
15
|
const live = source.error ?? null;
|
|
21
16
|
if (live !== null)
|
|
@@ -31,12 +26,6 @@ function toRetryState(source, held) {
|
|
|
31
26
|
isLoading: error === null && source.isLoading,
|
|
32
27
|
};
|
|
33
28
|
}
|
|
34
|
-
/**
|
|
35
|
-
* The failure and retry state of each of several reads, in entry order — the keyed form of
|
|
36
|
-
* `useReadRetryState` for a set whose size changes between renders (a `useQueries` list, a batch
|
|
37
|
-
* of per-id reads). Each read's failure is held under its own `queryKey`, so one read's retry
|
|
38
|
-
* never shows another's failure, and a read that leaves the set drops what it held.
|
|
39
|
-
*/
|
|
40
29
|
export function useReadRetryStates(entries) {
|
|
41
30
|
const [held, setHeld] = useState({});
|
|
42
31
|
const next = {};
|
|
@@ -47,7 +36,6 @@ export function useReadRetryStates(entries) {
|
|
|
47
36
|
next[identity] = failure;
|
|
48
37
|
return toRetryState(source, failure);
|
|
49
38
|
});
|
|
50
|
-
// Render-phase adjustment: the held failures follow the reads without an effect's extra render.
|
|
51
39
|
if (!sameHeld(held, next))
|
|
52
40
|
setHeld(next);
|
|
53
41
|
return states;
|
|
@@ -56,14 +44,7 @@ function sameHeld(a, b) {
|
|
|
56
44
|
const keys = Object.keys(a);
|
|
57
45
|
return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
|
|
58
46
|
}
|
|
59
|
-
/**
|
|
60
|
-
* The failure and retry state of one read. TanStack Query clears the `error` of a query that never
|
|
61
|
-
* succeeded and puts it back to `pending` the moment a refetch of it starts, so a retry state built
|
|
62
|
-
* from `isFetching && error` is never true for a failed first read, and its failure panel turns into
|
|
63
|
-
* a loading state under the retry the person pressed. This holds the read's last failure until the
|
|
64
|
-
* retry settles — an answer clears it, a new failure replaces it — and reports the retry as running
|
|
65
|
-
* meanwhile. The failure belongs to `queryKey`: a read under another key never shows it.
|
|
66
|
-
*/
|
|
47
|
+
/** TanStack clears a never-succeeded query's `error` as its refetch starts, so the failure is held until the retry settles. */
|
|
67
48
|
export function useReadRetryState(source, queryKey) {
|
|
68
49
|
return useReadRetryStates([{ source, queryKey }])[0];
|
|
69
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-read-retry-state.js","sourceRoot":"","sources":["../../src/react/use-read-retry-state.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,OAAO,EAAiB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-read-retry-state.js","sourceRoot":"","sources":["../../src/react/use-read-retry-state.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { hashKey, type QueryKey } from '@tanstack/react-query';\n\nexport interface ReadRetrySource<TError = Error> {\n error: TError | null | undefined;\n isFetching: boolean;\n isLoading: boolean;\n isPaused?: boolean;\n}\n\nexport interface ReadRetryState<TError = Error> {\n error: TError | null;\n isRetrying: boolean;\n isLoading: boolean;\n}\n\ninterface HeldFailure<TError> {\n error: TError;\n}\n\n/** A composite source builds a new error object every render, so a failure is compared by name, message, and `code`. */\nfunction sameFailure(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;\n const x = a as { name?: unknown; message?: unknown; code?: unknown };\n const y = b as { name?: unknown; message?: unknown; code?: unknown };\n return x.name === y.name && x.message === y.message && x.code === y.code;\n}\n\nexport interface ReadRetryEntry<TError = Error> {\n source: ReadRetrySource<TError>;\n queryKey: QueryKey;\n}\n\nfunction nextHeldFailure<TError>(\n held: HeldFailure<TError> | undefined,\n source: ReadRetrySource<TError>,\n): HeldFailure<TError> | undefined {\n const live = source.error ?? null;\n if (live !== null)\n return held !== undefined && sameFailure(held.error, live) ? held : { error: live };\n const running = source.isFetching || source.isPaused === true;\n return running ? held : undefined;\n}\n\nfunction toRetryState<TError>(\n source: ReadRetrySource<TError>,\n held: HeldFailure<TError> | undefined,\n): ReadRetryState<TError> {\n const error = source.error ?? held?.error ?? null;\n return {\n error,\n isRetrying: error !== null && (source.isFetching || source.isPaused === true),\n isLoading: error === null && source.isLoading,\n };\n}\n\nexport function useReadRetryStates<TError = Error>(\n entries: readonly ReadRetryEntry<TError>[],\n): ReadRetryState<TError>[] {\n const [held, setHeld] = useState<Readonly<Record<string, HeldFailure<TError>>>>({});\n\n const next: Record<string, HeldFailure<TError>> = {};\n const states = entries.map(({ source, queryKey }) => {\n const identity = hashKey(queryKey);\n const failure = nextHeldFailure(held[identity], source);\n if (failure !== undefined) next[identity] = failure;\n return toRetryState(source, failure);\n });\n if (!sameHeld(held, next)) setHeld(next);\n return states;\n}\n\nfunction sameHeld<TError>(\n a: Readonly<Record<string, HeldFailure<TError>>>,\n b: Readonly<Record<string, HeldFailure<TError>>>,\n): boolean {\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);\n}\n\n/** TanStack clears a never-succeeded query's `error` as its refetch starts, so the failure is held until the retry settles. */\nexport function useReadRetryState<TError = Error>(\n source: ReadRetrySource<TError>,\n queryKey: QueryKey,\n): ReadRetryState<TError> {\n return useReadRetryStates([{ source, queryKey }])[0]!;\n}\n"]}
|
package/dist/search/rule.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/** The fewest characters, counted on the normalized text, that `useFirestoreSearch` searches. */
|
|
2
1
|
export declare const FIRESTORE_SEARCH_MIN_LENGTH = 3;
|
|
3
|
-
/**
|
|
4
|
-
* The text `useFirestoreSearch` reads for a typed text: lowercased, surrounding whitespace dropped.
|
|
5
|
-
* Two typed texts with the same normalized text are the same search.
|
|
6
|
-
*/
|
|
7
2
|
export declare function normalizeSearchText(text: string): string;
|
|
8
|
-
/** Whether `useFirestoreSearch` searches this typed text (its normalized length reaches the minimum). */
|
|
9
3
|
export declare function isSearchableText(text: string): boolean;
|
|
10
4
|
//# sourceMappingURL=rule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/search/rule.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../src/search/rule.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|
package/dist/search/rule.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
/** The fewest characters, counted on the normalized text, that `useFirestoreSearch` searches. */
|
|
2
1
|
export const FIRESTORE_SEARCH_MIN_LENGTH = 3;
|
|
3
|
-
/**
|
|
4
|
-
* The text `useFirestoreSearch` reads for a typed text: lowercased, surrounding whitespace dropped.
|
|
5
|
-
* Two typed texts with the same normalized text are the same search.
|
|
6
|
-
*/
|
|
7
2
|
export function normalizeSearchText(text) {
|
|
8
3
|
return text.toLowerCase().trim();
|
|
9
4
|
}
|
|
10
|
-
/** Whether `useFirestoreSearch` searches this typed text (its normalized length reaches the minimum). */
|
|
11
5
|
export function isSearchableText(text) {
|
|
12
6
|
return normalizeSearchText(text).length >= FIRESTORE_SEARCH_MIN_LENGTH;
|
|
13
7
|
}
|
package/dist/search/rule.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../src/search/rule.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../src/search/rule.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAE7C,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,2BAA2B,CAAC;AACzE,CAAC","sourcesContent":["export const FIRESTORE_SEARCH_MIN_LENGTH = 3;\n\nexport function normalizeSearchText(text: string): string {\n return text.toLowerCase().trim();\n}\n\nexport function isSearchableText(text: string): boolean {\n return normalizeSearchText(text).length >= FIRESTORE_SEARCH_MIN_LENGTH;\n}\n"]}
|
package/dist/search/types.d.ts
CHANGED
|
@@ -1,50 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Equality filter applied before the prefix range in a Firestore search.
|
|
4
|
-
* Each becomes a `where(field, '==', value)` constraint, ordered ahead of the
|
|
5
|
-
* search field. The composite index must list these equality fields (ASC)
|
|
6
|
-
* before the search field (ASC).
|
|
7
|
-
*/
|
|
1
|
+
/** The composite index must list these equality fields (ASC) before the search field (ASC). */
|
|
8
2
|
export interface SearchEqualityFilter {
|
|
9
3
|
field: string;
|
|
10
4
|
value: unknown;
|
|
11
5
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Configuration for a Firestore search query
|
|
14
|
-
*/
|
|
15
6
|
export interface FirestoreSearchConfig {
|
|
16
|
-
/** Firestore collection path to search */
|
|
17
7
|
collectionPath: string;
|
|
18
|
-
/** Field name to search on (must be indexed and lowercase) */
|
|
19
8
|
searchField: string;
|
|
20
|
-
/** Maximum number of results to return */
|
|
21
9
|
limit: number;
|
|
22
|
-
/** Optional equality filters applied before the prefix range. */
|
|
23
|
-
equalityFilters?: SearchEqualityFilter[];
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Options for useFirestoreSearch hook
|
|
27
|
-
*/
|
|
28
|
-
export interface FirestoreSearchOptions<T extends DocumentData = DocumentData> {
|
|
29
|
-
/** Firestore collection path to search */
|
|
30
|
-
collectionPath: string;
|
|
31
|
-
/** Field name to search on (must be indexed and lowercase) */
|
|
32
|
-
searchField: string;
|
|
33
|
-
/** Search query text */
|
|
34
|
-
queryText: string;
|
|
35
|
-
/** Maximum number of results (default: 5) */
|
|
36
|
-
limit?: number;
|
|
37
|
-
/** Enable/disable the search (default: true) */
|
|
38
|
-
enabled?: boolean;
|
|
39
|
-
/** Transform function to convert raw Firestore data to typed result */
|
|
40
|
-
select?: (data: DocumentData & {
|
|
41
|
-
id: string;
|
|
42
|
-
}) => T;
|
|
43
|
-
/** Debounce delay in milliseconds (default: 300) */
|
|
44
|
-
debounceMs?: number;
|
|
45
|
-
/** React Query stale time (default: 60000ms) */
|
|
46
|
-
staleTime?: number;
|
|
47
|
-
/** Optional equality filters applied before the prefix range. */
|
|
48
10
|
equalityFilters?: SearchEqualityFilter[];
|
|
49
11
|
}
|
|
50
12
|
//# sourceMappingURL=types.d.ts.map
|