@tatsuokaniwa/swr-firestore 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@tatsuokaniwa%2Fswr-firestore.svg)](https://badge.fury.io/js/@tatsuokaniwa%2Fswr-firestore)
4
4
  [![Node.js CI](https://github.com/t-k/swr-firestore/actions/workflows/test.yaml/badge.svg)](https://github.com/t-k/swr-firestore/actions/workflows/test.yaml)
5
+ [![codecov](https://codecov.io/gh/t-k/swr-firestore/branch/main/graph/badge.svg?token=6WREEC5HKZ)](https://codecov.io/gh/t-k/swr-firestore)
5
6
 
6
7
  React Hooks library for Firestore, built using the Firebase v9 modular SDK. It utilizes the [`useSWRSubscription`](https://swr.vercel.app/ja/docs/subscription) function from SWR library to enable subscription-based data fetching and caching.
7
8
 
@@ -39,7 +40,7 @@ export default function App() {
39
40
  const { data } = useCollection<Post>(
40
41
  isLogin
41
42
  ? {
42
- path: "Posts",
43
+ path: "posts",
43
44
  where: [["status", "==", "published"]],
44
45
  orderBy: [["createdAt", "desc"]],
45
46
  parseDates: ["createdAt"],
@@ -47,7 +48,7 @@ export default function App() {
47
48
  : null
48
49
  );
49
50
  const { data: postCount } = useCollectionCount<Post>({
50
- path: "Posts",
51
+ path: "posts",
51
52
  where: [["status", "==", "published"]],
52
53
  });
53
54
  return (
@@ -64,6 +65,29 @@ export default function App() {
64
65
  }
65
66
  ```
66
67
 
68
+ ### For more complex queries
69
+ To perform complex queries like using `OR` queries or raw `QueryConstraint`, use the `queryConstraints` parameter.
70
+ However, this method does not provide input completion for field names from type definitions.
71
+
72
+ ```tsx
73
+ import {
74
+ or,
75
+ orderBy,
76
+ where,
77
+ } from "firebase/firestore";
78
+
79
+ useCollection<City>({
80
+ path: "cities",
81
+ queryConstraints: [
82
+ or(
83
+ where('capital', '==', true),
84
+ where('population', '>=', 1000000)
85
+ ),
86
+ orderBy("createdAt", "desc"),
87
+ ],
88
+ })
89
+ ```
90
+
67
91
  ## API
68
92
 
69
93
  ```ts
@@ -74,7 +98,7 @@ import {
74
98
  useCollectionGroupCount, // Wrapper for getCountFromServer for collectionGroup
75
99
  useDoc, // Subscription for document
76
100
  useGetDocs, // Fetch documents with firestore's getDocs
77
- useGetDoc, // Fetch documents with firestore's getDoc
101
+ useGetDoc, // Fetch document with firestore's getDoc
78
102
  } from "@tatsuokaniwa/swr-firestore";
79
103
  ```
80
104
 
@@ -83,16 +107,29 @@ import {
83
107
  ```ts
84
108
  import type { orderBy, where } from "firebase/firestore";
85
109
  // First argument of hook, specifies options to firestore, and is also used as a key for SWR.
86
- type KeyParams<T> = {
87
- // The path to the collection or document of Firestore.
88
- path: string;
89
- // `Paths` means object's property path, including nested object
90
- where?: [Paths<T>, Parameters<typeof where>[1], ValueOf<T> | unknown][];
91
- orderBy?: [Paths<T>, Parameters<typeof orderBy>[1]][];
92
- limit?: number;
93
- // Array of field names that should be parsed as dates.
94
- parseDates?: Paths<T>[];
95
- };
110
+ type KeyParams<T> =
111
+ | {
112
+ // The path to the collection or document of Firestore.
113
+ path: string
114
+ // `Paths` means object's property path, including nested object
115
+ where?: [Paths<T>, Parameters<typeof where>[1], ValueOf<T> | unknown][]
116
+ orderBy?: [Paths<T>, Parameters<typeof orderBy>[1]][]
117
+ limit?: number
118
+ // Array of field names that should be parsed as dates.
119
+ parseDates?: Paths<T>[]
120
+ }
121
+ // OR for more complex query
122
+ | {
123
+ // The path to the collection or document of Firestore.
124
+ path: string
125
+ // raw query constraints from `firebase/firestore`
126
+ queryConstraints?:
127
+ | [QueryCompositeFilterConstraint, ...Array<QueryNonFilterConstraint>]
128
+ | QueryConstraint[]
129
+ // Array of field names that should be parsed as dates.
130
+ parseDates?: Paths<T>[]
131
+ }
132
+
96
133
  ```
97
134
 
98
135
  ### Type definitions for return data
@@ -114,7 +151,7 @@ Subscription for collection
114
151
  #### Return values
115
152
 
116
153
  - `data`: data for given path's collection
117
- - `error`: FirestoreError | Error
154
+ - `error`: FirestoreError
118
155
 
119
156
  ```ts
120
157
  import { useCollection } from "@tatsuokaniwa/swr-firestore";
@@ -138,7 +175,7 @@ Wrapper for getCountFromServer for collection
138
175
  Returns [`SWRResponse`](https://swr.vercel.app/docs/api#return-values)
139
176
 
140
177
  - `data`: number for given path's collection count result
141
- - `error`: FirestoreError | Error
178
+ - `error`: FirestoreError
142
179
  - `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
143
180
  - `isValidating`: if there's a request or revalidation loading
144
181
  - `mutate(data?, options?)`: function to mutate the cached data (details)
@@ -166,7 +203,7 @@ Subscription for collectionGroup
166
203
  #### Return values
167
204
 
168
205
  - `data`: data for given path's collectionGroup
169
- - `error`: FirestoreError | Error
206
+ - `error`: FirestoreError
170
207
 
171
208
  ### `useCollectionGroupCount(params, swrOptions)`
172
209
 
@@ -182,7 +219,7 @@ Wrapper for getCountFromServer for collectionGroup
182
219
  Returns [`SWRResponse`](https://swr.vercel.app/docs/api#return-values)
183
220
 
184
221
  - `data`: number for given path's collectionGroup count result
185
- - `error`: FirestoreError | Error
222
+ - `error`: FirestoreError
186
223
  - `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
187
224
  - `isValidating`: if there's a request or revalidation loading
188
225
  - `mutate(data?, options?)`: function to mutate the cached data (details)
@@ -198,7 +235,7 @@ Subscription for document
198
235
  #### Return values
199
236
 
200
237
  - `data`: data for given path's document
201
- - `error`: FirestoreError | Error
238
+ - `error`: FirestoreError
202
239
 
203
240
  ```ts
204
241
  import { useDoc } from "@tatsuokaniwa/swr-firestore";
@@ -222,7 +259,7 @@ Fetch documents with firestore's [getDocs](https://firebase.google.com/docs/refe
222
259
  Returns [`SWRResponse`](https://swr.vercel.app/docs/api#return-values)
223
260
 
224
261
  - `data`: data for given path's collection
225
- - `error`: FirestoreError | Error
262
+ - `error`: FirestoreError
226
263
  - `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
227
264
  - `isValidating`: if there's a request or revalidation loading
228
265
  - `mutate(data?, options?)`: function to mutate the cached data (details)
@@ -249,7 +286,7 @@ Fetch the document with firestore's [getDoc](https://firebase.google.com/docs/re
249
286
  Returns [`SWRResponse`](https://swr.vercel.app/docs/api#return-values)
250
287
 
251
288
  - `data`: data for given path's document
252
- - `error`: FirestoreError | Error
289
+ - `error`: FirestoreError
253
290
  - `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
254
291
  - `isValidating`: if there's a request or revalidation loading
255
292
  - `mutate(data?, options?)`: function to mutate the cached data (details)
@@ -1,5 +1,5 @@
1
1
  import type { SWRSubscriptionResponse } from "swr/subscription";
2
2
  import type { FirestoreError } from "firebase/firestore";
3
3
  import type { DocumentData, KeyParams } from "../util/type";
4
- declare const useCollection: <T>(params: KeyParams<T> | null) => SWRSubscriptionResponse<DocumentData<T>[], FirestoreError | Error>;
4
+ declare const useCollection: <T>(params: KeyParams<T> | null) => SWRSubscriptionResponse<DocumentData<T>[], FirestoreError>;
5
5
  export default useCollection;
@@ -1,4 +1,4 @@
1
1
  import type { SWRHook } from "swr";
2
- import type { KeyParams } from "../util/type";
3
- declare const useCollectionCount: <T>(params: Omit<KeyParams<T>, "orderBy" | "parseDates"> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<number, any, Required<Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher">>>;
2
+ import type { KeyParamsForCount } from "../util/type";
3
+ declare const useCollectionCount: <T>(params: KeyParamsForCount<T> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<number | undefined, any, Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher"> | undefined>;
4
4
  export default useCollectionCount;
@@ -1,4 +1,4 @@
1
1
  import type { SWRHook } from "swr";
2
- import type { KeyParams } from "../util/type";
3
- declare const useCollectionGroupCount: <T>(params: Omit<KeyParams<T>, "orderBy" | "parseDates"> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<number, any, Required<Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher">>>;
2
+ import type { KeyParamsForCount } from "../util/type";
3
+ declare const useCollectionGroupCount: <T>(params: KeyParamsForCount<T> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<number | undefined, any, Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher"> | undefined>;
4
4
  export default useCollectionGroupCount;
@@ -1,5 +1,5 @@
1
- import { FirestoreError } from "firebase/firestore";
1
+ import type { FirestoreError } from "firebase/firestore";
2
2
  import type { SWRSubscriptionResponse } from "swr/subscription";
3
3
  import type { DocumentData, KeyParams } from "../util/type";
4
- declare const useDoc: <T>(params: Omit<KeyParams<T>, "where" | "orderBy" | "limit"> | null) => SWRSubscriptionResponse<DocumentData<T>, FirestoreError>;
4
+ declare const useDoc: <T>(params: Omit<KeyParams<T>, "orderBy" | "where" | "limit"> | null) => SWRSubscriptionResponse<DocumentData<T>, FirestoreError>;
5
5
  export default useDoc;
@@ -1,4 +1,4 @@
1
1
  import type { SWRHook } from "swr";
2
2
  import type { DocumentData, GetDocKeyParams } from "../util/type";
3
- declare const useGetDoc: <T>(params: Omit<GetDocKeyParams<T>, "where" | "orderBy" | "limit"> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<DocumentData<T> | undefined, any, Required<Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher">>>;
3
+ declare const useGetDoc: <T>(params: Omit<GetDocKeyParams<T>, "orderBy" | "where" | "limit"> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<DocumentData<T> | undefined, any, Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher"> | undefined>;
4
4
  export default useGetDoc;
@@ -1,4 +1,4 @@
1
1
  import type { SWRHook } from "swr";
2
2
  import type { DocumentData, GetDocKeyParams } from "../util/type";
3
- declare const useGetDocs: <T>(params: GetDocKeyParams<T> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<DocumentData<T>[] | undefined, any, Required<Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher">>>;
3
+ declare const useGetDocs: <T>(params: GetDocKeyParams<T> | null, swrOptions?: Omit<Parameters<SWRHook>[2], "fetcher">) => import("swr/_internal").SWRResponse<DocumentData<T>[] | undefined, any, Omit<Partial<import("swr/_internal").PublicConfiguration<unknown, unknown, import("swr/_internal").BareFetcher<unknown>>> | undefined, "fetcher"> | undefined>;
4
4
  export default useGetDocs;
package/dist/index.js CHANGED
@@ -24,21 +24,30 @@ const getFirestoreConverter = (parseDates) => ({
24
24
  return model;
25
25
  }
26
26
  });
27
+ const isQueryConstraintParams = (params) => {
28
+ return params.queryConstraints != null;
29
+ };
27
30
  const useCollection = (params) => {
28
- return useSWRSubscription(params, (_, { next }) => {
31
+ let swrKey = params;
32
+ if (params != null && isQueryConstraintParams(params)) {
33
+ swrKey = JSON.parse(JSON.stringify(params));
34
+ }
35
+ return useSWRSubscription(swrKey, (_, { next }) => {
29
36
  if (params == null) {
30
37
  return () => {
31
38
  };
32
39
  }
33
- const { path, ...options } = params;
34
- const converter = getFirestoreConverter(options == null ? void 0 : options.parseDates);
40
+ const { path, parseDates } = params;
41
+ const converter = getFirestoreConverter(parseDates);
35
42
  const ref = collection(getFirestore(), path);
36
43
  let q;
37
- if (options != null) {
38
- const { where: w, orderBy: o, limit: l } = options;
44
+ if (isQueryConstraintParams(params)) {
45
+ q = query(ref, ...params.queryConstraints);
46
+ } else {
47
+ const { where: w, orderBy: o, limit: l } = params;
39
48
  q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...(o ? o : []).map((q2) => orderBy(...q2)), ...l ? [limit(l)] : []);
40
49
  }
41
- const unsub = onSnapshot((q ?? ref).withConverter(converter), (qs) => {
50
+ const unsub = onSnapshot(q.withConverter(converter), (qs) => {
42
51
  next(null, qs.docs.map((x) => x.data()));
43
52
  }, (error) => {
44
53
  next(error);
@@ -47,34 +56,49 @@ const useCollection = (params) => {
47
56
  });
48
57
  };
49
58
  const useCollectionCount = (params, swrOptions) => {
50
- const fetcher = async (key) => {
51
- const { path, ...option } = key;
59
+ let swrKey = params;
60
+ if (params != null && isQueryConstraintParams(params)) {
61
+ swrKey = JSON.parse(JSON.stringify(params));
62
+ }
63
+ const fetcher = async () => {
64
+ if (params == null) {
65
+ return;
66
+ }
67
+ const { path } = params;
52
68
  const ref = collection(getFirestore(), path);
53
69
  let q;
54
- if (option) {
55
- const { where: w, orderBy: o, limit: l } = option;
56
- q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...(o ? o : []).map((q2) => orderBy(...q2)), ...l ? [limit(l)] : []);
70
+ if (isQueryConstraintParams(params)) {
71
+ q = query(ref, ...params.queryConstraints);
72
+ } else {
73
+ const { where: w, limit: l } = params;
74
+ q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...l ? [limit(l)] : []);
57
75
  }
58
- const sn = await getCountFromServer(q ?? ref);
76
+ const sn = await getCountFromServer(q);
59
77
  return sn.data().count;
60
78
  };
61
- return useSWR(params, fetcher, swrOptions);
79
+ return useSWR(swrKey, fetcher, swrOptions);
62
80
  };
63
81
  const useCollectionGroup = (params) => {
64
- return useSWRSubscription(params, (_, { next }) => {
82
+ let swrKey = params;
83
+ if (params != null && isQueryConstraintParams(params)) {
84
+ swrKey = JSON.parse(JSON.stringify(params));
85
+ }
86
+ return useSWRSubscription(swrKey, (_, { next }) => {
65
87
  if (params == null) {
66
88
  return () => {
67
89
  };
68
90
  }
69
- const { path, ...options } = params;
70
- const converter = getFirestoreConverter(options == null ? void 0 : options.parseDates);
91
+ const { path, parseDates } = params;
92
+ const converter = getFirestoreConverter(parseDates);
71
93
  const ref = collectionGroup(getFirestore(), path);
72
94
  let q;
73
- if (options != null) {
74
- const { where: w, orderBy: o, limit: l } = options;
95
+ if (isQueryConstraintParams(params)) {
96
+ q = query(ref, ...params.queryConstraints);
97
+ } else {
98
+ const { where: w, orderBy: o, limit: l } = params;
75
99
  q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...(o ? o : []).map((q2) => orderBy(...q2)), ...l ? [limit(l)] : []);
76
100
  }
77
- const unsub = onSnapshot((q ?? ref).withConverter(converter), (qs) => {
101
+ const unsub = onSnapshot(q.withConverter(converter), (qs) => {
78
102
  next(null, qs.docs.map((x) => x.data()));
79
103
  }, (error) => {
80
104
  next(error);
@@ -83,18 +107,27 @@ const useCollectionGroup = (params) => {
83
107
  });
84
108
  };
85
109
  const useCollectionGroupCount = (params, swrOptions) => {
86
- const fetcher = async (key) => {
87
- const { path, ...option } = key;
110
+ let swrKey = params;
111
+ if (params != null && isQueryConstraintParams(params)) {
112
+ swrKey = JSON.parse(JSON.stringify(params));
113
+ }
114
+ const fetcher = async () => {
115
+ if (params == null) {
116
+ return;
117
+ }
118
+ const { path } = params;
88
119
  const ref = collectionGroup(getFirestore(), path);
89
120
  let q;
90
- if (option) {
91
- const { where: w, orderBy: o, limit: l } = option;
92
- q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...(o ? o : []).map((q2) => orderBy(...q2)), ...l ? [limit(l)] : []);
121
+ if (isQueryConstraintParams(params)) {
122
+ q = query(ref, ...params.queryConstraints);
123
+ } else {
124
+ const { where: w, limit: l } = params;
125
+ q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...l ? [limit(l)] : []);
93
126
  }
94
- const sn = await getCountFromServer(q ?? ref);
127
+ const sn = await getCountFromServer(q);
95
128
  return sn.data().count;
96
129
  };
97
- return useSWR(params, fetcher, swrOptions);
130
+ return useSWR(swrKey, fetcher, swrOptions);
98
131
  };
99
132
  const useDoc = (params) => {
100
133
  return useSWRSubscription(params, (_, { next }) => {
@@ -114,31 +147,43 @@ const useDoc = (params) => {
114
147
  });
115
148
  };
116
149
  const useGetDoc = (params, swrOptions) => {
117
- const fetcher = async (key) => {
118
- const { path, ...option } = key;
119
- const converter = getFirestoreConverter(option == null ? void 0 : option.parseDates);
150
+ const fetcher = async () => {
151
+ if (params == null) {
152
+ return;
153
+ }
154
+ const { path, parseDates, ...options } = params;
155
+ const converter = getFirestoreConverter(parseDates);
120
156
  const ref = doc(getFirestore(), path);
121
- const getFn = option.useOfflineCache ? getDocFromCache : getDoc;
157
+ const getFn = options.useOfflineCache ? getDocFromCache : getDoc;
122
158
  const sn = await getFn(ref.withConverter(converter));
123
159
  return sn.data();
124
160
  };
125
161
  return useSWR(params, fetcher, swrOptions);
126
162
  };
127
163
  const useGetDocs = (params, swrOptions) => {
128
- const fetcher = async (key) => {
129
- const { path, ...option } = key;
130
- const converter = getFirestoreConverter(option == null ? void 0 : option.parseDates);
164
+ let swrKey = params;
165
+ if (params != null && isQueryConstraintParams(params)) {
166
+ swrKey = JSON.parse(JSON.stringify(params));
167
+ }
168
+ const fetcher = async () => {
169
+ if (params == null) {
170
+ return;
171
+ }
172
+ const { path, parseDates } = params;
173
+ const converter = getFirestoreConverter(parseDates);
131
174
  const ref = collection(getFirestore(), path);
132
175
  let q;
133
- if (option) {
134
- const { where: w, orderBy: o, limit: l } = option;
176
+ if (isQueryConstraintParams(params)) {
177
+ q = query(ref, ...params.queryConstraints);
178
+ } else {
179
+ const { where: w, orderBy: o, limit: l } = params;
135
180
  q = query(ref, ...(w ? w : []).map((q2) => where(...q2)), ...(o ? o : []).map((q2) => orderBy(...q2)), ...l ? [limit(l)] : []);
136
181
  }
137
- const getFn = option.useOfflineCache ? getDocsFromCache : getDocs;
138
- const sn = await getFn((q ?? ref).withConverter(converter));
182
+ const getFn = params.useOfflineCache ? getDocsFromCache : getDocs;
183
+ const sn = await getFn(q.withConverter(converter));
139
184
  return sn.docs.map((x) => x.data());
140
185
  };
141
- return useSWR(params, fetcher, swrOptions);
186
+ return useSWR(swrKey, fetcher, swrOptions);
142
187
  };
143
188
  export {
144
189
  useCollection,
@@ -24,21 +24,30 @@
24
24
  return model;
25
25
  }
26
26
  });
27
+ const isQueryConstraintParams = (params) => {
28
+ return params.queryConstraints != null;
29
+ };
27
30
  const useCollection = (params) => {
28
- return useSWRSubscription(params, (_, { next }) => {
31
+ let swrKey = params;
32
+ if (params != null && isQueryConstraintParams(params)) {
33
+ swrKey = JSON.parse(JSON.stringify(params));
34
+ }
35
+ return useSWRSubscription(swrKey, (_, { next }) => {
29
36
  if (params == null) {
30
37
  return () => {
31
38
  };
32
39
  }
33
- const { path, ...options } = params;
34
- const converter = getFirestoreConverter(options == null ? void 0 : options.parseDates);
40
+ const { path, parseDates } = params;
41
+ const converter = getFirestoreConverter(parseDates);
35
42
  const ref = firestore.collection(firestore.getFirestore(), path);
36
43
  let q;
37
- if (options != null) {
38
- const { where: w, orderBy: o, limit: l } = options;
44
+ if (isQueryConstraintParams(params)) {
45
+ q = firestore.query(ref, ...params.queryConstraints);
46
+ } else {
47
+ const { where: w, orderBy: o, limit: l } = params;
39
48
  q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...(o ? o : []).map((q2) => firestore.orderBy(...q2)), ...l ? [firestore.limit(l)] : []);
40
49
  }
41
- const unsub = firestore.onSnapshot((q ?? ref).withConverter(converter), (qs) => {
50
+ const unsub = firestore.onSnapshot(q.withConverter(converter), (qs) => {
42
51
  next(null, qs.docs.map((x) => x.data()));
43
52
  }, (error) => {
44
53
  next(error);
@@ -47,34 +56,49 @@
47
56
  });
48
57
  };
49
58
  const useCollectionCount = (params, swrOptions) => {
50
- const fetcher = async (key) => {
51
- const { path, ...option } = key;
59
+ let swrKey = params;
60
+ if (params != null && isQueryConstraintParams(params)) {
61
+ swrKey = JSON.parse(JSON.stringify(params));
62
+ }
63
+ const fetcher = async () => {
64
+ if (params == null) {
65
+ return;
66
+ }
67
+ const { path } = params;
52
68
  const ref = firestore.collection(firestore.getFirestore(), path);
53
69
  let q;
54
- if (option) {
55
- const { where: w, orderBy: o, limit: l } = option;
56
- q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...(o ? o : []).map((q2) => firestore.orderBy(...q2)), ...l ? [firestore.limit(l)] : []);
70
+ if (isQueryConstraintParams(params)) {
71
+ q = firestore.query(ref, ...params.queryConstraints);
72
+ } else {
73
+ const { where: w, limit: l } = params;
74
+ q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...l ? [firestore.limit(l)] : []);
57
75
  }
58
- const sn = await firestore.getCountFromServer(q ?? ref);
76
+ const sn = await firestore.getCountFromServer(q);
59
77
  return sn.data().count;
60
78
  };
61
- return useSWR(params, fetcher, swrOptions);
79
+ return useSWR(swrKey, fetcher, swrOptions);
62
80
  };
63
81
  const useCollectionGroup = (params) => {
64
- return useSWRSubscription(params, (_, { next }) => {
82
+ let swrKey = params;
83
+ if (params != null && isQueryConstraintParams(params)) {
84
+ swrKey = JSON.parse(JSON.stringify(params));
85
+ }
86
+ return useSWRSubscription(swrKey, (_, { next }) => {
65
87
  if (params == null) {
66
88
  return () => {
67
89
  };
68
90
  }
69
- const { path, ...options } = params;
70
- const converter = getFirestoreConverter(options == null ? void 0 : options.parseDates);
91
+ const { path, parseDates } = params;
92
+ const converter = getFirestoreConverter(parseDates);
71
93
  const ref = firestore.collectionGroup(firestore.getFirestore(), path);
72
94
  let q;
73
- if (options != null) {
74
- const { where: w, orderBy: o, limit: l } = options;
95
+ if (isQueryConstraintParams(params)) {
96
+ q = firestore.query(ref, ...params.queryConstraints);
97
+ } else {
98
+ const { where: w, orderBy: o, limit: l } = params;
75
99
  q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...(o ? o : []).map((q2) => firestore.orderBy(...q2)), ...l ? [firestore.limit(l)] : []);
76
100
  }
77
- const unsub = firestore.onSnapshot((q ?? ref).withConverter(converter), (qs) => {
101
+ const unsub = firestore.onSnapshot(q.withConverter(converter), (qs) => {
78
102
  next(null, qs.docs.map((x) => x.data()));
79
103
  }, (error) => {
80
104
  next(error);
@@ -83,18 +107,27 @@
83
107
  });
84
108
  };
85
109
  const useCollectionGroupCount = (params, swrOptions) => {
86
- const fetcher = async (key) => {
87
- const { path, ...option } = key;
110
+ let swrKey = params;
111
+ if (params != null && isQueryConstraintParams(params)) {
112
+ swrKey = JSON.parse(JSON.stringify(params));
113
+ }
114
+ const fetcher = async () => {
115
+ if (params == null) {
116
+ return;
117
+ }
118
+ const { path } = params;
88
119
  const ref = firestore.collectionGroup(firestore.getFirestore(), path);
89
120
  let q;
90
- if (option) {
91
- const { where: w, orderBy: o, limit: l } = option;
92
- q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...(o ? o : []).map((q2) => firestore.orderBy(...q2)), ...l ? [firestore.limit(l)] : []);
121
+ if (isQueryConstraintParams(params)) {
122
+ q = firestore.query(ref, ...params.queryConstraints);
123
+ } else {
124
+ const { where: w, limit: l } = params;
125
+ q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...l ? [firestore.limit(l)] : []);
93
126
  }
94
- const sn = await firestore.getCountFromServer(q ?? ref);
127
+ const sn = await firestore.getCountFromServer(q);
95
128
  return sn.data().count;
96
129
  };
97
- return useSWR(params, fetcher, swrOptions);
130
+ return useSWR(swrKey, fetcher, swrOptions);
98
131
  };
99
132
  const useDoc = (params) => {
100
133
  return useSWRSubscription(params, (_, { next }) => {
@@ -114,31 +147,43 @@
114
147
  });
115
148
  };
116
149
  const useGetDoc = (params, swrOptions) => {
117
- const fetcher = async (key) => {
118
- const { path, ...option } = key;
119
- const converter = getFirestoreConverter(option == null ? void 0 : option.parseDates);
150
+ const fetcher = async () => {
151
+ if (params == null) {
152
+ return;
153
+ }
154
+ const { path, parseDates, ...options } = params;
155
+ const converter = getFirestoreConverter(parseDates);
120
156
  const ref = firestore.doc(firestore.getFirestore(), path);
121
- const getFn = option.useOfflineCache ? firestore.getDocFromCache : firestore.getDoc;
157
+ const getFn = options.useOfflineCache ? firestore.getDocFromCache : firestore.getDoc;
122
158
  const sn = await getFn(ref.withConverter(converter));
123
159
  return sn.data();
124
160
  };
125
161
  return useSWR(params, fetcher, swrOptions);
126
162
  };
127
163
  const useGetDocs = (params, swrOptions) => {
128
- const fetcher = async (key) => {
129
- const { path, ...option } = key;
130
- const converter = getFirestoreConverter(option == null ? void 0 : option.parseDates);
164
+ let swrKey = params;
165
+ if (params != null && isQueryConstraintParams(params)) {
166
+ swrKey = JSON.parse(JSON.stringify(params));
167
+ }
168
+ const fetcher = async () => {
169
+ if (params == null) {
170
+ return;
171
+ }
172
+ const { path, parseDates } = params;
173
+ const converter = getFirestoreConverter(parseDates);
131
174
  const ref = firestore.collection(firestore.getFirestore(), path);
132
175
  let q;
133
- if (option) {
134
- const { where: w, orderBy: o, limit: l } = option;
176
+ if (isQueryConstraintParams(params)) {
177
+ q = firestore.query(ref, ...params.queryConstraints);
178
+ } else {
179
+ const { where: w, orderBy: o, limit: l } = params;
135
180
  q = firestore.query(ref, ...(w ? w : []).map((q2) => firestore.where(...q2)), ...(o ? o : []).map((q2) => firestore.orderBy(...q2)), ...l ? [firestore.limit(l)] : []);
136
181
  }
137
- const getFn = option.useOfflineCache ? firestore.getDocsFromCache : firestore.getDocs;
138
- const sn = await getFn((q ?? ref).withConverter(converter));
182
+ const getFn = params.useOfflineCache ? firestore.getDocsFromCache : firestore.getDocs;
183
+ const sn = await getFn(q.withConverter(converter));
139
184
  return sn.docs.map((x) => x.data());
140
185
  };
141
- return useSWR(params, fetcher, swrOptions);
186
+ return useSWR(swrKey, fetcher, swrOptions);
142
187
  };
143
188
  exports2.useCollection = useCollection;
144
189
  exports2.useCollectionCount = useCollectionCount;
@@ -1,4 +1,4 @@
1
- import type { orderBy, where } from "firebase/firestore";
1
+ import type { orderBy, QueryCompositeFilterConstraint, QueryConstraint, QueryNonFilterConstraint, where } from "firebase/firestore";
2
2
  import type { QueryDocumentSnapshot } from "firebase/firestore";
3
3
  type Join<K, P> = K extends string | number ? P extends string | number ? `${K}${"" extends P ? "" : "."}${P}` : never : never;
4
4
  type Prev = [
@@ -30,13 +30,20 @@ export type Paths<T, D extends number = 3> = [D] extends [never] ? never : T ext
30
30
  [K in keyof T]-?: K extends string | number ? `${K}` | Join<K, Paths<T[K], Prev[D]>> : never;
31
31
  }[keyof T] : "";
32
32
  export type ValueOf<T> = T[keyof T];
33
- export type KeyParams<T> = {
34
- path: string;
33
+ export type QueryParams<T> = {
35
34
  where?: [Paths<T>, Parameters<typeof where>[1], ValueOf<T> | unknown][];
36
35
  orderBy?: [Paths<T>, Parameters<typeof orderBy>[1]][];
37
36
  limit?: number;
37
+ };
38
+ export type QueryConstraintParams = {
39
+ queryConstraints?: [QueryCompositeFilterConstraint, ...Array<QueryNonFilterConstraint>] | QueryConstraint[];
40
+ };
41
+ type BaseParams<T> = {
42
+ path: string;
38
43
  parseDates?: Paths<T>[];
39
44
  };
45
+ export type KeyParams<T> = BaseParams<T> & (QueryParams<T> | QueryConstraintParams);
46
+ export type KeyParamsForCount<T> = BaseParams<T> & (Omit<QueryParams<T>, "orderBy" | "parseDates"> | QueryConstraintParams);
40
47
  export type GetDocKeyParams<T> = KeyParams<T> & {
41
48
  useOfflineCache?: boolean;
42
49
  };
@@ -0,0 +1,5 @@
1
+ import type { KeyParams, QueryConstraintParams } from "./type";
2
+ export declare const isQueryConstraintParams: <T>(params: KeyParams<T>) => params is {
3
+ path: string;
4
+ parseDates?: (T extends object ? (T extends infer T_1 extends object ? { [K in keyof T_1]-?: K extends string | number ? `${K}` | (K extends infer T_2 ? T_2 extends K ? T_2 extends string | number ? (T[K] extends infer T_3 ? T_3 extends T[K] ? T_3 extends object ? (T_3 extends infer T_4 extends object ? { [K_1 in keyof T_4]-?: K_1 extends string | number ? `${K_1}` | (K_1 extends infer T_5 ? T_5 extends K_1 ? T_5 extends string | number ? (T_3[K_1] extends infer T_6 ? T_6 extends T_3[K_1] ? T_6 extends object ? (T_6 extends infer T_7 extends object ? { [K_2 in keyof T_7]-?: K_2 extends string | number ? `${K_2}` | (K_2 extends infer T_8 ? T_8 extends K_2 ? T_8 extends string | number ? (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) extends infer T_12 ? T_12 extends (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) ? T_12 extends string | number ? `${T_8}${"" extends T_12 ? T_12 & "" : "."}${T_12}` : never : never : never : never : never : never) : never; } : never)[keyof T_6] : "" : never : never) extends infer T_13 ? T_13 extends (T_3[K_1] extends infer T_6 ? T_6 extends T_3[K_1] ? T_6 extends object ? (T_6 extends infer T_7 extends object ? { [K_2 in keyof T_7]-?: K_2 extends string | number ? `${K_2}` | (K_2 extends infer T_8 ? T_8 extends K_2 ? T_8 extends string | number ? (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) extends infer T_12 ? T_12 extends (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) ? T_12 extends string | number ? `${T_8}${"" extends T_12 ? T_12 & "" : "."}${T_12}` : never : never : never : never : never : never) : never; } : never)[keyof T_6] : "" : never : never) ? T_13 extends string | number ? `${T_5}${"" extends T_13 ? T_13 & "" : "."}${T_13}` : never : never : never : never : never : never) : never; } : never)[keyof T_3] : "" : never : never) extends infer T_14 ? T_14 extends (T[K] extends infer T_3 ? T_3 extends T[K] ? T_3 extends object ? (T_3 extends infer T_4 extends object ? { [K_1 in keyof T_4]-?: K_1 extends string | number ? `${K_1}` | (K_1 extends infer T_5 ? T_5 extends K_1 ? T_5 extends string | number ? (T_3[K_1] extends infer T_6 ? T_6 extends T_3[K_1] ? T_6 extends object ? (T_6 extends infer T_7 extends object ? { [K_2 in keyof T_7]-?: K_2 extends string | number ? `${K_2}` | (K_2 extends infer T_8 ? T_8 extends K_2 ? T_8 extends string | number ? (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) extends infer T_12 ? T_12 extends (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) ? T_12 extends string | number ? `${T_8}${"" extends T_12 ? T_12 & "" : "."}${T_12}` : never : never : never : never : never : never) : never; } : never)[keyof T_6] : "" : never : never) extends infer T_13 ? T_13 extends (T_3[K_1] extends infer T_6 ? T_6 extends T_3[K_1] ? T_6 extends object ? (T_6 extends infer T_7 extends object ? { [K_2 in keyof T_7]-?: K_2 extends string | number ? `${K_2}` | (K_2 extends infer T_8 ? T_8 extends K_2 ? T_8 extends string | number ? (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) extends infer T_12 ? T_12 extends (T_6[K_2] extends infer T_9 ? T_9 extends T_6[K_2] ? T_9 extends object ? (T_9 extends infer T_10 extends object ? { [K_3 in keyof T_10]-?: K_3 extends string | number ? `${K_3}` | (K_3 extends infer T_11 ? T_11 extends K_3 ? T_11 extends string | number ? never : never : never : never) : never; } : never)[keyof T_9] : "" : never : never) ? T_12 extends string | number ? `${T_8}${"" extends T_12 ? T_12 & "" : "."}${T_12}` : never : never : never : never : never : never) : never; } : never)[keyof T_6] : "" : never : never) ? T_13 extends string | number ? `${T_5}${"" extends T_13 ? T_13 & "" : "."}${T_13}` : never : never : never : never : never : never) : never; } : never)[keyof T_3] : "" : never : never) ? T_14 extends string | number ? `${T_2}${"" extends T_14 ? T_14 & "" : "."}${T_14}` : never : never : never : never : never : never) : never; } : never)[keyof T] : "")[] | undefined;
5
+ } & QueryConstraintParams;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tatsuokaniwa/swr-firestore",
3
3
  "description": "React Hooks library for Firestore using SWR's subscription feature.",
4
- "version": "1.0.1",
4
+ "version": "1.1.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/t-k/swr-firestore.git"
@@ -34,7 +34,7 @@
34
34
  "build": "tsc --noEmit && vite build",
35
35
  "emulator": "firebase emulators:start --project swr-firestore-project --only firestore",
36
36
  "test": "vitest",
37
- "test:ci": "firebase emulators:exec --only auth,firestore,storage 'vitest run'",
37
+ "test:ci": "firebase emulators:exec --only auth,firestore,storage 'vitest run --coverage'",
38
38
  "coverage": "vitest run --coverage",
39
39
  "eslint": "eslint --fix --ext .ts,.tsx .",
40
40
  "prettier": "prettier --check --write .",
@@ -60,6 +60,7 @@
60
60
  "eslint": "^8.36.0",
61
61
  "eslint-plugin-react": "^7.32.2",
62
62
  "firebase": "^9.11.0 < 10.0.0",
63
+ "firebase-admin": "^11.5.0",
63
64
  "jsdom": "^21.1.1",
64
65
  "prettier": "^2.8.5",
65
66
  "react": "^18.2.0",