@data-weave/backend-firestore 0.7.0 → 0.7.2
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.
|
@@ -24,6 +24,7 @@ export declare class FirestoreDataManager<T extends FirestoreTypes.DocumentData,
|
|
|
24
24
|
private readonly converter;
|
|
25
25
|
private readonly opts?;
|
|
26
26
|
private mergedConverter;
|
|
27
|
+
private metadataConverter;
|
|
27
28
|
private collection;
|
|
28
29
|
private collectionQuery;
|
|
29
30
|
private managerOptions;
|
|
@@ -35,6 +36,15 @@ export declare class FirestoreDataManager<T extends FirestoreTypes.DocumentData,
|
|
|
35
36
|
read(id: string, options?: FirestoreReadOptions): Promise<WithMetadata<T> | undefined>;
|
|
36
37
|
create(data: WithFieldValue<WithoutId<T>>, options?: FirebaseCreateOptions): Promise<IdentifiableReference<WithMetadata<T>>>;
|
|
37
38
|
private _update;
|
|
39
|
+
/**
|
|
40
|
+
* Update only the internal metadata fields of a document.
|
|
41
|
+
*
|
|
42
|
+
* Unlike {@link _update}, this bypasses the user-provided converter so it never re-serializes
|
|
43
|
+
* (and therefore never clobbers) user data. This matters because the user converter rebuilds the
|
|
44
|
+
* full document shape, which turns absent fields into `undefined` and can overwrite existing map
|
|
45
|
+
* fields with empty objects when Firestore strips the undefined values.
|
|
46
|
+
*/
|
|
47
|
+
private _updateMetadata;
|
|
38
48
|
update(id: string, data: WithoutId<Partial<WithFieldValue<T>>>, options?: FirestoreWriteOptions): Promise<void>;
|
|
39
49
|
upsert(id: string, data: WithFieldValue<WithoutId<T>>, options?: FirestoreWriteOptions): Promise<void>;
|
|
40
50
|
count(params?: QueryParams<SerializedT>): Promise<number>;
|
|
@@ -18,6 +18,7 @@ class FirestoreDataManager {
|
|
|
18
18
|
converter;
|
|
19
19
|
opts;
|
|
20
20
|
mergedConverter;
|
|
21
|
+
metadataConverter;
|
|
21
22
|
collection;
|
|
22
23
|
collectionQuery;
|
|
23
24
|
managerOptions;
|
|
@@ -29,8 +30,9 @@ class FirestoreDataManager {
|
|
|
29
30
|
this.collectionPath = collectionPath;
|
|
30
31
|
this.converter = converter;
|
|
31
32
|
this.opts = opts;
|
|
33
|
+
this.metadataConverter = new FirestoreMetadata_1.FirestoreMetadataConverter();
|
|
32
34
|
// @ts-expect-error - Force merge FirestoreDataConverter and InternalFirestoreDataConverter
|
|
33
|
-
this.mergedConverter = new utils_1.MergeConverters(this.converter,
|
|
35
|
+
this.mergedConverter = new utils_1.MergeConverters(this.converter, this.metadataConverter);
|
|
34
36
|
this.managerOptions = this.validateOptions({ ...defaultFirebaseDataManagerOptions, ...this.opts });
|
|
35
37
|
this.refCache = this.managerOptions.refCache || new datamanager_1.MapCache(100);
|
|
36
38
|
this.listCache = this.managerOptions.listCache || new datamanager_1.MapCache(100);
|
|
@@ -44,6 +46,7 @@ class FirestoreDataManager {
|
|
|
44
46
|
this.referenceOptions = {
|
|
45
47
|
readMode: this.managerOptions.readMode,
|
|
46
48
|
snapshotOptions: this.managerOptions.snapshotOptions,
|
|
49
|
+
filterDeleted: this.managerOptions.deleteMode === 'soft',
|
|
47
50
|
};
|
|
48
51
|
}
|
|
49
52
|
validateOptions(options) {
|
|
@@ -55,7 +58,11 @@ class FirestoreDataManager {
|
|
|
55
58
|
const snapshot = await options.transaction.get(this.firestore.doc(this.collection, id));
|
|
56
59
|
if (!(0, utils_1.checkIfReferenceExists)(snapshot))
|
|
57
60
|
return undefined;
|
|
58
|
-
|
|
61
|
+
const data = snapshot.data(this.referenceOptions.snapshotOptions);
|
|
62
|
+
if (this.managerOptions.deleteMode === 'soft' && data?.deleted === true) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return data;
|
|
59
66
|
}
|
|
60
67
|
return await ref.resolve();
|
|
61
68
|
}
|
|
@@ -104,6 +111,25 @@ class FirestoreDataManager {
|
|
|
104
111
|
}
|
|
105
112
|
return this.firestore.updateDoc(ref, serializedData);
|
|
106
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Update only the internal metadata fields of a document.
|
|
116
|
+
*
|
|
117
|
+
* Unlike {@link _update}, this bypasses the user-provided converter so it never re-serializes
|
|
118
|
+
* (and therefore never clobbers) user data. This matters because the user converter rebuilds the
|
|
119
|
+
* full document shape, which turns absent fields into `undefined` and can overwrite existing map
|
|
120
|
+
* fields with empty objects when Firestore strips the undefined values.
|
|
121
|
+
*/
|
|
122
|
+
async _updateMetadata(id, metadata, options) {
|
|
123
|
+
const serializedData = this.metadataConverter.toFirestore({
|
|
124
|
+
...metadata,
|
|
125
|
+
[FirestoreMetadata_1.FIRESTORE_KEYS.UPDATED_AT]: this.firestore.serverTimestamp(),
|
|
126
|
+
});
|
|
127
|
+
const ref = this.firestore.doc(this.collection, id);
|
|
128
|
+
if (options?.transaction) {
|
|
129
|
+
return options.transaction.update(ref, serializedData);
|
|
130
|
+
}
|
|
131
|
+
return this.firestore.updateDoc(ref, serializedData);
|
|
132
|
+
}
|
|
107
133
|
async update(id, data, options) {
|
|
108
134
|
await this._update(id, data, options);
|
|
109
135
|
}
|
|
@@ -178,10 +204,7 @@ class FirestoreDataManager {
|
|
|
178
204
|
}
|
|
179
205
|
async delete(id, options) {
|
|
180
206
|
if (this.managerOptions.deleteMode === 'soft') {
|
|
181
|
-
await this.
|
|
182
|
-
...{},
|
|
183
|
-
[FirestoreMetadata_1.FIRESTORE_KEYS.DELETED]: true,
|
|
184
|
-
}, options);
|
|
207
|
+
await this._updateMetadata(id, { [FirestoreMetadata_1.FIRESTORE_KEYS.DELETED]: true }, options);
|
|
185
208
|
return;
|
|
186
209
|
}
|
|
187
210
|
if (options?.transaction) {
|
|
@@ -16,8 +16,8 @@ export type FirestoreSerializedMetadata = {
|
|
|
16
16
|
[FIRESTORE_INTERAL_KEYS.UPDATED_AT]: FirestoreTypes.Timestamp;
|
|
17
17
|
};
|
|
18
18
|
export declare class FirestoreMetadataConverter implements InternalFirestoreDataConverter<Metadata, FirestoreSerializedMetadata> {
|
|
19
|
-
toFirestore(data: WithFieldValue<WithoutId<Metadata
|
|
20
|
-
__deleted:
|
|
19
|
+
toFirestore(data: Partial<WithFieldValue<WithoutId<Metadata>>>): {
|
|
20
|
+
__deleted: boolean;
|
|
21
21
|
__createdAt: FirestoreTypes.Timestamp;
|
|
22
22
|
__updatedAt: FirestoreTypes.Timestamp;
|
|
23
23
|
};
|
|
@@ -16,6 +16,7 @@ var FIRESTORE_KEYS;
|
|
|
16
16
|
class FirestoreMetadataConverter {
|
|
17
17
|
toFirestore(data) {
|
|
18
18
|
return {
|
|
19
|
+
// Cast: value may be absent on partial metadata writes; Firestore strips undefined fields
|
|
19
20
|
[FIRESTORE_INTERAL_KEYS.DELETED]: data[FIRESTORE_KEYS.DELETED],
|
|
20
21
|
// Cast: Firestore handles conversion from Date to Timestamp internally
|
|
21
22
|
[FIRESTORE_INTERAL_KEYS.CREATED_AT]: data[FIRESTORE_KEYS.CREATED_AT],
|
|
@@ -4,6 +4,12 @@ export type { FirestoreReferenceContext } from './errors';
|
|
|
4
4
|
export interface FirestoreReferenceOptions<T> extends LiveReferenceOptions<T> {
|
|
5
5
|
readMode?: FirestoreReadMode;
|
|
6
6
|
snapshotOptions?: FirestoreTypes.SnapshotOptions;
|
|
7
|
+
/**
|
|
8
|
+
* When true, documents flagged as soft-deleted (`__deleted === true`) are treated as
|
|
9
|
+
* non-existent and resolve to `undefined`. Set by the data manager when running in
|
|
10
|
+
* `deleteMode: 'soft'` so single-document reads stay consistent with list queries.
|
|
11
|
+
*/
|
|
12
|
+
filterDeleted?: boolean;
|
|
7
13
|
}
|
|
8
14
|
export declare class FirestoreReference<T extends DocumentData, S extends DocumentData> extends LiveReference<T> {
|
|
9
15
|
private readonly firestore;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FirestoreReference = void 0;
|
|
4
4
|
const datamanager_1 = require("@data-weave/datamanager");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
|
+
const FirestoreMetadata_1 = require("./FirestoreMetadata");
|
|
6
7
|
const utils_1 = require("./utils");
|
|
7
8
|
class FirestoreReference extends datamanager_1.LiveReference {
|
|
8
9
|
firestore;
|
|
@@ -68,7 +69,12 @@ class FirestoreReference extends datamanager_1.LiveReference {
|
|
|
68
69
|
parseDocumentSnapshot(docSnapshot) {
|
|
69
70
|
if (!(0, utils_1.checkIfReferenceExists)(docSnapshot))
|
|
70
71
|
throw new Error(`Document does not exist ${this.docRef.path}`);
|
|
71
|
-
|
|
72
|
+
const data = docSnapshot.data(this.options?.snapshotOptions);
|
|
73
|
+
const deletedFlag = data?.[FirestoreMetadata_1.FIRESTORE_KEYS.DELETED];
|
|
74
|
+
if (this.options?.filterDeleted && deletedFlag === true) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return data;
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
exports.FirestoreReference = FirestoreReference;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-weave/backend-firestore",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"author": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"registry": "https://registry.npmjs.org/"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@data-weave/datamanager": "~0.7.
|
|
26
|
+
"@data-weave/datamanager": "~0.7.2",
|
|
27
27
|
"@firebase/firestore": "^4.7.8",
|
|
28
28
|
"@firebase/functions-types": "^0.6.3",
|
|
29
29
|
"@google-cloud/firestore": "^7.11.1",
|