@ttt-productions/query-core 0.14.22 → 0.14.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +6 -6
  2. package/dist/defaults.d.ts +0 -15
  3. package/dist/defaults.d.ts.map +1 -1
  4. package/dist/defaults.js +0 -15
  5. package/dist/defaults.js.map +1 -1
  6. package/dist/firestore/types.d.ts +5 -49
  7. package/dist/firestore/types.d.ts.map +1 -1
  8. package/dist/firestore/types.js.map +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +0 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/keys.d.ts +0 -22
  13. package/dist/keys.d.ts.map +1 -1
  14. package/dist/keys.js +0 -4
  15. package/dist/keys.js.map +1 -1
  16. package/dist/react/cache-helpers.d.ts +0 -32
  17. package/dist/react/cache-helpers.d.ts.map +1 -1
  18. package/dist/react/cache-helpers.js +0 -28
  19. package/dist/react/cache-helpers.js.map +1 -1
  20. package/dist/react/callables/create-callables.d.ts +40 -0
  21. package/dist/react/callables/create-callables.d.ts.map +1 -0
  22. package/dist/react/callables/create-callables.js +101 -0
  23. package/dist/react/callables/create-callables.js.map +1 -0
  24. package/dist/react/defaults.d.ts +0 -4
  25. package/dist/react/defaults.d.ts.map +1 -1
  26. package/dist/react/defaults.js +2 -6
  27. package/dist/react/defaults.js.map +1 -1
  28. package/dist/react/domain-events/cache-invalidation.d.ts +0 -37
  29. package/dist/react/domain-events/cache-invalidation.d.ts.map +1 -1
  30. package/dist/react/domain-events/cache-invalidation.js +4 -53
  31. package/dist/react/domain-events/cache-invalidation.js.map +1 -1
  32. package/dist/react/domain-events/create-domain-event-invalidator.d.ts +0 -18
  33. package/dist/react/domain-events/create-domain-event-invalidator.d.ts.map +1 -1
  34. package/dist/react/domain-events/create-domain-event-invalidator.js +0 -5
  35. package/dist/react/domain-events/create-domain-event-invalidator.js.map +1 -1
  36. package/dist/react/firestore/absence-scheduler.d.ts +0 -28
  37. package/dist/react/firestore/absence-scheduler.d.ts.map +1 -1
  38. package/dist/react/firestore/absence-scheduler.js +1 -39
  39. package/dist/react/firestore/absence-scheduler.js.map +1 -1
  40. package/dist/react/firestore/batch-doc-loader.d.ts +0 -30
  41. package/dist/react/firestore/batch-doc-loader.d.ts.map +1 -1
  42. package/dist/react/firestore/batch-doc-loader.js +0 -34
  43. package/dist/react/firestore/batch-doc-loader.js.map +1 -1
  44. package/dist/react/firestore/context.d.ts +0 -19
  45. package/dist/react/firestore/context.d.ts.map +1 -1
  46. package/dist/react/firestore/context.js +0 -19
  47. package/dist/react/firestore/context.js.map +1 -1
  48. package/dist/react/firestore/doc-subscription-registry.d.ts +0 -23
  49. package/dist/react/firestore/doc-subscription-registry.d.ts.map +1 -1
  50. package/dist/react/firestore/doc-subscription-registry.js +5 -26
  51. package/dist/react/firestore/doc-subscription-registry.js.map +1 -1
  52. package/dist/react/firestore/infinite-helpers.d.ts +0 -12
  53. package/dist/react/firestore/infinite-helpers.d.ts.map +1 -1
  54. package/dist/react/firestore/infinite-helpers.js +0 -12
  55. package/dist/react/firestore/infinite-helpers.js.map +1 -1
  56. package/dist/react/firestore/resubscribe.d.ts +0 -20
  57. package/dist/react/firestore/resubscribe.d.ts.map +1 -1
  58. package/dist/react/firestore/resubscribe.js +0 -20
  59. package/dist/react/firestore/resubscribe.js.map +1 -1
  60. package/dist/react/firestore/subscription-status.d.ts +0 -29
  61. package/dist/react/firestore/subscription-status.d.ts.map +1 -1
  62. package/dist/react/firestore/subscription-status.js +1 -34
  63. package/dist/react/firestore/subscription-status.js.map +1 -1
  64. package/dist/react/firestore/types.d.ts +0 -64
  65. package/dist/react/firestore/types.d.ts.map +1 -1
  66. package/dist/react/firestore/types.js +0 -3
  67. package/dist/react/firestore/types.js.map +1 -1
  68. package/dist/react/firestore/use-firestore-collection.d.ts +0 -20
  69. package/dist/react/firestore/use-firestore-collection.d.ts.map +1 -1
  70. package/dist/react/firestore/use-firestore-collection.js +1 -35
  71. package/dist/react/firestore/use-firestore-collection.js.map +1 -1
  72. package/dist/react/firestore/use-firestore-count.d.ts +0 -15
  73. package/dist/react/firestore/use-firestore-count.d.ts.map +1 -1
  74. package/dist/react/firestore/use-firestore-count.js +0 -15
  75. package/dist/react/firestore/use-firestore-count.js.map +1 -1
  76. package/dist/react/firestore/use-firestore-doc.d.ts +0 -23
  77. package/dist/react/firestore/use-firestore-doc.d.ts.map +1 -1
  78. package/dist/react/firestore/use-firestore-doc.js +3 -40
  79. package/dist/react/firestore/use-firestore-doc.js.map +1 -1
  80. package/dist/react/firestore/use-firestore-infinite.d.ts +0 -29
  81. package/dist/react/firestore/use-firestore-infinite.d.ts.map +1 -1
  82. package/dist/react/firestore/use-firestore-infinite.js +0 -32
  83. package/dist/react/firestore/use-firestore-infinite.js.map +1 -1
  84. package/dist/react/firestore/use-firestore-live-infinite.d.ts +0 -34
  85. package/dist/react/firestore/use-firestore-live-infinite.d.ts.map +1 -1
  86. package/dist/react/firestore/use-firestore-live-infinite.js +1 -35
  87. package/dist/react/firestore/use-firestore-live-infinite.js.map +1 -1
  88. package/dist/react/firestore/use-firestore-mutations.d.ts.map +1 -1
  89. package/dist/react/firestore/use-firestore-mutations.js +6 -5
  90. package/dist/react/firestore/use-firestore-mutations.js.map +1 -1
  91. package/dist/react/firestore/use-firestore-paginated.d.ts +0 -30
  92. package/dist/react/firestore/use-firestore-paginated.d.ts.map +1 -1
  93. package/dist/react/firestore/use-firestore-paginated.js +2 -33
  94. package/dist/react/firestore/use-firestore-paginated.js.map +1 -1
  95. package/dist/react/firestore/useBatchFirestoreDocs.d.ts +1 -123
  96. package/dist/react/firestore/useBatchFirestoreDocs.d.ts.map +1 -1
  97. package/dist/react/firestore/useBatchFirestoreDocs.js +23 -64
  98. package/dist/react/firestore/useBatchFirestoreDocs.js.map +1 -1
  99. package/dist/react/index.d.ts +4 -0
  100. package/dist/react/index.d.ts.map +1 -1
  101. package/dist/react/index.js +2 -13
  102. package/dist/react/index.js.map +1 -1
  103. package/dist/react/pending-mutations.d.ts +1 -21
  104. package/dist/react/pending-mutations.d.ts.map +1 -1
  105. package/dist/react/pending-mutations.js +1 -32
  106. package/dist/react/pending-mutations.js.map +1 -1
  107. package/dist/react/provider.d.ts +0 -5
  108. package/dist/react/provider.d.ts.map +1 -1
  109. package/dist/react/provider.js +0 -4
  110. package/dist/react/provider.js.map +1 -1
  111. package/dist/react/query-client.d.ts +0 -4
  112. package/dist/react/query-client.d.ts.map +1 -1
  113. package/dist/react/query-client.js +0 -4
  114. package/dist/react/query-client.js.map +1 -1
  115. package/dist/react/search/types.d.ts +0 -12
  116. package/dist/react/search/types.d.ts.map +1 -1
  117. package/dist/react/search/types.js.map +1 -1
  118. package/dist/react/search/use-firestore-search.d.ts +0 -29
  119. package/dist/react/search/use-firestore-search.d.ts.map +1 -1
  120. package/dist/react/search/use-firestore-search.js +3 -41
  121. package/dist/react/search/use-firestore-search.js.map +1 -1
  122. package/dist/react/session-mutation.d.ts +27 -0
  123. package/dist/react/session-mutation.d.ts.map +1 -0
  124. package/dist/react/session-mutation.js +150 -0
  125. package/dist/react/session-mutation.js.map +1 -0
  126. package/dist/react/use-read-retry-state.d.ts +1 -21
  127. package/dist/react/use-read-retry-state.d.ts.map +1 -1
  128. package/dist/react/use-read-retry-state.js +2 -21
  129. package/dist/react/use-read-retry-state.js.map +1 -1
  130. package/dist/search/rule.d.ts +0 -6
  131. package/dist/search/rule.d.ts.map +1 -1
  132. package/dist/search/rule.js +0 -6
  133. package/dist/search/rule.js.map +1 -1
  134. package/dist/search/types.d.ts +1 -13
  135. package/dist/search/types.d.ts.map +1 -1
  136. package/dist/search/types.js.map +1 -1
  137. package/package.json +4 -4
@@ -4,15 +4,6 @@ import { isPermissionDeniedError } from './resubscribe.js';
4
4
  function freshStatus(identity) {
5
5
  return { identity, error: null, sourceState: 'connecting', restart: null, restarts: 0 };
6
6
  }
7
- /**
8
- * A realtime listener's source state and error, tagged to the subscription identity that
9
- * produced them. A new identity reads `connecting` with no error from its first render;
10
- * writes made for a retired identity are dropped once the new identity's listener has
11
- * started (it calls `reset`).
12
- *
13
- * `restart()` clears the error and bumps `restarts`; the listener effect re-opens on the bump,
14
- * and its first snapshot or next surfaced error ends the restart (`settled` in the patch).
15
- */
16
7
  export function useSubscriptionStatus(identity) {
17
8
  const [status, setStatus] = useState(() => freshStatus(identity));
18
9
  const current = status.identity === identity ? status : freshStatus(identity);
@@ -29,13 +20,10 @@ export function useSubscriptionStatus(identity) {
29
20
  return { ...prev, ...next };
30
21
  });
31
22
  }, [identity]);
32
- // The identity the hook renders now. A `restart` captured under an earlier identity must not
33
- // touch the current one's status, or every write the current listener makes would be dropped.
34
23
  const renderedIdentity = useRef(identity);
35
24
  useEffect(() => {
36
25
  renderedIdentity.current = identity;
37
26
  }, [identity]);
38
- /** Re-opens the listener; false (and nothing changes) when this identity is no longer rendered. */
39
27
  const restart = useCallback(() => {
40
28
  if (renderedIdentity.current !== identity)
41
29
  return false;
@@ -64,22 +52,6 @@ export function useSubscriptionStatus(identity) {
64
52
  restart,
65
53
  };
66
54
  }
67
- /**
68
- * The subscribed hooks' result: the query's own result, with the listener's error and source
69
- * state merged in. In subscribe mode `refetch()` re-opens the listener (clearing its error, with a
70
- * fresh resubscribe ladder) and resolves once the re-opened listener delivers its first snapshot or
71
- * surfaces its next error; until then the result reports fetching.
72
- *
73
- * A listener that fails after it answered keeps its last snapshot as `data` beside the failure, the
74
- * way a one-shot read that fails a refetch keeps its data: `status: 'error'` and `isRefetchError`
75
- * say the rows are not current, and a consumer can keep what it showed instead of tearing it down.
76
- * A restart from that failure keeps both — the held data and the failure — until the re-opened
77
- * listener answers. A listener that failed before it ever answered has no data to hold.
78
- *
79
- * A `permission-denied` failure holds nothing: the reader lost access, and a snapshot it may no
80
- * longer read is never presented as readable. It reads like a failure before any answer, and so
81
- * does its restart.
82
- */
83
55
  export function useSubscribedResult(query, subscribe, enabled, status) {
84
56
  const { error, sourceState, pendingRestart, restart } = status;
85
57
  const waiters = useRef([]);
@@ -87,8 +59,6 @@ export function useSubscribedResult(query, subscribe, enabled, status) {
87
59
  const mounted = useRef(true);
88
60
  const active = subscribe && enabled;
89
61
  const refetch = useCallback((options) => {
90
- // An unmounted hook, or a refetch captured under an identity the hook has left, re-opens
91
- // nothing: it settles at once rather than waiting on a listener that will never report.
92
62
  if (!active || !mounted.current)
93
63
  return Promise.resolve(latest.current);
94
64
  return new Promise((resolve, reject) => {
@@ -167,9 +137,7 @@ export function useSubscribedResult(query, subscribe, enabled, status) {
167
137
  };
168
138
  }
169
139
  else if (active && query.isPending) {
170
- // The listener has not delivered its first snapshot. The query underneath is disabled in
171
- // subscribe mode, so it reports pending but not loading; the read IS loading, exactly like a
172
- // one-shot read's first fetch — never an answered absence.
140
+ // The query underneath is disabled in subscribe mode, so it reports pending but not loading.
173
141
  result = {
174
142
  ...query,
175
143
  isLoading: true,
@@ -195,7 +163,6 @@ export function useSubscribedResult(query, subscribe, enabled, status) {
195
163
  waiter.resolve(result);
196
164
  }
197
165
  });
198
- // A refetch still open when the hook unmounts resolves with the last result instead of hanging.
199
166
  useEffect(() => {
200
167
  mounted.current = true;
201
168
  return () => {
@@ -1 +1 @@
1
- {"version":3,"file":"subscription-status.js","sourceRoot":"","sources":["../../../src/react/firestore/subscription-status.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { QueryObserverResult, RefetchOptions, UseQueryResult } from '@tanstack/react-query';\nimport type { FirestoreSourceState, WithSourceState } from '../../firestore/types.js';\nimport { isPermissionDeniedError } from './resubscribe.js';\n\n/** A restart a subscribed result's `refetch()` asked for, open until the listener's first snapshot or its next error. */\ninterface PendingRestart {\n /** The listener had failed when the restart was asked for. */\n fromError: boolean;\n /** That failure, kept beside the held data while the restart is open, as a one-shot refetch keeps it. */\n failure: Error | null;\n}\n\ninterface SubscriptionStatus {\n identity: string;\n error: Error | null;\n sourceState: FirestoreSourceState;\n restart: PendingRestart | null;\n /** Bumped by every restart; the listener effect depends on it, so a bump re-opens the listener. */\n restarts: number;\n}\n\ntype StatusPatch = Partial<Omit<SubscriptionStatus, 'identity' | 'restarts'>>;\n\nfunction freshStatus(identity: string): SubscriptionStatus {\n return { identity, error: null, sourceState: 'connecting', restart: null, restarts: 0 };\n}\n\n/**\n * A realtime listener's source state and error, tagged to the subscription identity that\n * produced them. A new identity reads `connecting` with no error from its first render;\n * writes made for a retired identity are dropped once the new identity's listener has\n * started (it calls `reset`).\n *\n * `restart()` clears the error and bumps `restarts`; the listener effect re-opens on the bump,\n * and its first snapshot or next surfaced error ends the restart (`settled` in the patch).\n */\nexport function useSubscriptionStatus(identity: string) {\n const [status, setStatus] = useState<SubscriptionStatus>(() => freshStatus(identity));\n\n const current: SubscriptionStatus = status.identity === identity ? status : freshStatus(identity);\n\n const reset = useCallback(() => {\n setStatus((prev) =>\n prev.identity === identity\n ? { ...prev, error: null, sourceState: 'connecting' }\n : freshStatus(identity),\n );\n }, [identity]);\n\n const update = useCallback(\n (patch: StatusPatch | ((prev: SubscriptionStatus) => StatusPatch)) => {\n setStatus((prev) => {\n if (prev.identity !== identity) return prev;\n const next = typeof patch === 'function' ? patch(prev) : patch;\n return { ...prev, ...next };\n });\n },\n [identity],\n );\n\n // The identity the hook renders now. A `restart` captured under an earlier identity must not\n // touch the current one's status, or every write the current listener makes would be dropped.\n const renderedIdentity = useRef(identity);\n useEffect(() => {\n renderedIdentity.current = identity;\n }, [identity]);\n\n /** Re-opens the listener; false (and nothing changes) when this identity is no longer rendered. */\n const restart = useCallback((): boolean => {\n if (renderedIdentity.current !== identity) return false;\n setStatus((prev) => {\n const base = prev.identity === identity ? prev : freshStatus(identity);\n return {\n ...base,\n error: null,\n sourceState: 'connecting',\n restart: {\n fromError: base.error !== null || base.restart?.fromError === true,\n failure: base.error ?? base.restart?.failure ?? null,\n },\n restarts: base.restarts + 1,\n };\n });\n return true;\n }, [identity]);\n\n return {\n error: current.error,\n sourceState: current.sourceState,\n pendingRestart: current.restart,\n restarts: current.restarts,\n reset,\n update,\n restart,\n };\n}\n\ntype SubscriptionStatusApi = ReturnType<typeof useSubscriptionStatus>;\n\ninterface RefetchWaiter<TData> {\n resolve: (result: QueryObserverResult<TData, Error>) => void;\n reject: (error: Error) => void;\n throwOnError: boolean;\n}\n\n/**\n * The subscribed hooks' result: the query's own result, with the listener's error and source\n * state merged in. In subscribe mode `refetch()` re-opens the listener (clearing its error, with a\n * fresh resubscribe ladder) and resolves once the re-opened listener delivers its first snapshot or\n * surfaces its next error; until then the result reports fetching.\n *\n * A listener that fails after it answered keeps its last snapshot as `data` beside the failure, the\n * way a one-shot read that fails a refetch keeps its data: `status: 'error'` and `isRefetchError`\n * say the rows are not current, and a consumer can keep what it showed instead of tearing it down.\n * A restart from that failure keeps both — the held data and the failure — until the re-opened\n * listener answers. A listener that failed before it ever answered has no data to hold.\n *\n * A `permission-denied` failure holds nothing: the reader lost access, and a snapshot it may no\n * longer read is never presented as readable. It reads like a failure before any answer, and so\n * does its restart.\n */\nexport function useSubscribedResult<TData>(\n query: UseQueryResult<TData, Error>,\n subscribe: boolean,\n enabled: boolean,\n status: SubscriptionStatusApi,\n): WithSourceState<UseQueryResult<TData, Error>> {\n const { error, sourceState, pendingRestart, restart } = status;\n const waiters = useRef<RefetchWaiter<TData>[]>([]);\n const latest = useRef(query as QueryObserverResult<TData, Error>);\n const mounted = useRef(true);\n const active = subscribe && enabled;\n\n const refetch = useCallback(\n (options?: RefetchOptions): Promise<QueryObserverResult<TData, Error>> => {\n // An unmounted hook, or a refetch captured under an identity the hook has left, re-opens\n // nothing: it settles at once rather than waiting on a listener that will never report.\n if (!active || !mounted.current) return Promise.resolve(latest.current);\n return new Promise((resolve, reject) => {\n if (!restart()) {\n resolve(latest.current);\n return;\n }\n waiters.current.push({ resolve, reject, throwOnError: options?.throwOnError === true });\n });\n },\n [active, restart],\n );\n\n const denied = isPermissionDeniedError(error ?? pendingRestart?.failure);\n const holdable = query.data !== undefined && !denied;\n\n let result: UseQueryResult<TData, Error>;\n if (!subscribe) {\n result = query;\n } else if (error) {\n const held = holdable;\n result = {\n ...query,\n data: held ? query.data : undefined,\n error,\n isError: true,\n isLoadingError: !held,\n isRefetchError: held,\n isSuccess: false,\n isPending: false,\n isLoading: false,\n status: 'error',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart?.fromError && holdable) {\n result = {\n ...query,\n error: pendingRestart.failure,\n isError: pendingRestart.failure !== null,\n isLoadingError: false,\n isRefetchError: pendingRestart.failure !== null,\n isSuccess: false,\n isPending: false,\n isLoading: false,\n isFetching: true,\n isRefetching: true,\n status: pendingRestart.failure !== null ? 'error' : query.status,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart?.fromError) {\n result = {\n ...query,\n data: undefined,\n error: null,\n isError: false,\n isLoadingError: false,\n isRefetchError: false,\n isSuccess: false,\n isPending: true,\n isLoading: true,\n isFetching: true,\n isRefetching: false,\n status: 'pending',\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart) {\n result = {\n ...query,\n isFetching: true,\n isLoading: query.isPending,\n isRefetching: !query.isPending,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (active && query.isPending) {\n // The listener has not delivered its first snapshot. The query underneath is disabled in\n // subscribe mode, so it reports pending but not loading; the read IS loading, exactly like a\n // one-shot read's first fetch — never an answered absence.\n result = {\n ...query,\n isLoading: true,\n isFetching: true,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else {\n result = { ...query, refetch } as UseQueryResult<TData, Error>;\n }\n\n const restartOpen = pendingRestart !== null;\n useEffect(() => {\n latest.current = result as QueryObserverResult<TData, Error>;\n if (restartOpen || waiters.current.length === 0) return;\n const settled = waiters.current;\n waiters.current = [];\n for (const waiter of settled) {\n if (waiter.throwOnError && result.error) waiter.reject(result.error);\n else waiter.resolve(result as QueryObserverResult<TData, Error>);\n }\n });\n\n // A refetch still open when the hook unmounts resolves with the last result instead of hanging.\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n const settled = waiters.current;\n waiters.current = [];\n for (const waiter of settled) waiter.resolve(latest.current);\n };\n }, []);\n\n return { ...result, sourceState } as WithSourceState<UseQueryResult<TData, Error>>;\n}\n"]}
1
+ {"version":3,"file":"subscription-status.js","sourceRoot":"","sources":["../../../src/react/firestore/subscription-status.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { QueryObserverResult, RefetchOptions, UseQueryResult } from '@tanstack/react-query';\nimport type { FirestoreSourceState, WithSourceState } from '../../firestore/types.js';\nimport { isPermissionDeniedError } from './resubscribe.js';\n\ninterface PendingRestart {\n fromError: boolean;\n failure: Error | null;\n}\n\ninterface SubscriptionStatus {\n identity: string;\n error: Error | null;\n sourceState: FirestoreSourceState;\n restart: PendingRestart | null;\n restarts: number;\n}\n\ntype StatusPatch = Partial<Omit<SubscriptionStatus, 'identity' | 'restarts'>>;\n\nfunction freshStatus(identity: string): SubscriptionStatus {\n return { identity, error: null, sourceState: 'connecting', restart: null, restarts: 0 };\n}\n\nexport function useSubscriptionStatus(identity: string) {\n const [status, setStatus] = useState<SubscriptionStatus>(() => freshStatus(identity));\n\n const current: SubscriptionStatus = status.identity === identity ? status : freshStatus(identity);\n\n const reset = useCallback(() => {\n setStatus((prev) =>\n prev.identity === identity\n ? { ...prev, error: null, sourceState: 'connecting' }\n : freshStatus(identity),\n );\n }, [identity]);\n\n const update = useCallback(\n (patch: StatusPatch | ((prev: SubscriptionStatus) => StatusPatch)) => {\n setStatus((prev) => {\n if (prev.identity !== identity) return prev;\n const next = typeof patch === 'function' ? patch(prev) : patch;\n return { ...prev, ...next };\n });\n },\n [identity],\n );\n\n const renderedIdentity = useRef(identity);\n useEffect(() => {\n renderedIdentity.current = identity;\n }, [identity]);\n\n const restart = useCallback((): boolean => {\n if (renderedIdentity.current !== identity) return false;\n setStatus((prev) => {\n const base = prev.identity === identity ? prev : freshStatus(identity);\n return {\n ...base,\n error: null,\n sourceState: 'connecting',\n restart: {\n fromError: base.error !== null || base.restart?.fromError === true,\n failure: base.error ?? base.restart?.failure ?? null,\n },\n restarts: base.restarts + 1,\n };\n });\n return true;\n }, [identity]);\n\n return {\n error: current.error,\n sourceState: current.sourceState,\n pendingRestart: current.restart,\n restarts: current.restarts,\n reset,\n update,\n restart,\n };\n}\n\ntype SubscriptionStatusApi = ReturnType<typeof useSubscriptionStatus>;\n\ninterface RefetchWaiter<TData> {\n resolve: (result: QueryObserverResult<TData, Error>) => void;\n reject: (error: Error) => void;\n throwOnError: boolean;\n}\n\nexport function useSubscribedResult<TData>(\n query: UseQueryResult<TData, Error>,\n subscribe: boolean,\n enabled: boolean,\n status: SubscriptionStatusApi,\n): WithSourceState<UseQueryResult<TData, Error>> {\n const { error, sourceState, pendingRestart, restart } = status;\n const waiters = useRef<RefetchWaiter<TData>[]>([]);\n const latest = useRef(query as QueryObserverResult<TData, Error>);\n const mounted = useRef(true);\n const active = subscribe && enabled;\n\n const refetch = useCallback(\n (options?: RefetchOptions): Promise<QueryObserverResult<TData, Error>> => {\n if (!active || !mounted.current) return Promise.resolve(latest.current);\n return new Promise((resolve, reject) => {\n if (!restart()) {\n resolve(latest.current);\n return;\n }\n waiters.current.push({ resolve, reject, throwOnError: options?.throwOnError === true });\n });\n },\n [active, restart],\n );\n\n const denied = isPermissionDeniedError(error ?? pendingRestart?.failure);\n const holdable = query.data !== undefined && !denied;\n\n let result: UseQueryResult<TData, Error>;\n if (!subscribe) {\n result = query;\n } else if (error) {\n const held = holdable;\n result = {\n ...query,\n data: held ? query.data : undefined,\n error,\n isError: true,\n isLoadingError: !held,\n isRefetchError: held,\n isSuccess: false,\n isPending: false,\n isLoading: false,\n status: 'error',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart?.fromError && holdable) {\n result = {\n ...query,\n error: pendingRestart.failure,\n isError: pendingRestart.failure !== null,\n isLoadingError: false,\n isRefetchError: pendingRestart.failure !== null,\n isSuccess: false,\n isPending: false,\n isLoading: false,\n isFetching: true,\n isRefetching: true,\n status: pendingRestart.failure !== null ? 'error' : query.status,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart?.fromError) {\n result = {\n ...query,\n data: undefined,\n error: null,\n isError: false,\n isLoadingError: false,\n isRefetchError: false,\n isSuccess: false,\n isPending: true,\n isLoading: true,\n isFetching: true,\n isRefetching: false,\n status: 'pending',\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (pendingRestart) {\n result = {\n ...query,\n isFetching: true,\n isLoading: query.isPending,\n isRefetching: !query.isPending,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else if (active && query.isPending) {\n // The query underneath is disabled in subscribe mode, so it reports pending but not loading.\n result = {\n ...query,\n isLoading: true,\n isFetching: true,\n fetchStatus: 'fetching',\n refetch,\n } as UseQueryResult<TData, Error>;\n } else {\n result = { ...query, refetch } as UseQueryResult<TData, Error>;\n }\n\n const restartOpen = pendingRestart !== null;\n useEffect(() => {\n latest.current = result as QueryObserverResult<TData, Error>;\n if (restartOpen || waiters.current.length === 0) return;\n const settled = waiters.current;\n waiters.current = [];\n for (const waiter of settled) {\n if (waiter.throwOnError && result.error) waiter.reject(result.error);\n else waiter.resolve(result as QueryObserverResult<TData, Error>);\n }\n });\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n const settled = waiters.current;\n waiters.current = [];\n for (const waiter of settled) waiter.resolve(latest.current);\n };\n }, []);\n\n return { ...result, sourceState } as WithSourceState<UseQueryResult<TData, Error>>;\n}\n"]}
@@ -1,133 +1,69 @@
1
1
  import type { DocumentData, DocumentSnapshot, QueryDocumentSnapshot, QueryConstraint } from 'firebase/firestore';
2
2
  import type { WithId, FirestoreBaseOptions } from '../../firestore/types.js';
3
- /**
4
- * Extract document data with ID from a QueryDocumentSnapshot.
5
- */
6
3
  export declare function docWithId<T extends DocumentData>(snap: QueryDocumentSnapshot<T>): WithId<T>;
7
- /**
8
- * Options for single document queries.
9
- */
10
4
  export interface FirestoreDocOptions<T> extends FirestoreBaseOptions {
11
- /** Full document path (e.g., 'users/abc123') */
12
5
  docPath: string;
13
- /** Enable realtime updates via onSnapshot (default: false) */
14
6
  subscribe?: boolean;
15
- /** Transform function applied to document data. Receives data WITH id included. */
16
7
  select?: (data: DocumentData & {
17
8
  id: string;
18
9
  }) => T;
19
10
  }
20
- /**
21
- * Options for collection queries.
22
- */
23
11
  export interface FirestoreCollectionOptions<T> extends FirestoreBaseOptions {
24
- /** Collection path (e.g., 'users' or 'entities/abc/members') */
25
12
  collectionPath: string;
26
- /** Firestore query constraints (where, orderBy, etc.) */
27
13
  constraints?: QueryConstraint[];
28
- /** Enable realtime updates via onSnapshot (default: false) */
29
14
  subscribe?: boolean;
30
- /** Transform function applied to each document. Receives data WITH id included. */
31
15
  select?: (data: DocumentData & {
32
16
  id: string;
33
17
  }) => T;
34
18
  }
35
- /**
36
- * Options for infinite scroll queries.
37
- */
38
19
  export interface FirestoreInfiniteOptions<T> extends FirestoreBaseOptions {
39
- /** Collection path */
40
20
  collectionPath: string;
41
- /** Firestore query constraints (where, orderBy - required for pagination) */
42
21
  constraints?: QueryConstraint[];
43
- /** Number of items per page (default: 20) */
44
22
  pageSize?: number;
45
- /** Transform function applied to each document. Receives data WITH id included. */
46
23
  select?: (data: DocumentData & {
47
24
  id: string;
48
25
  }) => T;
49
26
  }
50
- /**
51
- * Options for paginated queries with page numbers.
52
- */
53
27
  export interface FirestorePaginatedOptions<T> extends FirestoreBaseOptions {
54
- /** Collection path */
55
28
  collectionPath: string;
56
- /** Firestore query constraints */
57
29
  constraints?: QueryConstraint[];
58
- /** Number of items per page (default: 10) */
59
30
  pageSize?: number;
60
- /** Initial page number (default: 1) */
61
31
  initialPage?: number;
62
- /** Re-read the displayed page every `refetchInterval` ms while it is mounted (omit for none). */
63
32
  refetchInterval?: number;
64
- /** Transform function applied to each document. Receives data WITH id included. */
65
33
  select?: (data: DocumentData & {
66
34
  id: string;
67
35
  }) => T;
68
36
  }
69
- /**
70
- * Options for count() aggregation queries.
71
- */
72
37
  export interface FirestoreCountOptions extends FirestoreBaseOptions {
73
- /** Collection path */
74
38
  collectionPath: string;
75
- /** Firestore query constraints (where, etc.) applied before counting */
76
39
  constraints?: QueryConstraint[];
77
- /** Polling interval in ms (omit for no polling) */
78
40
  refetchInterval?: number;
79
41
  }
80
- /**
81
- * Options for a live newest-window + cursor-bridged older-pages list
82
- * (e.g. chat messages, live comment threads, live activity feeds). The newest
83
- * page is a realtime onSnapshot window; older pages load on demand via getDocs,
84
- * seeded from the window's oldest document so there is no gap or re-read.
85
- */
86
42
  export interface FirestoreLiveInfiniteOptions<T = DocumentData & {
87
43
  id: string;
88
44
  }> {
89
- /** Collection path (slash-joined for nested paths, e.g. 'chats/t1/messages'). */
90
45
  collectionPath: string;
91
- /** React Query cache key. The older-pages query is keyed under `[...queryKey, 'older']`. */
92
46
  queryKey: readonly unknown[];
93
- /** Field both the live window and older pages order by (descending under the hood). */
94
47
  orderByField: string;
95
- /** Equality/filter constraints. Do NOT pass orderBy/limit/startAfter — the hook adds those. */
96
48
  constraints?: QueryConstraint[];
97
- /** Items per page / live-window size (default 20, capped at 100). */
98
49
  pageSize?: number;
99
- /** When false, no listener is opened and no pages are fetched. */
100
50
  enabled?: boolean;
101
- /** Map each raw doc (id included) to the item shape. Default: identity (WithId). */
102
51
  select?: (data: DocumentData & {
103
52
  id: string;
104
53
  }) => T;
105
- /** Output ordering of the merged list, by `orderByField` as Firestore orders it. Default 'asc' (oldest → newest). */
106
54
  sort?: 'asc' | 'desc';
107
55
  }
108
- /**
109
- * Result page for infinite queries.
110
- */
111
56
  export interface InfinitePage<T> {
112
57
  items: WithId<T>[];
113
58
  lastDoc: DocumentSnapshot | null;
114
59
  size: number;
115
60
  }
116
- /**
117
- * One page's cache entry for `useFirestorePaginated`, stored at `[...queryKey, 'page', n]`:
118
- * the page's rows, its last row (the next page's cursor; `null` for an empty page), and
119
- * whether a further page exists (read with a one-row look-ahead), and the id of the row it
120
- * was read after (`null` for page 1), checked against the previous page's cursor.
121
- */
122
61
  export interface PaginatedPage<T> {
123
62
  items: T[];
124
63
  cursor: DocumentSnapshot | null;
125
64
  hasMore: boolean;
126
65
  after: string | null;
127
66
  }
128
- /**
129
- * Mutation operation types for batch mutations.
130
- */
131
67
  export type MutationOperation = {
132
68
  type: 'set';
133
69
  docPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAE7E;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAE3F;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,oBAAoB;IAClE,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B,CAAC,CAAC,CAAE,SAAQ,oBAAoB;IACzE,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,CAAC,CAAE,SAAQ,oBAAoB;IACvE,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC,CAAE,SAAQ,oBAAoB;IACxE,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,sBAAsB;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,4BAA4B,CAAC,CAAC,GAAG,YAAY,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;IAC7E,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,4FAA4F;IAC5F,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,uFAAuF;IACvF,YAAY,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oFAAoF;IACpF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,KAAK,CAAC,CAAC;IACpD,qHAAqH;IACrH,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACnB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/types.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,3 @@
1
- /**
2
- * Extract document data with ID from a QueryDocumentSnapshot.
3
- */
4
1
  export function docWithId(snap) {
5
2
  return { id: snap.id, ...snap.data() };
6
3
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/react/firestore/types.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,UAAU,SAAS,CAAyB,IAA8B;IAC9E,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AACzC,CAAC","sourcesContent":["import type {\n DocumentData,\n DocumentSnapshot,\n QueryDocumentSnapshot,\n QueryConstraint,\n} from 'firebase/firestore';\nimport type { WithId, FirestoreBaseOptions } from '../../firestore/types.js';\n\n/**\n * Extract document data with ID from a QueryDocumentSnapshot.\n */\nexport function docWithId<T extends DocumentData>(snap: QueryDocumentSnapshot<T>): WithId<T> {\n return { id: snap.id, ...snap.data() };\n}\n\n/**\n * Options for single document queries.\n */\nexport interface FirestoreDocOptions<T> extends FirestoreBaseOptions {\n /** Full document path (e.g., 'users/abc123') */\n docPath: string;\n /** Enable realtime updates via onSnapshot (default: false) */\n subscribe?: boolean;\n /** Transform function applied to document data. Receives data WITH id included. */\n select?: (data: DocumentData & { id: string }) => T;\n}\n\n/**\n * Options for collection queries.\n */\nexport interface FirestoreCollectionOptions<T> extends FirestoreBaseOptions {\n /** Collection path (e.g., 'users' or 'entities/abc/members') */\n collectionPath: string;\n /** Firestore query constraints (where, orderBy, etc.) */\n constraints?: QueryConstraint[];\n /** Enable realtime updates via onSnapshot (default: false) */\n subscribe?: boolean;\n /** Transform function applied to each document. Receives data WITH id included. */\n select?: (data: DocumentData & { id: string }) => T;\n}\n\n/**\n * Options for infinite scroll queries.\n */\nexport interface FirestoreInfiniteOptions<T> extends FirestoreBaseOptions {\n /** Collection path */\n collectionPath: string;\n /** Firestore query constraints (where, orderBy - required for pagination) */\n constraints?: QueryConstraint[];\n /** Number of items per page (default: 20) */\n pageSize?: number;\n /** Transform function applied to each document. Receives data WITH id included. */\n select?: (data: DocumentData & { id: string }) => T;\n}\n\n/**\n * Options for paginated queries with page numbers.\n */\nexport interface FirestorePaginatedOptions<T> extends FirestoreBaseOptions {\n /** Collection path */\n collectionPath: string;\n /** Firestore query constraints */\n constraints?: QueryConstraint[];\n /** Number of items per page (default: 10) */\n pageSize?: number;\n /** Initial page number (default: 1) */\n initialPage?: number;\n /** Re-read the displayed page every `refetchInterval` ms while it is mounted (omit for none). */\n refetchInterval?: number;\n /** Transform function applied to each document. Receives data WITH id included. */\n select?: (data: DocumentData & { id: string }) => T;\n}\n\n/**\n * Options for count() aggregation queries.\n */\nexport interface FirestoreCountOptions extends FirestoreBaseOptions {\n /** Collection path */\n collectionPath: string;\n /** Firestore query constraints (where, etc.) applied before counting */\n constraints?: QueryConstraint[];\n /** Polling interval in ms (omit for no polling) */\n refetchInterval?: number;\n}\n\n/**\n * Options for a live newest-window + cursor-bridged older-pages list\n * (e.g. chat messages, live comment threads, live activity feeds). The newest\n * page is a realtime onSnapshot window; older pages load on demand via getDocs,\n * seeded from the window's oldest document so there is no gap or re-read.\n */\nexport interface FirestoreLiveInfiniteOptions<T = DocumentData & { id: string }> {\n /** Collection path (slash-joined for nested paths, e.g. 'chats/t1/messages'). */\n collectionPath: string;\n /** React Query cache key. The older-pages query is keyed under `[...queryKey, 'older']`. */\n queryKey: readonly unknown[];\n /** Field both the live window and older pages order by (descending under the hood). */\n orderByField: string;\n /** Equality/filter constraints. Do NOT pass orderBy/limit/startAfter — the hook adds those. */\n constraints?: QueryConstraint[];\n /** Items per page / live-window size (default 20, capped at 100). */\n pageSize?: number;\n /** When false, no listener is opened and no pages are fetched. */\n enabled?: boolean;\n /** Map each raw doc (id included) to the item shape. Default: identity (WithId). */\n select?: (data: DocumentData & { id: string }) => T;\n /** Output ordering of the merged list, by `orderByField` as Firestore orders it. Default 'asc' (oldest → newest). */\n sort?: 'asc' | 'desc';\n}\n\n/**\n * Result page for infinite queries.\n */\nexport interface InfinitePage<T> {\n items: WithId<T>[];\n lastDoc: DocumentSnapshot | null;\n size: number;\n}\n\n/**\n * One page's cache entry for `useFirestorePaginated`, stored at `[...queryKey, 'page', n]`:\n * the page's rows, its last row (the next page's cursor; `null` for an empty page), and\n * whether a further page exists (read with a one-row look-ahead), and the id of the row it\n * was read after (`null` for page 1), checked against the previous page's cursor.\n */\nexport interface PaginatedPage<T> {\n items: T[];\n cursor: DocumentSnapshot | null;\n hasMore: boolean;\n after: string | null;\n}\n\n/**\n * Mutation operation types for batch mutations.\n */\nexport type MutationOperation =\n | { type: 'set'; docPath: string; data: DocumentData; merge?: boolean }\n | { type: 'update'; docPath: string; data: DocumentData }\n | { type: 'delete'; docPath: string };\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/react/firestore/types.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,SAAS,CAAyB,IAA8B;IAC9E,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;AACzC,CAAC","sourcesContent":["import type {\n DocumentData,\n DocumentSnapshot,\n QueryDocumentSnapshot,\n QueryConstraint,\n} from 'firebase/firestore';\nimport type { WithId, FirestoreBaseOptions } from '../../firestore/types.js';\n\nexport function docWithId<T extends DocumentData>(snap: QueryDocumentSnapshot<T>): WithId<T> {\n return { id: snap.id, ...snap.data() };\n}\n\nexport interface FirestoreDocOptions<T> extends FirestoreBaseOptions {\n docPath: string;\n subscribe?: boolean;\n select?: (data: DocumentData & { id: string }) => T;\n}\n\nexport interface FirestoreCollectionOptions<T> extends FirestoreBaseOptions {\n collectionPath: string;\n constraints?: QueryConstraint[];\n subscribe?: boolean;\n select?: (data: DocumentData & { id: string }) => T;\n}\n\nexport interface FirestoreInfiniteOptions<T> extends FirestoreBaseOptions {\n collectionPath: string;\n constraints?: QueryConstraint[];\n pageSize?: number;\n select?: (data: DocumentData & { id: string }) => T;\n}\n\nexport interface FirestorePaginatedOptions<T> extends FirestoreBaseOptions {\n collectionPath: string;\n constraints?: QueryConstraint[];\n pageSize?: number;\n initialPage?: number;\n refetchInterval?: number;\n select?: (data: DocumentData & { id: string }) => T;\n}\n\nexport interface FirestoreCountOptions extends FirestoreBaseOptions {\n collectionPath: string;\n constraints?: QueryConstraint[];\n refetchInterval?: number;\n}\n\nexport interface FirestoreLiveInfiniteOptions<T = DocumentData & { id: string }> {\n collectionPath: string;\n queryKey: readonly unknown[];\n orderByField: string;\n constraints?: QueryConstraint[];\n pageSize?: number;\n enabled?: boolean;\n select?: (data: DocumentData & { id: string }) => T;\n sort?: 'asc' | 'desc';\n}\n\nexport interface InfinitePage<T> {\n items: WithId<T>[];\n lastDoc: DocumentSnapshot | null;\n size: number;\n}\n\nexport interface PaginatedPage<T> {\n items: T[];\n cursor: DocumentSnapshot | null;\n hasMore: boolean;\n after: string | null;\n}\n\nexport type MutationOperation =\n | { type: 'set'; docPath: string; data: DocumentData; merge?: boolean }\n | { type: 'update'; docPath: string; data: DocumentData }\n | { type: 'delete'; docPath: string };\n"]}
@@ -2,25 +2,5 @@ import { type UseQueryResult } from '@tanstack/react-query';
2
2
  import { type DocumentData } from 'firebase/firestore';
3
3
  import type { WithId, WithSourceState } from '../../firestore/types.js';
4
4
  import type { FirestoreCollectionOptions } from './types.js';
5
- /**
6
- * Fetch all documents from a Firestore collection with optional realtime updates.
7
- * Use this for small collections where you need all documents at once.
8
- * For large collections, use useFirestoreInfinite or useFirestorePaginated.
9
- * * @example
10
- * ```tsx
11
- * // Fetch all channels in an entity
12
- * const { data: channels } = useFirestoreCollection<Channel>({
13
- * collectionPath: `entities/${entityId}/channels`,
14
- * queryKey: ['channels', entityId],
15
- * constraints: [orderBy('createdAt', 'asc')],
16
- * });
17
- * * // With realtime updates
18
- * const { data: members } = useFirestoreCollection<Member>({
19
- * collectionPath: `entities/${entityId}/members`,
20
- * queryKey: ['members', entityId],
21
- * subscribe: true,
22
- * });
23
- * ```
24
- */
25
5
  export declare function useFirestoreCollection<T extends DocumentData = DocumentData>({ collectionPath, queryKey, constraints, enabled, subscribe, staleTime, gcTime, select, }: FirestoreCollectionOptions<T>): WithSourceState<UseQueryResult<WithId<T>[], Error>>;
26
6
  //# sourceMappingURL=use-firestore-collection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-firestore-collection.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-collection.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI/F,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EAC5E,cAAc,EACd,QAAQ,EACR,WAAgB,EAChB,OAAc,EACd,SAAiB,EACjB,SAAS,EACT,MAAM,EACN,MAAM,GACP,EAAE,0BAA0B,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAyHrF"}
1
+ {"version":3,"file":"use-firestore-collection.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-collection.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI/F,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EAC5E,cAAc,EACd,QAAQ,EACR,WAAgB,EAChB,OAAc,EACd,SAAiB,EACjB,SAAS,EACT,MAAM,EACN,MAAM,GACP,EAAE,0BAA0B,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CA2GrF"}
@@ -5,45 +5,17 @@ import { collection, query, getDocs, onSnapshot } from 'firebase/firestore';
5
5
  import { useFirestoreDb } from './context.js';
6
6
  import { RESUBSCRIBE_DELAYS_MS, isPermissionDeniedError } from './resubscribe.js';
7
7
  import { useSubscribedResult, useSubscriptionStatus } from './subscription-status.js';
8
- /**
9
- * Fetch all documents from a Firestore collection with optional realtime updates.
10
- * Use this for small collections where you need all documents at once.
11
- * For large collections, use useFirestoreInfinite or useFirestorePaginated.
12
- * * @example
13
- * ```tsx
14
- * // Fetch all channels in an entity
15
- * const { data: channels } = useFirestoreCollection<Channel>({
16
- * collectionPath: `entities/${entityId}/channels`,
17
- * queryKey: ['channels', entityId],
18
- * constraints: [orderBy('createdAt', 'asc')],
19
- * });
20
- * * // With realtime updates
21
- * const { data: members } = useFirestoreCollection<Member>({
22
- * collectionPath: `entities/${entityId}/members`,
23
- * queryKey: ['members', entityId],
24
- * subscribe: true,
25
- * });
26
- * ```
27
- */
28
8
  export function useFirestoreCollection({ collectionPath, queryKey, constraints = [], enabled = true, subscribe = false, staleTime, gcTime, select, }) {
29
9
  const db = useFirestoreDb();
30
10
  const queryClient = useQueryClient();
31
- // STABILIZE DEPENDENCIES:
32
- // We stringify these because [where('a', '==', 'b')] !== [where('a', '==', 'b')] in JS.
33
11
  const constraintsMemo = JSON.stringify(constraints);
34
12
  const queryKeyMemo = JSON.stringify(queryKey);
35
- // Listener errors can't surface through useQuery (the queryFn is disabled while
36
- // subscribed), so they're tracked per subscription identity and merged into the result.
37
13
  const subscription = useSubscriptionStatus(JSON.stringify([collectionPath, queryKeyMemo, constraintsMemo, enabled, subscribe]));
38
14
  const { reset: resetStatus, update: updateStatus, restarts } = subscription;
39
15
  useEffect(() => {
40
16
  if (!subscribe || !enabled)
41
17
  return;
42
18
  resetStatus();
43
- // Bounded resubscribe: a transient `permission-denied` (e.g. a failed mid-session
44
- // App Check token refresh) is retried on the RESUBSCRIBE_DELAYS_MS ladder before the
45
- // error is surfaced. `attempt` resets on any healthy snapshot; `disposed`/timers are
46
- // cleaned up on unmount so a pending retry never fires after teardown.
47
19
  let currentUnsubscribe = null;
48
20
  let retryTimer = null;
49
21
  let attempt = 0;
@@ -72,9 +44,6 @@ export function useFirestoreCollection({ collectionPath, queryKey, constraints =
72
44
  : 'live',
73
45
  }));
74
46
  }, (error) => {
75
- // Heal a transient permission-denied by resubscribing on the backoff ladder,
76
- // keeping the last-known data visible. Only surface the error once the ladder
77
- // is exhausted; any other error surfaces immediately, as before.
78
47
  if (isPermissionDeniedError(error) && attempt < RESUBSCRIBE_DELAYS_MS.length) {
79
48
  currentUnsubscribe?.();
80
49
  currentUnsubscribe = null;
@@ -96,9 +65,7 @@ export function useFirestoreCollection({ collectionPath, queryKey, constraints =
96
65
  clearTimeout(retryTimer);
97
66
  currentUnsubscribe?.();
98
67
  };
99
- // queryKey & constraints are tracked via their stringified forms above; select is intentionally
100
- // excluded so an inline caller function doesn't force a re-subscribe on every render.
101
- // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- queryKey and constraints are tracked by their serialized forms; an inline select must not re-subscribe every render
102
69
  }, [
103
70
  db,
104
71
  collectionPath,
@@ -119,7 +86,6 @@ export function useFirestoreCollection({ collectionPath, queryKey, constraints =
119
86
  const snapshot = await getDocs(q);
120
87
  return snapshot.docs.map((docSnap) => {
121
88
  const rawData = docSnap.data();
122
- // Include id in data passed to select, so it can be renamed/transformed
123
89
  const dataWithId = { id: docSnap.id, ...rawData };
124
90
  const data = select ? select(dataWithId) : dataWithId;
125
91
  return data;
@@ -1 +1 @@
1
- {"version":3,"file":"use-firestore-collection.js","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-collection.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { useQuery, useQueryClient, type UseQueryResult } from '@tanstack/react-query';\nimport { collection, query, getDocs, onSnapshot, type DocumentData } from 'firebase/firestore';\nimport { useFirestoreDb } from './context.js';\nimport { RESUBSCRIBE_DELAYS_MS, isPermissionDeniedError } from './resubscribe.js';\nimport { useSubscribedResult, useSubscriptionStatus } from './subscription-status.js';\nimport type { WithId, WithSourceState } from '../../firestore/types.js';\nimport type { FirestoreCollectionOptions } from './types.js';\n\n/**\n * Fetch all documents from a Firestore collection with optional realtime updates.\n * Use this for small collections where you need all documents at once.\n * For large collections, use useFirestoreInfinite or useFirestorePaginated.\n * * @example\n * ```tsx\n * // Fetch all channels in an entity\n * const { data: channels } = useFirestoreCollection<Channel>({\n * collectionPath: `entities/${entityId}/channels`,\n * queryKey: ['channels', entityId],\n * constraints: [orderBy('createdAt', 'asc')],\n * });\n * * // With realtime updates\n * const { data: members } = useFirestoreCollection<Member>({\n * collectionPath: `entities/${entityId}/members`,\n * queryKey: ['members', entityId],\n * subscribe: true,\n * });\n * ```\n */\nexport function useFirestoreCollection<T extends DocumentData = DocumentData>({\n collectionPath,\n queryKey,\n constraints = [],\n enabled = true,\n subscribe = false,\n staleTime,\n gcTime,\n select,\n}: FirestoreCollectionOptions<T>): WithSourceState<UseQueryResult<WithId<T>[], Error>> {\n const db = useFirestoreDb();\n const queryClient = useQueryClient();\n\n // STABILIZE DEPENDENCIES:\n // We stringify these because [where('a', '==', 'b')] !== [where('a', '==', 'b')] in JS.\n const constraintsMemo = JSON.stringify(constraints);\n const queryKeyMemo = JSON.stringify(queryKey);\n\n // Listener errors can't surface through useQuery (the queryFn is disabled while\n // subscribed), so they're tracked per subscription identity and merged into the result.\n const subscription = useSubscriptionStatus(\n JSON.stringify([collectionPath, queryKeyMemo, constraintsMemo, enabled, subscribe]),\n );\n const { reset: resetStatus, update: updateStatus, restarts } = subscription;\n\n useEffect(() => {\n if (!subscribe || !enabled) return;\n\n resetStatus();\n\n // Bounded resubscribe: a transient `permission-denied` (e.g. a failed mid-session\n // App Check token refresh) is retried on the RESUBSCRIBE_DELAYS_MS ladder before the\n // error is surfaced. `attempt` resets on any healthy snapshot; `disposed`/timers are\n // cleaned up on unmount so a pending retry never fires after teardown.\n let currentUnsubscribe: (() => void) | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let disposed = false;\n\n const start = () => {\n if (disposed) return;\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n\n currentUnsubscribe = onSnapshot(\n q,\n { includeMetadataChanges: true },\n (snapshot) => {\n attempt = 0;\n updateStatus({ error: null, restart: null });\n const items = 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 queryClient.setQueryData(queryKey, items);\n const fromCache = snapshot.metadata?.fromCache ?? false;\n updateStatus((prev) => ({\n sourceState: fromCache\n ? prev.sourceState === 'connecting'\n ? 'connecting'\n : 'offline'\n : 'live',\n }));\n },\n (error) => {\n // Heal a transient permission-denied by resubscribing on the backoff ladder,\n // keeping the last-known data visible. Only surface the error once the ladder\n // is exhausted; any other error surfaces immediately, as before.\n if (isPermissionDeniedError(error) && attempt < RESUBSCRIBE_DELAYS_MS.length) {\n currentUnsubscribe?.();\n currentUnsubscribe = null;\n const delay = RESUBSCRIBE_DELAYS_MS[attempt];\n attempt += 1;\n updateStatus({ sourceState: 'connecting' });\n retryTimer = setTimeout(start, delay);\n return;\n }\n console.error('[useFirestoreCollection] Subscription error:', error);\n updateStatus({ error, sourceState: 'error', restart: null });\n queryClient.setQueryData(queryKey, undefined);\n },\n );\n };\n\n start();\n\n return () => {\n disposed = true;\n if (retryTimer) clearTimeout(retryTimer);\n currentUnsubscribe?.();\n };\n // queryKey & constraints are tracked via their stringified forms above; select is intentionally\n // excluded so an inline caller function doesn't force a re-subscribe on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n db,\n collectionPath,\n queryKeyMemo,\n constraintsMemo,\n enabled,\n subscribe,\n restarts,\n queryClient,\n resetStatus,\n updateStatus,\n ]);\n\n const queryResult = useQuery({\n queryKey,\n queryFn: async (): Promise<WithId<T>[]> => {\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n\n const snapshot = await getDocs(q);\n return snapshot.docs.map((docSnap) => {\n const rawData = docSnap.data();\n // Include id in data passed to select, so it can be renamed/transformed\n const dataWithId = { id: docSnap.id, ...rawData };\n const data = select ? select(dataWithId) : dataWithId;\n return data as WithId<T>;\n });\n },\n enabled: enabled && !subscribe,\n staleTime: subscribe ? Infinity : staleTime,\n gcTime,\n });\n\n return useSubscribedResult(queryResult, subscribe, enabled, subscription);\n}\n"]}
1
+ {"version":3,"file":"use-firestore-collection.js","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-collection.ts"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { useQuery, useQueryClient, type UseQueryResult } from '@tanstack/react-query';\nimport { collection, query, getDocs, onSnapshot, type DocumentData } from 'firebase/firestore';\nimport { useFirestoreDb } from './context.js';\nimport { RESUBSCRIBE_DELAYS_MS, isPermissionDeniedError } from './resubscribe.js';\nimport { useSubscribedResult, useSubscriptionStatus } from './subscription-status.js';\nimport type { WithId, WithSourceState } from '../../firestore/types.js';\nimport type { FirestoreCollectionOptions } from './types.js';\n\nexport function useFirestoreCollection<T extends DocumentData = DocumentData>({\n collectionPath,\n queryKey,\n constraints = [],\n enabled = true,\n subscribe = false,\n staleTime,\n gcTime,\n select,\n}: FirestoreCollectionOptions<T>): WithSourceState<UseQueryResult<WithId<T>[], Error>> {\n const db = useFirestoreDb();\n const queryClient = useQueryClient();\n\n const constraintsMemo = JSON.stringify(constraints);\n const queryKeyMemo = JSON.stringify(queryKey);\n\n const subscription = useSubscriptionStatus(\n JSON.stringify([collectionPath, queryKeyMemo, constraintsMemo, enabled, subscribe]),\n );\n const { reset: resetStatus, update: updateStatus, restarts } = subscription;\n\n useEffect(() => {\n if (!subscribe || !enabled) return;\n\n resetStatus();\n\n let currentUnsubscribe: (() => void) | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let disposed = false;\n\n const start = () => {\n if (disposed) return;\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n\n currentUnsubscribe = onSnapshot(\n q,\n { includeMetadataChanges: true },\n (snapshot) => {\n attempt = 0;\n updateStatus({ error: null, restart: null });\n const items = 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 queryClient.setQueryData(queryKey, items);\n const fromCache = snapshot.metadata?.fromCache ?? false;\n updateStatus((prev) => ({\n sourceState: fromCache\n ? prev.sourceState === 'connecting'\n ? 'connecting'\n : 'offline'\n : 'live',\n }));\n },\n (error) => {\n if (isPermissionDeniedError(error) && attempt < RESUBSCRIBE_DELAYS_MS.length) {\n currentUnsubscribe?.();\n currentUnsubscribe = null;\n const delay = RESUBSCRIBE_DELAYS_MS[attempt];\n attempt += 1;\n updateStatus({ sourceState: 'connecting' });\n retryTimer = setTimeout(start, delay);\n return;\n }\n console.error('[useFirestoreCollection] Subscription error:', error);\n updateStatus({ error, sourceState: 'error', restart: null });\n queryClient.setQueryData(queryKey, undefined);\n },\n );\n };\n\n start();\n\n return () => {\n disposed = true;\n if (retryTimer) clearTimeout(retryTimer);\n currentUnsubscribe?.();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- queryKey and constraints are tracked by their serialized forms; an inline select must not re-subscribe every render\n }, [\n db,\n collectionPath,\n queryKeyMemo,\n constraintsMemo,\n enabled,\n subscribe,\n restarts,\n queryClient,\n resetStatus,\n updateStatus,\n ]);\n\n const queryResult = useQuery({\n queryKey,\n queryFn: async (): Promise<WithId<T>[]> => {\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n\n const snapshot = await getDocs(q);\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: enabled && !subscribe,\n staleTime: subscribe ? Infinity : staleTime,\n gcTime,\n });\n\n return useSubscribedResult(queryResult, subscribe, enabled, subscription);\n}\n"]}
@@ -1,19 +1,4 @@
1
1
  import { type UseQueryResult } from '@tanstack/react-query';
2
2
  import type { FirestoreCountOptions } from './types.js';
3
- /**
4
- * Count documents in a Firestore collection via a server-side `count()`
5
- * aggregation (cheap — it does not read the matching documents). Optional
6
- * polling via `refetchInterval`.
7
- *
8
- * @example
9
- * ```tsx
10
- * const { data: count } = useFirestoreCount({
11
- * collectionPath: 'activeUserNotifications',
12
- * queryKey: ['notifications', 'unread-count', userId],
13
- * constraints: [where('targetUserId', '==', userId), where('seenAt', '==', 0)],
14
- * refetchInterval: 30_000,
15
- * });
16
- * ```
17
- */
18
3
  export declare function useFirestoreCount({ collectionPath, queryKey, constraints, enabled, staleTime, gcTime, refetchInterval, }: FirestoreCountOptions): UseQueryResult<number, Error>;
19
4
  //# sourceMappingURL=use-firestore-count.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-firestore-count.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-count.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,cAAc,EACd,QAAQ,EACR,WAAgB,EAChB,OAAc,EACd,SAAS,EACT,MAAM,EACN,eAAe,GAChB,EAAE,qBAAqB,GAAG,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAgBvD"}
1
+ {"version":3,"file":"use-firestore-count.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-count.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,wBAAgB,iBAAiB,CAAC,EAChC,cAAc,EACd,QAAQ,EACR,WAAgB,EAChB,OAAc,EACd,SAAS,EACT,MAAM,EACN,eAAe,GAChB,EAAE,qBAAqB,GAAG,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAgBvD"}
@@ -2,21 +2,6 @@
2
2
  import { useQuery } from '@tanstack/react-query';
3
3
  import { collection, query, getCountFromServer } from 'firebase/firestore';
4
4
  import { useFirestoreDb } from './context.js';
5
- /**
6
- * Count documents in a Firestore collection via a server-side `count()`
7
- * aggregation (cheap — it does not read the matching documents). Optional
8
- * polling via `refetchInterval`.
9
- *
10
- * @example
11
- * ```tsx
12
- * const { data: count } = useFirestoreCount({
13
- * collectionPath: 'activeUserNotifications',
14
- * queryKey: ['notifications', 'unread-count', userId],
15
- * constraints: [where('targetUserId', '==', userId), where('seenAt', '==', 0)],
16
- * refetchInterval: 30_000,
17
- * });
18
- * ```
19
- */
20
5
  export function useFirestoreCount({ collectionPath, queryKey, constraints = [], enabled = true, staleTime, gcTime, refetchInterval, }) {
21
6
  const db = useFirestoreDb();
22
7
  return useQuery({
@@ -1 +1 @@
1
- {"version":3,"file":"use-firestore-count.js","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-count.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,cAAc,EACd,QAAQ,EACR,WAAW,GAAG,EAAE,EAChB,OAAO,GAAG,IAAI,EACd,SAAS,EACT,MAAM,EACN,eAAe,GACO;IACtB,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAE5B,OAAO,QAAQ,CAAC;QACd,QAAQ;QACR,OAAO,EAAE,KAAK,IAAqB,EAAE;YACnC,MAAM,aAAa,GAAG,UAAU,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YACxF,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC/B,CAAC;QACD,OAAO;QACP,SAAS;QACT,MAAM;QACN,eAAe;KAChB,CAAC,CAAC;AACL,CAAC","sourcesContent":["'use client';\n\nimport { useQuery, type UseQueryResult } from '@tanstack/react-query';\nimport { collection, query, getCountFromServer } from 'firebase/firestore';\nimport { useFirestoreDb } from './context.js';\nimport type { FirestoreCountOptions } from './types.js';\n\n/**\n * Count documents in a Firestore collection via a server-side `count()`\n * aggregation (cheap — it does not read the matching documents). Optional\n * polling via `refetchInterval`.\n *\n * @example\n * ```tsx\n * const { data: count } = useFirestoreCount({\n * collectionPath: 'activeUserNotifications',\n * queryKey: ['notifications', 'unread-count', userId],\n * constraints: [where('targetUserId', '==', userId), where('seenAt', '==', 0)],\n * refetchInterval: 30_000,\n * });\n * ```\n */\nexport function useFirestoreCount({\n collectionPath,\n queryKey,\n constraints = [],\n enabled = true,\n staleTime,\n gcTime,\n refetchInterval,\n}: FirestoreCountOptions): UseQueryResult<number, Error> {\n const db = useFirestoreDb();\n\n return useQuery({\n queryKey,\n queryFn: async (): Promise<number> => {\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n const snapshot = await getCountFromServer(q);\n return snapshot.data().count;\n },\n enabled,\n staleTime,\n gcTime,\n refetchInterval,\n });\n}\n"]}
1
+ {"version":3,"file":"use-firestore-count.js","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-count.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,UAAU,iBAAiB,CAAC,EAChC,cAAc,EACd,QAAQ,EACR,WAAW,GAAG,EAAE,EAChB,OAAO,GAAG,IAAI,EACd,SAAS,EACT,MAAM,EACN,eAAe,GACO;IACtB,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAE5B,OAAO,QAAQ,CAAC;QACd,QAAQ;QACR,OAAO,EAAE,KAAK,IAAqB,EAAE;YACnC,MAAM,aAAa,GAAG,UAAU,CAAC,EAAE,EAAE,cAAc,CAAC,CAAC;YACrD,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;YACxF,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAC/B,CAAC;QACD,OAAO;QACP,SAAS;QACT,MAAM;QACN,eAAe;KAChB,CAAC,CAAC;AACL,CAAC","sourcesContent":["'use client';\n\nimport { useQuery, type UseQueryResult } from '@tanstack/react-query';\nimport { collection, query, getCountFromServer } from 'firebase/firestore';\nimport { useFirestoreDb } from './context.js';\nimport type { FirestoreCountOptions } from './types.js';\n\nexport function useFirestoreCount({\n collectionPath,\n queryKey,\n constraints = [],\n enabled = true,\n staleTime,\n gcTime,\n refetchInterval,\n}: FirestoreCountOptions): UseQueryResult<number, Error> {\n const db = useFirestoreDb();\n\n return useQuery({\n queryKey,\n queryFn: async (): Promise<number> => {\n const collectionRef = collection(db, collectionPath);\n const q = constraints.length > 0 ? query(collectionRef, ...constraints) : collectionRef;\n const snapshot = await getCountFromServer(q);\n return snapshot.data().count;\n },\n enabled,\n staleTime,\n gcTime,\n refetchInterval,\n });\n}\n"]}
@@ -2,28 +2,5 @@ import { type UseQueryResult } from '@tanstack/react-query';
2
2
  import { type DocumentData } from 'firebase/firestore';
3
3
  import type { WithId, WithSourceState } from '../../firestore/types.js';
4
4
  import type { FirestoreDocOptions } from './types.js';
5
- /**
6
- * Fetch a single Firestore document with optional realtime updates.
7
- * * @example
8
- * ```tsx
9
- * // Simple fetch
10
- * const { data: user } = useFirestoreDoc<User>({
11
- * docPath: `users/${userId}`,
12
- * queryKey: ['user', userId],
13
- * });
14
- * * // With realtime updates
15
- * const { data: entity } = useFirestoreDoc<Entity>({
16
- * docPath: `entities/${entityId}`,
17
- * queryKey: ['entity', entityId],
18
- * subscribe: true,
19
- * });
20
- * * // Conditional fetch
21
- * const { data: profile } = useFirestoreDoc<Profile>({
22
- * docPath: `profiles/${userId}`,
23
- * queryKey: ['profile', userId],
24
- * enabled: !!userId,
25
- * });
26
- * ```
27
- */
28
5
  export declare function useFirestoreDoc<T extends DocumentData = DocumentData>({ docPath, queryKey, enabled, subscribe, staleTime, gcTime, select, }: FirestoreDocOptions<T>): WithSourceState<UseQueryResult<WithId<T> | null, Error>>;
29
6
  //# sourceMappingURL=use-firestore-doc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-firestore-doc.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-doc.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIhF,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EACrE,OAAO,EACP,QAAQ,EACR,OAAc,EACd,SAAiB,EACjB,SAAS,EACT,MAAM,EACN,MAAM,GACP,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAsHnF"}
1
+ {"version":3,"file":"use-firestore-doc.d.ts","sourceRoot":"","sources":["../../../src/react/firestore/use-firestore-doc.ts"],"names":[],"mappings":"AAGA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIhF,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,wBAAgB,eAAe,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAAE,EACrE,OAAO,EACP,QAAQ,EACR,OAAc,EACd,SAAiB,EACjB,SAAS,EACT,MAAM,EACN,MAAM,GACP,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAwGnF"}