@naturalcycles/datastore-lib 3.39.4 → 4.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.
@@ -2,7 +2,7 @@ import { Readable } from 'node:stream';
2
2
  import type { Query } from '@google-cloud/datastore';
3
3
  import type { CommonLogger } from '@naturalcycles/js-lib';
4
4
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
5
- import type { DatastoreDBStreamOptions } from './datastore.model';
5
+ import type { DatastoreDBStreamOptions } from './datastore.model.js';
6
6
  export declare class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
7
7
  private q;
8
8
  private logger;
@@ -1,26 +1,27 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreStreamReadable = void 0;
4
- const node_stream_1 = require("node:stream");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
6
- class DatastoreStreamReadable extends node_stream_1.Readable {
1
+ import { Readable } from 'node:stream';
2
+ import { _ms, pRetry } from '@naturalcycles/js-lib';
3
+ export class DatastoreStreamReadable extends Readable {
4
+ q;
5
+ logger;
6
+ originalLimit;
7
+ rowsRetrieved = 0;
8
+ endCursor;
9
+ running = false;
10
+ done = false;
11
+ lastQueryDone;
12
+ totalWait = 0;
13
+ table;
14
+ /**
15
+ * Used to support maxWait
16
+ */
17
+ lastReadTimestamp = 0;
18
+ maxWaitInterval;
19
+ opt;
20
+ dsOpt;
7
21
  constructor(q, opt, logger) {
8
22
  super({ objectMode: true });
9
23
  this.q = q;
10
24
  this.logger = logger;
11
- this.rowsRetrieved = 0;
12
- this.running = false;
13
- this.done = false;
14
- this.totalWait = 0;
15
- /**
16
- * Used to support maxWait
17
- */
18
- this.lastReadTimestamp = 0;
19
- /**
20
- * Counts how many times _read was called.
21
- * For debugging.
22
- */
23
- this.count = 0;
24
25
  this.opt = {
25
26
  rssLimitMB: 1000,
26
27
  batchSize: 1000,
@@ -75,7 +76,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
75
76
  let rows = [];
76
77
  let info = {};
77
78
  try {
78
- await (0, js_lib_1.pRetry)(async () => {
79
+ await pRetry(async () => {
79
80
  const res = await q.run(this.dsOpt);
80
81
  rows = res[0];
81
82
  info = res[1];
@@ -98,9 +99,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
98
99
  return;
99
100
  }
100
101
  this.rowsRetrieved += rows.length;
101
- this.logger.log(`${this.table} got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
102
+ this.logger.log(`${this.table} got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`, info.moreResults);
102
103
  if (!rows.length) {
103
- this.logger.warn(`${this.table} got 0 rows, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
104
+ this.logger.warn(`${this.table} got 0 rows, totalWait: ${_ms(this.totalWait)}`, info.moreResults);
104
105
  }
105
106
  this.endCursor = info.endCursor;
106
107
  this.running = false; // ready to take more _reads
@@ -109,7 +110,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
109
110
  if (!info.endCursor ||
110
111
  info.moreResults === 'NO_MORE_RESULTS' ||
111
112
  (this.originalLimit && this.rowsRetrieved >= this.originalLimit)) {
112
- this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
113
+ this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`);
113
114
  this.push(null);
114
115
  this.done = true;
115
116
  clearInterval(this.maxWaitInterval);
@@ -128,6 +129,11 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
128
129
  }
129
130
  }
130
131
  }
132
+ /**
133
+ * Counts how many times _read was called.
134
+ * For debugging.
135
+ */
136
+ count = 0;
131
137
  _read() {
132
138
  this.lastReadTimestamp = Date.now();
133
139
  // console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
@@ -147,4 +153,3 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
147
153
  }
148
154
  }
149
155
  }
150
- exports.DatastoreStreamReadable = DatastoreStreamReadable;
@@ -3,7 +3,7 @@ import type { CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOption
3
3
  import { BaseCommonDB } from '@naturalcycles/db-lib';
4
4
  import type { CommonLogger, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
5
5
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
6
- import type { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model';
6
+ import type { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model.js';
7
7
  /**
8
8
  * Datastore API:
9
9
  * https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
@@ -20,7 +20,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
20
20
  * Datastore.KEY
21
21
  */
22
22
  protected KEY: symbol;
23
- ds(): Datastore;
23
+ ds(): Promise<Datastore>;
24
24
  private getPropertyFilter;
25
25
  private getDatastoreLib;
26
26
  ping(): Promise<void>;
@@ -28,8 +28,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
28
28
  runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<RunQueryResult<ROW>>;
29
29
  runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
30
30
  private runDatastoreQuery;
31
- private runQueryStream;
32
- streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
31
+ streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
33
32
  /**
34
33
  * Returns saved entities with generated id/updated/created (non-mutating!)
35
34
  */
@@ -50,7 +49,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
50
49
  getTableProperties(table: string): Promise<DatastorePropertyStats[]>;
51
50
  private mapId;
52
51
  private toDatastoreEntity;
53
- key(kind: string, id: string): Key;
52
+ key(ds: Datastore, kind: string, id: string): Key;
54
53
  private getDsKey;
55
54
  getKey(key: Key): string | undefined;
56
55
  createTable<ROW extends ObjectWithId>(_table: string, _schema: JsonSchemaObject<ROW>): Promise<void>;
@@ -1,12 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreDBTransaction = exports.DatastoreDB = void 0;
4
- const db_lib_1 = require("@naturalcycles/db-lib");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
6
- const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
7
- const datastore_model_1 = require("./datastore.model");
8
- const DatastoreStreamReadable_1 = require("./DatastoreStreamReadable");
9
- const query_util_1 = require("./query.util");
1
+ import { Transform } from 'node:stream';
2
+ import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib';
3
+ import { _assert, _chunk, _errorDataAppend, _omit, commonLoggerMinLevel, pMap, pRetry, pRetryFn, pTimeout, TimeoutError, } from '@naturalcycles/js-lib';
4
+ import { boldWhite } from '@naturalcycles/nodejs-lib';
5
+ import { DatastoreType } from './datastore.model.js';
6
+ import { DatastoreStreamReadable } from './DatastoreStreamReadable.js';
7
+ import { dbQueryToDatastoreQuery } from './query.util.js';
10
8
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
11
9
  const MAX_ITEMS = 500;
12
10
  // It's an empyrical value, but anything less than infinity is better than infinity
@@ -32,27 +30,33 @@ const methodMap = {
32
30
  * https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
33
31
  * https://cloud.google.com/datastore/docs/datastore-api-tutorial
34
32
  */
35
- class DatastoreDB extends db_lib_1.BaseCommonDB {
33
+ export class DatastoreDB extends BaseCommonDB {
34
+ support = {
35
+ ...commonDBFullSupport,
36
+ patchByQuery: false,
37
+ increment: false,
38
+ };
36
39
  constructor(cfg = {}) {
37
40
  super();
38
- this.support = {
39
- ...db_lib_1.commonDBFullSupport,
40
- patchByQuery: false,
41
- increment: false,
42
- };
43
41
  this.cfg = {
44
42
  logger: console,
45
43
  ...cfg,
46
44
  };
47
45
  }
46
+ cfg;
47
+ cachedDatastore;
48
+ /**
49
+ * Datastore.KEY
50
+ */
51
+ KEY;
48
52
  // @memo() // not used to be able to connect to many DBs in the same server instance
49
- ds() {
53
+ async ds() {
50
54
  if (!this.cachedDatastore) {
51
- (0, js_lib_1._assert)(process.env['APP_ENV'] !== 'test', 'DatastoreDB cannot be used in Test env, please use InMemoryDB');
52
- const DS = this.getDatastoreLib().Datastore;
55
+ _assert(process.env['APP_ENV'] !== 'test', 'DatastoreDB cannot be used in Test env, please use InMemoryDB');
56
+ const DS = (await this.getDatastoreLib()).Datastore;
53
57
  this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT'];
54
58
  if (this.cfg.projectId) {
55
- this.cfg.logger.log(`DatastoreDB connected to ${(0, nodejs_lib_1.boldWhite)(this.cfg.projectId)}`);
59
+ this.cfg.logger.log(`DatastoreDB connected to ${boldWhite(this.cfg.projectId)}`);
56
60
  }
57
61
  else if (process.env['GOOGLE_APPLICATION_CREDENTIALS']) {
58
62
  this.cfg.logger.log(`DatastoreDB connected via GOOGLE_APPLICATION_CREDENTIALS`);
@@ -65,12 +69,13 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
65
69
  }
66
70
  return this.cachedDatastore;
67
71
  }
68
- getPropertyFilter() {
69
- return this.getDatastoreLib().PropertyFilter;
72
+ async getPropertyFilter() {
73
+ return (await this.getDatastoreLib()).PropertyFilter;
70
74
  }
71
- getDatastoreLib() {
75
+ async getDatastoreLib() {
72
76
  // Lazy-loading
73
- return require('@google-cloud/datastore');
77
+ const { default: lib } = await import('@google-cloud/datastore');
78
+ return lib;
74
79
  }
75
80
  async ping() {
76
81
  await this.getAllStats();
@@ -78,31 +83,32 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
78
83
  async getByIds(table, ids, opt = {}) {
79
84
  if (!ids.length)
80
85
  return [];
81
- const keys = ids.map(id => this.key(table, id));
86
+ let ds = await this.ds();
87
+ const keys = ids.map(id => this.key(ds, table, id));
82
88
  let rows;
83
89
  const dsOpt = this.getRunQueryOptions(opt);
84
90
  if (this.cfg.timeout) {
85
91
  // First try
86
92
  try {
87
- const r = await (0, js_lib_1.pTimeout)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
93
+ const r = await pTimeout(() => (opt.tx?.tx || ds).get(keys, dsOpt), {
88
94
  timeout: this.cfg.timeout,
89
95
  name: `datastore.getByIds(${table})`,
90
96
  });
91
97
  rows = r[0];
92
98
  }
93
99
  catch (err) {
94
- if (!(err instanceof js_lib_1.TimeoutError)) {
100
+ if (!(err instanceof TimeoutError)) {
95
101
  // Not a timeout error, re-throw
96
102
  throw err;
97
103
  }
98
104
  this.cfg.logger.log('datastore recreated on error');
99
105
  // This is to debug "GCP Datastore Timeout issue"
100
- const datastoreLib = require('@google-cloud/datastore');
106
+ const datastoreLib = await this.getDatastoreLib();
101
107
  const DS = datastoreLib.Datastore;
102
- this.cachedDatastore = new DS(this.cfg);
108
+ ds = this.cachedDatastore = new DS(this.cfg);
103
109
  // Second try (will throw)
104
110
  try {
105
- const r = await (0, js_lib_1.pRetry)(() => (opt.tx?.tx || this.ds()).get(keys, dsOpt), {
111
+ const r = await pRetry(() => (opt.tx?.tx || ds).get(keys, dsOpt), {
106
112
  ...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
107
113
  maxAttempts: 3,
108
114
  timeout: this.cfg.timeout,
@@ -110,8 +116,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
110
116
  rows = r[0];
111
117
  }
112
118
  catch (err) {
113
- if (err instanceof js_lib_1.TimeoutError) {
114
- (0, js_lib_1._errorDataAppend)(err, {
119
+ if (err instanceof TimeoutError) {
120
+ _errorDataAppend(err, {
115
121
  fingerprint: [DATASTORE_TIMEOUT],
116
122
  });
117
123
  }
@@ -120,8 +126,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
120
126
  }
121
127
  }
122
128
  else {
123
- rows = await (0, js_lib_1.pRetry)(async () => {
124
- return (await this.ds().get(keys, dsOpt))[0];
129
+ rows = await pRetry(async () => {
130
+ return (await ds.get(keys, dsOpt))[0];
125
131
  }, this.getPRetryOptions(`datastore.getByIds(${table})`));
126
132
  }
127
133
  return (rows
@@ -142,69 +148,80 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
142
148
  rows: await this.getByIds(dbQuery.table, ids, opt),
143
149
  };
144
150
  }
145
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table), this.getPropertyFilter());
151
+ const ds = await this.ds();
152
+ const q = dbQueryToDatastoreQuery(dbQuery, ds.createQuery(dbQuery.table), await this.getPropertyFilter());
146
153
  const dsOpt = this.getRunQueryOptions(opt);
147
154
  const qr = await this.runDatastoreQuery(q, dsOpt);
148
155
  // Special case when projection query didn't specify 'id'
149
156
  if (dbQuery._selectedFieldNames && !dbQuery._selectedFieldNames.includes('id')) {
150
- qr.rows = qr.rows.map(r => (0, js_lib_1._omit)(r, ['id']));
157
+ qr.rows = qr.rows.map(r => _omit(r, ['id']));
151
158
  }
152
159
  return qr;
153
160
  }
154
161
  async runQueryCount(dbQuery, opt = {}) {
155
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery.select([]), this.ds().createQuery(dbQuery.table), this.getPropertyFilter());
156
- const aq = this.ds().createAggregationQuery(q).count('count');
162
+ const ds = await this.ds();
163
+ const q = dbQueryToDatastoreQuery(dbQuery.select([]), ds.createQuery(dbQuery.table), await this.getPropertyFilter());
164
+ const aq = ds.createAggregationQuery(q).count('count');
157
165
  const dsOpt = this.getRunQueryOptions(opt);
158
- const [entities] = await this.ds().runAggregationQuery(aq, dsOpt);
166
+ const [entities] = await ds.runAggregationQuery(aq, dsOpt);
159
167
  return entities[0]?.count;
160
168
  }
161
169
  async runDatastoreQuery(q, dsOpt) {
162
- const [entities, queryResult] = await this.ds().runQuery(q, dsOpt);
170
+ const ds = await this.ds();
171
+ const [entities, queryResult] = await ds.runQuery(q, dsOpt);
163
172
  const rows = entities.map(e => this.mapId(e));
164
173
  return {
165
174
  ...queryResult,
166
175
  rows,
167
176
  };
168
177
  }
169
- runQueryStream(q, _opt) {
170
- const opt = {
171
- ...this.cfg.streamOptions,
172
- ..._opt,
173
- };
174
- const dsOpt = this.getRunQueryOptions(opt);
175
- return (opt.experimentalCursorStream
176
- ? new DatastoreStreamReadable_1.DatastoreStreamReadable(q, opt, (0, js_lib_1.commonLoggerMinLevel)(this.cfg.logger, opt.debug ? 'log' : 'warn'))
177
- : this.ds().runQueryStream(q, dsOpt)).map(chunk => this.mapId(chunk));
178
- }
179
- streamQuery(dbQuery, opt) {
180
- const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table), this.getPropertyFilter());
181
- return this.runQueryStream(q, opt);
178
+ streamQuery(dbQuery, _opt) {
179
+ const transform = new Transform({
180
+ objectMode: true,
181
+ transform: (chunk, _, cb) => {
182
+ cb(null, this.mapId(chunk));
183
+ },
184
+ });
185
+ void this.ds().then(async (ds) => {
186
+ const q = dbQueryToDatastoreQuery(dbQuery, ds.createQuery(dbQuery.table), await this.getPropertyFilter());
187
+ const opt = {
188
+ ...this.cfg.streamOptions,
189
+ ..._opt,
190
+ };
191
+ (opt.experimentalCursorStream
192
+ ? new DatastoreStreamReadable(q, opt, commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'))
193
+ : ds.runQueryStream(q, this.getRunQueryOptions(opt)))
194
+ .on('error', err => transform.emit('error', err))
195
+ .pipe(transform);
196
+ });
197
+ return transform;
182
198
  }
183
199
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
184
200
  /**
185
201
  * Returns saved entities with generated id/updated/created (non-mutating!)
186
202
  */
187
203
  async saveBatch(table, rows, opt = {}) {
188
- const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes));
204
+ const ds = await this.ds();
205
+ const entities = rows.map(obj => this.toDatastoreEntity(ds, table, obj, opt.excludeFromIndexes));
189
206
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save';
190
- const save = (0, js_lib_1.pRetryFn)(async (batch) => {
191
- await (opt.tx?.tx || this.ds())[method](batch);
207
+ const save = pRetryFn(async (batch) => {
208
+ await (opt.tx?.tx || ds)[method](batch);
192
209
  }, this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`));
193
210
  try {
194
- const chunks = (0, js_lib_1._chunk)(entities, MAX_ITEMS);
211
+ const chunks = _chunk(entities, MAX_ITEMS);
195
212
  if (chunks.length === 1) {
196
213
  // Not using pMap in hope to preserve stack trace
197
214
  await save(chunks[0]);
198
215
  }
199
216
  else {
200
- await (0, js_lib_1.pMap)(chunks, async (batch) => await save(batch), {
217
+ await pMap(chunks, async (batch) => await save(batch), {
201
218
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
202
219
  });
203
220
  }
204
221
  }
205
222
  catch (err) {
206
- if (err instanceof js_lib_1.TimeoutError) {
207
- (0, js_lib_1._errorDataAppend)(err, {
223
+ if (err instanceof TimeoutError) {
224
+ _errorDataAppend(err, {
208
225
  fingerprint: [DATASTORE_TIMEOUT],
209
226
  });
210
227
  }
@@ -219,7 +236,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
219
236
  const ids = idFilter.op === '==' ? [idFilter.val] : idFilter.val;
220
237
  return await this.deleteByIds(q.table, ids, opt);
221
238
  }
222
- const datastoreQuery = (0, query_util_1.dbQueryToDatastoreQuery)(q.select([]), this.ds().createQuery(q.table), this.getPropertyFilter());
239
+ const ds = await this.ds();
240
+ const datastoreQuery = dbQueryToDatastoreQuery(q.select([]), ds.createQuery(q.table), await this.getPropertyFilter());
223
241
  const dsOpt = this.getRunQueryOptions(opt);
224
242
  const { rows } = await this.runDatastoreQuery(datastoreQuery, dsOpt);
225
243
  return await this.deleteByIds(q.table, rows.map(obj => obj.id), opt);
@@ -229,15 +247,17 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
229
247
  * regardless if they were actually deleted or not.
230
248
  */
231
249
  async deleteByIds(table, ids, opt = {}) {
232
- const keys = ids.map(id => this.key(table, id));
233
- await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(keys, MAX_ITEMS), async (batch) => await (opt.tx?.tx || this.ds()).delete(batch), {
250
+ const ds = await this.ds();
251
+ const keys = ids.map(id => this.key(ds, table, id));
252
+ await pMap(_chunk(keys, MAX_ITEMS), async (batch) => await (opt.tx?.tx || ds).delete(batch), {
234
253
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
235
254
  });
236
255
  return ids.length;
237
256
  }
238
257
  async runInTransaction(fn, opt = {}) {
258
+ const ds = await this.ds();
239
259
  const { readOnly } = opt;
240
- const datastoreTx = this.ds().transaction({
260
+ const datastoreTx = ds.transaction({
241
261
  readOnly,
242
262
  });
243
263
  try {
@@ -252,21 +272,23 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
252
272
  }
253
273
  }
254
274
  async getAllStats() {
255
- const q = this.ds().createQuery('__Stat_Kind__');
256
- const [statsArray] = await this.ds().runQuery(q);
275
+ const ds = await this.ds();
276
+ const q = ds.createQuery('__Stat_Kind__');
277
+ const [statsArray] = await ds.runQuery(q);
257
278
  return statsArray || [];
258
279
  }
259
280
  /**
260
281
  * Returns undefined e.g when Table is non-existing
261
282
  */
262
283
  async getStats(table) {
263
- const { PropertyFilter } = this.getDatastoreLib();
264
- const q = this.ds()
284
+ const ds = await this.ds();
285
+ const propertyFilter = await this.getPropertyFilter();
286
+ const q = ds
265
287
  .createQuery('__Stat_Kind__')
266
288
  // .filter('kind_name', table)
267
- .filter(new PropertyFilter('kind_name', '=', table))
289
+ .filter(new propertyFilter('kind_name', '=', table))
268
290
  .limit(1);
269
- const [statsArray] = await this.ds().runQuery(q);
291
+ const [statsArray] = await ds.runQuery(q);
270
292
  const [stats] = statsArray;
271
293
  return stats;
272
294
  }
@@ -275,11 +297,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
275
297
  return stats?.count;
276
298
  }
277
299
  async getTableProperties(table) {
278
- const q = this.ds()
300
+ const ds = await this.ds();
301
+ const q = ds
279
302
  .createQuery('__Stat_PropertyType_PropertyName_Kind__')
280
303
  // .filter('kind_name', table)
281
- .filter(new (this.getPropertyFilter())('kind_name', '=', table));
282
- const [stats] = await this.ds().runQuery(q);
304
+ .filter(new (await this.getPropertyFilter())('kind_name', '=', table));
305
+ const [stats] = await ds.runQuery(q);
283
306
  return stats;
284
307
  }
285
308
  mapId(o, preserveKey = false) {
@@ -294,8 +317,8 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
294
317
  return r;
295
318
  }
296
319
  // if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
297
- toDatastoreEntity(kind, o, excludeFromIndexes = []) {
298
- const key = this.getDsKey(o) || this.key(kind, o.id);
320
+ toDatastoreEntity(ds, kind, o, excludeFromIndexes = []) {
321
+ const key = this.getDsKey(o) || this.key(ds, kind, o.id);
299
322
  const data = Object.assign({}, o);
300
323
  delete data.id;
301
324
  delete data[this.KEY];
@@ -305,9 +328,9 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
305
328
  excludeFromIndexes,
306
329
  };
307
330
  }
308
- key(kind, id) {
309
- (0, js_lib_1._assert)(id, `Cannot save "${kind}" entity without "id"`);
310
- return this.ds().key([kind, id]);
331
+ key(ds, kind, id) {
332
+ _assert(id, `Cannot save "${kind}" entity without "id"`);
333
+ return ds.key([kind, id]);
311
334
  }
312
335
  getDsKey(o) {
313
336
  return o?.[this.KEY];
@@ -338,17 +361,17 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
338
361
  .forEach(stats => {
339
362
  const { property_type: dtype } = stats;
340
363
  const name = stats.property_name;
341
- if (dtype === datastore_model_1.DatastoreType.Blob) {
364
+ if (dtype === DatastoreType.Blob) {
342
365
  s.properties[name] = {
343
366
  instanceof: 'Buffer',
344
367
  };
345
368
  }
346
- else if (dtype === datastore_model_1.DatastoreType.Text || dtype === datastore_model_1.DatastoreType.String) {
369
+ else if (dtype === DatastoreType.Text || dtype === DatastoreType.String) {
347
370
  s.properties[name] = {
348
371
  type: 'string',
349
372
  };
350
373
  }
351
- else if (dtype === datastore_model_1.DatastoreType.EmbeddedEntity) {
374
+ else if (dtype === DatastoreType.EmbeddedEntity) {
352
375
  s.properties[name] = {
353
376
  type: 'object',
354
377
  additionalProperties: true,
@@ -356,26 +379,26 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
356
379
  required: [],
357
380
  };
358
381
  }
359
- else if (dtype === datastore_model_1.DatastoreType.Integer) {
382
+ else if (dtype === DatastoreType.Integer) {
360
383
  s.properties[name] = {
361
384
  type: 'integer',
362
385
  };
363
386
  }
364
- else if (dtype === datastore_model_1.DatastoreType.Float) {
387
+ else if (dtype === DatastoreType.Float) {
365
388
  s.properties[name] = {
366
389
  type: 'number',
367
390
  };
368
391
  }
369
- else if (dtype === datastore_model_1.DatastoreType.Boolean) {
392
+ else if (dtype === DatastoreType.Boolean) {
370
393
  s.properties[name] = {
371
394
  type: 'boolean',
372
395
  };
373
396
  }
374
- else if (dtype === datastore_model_1.DatastoreType.DATE_TIME) {
397
+ else if (dtype === DatastoreType.DATE_TIME) {
375
398
  // Don't know how to map it properly
376
399
  s.properties[name] = {};
377
400
  }
378
- else if (dtype === datastore_model_1.DatastoreType.NULL) {
401
+ else if (dtype === DatastoreType.NULL) {
379
402
  // check, maybe we can just skip this type and do nothing?
380
403
  s.properties[name] ||= {
381
404
  type: 'null',
@@ -427,11 +450,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
427
450
  };
428
451
  }
429
452
  }
430
- exports.DatastoreDB = DatastoreDB;
431
453
  /**
432
454
  * https://cloud.google.com/datastore/docs/concepts/transactions#datastore-datastore-transactional-update-nodejs
433
455
  */
434
- class DatastoreDBTransaction {
456
+ export class DatastoreDBTransaction {
457
+ db;
458
+ tx;
435
459
  constructor(db, tx) {
436
460
  this.db = db;
437
461
  this.tx = tx;
@@ -455,4 +479,3 @@ class DatastoreDBTransaction {
455
479
  return await this.db.deleteByIds(table, ids, { ...opt, tx: this });
456
480
  }
457
481
  }
458
- exports.DatastoreDBTransaction = DatastoreDBTransaction;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreType = void 0;
4
- var DatastoreType;
1
+ export var DatastoreType;
5
2
  (function (DatastoreType) {
6
3
  DatastoreType["Blob"] = "Blob";
7
4
  DatastoreType["Text"] = "Text";
@@ -12,4 +9,4 @@ var DatastoreType;
12
9
  DatastoreType["DATE_TIME"] = "Date/Time";
13
10
  DatastoreType["Boolean"] = "Boolean";
14
11
  DatastoreType["NULL"] = "NULL";
15
- })(DatastoreType || (exports.DatastoreType = DatastoreType = {}));
12
+ })(DatastoreType || (DatastoreType = {}));
@@ -1,7 +1,7 @@
1
1
  import type { CommonKeyValueDB, IncrementTuple, KeyValueDBTuple } from '@naturalcycles/db-lib';
2
2
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
3
- import { DatastoreDB } from './datastore.db';
4
- import type { DatastoreDBCfg } from './datastore.model';
3
+ import { DatastoreDB } from './datastore.db.js';
4
+ import type { DatastoreDBCfg } from './datastore.model.js';
5
5
  export interface DatastoreKeyValueDBCfg extends DatastoreDBCfg {
6
6
  }
7
7
  export declare class DatastoreKeyValueDB implements CommonKeyValueDB {
@@ -1,19 +1,18 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DatastoreKeyValueDB = void 0;
4
- const db_lib_1 = require("@naturalcycles/db-lib");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
6
- const datastore_db_1 = require("./datastore.db");
1
+ import { commonKeyValueDBFullSupport, DBQuery } from '@naturalcycles/db-lib';
2
+ import { AppError } from '@naturalcycles/js-lib';
3
+ import { DatastoreDB } from './datastore.db.js';
7
4
  const excludeFromIndexes = ['v'];
8
- class DatastoreKeyValueDB {
5
+ export class DatastoreKeyValueDB {
6
+ cfg;
9
7
  constructor(cfg) {
10
8
  this.cfg = cfg;
11
- this.support = {
12
- ...db_lib_1.commonKeyValueDBFullSupport,
13
- increment: false,
14
- };
15
- this.db = new datastore_db_1.DatastoreDB(cfg);
9
+ this.db = new DatastoreDB(cfg);
16
10
  }
11
+ db;
12
+ support = {
13
+ ...commonKeyValueDBFullSupport,
14
+ increment: false,
15
+ };
17
16
  async ping() {
18
17
  await this.db.ping();
19
18
  }
@@ -30,7 +29,7 @@ class DatastoreKeyValueDB {
30
29
  });
31
30
  }
32
31
  streamIds(table, limit) {
33
- const q = db_lib_1.DBQuery.create(table)
32
+ const q = DBQuery.create(table)
34
33
  .select(['id'])
35
34
  .limit(limit || 0);
36
35
  return (this.db
@@ -40,25 +39,24 @@ class DatastoreKeyValueDB {
40
39
  }
41
40
  streamValues(table, limit) {
42
41
  // `select v` doesn't work for some reason
43
- const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
42
+ const q = DBQuery.create(table).limit(limit || 0);
44
43
  return (this.db
45
44
  .streamQuery(q)
46
45
  // .on('error', err => stream.emit('error', err))
47
46
  .map(r => r.v));
48
47
  }
49
48
  streamEntries(table, limit) {
50
- const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
49
+ const q = DBQuery.create(table).limit(limit || 0);
51
50
  return (this.db
52
51
  .streamQuery(q)
53
52
  // .on('error', err => stream.emit('error', err))
54
53
  .map(r => [r.id, r.v]));
55
54
  }
56
55
  async count(table) {
57
- const q = db_lib_1.DBQuery.create(table);
56
+ const q = DBQuery.create(table);
58
57
  return await this.db.runQueryCount(q);
59
58
  }
60
59
  async incrementBatch(_table, _entries) {
61
- throw new js_lib_1.AppError('DatastoreKeyValueDB.incrementBatch() is not implemented');
60
+ throw new AppError('DatastoreKeyValueDB.incrementBatch() is not implemented');
62
61
  }
63
62
  }
64
- exports.DatastoreKeyValueDB = DatastoreKeyValueDB;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './datastore.db';
2
- export * from './datastore.model';
3
- export * from './datastoreKeyValueDB';
1
+ export * from './datastore.db.js';
2
+ export * from './datastore.model.js';
3
+ export * from './datastoreKeyValueDB.js';
package/dist/index.js CHANGED
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./datastore.db"), exports);
5
- tslib_1.__exportStar(require("./datastore.model"), exports);
6
- tslib_1.__exportStar(require("./datastoreKeyValueDB"), exports);
1
+ export * from './datastore.db.js';
2
+ export * from './datastore.model.js';
3
+ export * from './datastoreKeyValueDB.js';
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dbQueryToDatastoreQuery = dbQueryToDatastoreQuery;
4
1
  const FNAME_MAP = {
5
2
  id: '__key__',
6
3
  };
@@ -9,7 +6,7 @@ const OP_MAP = {
9
6
  in: 'IN',
10
7
  'not-in': 'NOT_IN',
11
8
  };
12
- function dbQueryToDatastoreQuery(dbQuery, emptyQuery, propertyFilterClass) {
9
+ export function dbQueryToDatastoreQuery(dbQuery, emptyQuery, propertyFilterClass) {
13
10
  let q = emptyQuery;
14
11
  // filter
15
12
  for (const f of dbQuery._filters) {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/datastore-lib",
3
- "version": "3.39.4",
3
+ "type": "module",
4
+ "version": "4.0.0",
4
5
  "description": "Opinionated library to work with Google Datastore",
5
6
  "scripts": {
6
7
  "prepare": "husky",
@@ -1,10 +1,10 @@
1
1
  import { Readable } from 'node:stream'
2
2
  import type { Query } from '@google-cloud/datastore'
3
- import type { RunQueryInfo, RunQueryOptions } from '@google-cloud/datastore/build/src/query'
3
+ import type { RunQueryInfo, RunQueryOptions } from '@google-cloud/datastore/build/src/query.js'
4
4
  import type { CommonLogger, UnixTimestampMillis } from '@naturalcycles/js-lib'
5
5
  import { _ms, pRetry } from '@naturalcycles/js-lib'
6
6
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
7
- import type { DatastoreDBStreamOptions } from './datastore.model'
7
+ import type { DatastoreDBStreamOptions } from './datastore.model.js'
8
8
 
9
9
  export class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
10
10
  private readonly originalLimit: number
@@ -1,5 +1,6 @@
1
+ import { Transform } from 'node:stream'
1
2
  import type { Datastore, Key, PropertyFilter, Query, Transaction } from '@google-cloud/datastore'
2
- import type { RunQueryOptions } from '@google-cloud/datastore/build/src/query'
3
+ import type { RunQueryOptions } from '@google-cloud/datastore/build/src/query.js'
3
4
  import type {
4
5
  CommonDB,
5
6
  CommonDBOptions,
@@ -49,10 +50,10 @@ import type {
49
50
  DatastorePayload,
50
51
  DatastorePropertyStats,
51
52
  DatastoreStats,
52
- } from './datastore.model'
53
- import { DatastoreType } from './datastore.model'
54
- import { DatastoreStreamReadable } from './DatastoreStreamReadable'
55
- import { dbQueryToDatastoreQuery } from './query.util'
53
+ } from './datastore.model.js'
54
+ import { DatastoreType } from './datastore.model.js'
55
+ import { DatastoreStreamReadable } from './DatastoreStreamReadable.js'
56
+ import { dbQueryToDatastoreQuery } from './query.util.js'
56
57
 
57
58
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
58
59
  const MAX_ITEMS = 500
@@ -108,14 +109,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
108
109
  protected KEY!: symbol
109
110
 
110
111
  // @memo() // not used to be able to connect to many DBs in the same server instance
111
- ds(): Datastore {
112
+ async ds(): Promise<Datastore> {
112
113
  if (!this.cachedDatastore) {
113
114
  _assert(
114
115
  process.env['APP_ENV'] !== 'test',
115
116
  'DatastoreDB cannot be used in Test env, please use InMemoryDB',
116
117
  )
117
118
 
118
- const DS = this.getDatastoreLib().Datastore as typeof Datastore
119
+ const DS = (await this.getDatastoreLib()).Datastore as typeof Datastore
119
120
  this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT']
120
121
 
121
122
  if (this.cfg.projectId) {
@@ -135,13 +136,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
135
136
  return this.cachedDatastore
136
137
  }
137
138
 
138
- private getPropertyFilter(): typeof PropertyFilter {
139
- return this.getDatastoreLib().PropertyFilter
139
+ private async getPropertyFilter(): Promise<typeof PropertyFilter> {
140
+ return (await this.getDatastoreLib()).PropertyFilter
140
141
  }
141
142
 
142
- private getDatastoreLib(): any {
143
+ private async getDatastoreLib(): Promise<any> {
143
144
  // Lazy-loading
144
- return require('@google-cloud/datastore')
145
+ const { default: lib } = await import('@google-cloud/datastore')
146
+ return lib
145
147
  }
146
148
 
147
149
  override async ping(): Promise<void> {
@@ -154,7 +156,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
154
156
  opt: DatastoreDBReadOptions = {},
155
157
  ): Promise<ROW[]> {
156
158
  if (!ids.length) return []
157
- const keys = ids.map(id => this.key(table, id))
159
+ let ds = await this.ds()
160
+ const keys = ids.map(id => this.key(ds, table, id))
158
161
  let rows: any[]
159
162
 
160
163
  const dsOpt = this.getRunQueryOptions(opt)
@@ -163,7 +166,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
163
166
  // First try
164
167
  try {
165
168
  const r = await pTimeout(
166
- () => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
169
+ () => ((opt.tx as DatastoreDBTransaction)?.tx || ds).get(keys, dsOpt),
167
170
  {
168
171
  timeout: this.cfg.timeout,
169
172
  name: `datastore.getByIds(${table})`,
@@ -179,14 +182,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
179
182
  this.cfg.logger.log('datastore recreated on error')
180
183
 
181
184
  // This is to debug "GCP Datastore Timeout issue"
182
- const datastoreLib = require('@google-cloud/datastore')
185
+ const datastoreLib = await this.getDatastoreLib()
183
186
  const DS = datastoreLib.Datastore as typeof Datastore
184
- this.cachedDatastore = new DS(this.cfg)
187
+ ds = this.cachedDatastore = new DS(this.cfg)
185
188
 
186
189
  // Second try (will throw)
187
190
  try {
188
191
  const r = await pRetry(
189
- () => ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).get(keys, dsOpt),
192
+ () => ((opt.tx as DatastoreDBTransaction)?.tx || ds).get(keys, dsOpt),
190
193
  {
191
194
  ...this.getPRetryOptions(`datastore.getByIds(${table}) second try`),
192
195
  maxAttempts: 3,
@@ -206,7 +209,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
206
209
  } else {
207
210
  rows = await pRetry(
208
211
  async () => {
209
- return (await this.ds().get(keys, dsOpt))[0]
212
+ return (await ds.get(keys, dsOpt))[0]
210
213
  },
211
214
  this.getPRetryOptions(`datastore.getByIds(${table})`),
212
215
  )
@@ -239,10 +242,11 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
239
242
  }
240
243
  }
241
244
 
245
+ const ds = await this.ds()
242
246
  const q = dbQueryToDatastoreQuery(
243
247
  dbQuery,
244
- this.ds().createQuery(dbQuery.table),
245
- this.getPropertyFilter(),
248
+ ds.createQuery(dbQuery.table),
249
+ await this.getPropertyFilter(),
246
250
  )
247
251
  const dsOpt = this.getRunQueryOptions(opt)
248
252
  const qr = await this.runDatastoreQuery<ROW>(q, dsOpt)
@@ -259,14 +263,15 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
259
263
  dbQuery: DBQuery<ROW>,
260
264
  opt: DatastoreDBReadOptions = {},
261
265
  ): Promise<number> {
266
+ const ds = await this.ds()
262
267
  const q = dbQueryToDatastoreQuery(
263
268
  dbQuery.select([]),
264
- this.ds().createQuery(dbQuery.table),
265
- this.getPropertyFilter(),
269
+ ds.createQuery(dbQuery.table),
270
+ await this.getPropertyFilter(),
266
271
  )
267
- const aq = this.ds().createAggregationQuery(q).count('count')
272
+ const aq = ds.createAggregationQuery(q).count('count')
268
273
  const dsOpt = this.getRunQueryOptions(opt)
269
- const [entities] = await this.ds().runAggregationQuery(aq, dsOpt)
274
+ const [entities] = await ds.runAggregationQuery(aq, dsOpt)
270
275
  return entities[0]?.count
271
276
  }
272
277
 
@@ -274,7 +279,8 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
274
279
  q: Query,
275
280
  dsOpt: RunQueryOptions,
276
281
  ): Promise<RunQueryResult<ROW>> {
277
- const [entities, queryResult] = await this.ds().runQuery(q, dsOpt)
282
+ const ds = await this.ds()
283
+ const [entities, queryResult] = await ds.runQuery(q, dsOpt)
278
284
 
279
285
  const rows = entities.map(e => this.mapId<ROW>(e))
280
286
 
@@ -284,37 +290,42 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
284
290
  }
285
291
  }
286
292
 
287
- private runQueryStream<ROW extends ObjectWithId>(
288
- q: Query,
293
+ override streamQuery<ROW extends ObjectWithId>(
294
+ dbQuery: DBQuery<ROW>,
289
295
  _opt?: DatastoreDBStreamOptions,
290
296
  ): ReadableTyped<ROW> {
291
- const opt = {
292
- ...this.cfg.streamOptions,
293
- ..._opt,
294
- }
295
- const dsOpt = this.getRunQueryOptions(opt)
297
+ const transform = new Transform({
298
+ objectMode: true,
299
+ transform: (chunk, _, cb) => {
300
+ cb(null, this.mapId(chunk))
301
+ },
302
+ })
296
303
 
297
- return (
298
- opt.experimentalCursorStream
304
+ void this.ds().then(async ds => {
305
+ const q = dbQueryToDatastoreQuery(
306
+ dbQuery,
307
+ ds.createQuery(dbQuery.table),
308
+ await this.getPropertyFilter(),
309
+ )
310
+
311
+ const opt = {
312
+ ...this.cfg.streamOptions,
313
+ ..._opt,
314
+ }
315
+
316
+ ;(opt.experimentalCursorStream
299
317
  ? new DatastoreStreamReadable<ROW>(
300
318
  q,
301
319
  opt,
302
320
  commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'),
303
321
  )
304
- : (this.ds().runQueryStream(q, dsOpt) as ReadableTyped<ROW>)
305
- ).map(chunk => this.mapId(chunk))
306
- }
322
+ : (ds.runQueryStream(q, this.getRunQueryOptions(opt)) as ReadableTyped<ROW>)
323
+ )
324
+ .on('error', err => transform.emit('error', err))
325
+ .pipe(transform)
326
+ })
307
327
 
308
- override streamQuery<ROW extends ObjectWithId>(
309
- dbQuery: DBQuery<ROW>,
310
- opt?: DatastoreDBStreamOptions,
311
- ): ReadableTyped<ROW> {
312
- const q = dbQueryToDatastoreQuery(
313
- dbQuery,
314
- this.ds().createQuery(dbQuery.table),
315
- this.getPropertyFilter(),
316
- )
317
- return this.runQueryStream(q, opt)
328
+ return transform
318
329
  }
319
330
 
320
331
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
@@ -327,15 +338,16 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
327
338
  rows: ROW[],
328
339
  opt: DatastoreDBSaveOptions<ROW> = {},
329
340
  ): Promise<void> {
341
+ const ds = await this.ds()
330
342
  const entities = rows.map(obj =>
331
- this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
343
+ this.toDatastoreEntity(ds, table, obj, opt.excludeFromIndexes as string[]),
332
344
  )
333
345
 
334
346
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save'
335
347
 
336
348
  const save = pRetryFn(
337
349
  async (batch: DatastorePayload<ROW>[]) => {
338
- await ((opt.tx as DatastoreDBTransaction)?.tx || this.ds())[method](batch)
350
+ await ((opt.tx as DatastoreDBTransaction)?.tx || ds)[method](batch)
339
351
  },
340
352
  this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`),
341
353
  )
@@ -377,10 +389,11 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
377
389
  return await this.deleteByIds(q.table, ids, opt)
378
390
  }
379
391
 
392
+ const ds = await this.ds()
380
393
  const datastoreQuery = dbQueryToDatastoreQuery(
381
394
  q.select([]),
382
- this.ds().createQuery(q.table),
383
- this.getPropertyFilter(),
395
+ ds.createQuery(q.table),
396
+ await this.getPropertyFilter(),
384
397
  )
385
398
  const dsOpt = this.getRunQueryOptions(opt)
386
399
  const { rows } = await this.runDatastoreQuery<ROW>(datastoreQuery, dsOpt)
@@ -400,12 +413,13 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
400
413
  ids: string[],
401
414
  opt: DatastoreDBOptions = {},
402
415
  ): Promise<number> {
403
- const keys = ids.map(id => this.key(table, id))
416
+ const ds = await this.ds()
417
+ const keys = ids.map(id => this.key(ds, table, id))
404
418
 
405
419
  await pMap(
406
420
  _chunk(keys, MAX_ITEMS),
407
421
 
408
- async batch => await ((opt.tx as DatastoreDBTransaction)?.tx || this.ds()).delete(batch),
422
+ async batch => await ((opt.tx as DatastoreDBTransaction)?.tx || ds).delete(batch),
409
423
  {
410
424
  concurrency: DATASTORE_RECOMMENDED_CONCURRENCY,
411
425
  },
@@ -417,8 +431,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
417
431
  fn: DBTransactionFn,
418
432
  opt: CommonDBTransactionOptions = {},
419
433
  ): Promise<void> {
434
+ const ds = await this.ds()
420
435
  const { readOnly } = opt
421
- const datastoreTx = this.ds().transaction({
436
+ const datastoreTx = ds.transaction({
422
437
  readOnly,
423
438
  })
424
439
 
@@ -434,8 +449,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
434
449
  }
435
450
 
436
451
  async getAllStats(): Promise<DatastoreStats[]> {
437
- const q = this.ds().createQuery('__Stat_Kind__')
438
- const [statsArray] = await this.ds().runQuery(q)
452
+ const ds = await this.ds()
453
+ const q = ds.createQuery('__Stat_Kind__')
454
+ const [statsArray] = await ds.runQuery(q)
439
455
  return statsArray || []
440
456
  }
441
457
 
@@ -443,14 +459,15 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
443
459
  * Returns undefined e.g when Table is non-existing
444
460
  */
445
461
  async getStats(table: string): Promise<DatastoreStats | undefined> {
446
- const { PropertyFilter } = this.getDatastoreLib()
462
+ const ds = await this.ds()
463
+ const propertyFilter = await this.getPropertyFilter()
447
464
 
448
- const q = this.ds()
465
+ const q = ds
449
466
  .createQuery('__Stat_Kind__')
450
467
  // .filter('kind_name', table)
451
- .filter(new PropertyFilter('kind_name', '=', table))
468
+ .filter(new propertyFilter('kind_name', '=', table))
452
469
  .limit(1)
453
- const [statsArray] = await this.ds().runQuery(q)
470
+ const [statsArray] = await ds.runQuery(q)
454
471
  const [stats] = statsArray
455
472
  return stats
456
473
  }
@@ -461,11 +478,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
461
478
  }
462
479
 
463
480
  async getTableProperties(table: string): Promise<DatastorePropertyStats[]> {
464
- const q = this.ds()
481
+ const ds = await this.ds()
482
+ const q = ds
465
483
  .createQuery('__Stat_PropertyType_PropertyName_Kind__')
466
484
  // .filter('kind_name', table)
467
- .filter(new (this.getPropertyFilter())('kind_name', '=', table))
468
- const [stats] = await this.ds().runQuery(q)
485
+ .filter(new (await this.getPropertyFilter())('kind_name', '=', table))
486
+ const [stats] = await ds.runQuery(q)
469
487
  return stats
470
488
  }
471
489
 
@@ -481,11 +499,12 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
481
499
 
482
500
  // if key field exists on entity, it will be used as key (prevent to duplication of numeric keyed entities)
483
501
  private toDatastoreEntity<T extends ObjectWithId>(
502
+ ds: Datastore,
484
503
  kind: string,
485
504
  o: T,
486
505
  excludeFromIndexes: string[] = [],
487
506
  ): DatastorePayload<T> {
488
- const key = this.getDsKey(o) || this.key(kind, o.id)
507
+ const key = this.getDsKey(o) || this.key(ds, kind, o.id)
489
508
  const data = Object.assign({}, o) as any
490
509
  delete data.id
491
510
  delete data[this.KEY]
@@ -497,9 +516,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
497
516
  }
498
517
  }
499
518
 
500
- key(kind: string, id: string): Key {
519
+ key(ds: Datastore, kind: string, id: string): Key {
501
520
  _assert(id, `Cannot save "${kind}" entity without "id"`)
502
- return this.ds().key([kind, id])
521
+ return ds.key([kind, id])
503
522
  }
504
523
 
505
524
  private getDsKey(o: any): Key | undefined {
@@ -3,8 +3,8 @@ import { commonKeyValueDBFullSupport, DBQuery } from '@naturalcycles/db-lib'
3
3
  import type { ObjectWithId } from '@naturalcycles/js-lib'
4
4
  import { AppError } from '@naturalcycles/js-lib'
5
5
  import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
6
- import { DatastoreDB } from './datastore.db'
7
- import type { DatastoreDBCfg } from './datastore.model'
6
+ import { DatastoreDB } from './datastore.db.js'
7
+ import type { DatastoreDBCfg } from './datastore.model.js'
8
8
 
9
9
  interface KVObject {
10
10
  id: string
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './datastore.db'
2
- export * from './datastore.model'
3
- export * from './datastoreKeyValueDB'
1
+ export * from './datastore.db.js'
2
+ export * from './datastore.model.js'
3
+ export * from './datastoreKeyValueDB.js'