@naturalcycles/firestore-lib 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/firestore.db.d.ts +5 -4
- package/dist/firestore.db.js +38 -40
- package/dist/firestore.util.js +2 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -8
- package/dist/query.util.d.ts +3 -3
- package/dist/query.util.js +1 -4
- package/package.json +9 -7
- package/src/firestore.db.ts +9 -10
- package/src/index.ts +2 -2
- package/src/query.util.ts +3 -3
package/dist/firestore.db.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Firestore, Query, Transaction } from '@google-cloud/firestore';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { Firestore, Query, Transaction } from '@google-cloud/firestore';
|
|
2
|
+
import type { CommonDB, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBSupport, CommonDBTransactionOptions, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
|
|
3
|
+
import { BaseCommonDB } from '@naturalcycles/db-lib';
|
|
4
|
+
import type { ObjectWithId, StringMap } from '@naturalcycles/js-lib';
|
|
5
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
6
|
export interface FirestoreDBCfg {
|
|
6
7
|
firestore: Firestore;
|
|
7
8
|
}
|
package/dist/firestore.db.js
CHANGED
|
@@ -1,49 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
7
|
-
const firestore_util_1 = require("./firestore.util");
|
|
8
|
-
const query_util_1 = require("./query.util");
|
|
1
|
+
import { FieldValue } from '@google-cloud/firestore';
|
|
2
|
+
import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib';
|
|
3
|
+
import { _assert, _chunk, _filterUndefinedValues, _isTruthy, _omit, _stringMapEntries, pMap, } from '@naturalcycles/js-lib';
|
|
4
|
+
import { escapeDocId, unescapeDocId } from './firestore.util.js';
|
|
5
|
+
import { dbQueryToFirestoreQuery } from './query.util.js';
|
|
9
6
|
const methodMap = {
|
|
10
7
|
insert: 'create',
|
|
11
8
|
update: 'update',
|
|
12
9
|
upsert: 'set',
|
|
13
10
|
};
|
|
14
|
-
class RollbackError extends Error {
|
|
11
|
+
export class RollbackError extends Error {
|
|
15
12
|
constructor() {
|
|
16
13
|
super('rollback');
|
|
17
14
|
}
|
|
18
15
|
}
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export class FirestoreDB extends BaseCommonDB {
|
|
17
|
+
cfg;
|
|
21
18
|
constructor(cfg) {
|
|
22
19
|
super();
|
|
23
20
|
this.cfg = cfg;
|
|
24
|
-
this.support = {
|
|
25
|
-
...db_lib_1.commonDBFullSupport,
|
|
26
|
-
patchByQuery: false, // todo: can be implemented
|
|
27
|
-
tableSchemas: false,
|
|
28
|
-
};
|
|
29
21
|
}
|
|
22
|
+
support = {
|
|
23
|
+
...commonDBFullSupport,
|
|
24
|
+
patchByQuery: false, // todo: can be implemented
|
|
25
|
+
tableSchemas: false,
|
|
26
|
+
};
|
|
30
27
|
// GET
|
|
31
28
|
async getByIds(table, ids, opt = {}) {
|
|
32
29
|
if (!ids.length)
|
|
33
30
|
return [];
|
|
34
31
|
const { firestore } = this.cfg;
|
|
35
32
|
const col = firestore.collection(table);
|
|
36
|
-
return (await (opt.tx?.tx || firestore).getAll(...ids.map(id => col.doc(
|
|
33
|
+
return (await (opt.tx?.tx || firestore).getAll(...ids.map(id => col.doc(escapeDocId(id)))))
|
|
37
34
|
.map(doc => {
|
|
38
35
|
const data = doc.data();
|
|
39
36
|
if (data === undefined)
|
|
40
37
|
return;
|
|
41
38
|
return {
|
|
42
|
-
id:
|
|
39
|
+
id: unescapeDocId(doc.id),
|
|
43
40
|
...data,
|
|
44
41
|
};
|
|
45
42
|
})
|
|
46
|
-
.filter(
|
|
43
|
+
.filter(_isTruthy);
|
|
47
44
|
}
|
|
48
45
|
// QUERY
|
|
49
46
|
async runQuery(q, opt) {
|
|
@@ -54,11 +51,11 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
54
51
|
rows: await this.getByIds(q.table, ids, opt),
|
|
55
52
|
};
|
|
56
53
|
}
|
|
57
|
-
const firestoreQuery =
|
|
54
|
+
const firestoreQuery = dbQueryToFirestoreQuery(q, this.cfg.firestore.collection(q.table));
|
|
58
55
|
let rows = await this.runFirestoreQuery(firestoreQuery, opt);
|
|
59
56
|
// Special case when projection query didn't specify 'id'
|
|
60
57
|
if (q._selectedFieldNames && !q._selectedFieldNames.includes('id')) {
|
|
61
|
-
rows = rows.map(r =>
|
|
58
|
+
rows = rows.map(r => _omit(r, ['id']));
|
|
62
59
|
}
|
|
63
60
|
return { rows };
|
|
64
61
|
}
|
|
@@ -66,15 +63,15 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
66
63
|
return this.querySnapshotToArray(await q.get());
|
|
67
64
|
}
|
|
68
65
|
async runQueryCount(q, _opt) {
|
|
69
|
-
const firestoreQuery =
|
|
66
|
+
const firestoreQuery = dbQueryToFirestoreQuery(q, this.cfg.firestore.collection(q.table));
|
|
70
67
|
const r = await firestoreQuery.count().get();
|
|
71
68
|
return r.data().count;
|
|
72
69
|
}
|
|
73
70
|
streamQuery(q, _opt) {
|
|
74
|
-
const firestoreQuery =
|
|
71
|
+
const firestoreQuery = dbQueryToFirestoreQuery(q, this.cfg.firestore.collection(q.table));
|
|
75
72
|
return firestoreQuery.stream().map(doc => {
|
|
76
73
|
return {
|
|
77
|
-
id:
|
|
74
|
+
id: unescapeDocId(doc.id),
|
|
78
75
|
...doc.data(),
|
|
79
76
|
};
|
|
80
77
|
});
|
|
@@ -87,17 +84,17 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
87
84
|
if (opt.tx) {
|
|
88
85
|
const { tx } = opt.tx;
|
|
89
86
|
rows.forEach(row => {
|
|
90
|
-
|
|
91
|
-
tx[method](col.doc(
|
|
87
|
+
_assert(row.id, `firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`);
|
|
88
|
+
tx[method](col.doc(escapeDocId(row.id)), _filterUndefinedValues(row));
|
|
92
89
|
});
|
|
93
90
|
return;
|
|
94
91
|
}
|
|
95
92
|
// Firestore allows max 500 items in one batch
|
|
96
|
-
await
|
|
93
|
+
await pMap(_chunk(rows, 500), async (chunk) => {
|
|
97
94
|
const batch = firestore.batch();
|
|
98
95
|
chunk.forEach(row => {
|
|
99
|
-
|
|
100
|
-
batch[method](col.doc(
|
|
96
|
+
_assert(row.id, `firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`);
|
|
97
|
+
batch[method](col.doc(escapeDocId(row.id)), _filterUndefinedValues(row));
|
|
101
98
|
});
|
|
102
99
|
await batch.commit();
|
|
103
100
|
}, { concurrency: 1 });
|
|
@@ -110,7 +107,7 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
110
107
|
ids = Array.isArray(idFilter.val) ? idFilter.val : [idFilter.val];
|
|
111
108
|
}
|
|
112
109
|
else {
|
|
113
|
-
const firestoreQuery =
|
|
110
|
+
const firestoreQuery = dbQueryToFirestoreQuery(q.select([]), this.cfg.firestore.collection(q.table));
|
|
114
111
|
ids = (await this.runFirestoreQuery(firestoreQuery)).map(obj => obj.id);
|
|
115
112
|
}
|
|
116
113
|
await this.deleteByIds(q.table, ids, opt);
|
|
@@ -122,14 +119,14 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
122
119
|
if (opt.tx) {
|
|
123
120
|
const { tx } = opt.tx;
|
|
124
121
|
ids.forEach(id => {
|
|
125
|
-
tx.delete(col.doc(
|
|
122
|
+
tx.delete(col.doc(escapeDocId(id)));
|
|
126
123
|
});
|
|
127
124
|
return ids.length;
|
|
128
125
|
}
|
|
129
|
-
await
|
|
126
|
+
await pMap(_chunk(ids, 500), async (chunk) => {
|
|
130
127
|
const batch = firestore.batch();
|
|
131
128
|
chunk.forEach(id => {
|
|
132
|
-
batch.delete(col.doc(
|
|
129
|
+
batch.delete(col.doc(escapeDocId(id)));
|
|
133
130
|
});
|
|
134
131
|
await batch.commit();
|
|
135
132
|
});
|
|
@@ -139,7 +136,7 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
139
136
|
const rows = [];
|
|
140
137
|
qs.forEach(doc => {
|
|
141
138
|
rows.push({
|
|
142
|
-
id:
|
|
139
|
+
id: unescapeDocId(doc.id),
|
|
143
140
|
...doc.data(),
|
|
144
141
|
});
|
|
145
142
|
});
|
|
@@ -170,9 +167,10 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
170
167
|
const { firestore } = this.cfg;
|
|
171
168
|
const col = firestore.collection(table);
|
|
172
169
|
const batch = firestore.batch();
|
|
173
|
-
for (const [id, increment] of
|
|
174
|
-
batch.set(col.doc(
|
|
175
|
-
|
|
170
|
+
for (const [id, increment] of _stringMapEntries(incrementMap)) {
|
|
171
|
+
batch.set(col.doc(escapeDocId(id)), {
|
|
172
|
+
// todo: lazy-load FieldValue
|
|
173
|
+
[prop]: FieldValue.increment(increment),
|
|
176
174
|
}, { merge: true });
|
|
177
175
|
}
|
|
178
176
|
await batch.commit();
|
|
@@ -185,11 +183,12 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
185
183
|
return [];
|
|
186
184
|
}
|
|
187
185
|
}
|
|
188
|
-
exports.FirestoreDB = FirestoreDB;
|
|
189
186
|
/**
|
|
190
187
|
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
191
188
|
*/
|
|
192
|
-
class FirestoreDBTransaction {
|
|
189
|
+
export class FirestoreDBTransaction {
|
|
190
|
+
db;
|
|
191
|
+
tx;
|
|
193
192
|
constructor(db, tx) {
|
|
194
193
|
this.db = db;
|
|
195
194
|
this.tx = tx;
|
|
@@ -207,4 +206,3 @@ class FirestoreDBTransaction {
|
|
|
207
206
|
return await this.db.deleteByIds(table, ids, { ...opt, tx: this });
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
|
-
exports.FirestoreDBTransaction = FirestoreDBTransaction;
|
package/dist/firestore.util.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.escapeDocId = escapeDocId;
|
|
4
|
-
exports.unescapeDocId = unescapeDocId;
|
|
5
1
|
const SLASH = '_SLASH_';
|
|
6
|
-
function escapeDocId(docId) {
|
|
2
|
+
export function escapeDocId(docId) {
|
|
7
3
|
if (typeof docId === 'number')
|
|
8
4
|
return String(docId);
|
|
9
5
|
return docId.replaceAll('/', SLASH);
|
|
10
6
|
}
|
|
11
|
-
function unescapeDocId(docId) {
|
|
7
|
+
export function unescapeDocId(docId) {
|
|
12
8
|
// if (typeof docId === 'number') return docId
|
|
13
9
|
return docId.replaceAll(SLASH, '/');
|
|
14
10
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const firestore_1 = require("@google-cloud/firestore");
|
|
6
|
-
Object.defineProperty(exports, "Firestore", { enumerable: true, get: function () { return firestore_1.Firestore; } });
|
|
7
|
-
tslib_1.__exportStar(require("./firestore.db"), exports);
|
|
8
|
-
tslib_1.__exportStar(require("./query.util"), exports);
|
|
1
|
+
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
+
export * from './firestore.db.js';
|
|
3
|
+
export * from './query.util.js';
|
|
4
|
+
export { Firestore };
|
package/dist/query.util.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Query } from '@google-cloud/firestore';
|
|
2
|
-
import { DBQuery } from '@naturalcycles/db-lib';
|
|
3
|
-
import { ObjectWithId } from '@naturalcycles/js-lib';
|
|
1
|
+
import type { Query } from '@google-cloud/firestore';
|
|
2
|
+
import type { DBQuery } from '@naturalcycles/db-lib';
|
|
3
|
+
import type { ObjectWithId } from '@naturalcycles/js-lib';
|
|
4
4
|
export declare function dbQueryToFirestoreQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, emptyQuery: Query): Query;
|
package/dist/query.util.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dbQueryToFirestoreQuery = dbQueryToFirestoreQuery;
|
|
4
1
|
// Map DBQueryFilterOp to WhereFilterOp
|
|
5
2
|
// Currently it's fully aligned!
|
|
6
3
|
const OP_MAP = {
|
|
7
4
|
// '=': '==',
|
|
8
5
|
// in: 'array-contains',
|
|
9
6
|
};
|
|
10
|
-
function dbQueryToFirestoreQuery(dbQuery, emptyQuery) {
|
|
7
|
+
export function dbQueryToFirestoreQuery(dbQuery, emptyQuery) {
|
|
11
8
|
// filter
|
|
12
9
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
13
10
|
let q = dbQuery._filters.reduce((q, f) => {
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/firestore-lib",
|
|
3
|
+
"type": "module",
|
|
3
4
|
"scripts": {
|
|
4
5
|
"prepare": "husky",
|
|
5
6
|
"build": "dev-lib build",
|
|
@@ -10,17 +11,18 @@
|
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
13
|
"@google-cloud/firestore": "^7.0.0",
|
|
13
|
-
"@naturalcycles/db-lib": "^
|
|
14
|
+
"@naturalcycles/db-lib": "^10.0.2",
|
|
14
15
|
"@naturalcycles/js-lib": "^14.6.0",
|
|
15
16
|
"@naturalcycles/nodejs-lib": "^13.1.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
|
-
"@naturalcycles/dev-lib": "^
|
|
19
|
-
"@naturalcycles/test-lib": "^1.0.3",
|
|
19
|
+
"@naturalcycles/dev-lib": "^17.3.0",
|
|
20
20
|
"@types/node": "^22.7.5",
|
|
21
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
21
22
|
"dotenv": "^16.0.0",
|
|
22
|
-
"firebase-admin": "^
|
|
23
|
-
"
|
|
23
|
+
"firebase-admin": "^13.2.0",
|
|
24
|
+
"tsx": "^4.19.3",
|
|
25
|
+
"vitest": "^3.1.1"
|
|
24
26
|
},
|
|
25
27
|
"files": [
|
|
26
28
|
"dist",
|
|
@@ -40,9 +42,9 @@
|
|
|
40
42
|
"url": "https://github.com/NaturalCycles/firestore-lib"
|
|
41
43
|
},
|
|
42
44
|
"engines": {
|
|
43
|
-
"node": ">=
|
|
45
|
+
"node": ">=22.12.0"
|
|
44
46
|
},
|
|
45
|
-
"version": "
|
|
47
|
+
"version": "2.0.0",
|
|
46
48
|
"description": "Firestore implementation of CommonDB interface",
|
|
47
49
|
"author": "Natural Cycles Team",
|
|
48
50
|
"license": "MIT"
|
package/src/firestore.db.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FieldValue,
|
|
1
|
+
import type {
|
|
3
2
|
Firestore,
|
|
4
3
|
Query,
|
|
5
4
|
QueryDocumentSnapshot,
|
|
6
5
|
QuerySnapshot,
|
|
7
6
|
Transaction,
|
|
8
7
|
} from '@google-cloud/firestore'
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import { FieldValue } from '@google-cloud/firestore'
|
|
9
|
+
import type {
|
|
11
10
|
CommonDB,
|
|
12
|
-
commonDBFullSupport,
|
|
13
11
|
CommonDBOptions,
|
|
14
12
|
CommonDBSaveMethod,
|
|
15
13
|
CommonDBSaveOptions,
|
|
@@ -21,6 +19,8 @@ import {
|
|
|
21
19
|
DBTransactionFn,
|
|
22
20
|
RunQueryResult,
|
|
23
21
|
} from '@naturalcycles/db-lib'
|
|
22
|
+
import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib'
|
|
23
|
+
import type { ObjectWithId, StringMap } from '@naturalcycles/js-lib'
|
|
24
24
|
import {
|
|
25
25
|
_assert,
|
|
26
26
|
_chunk,
|
|
@@ -28,13 +28,11 @@ import {
|
|
|
28
28
|
_isTruthy,
|
|
29
29
|
_omit,
|
|
30
30
|
_stringMapEntries,
|
|
31
|
-
ObjectWithId,
|
|
32
31
|
pMap,
|
|
33
|
-
StringMap,
|
|
34
32
|
} from '@naturalcycles/js-lib'
|
|
35
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
36
|
-
import { escapeDocId, unescapeDocId } from './firestore.util'
|
|
37
|
-
import { dbQueryToFirestoreQuery } from './query.util'
|
|
33
|
+
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
34
|
+
import { escapeDocId, unescapeDocId } from './firestore.util.js'
|
|
35
|
+
import { dbQueryToFirestoreQuery } from './query.util.js'
|
|
38
36
|
|
|
39
37
|
export interface FirestoreDBCfg {
|
|
40
38
|
firestore: Firestore
|
|
@@ -306,6 +304,7 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
306
304
|
batch.set(
|
|
307
305
|
col.doc(escapeDocId(id)),
|
|
308
306
|
{
|
|
307
|
+
// todo: lazy-load FieldValue
|
|
309
308
|
[prop]: FieldValue.increment(increment),
|
|
310
309
|
},
|
|
311
310
|
{ merge: true },
|
package/src/index.ts
CHANGED
package/src/query.util.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Query, WhereFilterOp } from '@google-cloud/firestore'
|
|
2
|
-
import { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
|
|
3
|
-
import { ObjectWithId } from '@naturalcycles/js-lib'
|
|
1
|
+
import type { Query, WhereFilterOp } from '@google-cloud/firestore'
|
|
2
|
+
import type { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
|
|
3
|
+
import type { ObjectWithId } from '@naturalcycles/js-lib'
|
|
4
4
|
|
|
5
5
|
// Map DBQueryFilterOp to WhereFilterOp
|
|
6
6
|
// Currently it's fully aligned!
|