@data-weave/backend-firestore 0.3.8 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.4.1](https://github.com/data-weave/datamanager/compare/v0.4.0...v0.4.1) (2025-10-15)
7
+
8
+ **Note:** Version bump only for package @data-weave/backend-firestore
9
+
10
+ ## [0.4.0](https://github.com/data-weave/datamanager/compare/v0.3.8...v0.4.0) (2025-10-15)
11
+
12
+ ### Features
13
+
14
+ - add error interceptor ([dde95c8](https://github.com/data-weave/datamanager/commit/dde95c8dba3d02d7861d132314db4d1cf23bec37))
15
+ - add new typing, snapshot options and sane type defaults ([7095568](https://github.com/data-weave/datamanager/commit/7095568892048dc75cccc284e2dd2ba23793a152))
16
+ - create shim for modular firebase as well ([591b25a](https://github.com/data-weave/datamanager/commit/591b25a8cbcf89687b3880cc3ca1cd95a735e11d))
17
+ - move firebase-admin shim to it's own package ([ac9f861](https://github.com/data-weave/datamanager/commit/ac9f861ca810a2a14706e10e87674b1b1f14a964))
18
+
6
19
  ## [0.3.8](https://github.com/data-weave/datamanager/compare/v0.3.7...v0.3.8) (2025-09-28)
7
20
 
8
21
  **Note:** Version bump only for package @data-weave/backend-firestore
@@ -4,14 +4,14 @@ import { Cache, MapCache } from '@data-weave/datamanager/lib/Cache'
4
4
  import { CreateOptions, DataManager, Metadata, WithMetadata } from '@data-weave/datamanager/lib/DataManager'
5
5
  import { List, ListPaginationParams } from '@data-weave/datamanager/lib/List'
6
6
  import { IdentifiableReference, WithoutId } from '@data-weave/datamanager/lib/Reference'
7
- import { FirestoreList } from './FirestoreList'
7
+ import { FirestoreList, FirestoreListContext } from './FirestoreList'
8
8
  import {
9
9
  FIRESTORE_KEYS,
10
10
  FirestoreMetadataConverter,
11
11
  FirestoreSerializedMetadata,
12
12
  queryNotDeleted,
13
13
  } from './FirestoreMetadata'
14
- import { FirestoreReference } from './FirestoreReference'
14
+ import { FirestoreReference, FirestoreReferenceContext } from './FirestoreReference'
15
15
  import {
16
16
  DocumentData,
17
17
  FilterBy,
@@ -35,6 +35,8 @@ export interface FirebaseDataManagerOptions {
35
35
  readonly deleteMode?: FirebaseDataManagerDeleteMode
36
36
  readonly readMode?: FirestoreReadMode
37
37
  readonly preventOverwriteOnCreate?: boolean
38
+ readonly errorInterceptor?: (error: unknown, ctx: FirestoreReferenceContext | FirestoreListContext) => void
39
+ readonly snapshotOptions?: FirestoreTypes.SnapshotOptions
38
40
  // TODO: Add preventUpdateIfNotExists?
39
41
  readonly Reference?: typeof FirestoreReference
40
42
  readonly List?: typeof FirestoreList
@@ -58,7 +60,7 @@ export interface QueryParams<T extends FirestoreTypes.DocumentData> {
58
60
  @injectable()
59
61
  export class FirestoreDataManager<
60
62
  T extends FirestoreTypes.DocumentData,
61
- SerializedT extends FirestoreTypes.DocumentData,
63
+ SerializedT extends FirestoreTypes.DocumentData = T,
62
64
  > implements DataManager<T>
63
65
  {
64
66
  private mergedConverter: InternalFirestoreDataConverter<T & Metadata, SerializedT & FirestoreSerializedMetadata>
@@ -195,6 +197,8 @@ export class FirestoreDataManager<
195
197
 
196
198
  const newRef = new this.managerOptions.Reference(this.firestore, this.firestore.doc(this.collection, id), {
197
199
  readMode: this.managerOptions.readMode,
200
+ errorInterceptor: this.managerOptions.errorInterceptor,
201
+ snapshotOptions: this.managerOptions.snapshotOptions,
198
202
  })
199
203
  this.refCache.set(id, newRef)
200
204
  return newRef
@@ -225,6 +229,7 @@ export class FirestoreDataManager<
225
229
  }
226
230
  const newList = new this.managerOptions.List(this.firestore, compoundQuery, {
227
231
  readMode: this.managerOptions.readMode,
232
+ errorInterceptor: this.managerOptions.errorInterceptor,
228
233
  ...params,
229
234
  })
230
235
  this.listCache.set(key, newList)
@@ -1,8 +1,15 @@
1
1
  import { ListPaginationParams, LiveList, LiveListOptions } from '@data-weave/datamanager'
2
2
  import { DocumentData, Firestore, FirestoreReadMode, FirestoreTypes } from './firestoreTypes'
3
3
 
4
+ export interface FirestoreListContext {
5
+ query: FirestoreTypes.Query<unknown>
6
+ readMode?: FirestoreReadMode
7
+ type: 'list'
8
+ }
9
+
4
10
  export interface FirestoreListOptions<T> extends LiveListOptions<T> {
5
11
  readMode?: FirestoreReadMode
12
+ errorInterceptor?: (error: unknown, ctx: FirestoreListContext) => void
6
13
  }
7
14
 
8
15
  export class FirestoreList<T extends DocumentData, S extends DocumentData> extends LiveList<T> {
@@ -79,6 +86,11 @@ export class FirestoreList<T extends DocumentData, S extends DocumentData> exten
79
86
  // Try to provide useful collection details using internal properties
80
87
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
88
  console.error(`FirestoreList Collection: ${(this.query as any)?._collectionPath?.id} error`, error)
89
+ this.options?.errorInterceptor?.(error, {
90
+ query: this.query,
91
+ readMode: this.options?.readMode,
92
+ type: 'list',
93
+ })
82
94
  super.onError(error)
83
95
  }
84
96
  }
@@ -2,8 +2,18 @@ import { LiveReference, LiveReferenceOptions } from '@data-weave/datamanager'
2
2
  import { DocumentData, Firestore, FirestoreReadMode, FirestoreTypes } from './firestoreTypes'
3
3
  import { checkIfReferenceExists } from './utils'
4
4
 
5
+ export interface FirestoreReferenceContext {
6
+ path: string
7
+ id: string
8
+ readMode?: FirestoreReadMode
9
+ snapshotOptions?: FirestoreTypes.SnapshotOptions
10
+ type: 'reference'
11
+ }
12
+
5
13
  export interface FirestoreReferenceOptions<T> extends LiveReferenceOptions<T> {
6
14
  readMode?: FirestoreReadMode
15
+ snapshotOptions?: FirestoreTypes.SnapshotOptions
16
+ errorInterceptor?: (error: unknown, ctx: FirestoreReferenceContext) => void
7
17
  }
8
18
 
9
19
  export class FirestoreReference<T extends DocumentData, S extends DocumentData> extends LiveReference<T> {
@@ -60,6 +70,13 @@ export class FirestoreReference<T extends DocumentData, S extends DocumentData>
60
70
 
61
71
  protected onError(error: unknown): void {
62
72
  console.error(`FirestoreReference error ${this.docRef.path}`, error)
73
+ this.options?.errorInterceptor?.(error, {
74
+ path: this.docRef.path,
75
+ id: this.docRef.id,
76
+ readMode: this.options?.readMode,
77
+ snapshotOptions: this.options?.snapshotOptions,
78
+ type: 'reference',
79
+ })
63
80
  super.onError(error)
64
81
  }
65
82
 
@@ -70,6 +87,6 @@ export class FirestoreReference<T extends DocumentData, S extends DocumentData>
70
87
 
71
88
  private parseDocumentSnapshot(docSnapshot: FirestoreTypes.DocumentSnapshot<T, S>): T | undefined {
72
89
  if (!checkIfReferenceExists(docSnapshot)) throw new Error(`Document does not exist ${this.docRef.path}`)
73
- return docSnapshot.data()
90
+ return docSnapshot.data(this.options?.snapshotOptions)
74
91
  }
75
92
  }
@@ -28,23 +28,38 @@ export declare interface InternalFirestoreDataConverter<
28
28
  ): T
29
29
  }
30
30
 
31
+ export type WithTimestamps<T> = {
32
+ [K in keyof T]: T[K] extends Date
33
+ ? FirestoreTypes.Timestamp
34
+ : T[K] extends Date | null
35
+ ? FirestoreTypes.Timestamp | null
36
+ : T[K] extends Date | undefined
37
+ ? FirestoreTypes.Timestamp | undefined
38
+ : T[K] extends Date | null | undefined
39
+ ? FirestoreTypes.Timestamp | null | undefined
40
+ : T[K] extends object
41
+ ? T[K] extends any[]
42
+ ? T[K] extends (infer U)[]
43
+ ? WithTimestamps<U>[]
44
+ : T[K]
45
+ : WithTimestamps<T[K]>
46
+ : T[K]
47
+ }
48
+
31
49
  /**
32
50
  * Converts data between model and serialized data. Differs from InternalFirestoreDataConverter
33
51
  * by using `OptionallyUndefined` and `Required` to ensure that all fields are included in de/serialization
34
52
  * even if some model defined properties are optional.
35
53
  */
36
- export declare interface FirestoreDataConverter<
37
- T extends DocumentData,
38
- SerializedT extends DocumentData = DocumentData,
39
- > {
54
+ export declare interface FirestoreDataConverter<ModelObject, SerializedModelObject = ModelObject> {
40
55
  toFirestore(
41
- modelObject: WithoutId<Partial<WithFieldValue<T>>>,
56
+ modelObject: WithoutId<ModelObject>,
42
57
  options?: FirestoreTypes.SetOptions
43
- ): OptionallyUndefined<Required<WithoutId<WithFieldValue<SerializedT>>>>
58
+ ): OptionallyUndefined<Required<WithoutId<WithFieldValue<SerializedModelObject>>>>
44
59
  fromFirestore(
45
- snapshot: FirestoreTypes.QueryDocumentSnapshot<T, SerializedT>,
60
+ snapshot: FirestoreTypes.QueryDocumentSnapshot<WithTimestamps<SerializedModelObject>>,
46
61
  options?: FirestoreTypes.SnapshotOptions
47
- ): OptionallyUndefined<Required<WithoutId<T>>>
62
+ ): ModelObject
48
63
  }
49
64
 
50
65
  export type FirestoreQuery<AppModelType = DocumentData, DbModelType extends DocumentData = DocumentData> = (
@@ -83,12 +98,15 @@ export type FilterableFields<
83
98
  ? // Handle firebase timestamp fields
84
99
  T[K] extends FirestoreTypes.Timestamp
85
100
  ? `${K}`
86
- : // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
- T[K] extends any[]
88
- ? GenNode<K, IsRoot>
89
- : T[K] extends object
90
- ? `${GenNode<K, IsRoot>}${FilterableFields<T[K], false>}`
91
- : GenNode<K, IsRoot>
101
+ : // Handle Date fields
102
+ T[K] extends Date
103
+ ? `${K}`
104
+ : // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ T[K] extends any[]
106
+ ? GenNode<K, IsRoot>
107
+ : T[K] extends object
108
+ ? `${GenNode<K, IsRoot>}${FilterableFields<T[K], false>}`
109
+ : GenNode<K, IsRoot>
92
110
  : never
93
111
 
94
112
  type GetValue<T, K extends string> = K extends `${infer L}.${infer R}`
@@ -96,7 +114,9 @@ type GetValue<T, K extends string> = K extends `${infer L}.${infer R}`
96
114
  ? GetValue<T[L], R>
97
115
  : never
98
116
  : K extends keyof T
99
- ? T[K]
117
+ ? T[K] extends Date
118
+ ? Date | FirestoreTypes.Timestamp
119
+ : T[K]
100
120
  : K extends string
101
121
  ? string | number | boolean
102
122
  : never
package/lib/utils.ts CHANGED
@@ -1,15 +1,32 @@
1
1
  import { WithoutId } from '@data-weave/datamanager'
2
2
  import {
3
3
  DocumentData,
4
- FieldValues,
5
4
  Firestore,
6
- FirestoreApp,
7
5
  FirestoreTypes,
8
6
  InternalFirestoreDataConverter,
9
7
  Transaction,
10
8
  WithFieldValue,
11
9
  } from './firestoreTypes'
12
10
 
11
+ import {
12
+ Firestore as FirebaseFirestore,
13
+ collection,
14
+ deleteDoc,
15
+ doc,
16
+ getDoc,
17
+ getDocs,
18
+ increment,
19
+ limit,
20
+ onSnapshot,
21
+ orderBy,
22
+ query,
23
+ runTransaction,
24
+ serverTimestamp,
25
+ setDoc,
26
+ updateDoc,
27
+ where,
28
+ } from 'firebase/firestore'
29
+
13
30
  export class MergeConverters<
14
31
  T extends DocumentData,
15
32
  SerializedT extends DocumentData,
@@ -50,111 +67,28 @@ export class MergeConverters<
50
67
  }
51
68
  }
52
69
 
53
- /*
54
- * FirestoreNamespacedConverter creates an interface between modular and namespaced
55
- * firestore libraries.
56
- * - admin sdk doesn't support modular imports
57
- */
58
-
59
- // TODO: add types
60
- /* eslint-disable @typescript-eslint/no-explicit-any */
61
- export class FirestoreNamespacedConverter extends Firestore {
62
- public app: FirestoreApp
63
- constructor(
64
- readonly firestore: FirestoreApp,
65
- readonly fieldValues: FieldValues
66
- ) {
67
- super()
68
- this.app = firestore
69
- this.fieldValues = fieldValues
70
- }
71
-
72
- public collection(reference: any, path: string) {
73
- return reference.collection(path)
74
- }
75
-
76
- public getDocs(reference: any) {
77
- return reference.get()
78
- }
79
-
80
- public getDoc(reference: any, path?: string) {
81
- return reference.get(path)
82
- }
83
-
84
- public serverTimestamp() {
85
- return this.fieldValues.serverTimestamp()
86
- }
87
-
88
- public increment(n: number) {
89
- return this.fieldValues.increment(n)
90
- }
91
-
92
- public query(reference: any, filter: any) {
93
- if (filter.type === 'where') {
94
- return reference.where(filter.field, filter.op, filter.value)
95
- }
96
- if (filter.type === 'orderBy') {
97
- return reference.orderBy(filter.field, filter.direction)
98
- }
99
- if (filter.type === 'limit') {
100
- return reference.limit(filter.limit)
101
- }
102
- }
103
-
104
- public where(field: string | FirestoreTypes.FieldPath, op: string, value: unknown): any {
105
- return {
106
- type: 'where',
107
- field,
108
- op,
109
- value,
110
- }
111
- }
112
-
113
- public limit(limit: number) {
114
- return {
115
- type: 'limit',
116
- limit,
117
- }
118
- }
119
-
120
- public orderBy(orderBy: string, direction: FirestoreTypes.OrderByDirection) {
121
- return {
122
- type: 'orderBy',
123
- orderBy,
124
- direction,
125
- }
126
- }
127
-
128
- public setDoc(reference: any, data: any, options?: FirestoreTypes.SetOptions) {
129
- return reference.set(data, options)
130
- }
131
-
132
- public updateDoc(reference: any, data: any) {
133
- return reference.update(data)
134
- }
135
-
136
- public deleteDoc(reference: any) {
137
- return reference.delete()
138
- }
139
-
140
- public doc(reference: any, path?: string) {
141
- if (!path) return reference.doc()
142
- return reference.doc(path)
143
- }
144
-
145
- public onSnapshot(reference: any, onNext: (snapshot: any) => void, onError?: (error: Error) => void) {
146
- return reference.onSnapshot(onNext, onError)
147
- }
148
-
149
- public runTransaction<T>(
150
- firestore: FirestoreApp,
151
- transaction: (transaction: Transaction) => Promise<T>,
152
- options?: FirestoreTypes.TransactionOptions
153
- ) {
154
- return firestore.runTransaction!(transaction, options)
70
+ export function createModularFirestoreAdapter(firestore: FirebaseFirestore): Firestore {
71
+ return {
72
+ app: firestore,
73
+ collection,
74
+ getDocs,
75
+ getDoc,
76
+ serverTimestamp,
77
+ query,
78
+ where,
79
+ limit,
80
+ orderBy,
81
+ setDoc,
82
+ updateDoc,
83
+ deleteDoc,
84
+ doc,
85
+ onSnapshot,
86
+ increment,
87
+ // TODO: fix this
88
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
+ runTransaction: runTransaction as any,
155
90
  }
156
91
  }
157
- /* eslint-enable @typescript-eslint/no-explicit-any */
158
92
 
159
93
  // Value can be anything
160
94
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '0.3.8';
2
+ module.exports = '0.4.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-weave/backend-firestore",
3
- "version": "0.3.8",
3
+ "version": "0.4.1",
4
4
  "author": "",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -17,12 +17,9 @@
17
17
  "access": "public",
18
18
  "registry": "https://registry.npmjs.org/"
19
19
  },
20
- "dependencies": {
21
- "firebase": "^11.9.0",
22
- "firebase-admin": "^13.0.2"
23
- },
24
20
  "peerDependencies": {
25
- "@data-weave/datamanager": "0.3.7"
21
+ "@data-weave/datamanager": "0.4.0",
22
+ "firebase": ">=11.0.0"
26
23
  },
27
- "gitHead": "15240bc24e96bab7e184edd81f18fbf64b52d8c9"
24
+ "gitHead": "b8faf5f44694259ca8d2b2abfcde699328b03879"
28
25
  }