@nx-ddd/firestore 19.2.0 → 19.3.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.
Files changed (93) hide show
  1. package/adapters/admin/index.d.ts +40 -1
  2. package/adapters/firebase/index.d.ts +35 -1
  3. package/{adapters/admin/admin.adapter.js → fesm2022/nx-ddd-firestore-adapters-admin.mjs} +21 -13
  4. package/fesm2022/nx-ddd-firestore-adapters-admin.mjs.map +1 -0
  5. package/{adapters/firebase/firebase.adapter.js → fesm2022/nx-ddd-firestore-adapters-firebase.mjs} +20 -13
  6. package/fesm2022/nx-ddd-firestore-adapters-firebase.mjs.map +1 -0
  7. package/fesm2022/nx-ddd-firestore.mjs +796 -0
  8. package/fesm2022/nx-ddd-firestore.mjs.map +1 -0
  9. package/index.d.ts +424 -7
  10. package/package.json +26 -6
  11. package/README.md +0 -172
  12. package/adapters/admin/admin.adapter.d.ts +0 -36
  13. package/adapters/admin/admin.adapter.js.map +0 -1
  14. package/adapters/admin/index.js +0 -2
  15. package/adapters/admin/index.js.map +0 -1
  16. package/adapters/base/base.adapter.d.ts +0 -35
  17. package/adapters/base/base.adapter.js +0 -189
  18. package/adapters/base/base.adapter.js.map +0 -1
  19. package/adapters/base/index.d.ts +0 -1
  20. package/adapters/base/index.js +0 -2
  21. package/adapters/base/index.js.map +0 -1
  22. package/adapters/dayjs/dayjs.adatper.d.ts +0 -15
  23. package/adapters/dayjs/dayjs.adatper.js +0 -44
  24. package/adapters/dayjs/dayjs.adatper.js.map +0 -1
  25. package/adapters/dayjs/index.d.ts +0 -1
  26. package/adapters/dayjs/index.js +0 -2
  27. package/adapters/dayjs/index.js.map +0 -1
  28. package/adapters/firebase/firebase.adapter.d.ts +0 -32
  29. package/adapters/firebase/firebase.adapter.js.map +0 -1
  30. package/adapters/firebase/index.js +0 -2
  31. package/adapters/firebase/index.js.map +0 -1
  32. package/adapters/index.d.ts +0 -1
  33. package/adapters/index.js +0 -2
  34. package/adapters/index.js.map +0 -1
  35. package/converter/converter.d.ts +0 -17
  36. package/converter/converter.js +0 -31
  37. package/converter/converter.js.map +0 -1
  38. package/converter/index.d.ts +0 -1
  39. package/converter/index.js +0 -2
  40. package/converter/index.js.map +0 -1
  41. package/dao/firestore.dao.d.ts +0 -17
  42. package/dao/firestore.dao.js +0 -22
  43. package/dao/firestore.dao.js.map +0 -1
  44. package/dao/index.d.ts +0 -1
  45. package/dao/index.js +0 -2
  46. package/dao/index.js.map +0 -1
  47. package/decorators/decorators.d.ts +0 -87
  48. package/decorators/decorators.js +0 -36
  49. package/decorators/decorators.js.map +0 -1
  50. package/decorators/index.d.ts +0 -1
  51. package/decorators/index.js +0 -2
  52. package/decorators/index.js.map +0 -1
  53. package/index.js +0 -8
  54. package/index.js.map +0 -1
  55. package/interfaces/index.d.ts +0 -1
  56. package/interfaces/index.js +0 -2
  57. package/interfaces/index.js.map +0 -1
  58. package/interfaces/interfaces.d.ts +0 -63
  59. package/interfaces/interfaces.js +0 -2
  60. package/interfaces/interfaces.js.map +0 -1
  61. package/path-builder/index.d.ts +0 -1
  62. package/path-builder/index.js +0 -2
  63. package/path-builder/index.js.map +0 -1
  64. package/path-builder/path-builder.d.ts +0 -34
  65. package/path-builder/path-builder.js +0 -33
  66. package/path-builder/path-builder.js.map +0 -1
  67. package/query/firestore.query.d.ts +0 -33
  68. package/query/firestore.query.js +0 -75
  69. package/query/firestore.query.js.map +0 -1
  70. package/query/index.d.ts +0 -1
  71. package/query/index.js +0 -2
  72. package/query/index.js.map +0 -1
  73. package/query/v2/cached-query.d.ts +0 -58
  74. package/query/v2/cached-query.js +0 -102
  75. package/query/v2/cached-query.js.map +0 -1
  76. package/query/v2/index.d.ts +0 -2
  77. package/query/v2/index.js +0 -3
  78. package/query/v2/index.js.map +0 -1
  79. package/query/v2/query.d.ts +0 -46
  80. package/query/v2/query.js +0 -81
  81. package/query/v2/query.js.map +0 -1
  82. package/repository/index.d.ts +0 -1
  83. package/repository/index.js +0 -2
  84. package/repository/index.js.map +0 -1
  85. package/repository/repository.d.ts +0 -65
  86. package/repository/repository.js +0 -218
  87. package/repository/repository.js.map +0 -1
  88. package/testing/common.d.ts +0 -7
  89. package/testing/common.js +0 -19
  90. package/testing/common.js.map +0 -1
  91. package/testing/index.d.ts +0 -1
  92. package/testing/index.js +0 -2
  93. package/testing/index.js.map +0 -1
@@ -0,0 +1,796 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, inject } from '@angular/core';
3
+ import { makeDecoratorFactories, walkObj, plainToInstanceWithValid } from '@nx-ddd/core';
4
+ import { get } from 'lodash-es';
5
+ import dayjs from 'dayjs';
6
+ import { ReplaySubject, throwError, of, lastValueFrom, map as map$1, take as take$1, switchMap as switchMap$1, catchError as catchError$1, NEVER, tap as tap$1, Observable, distinctUntilChanged as distinctUntilChanged$1, retry, throttleTime, combineLatest } from 'rxjs';
7
+ import { switchMap, catchError, shareReplay, tap, map, distinctUntilChanged, take } from 'rxjs/operators';
8
+ import Dexie, { liveQuery } from 'dexie';
9
+ import { Repository } from '@nx-ddd/common/domain';
10
+ import { generateId, toObject } from '@nx-ddd/common';
11
+
12
+ const FIRESTORE_ANNOTATIONS = 'firestore_annotations';
13
+ const { createDecorator, createPropsDecorator, createClassDecorator, getAnnotations, } = makeDecoratorFactories((type, fieldName, propName, options) => ({ type, fieldName, propName, childType: options.childType }), FIRESTORE_ANNOTATIONS);
14
+ /** @deprecated use getAnnotations */
15
+ function getFirestoreAnnotations(target) {
16
+ return getAnnotations(target);
17
+ }
18
+ const ID = createDecorator('id');
19
+ const String$1 = createDecorator('string');
20
+ const Null = createDecorator('null');
21
+ const Geopoint = createDecorator('geopoint');
22
+ const Boolean$1 = createDecorator('boolean');
23
+ const Timestamp = createDecorator('timestamp');
24
+ const Number$1 = createDecorator('number');
25
+ const Map$1 = (childType, props) => createDecorator('map', { childType })(props);
26
+ const Array$1 = (childType, props) => createDecorator('array', { childType })(props);
27
+ const Reference = createDecorator('reference');
28
+ function getCollection(target) {
29
+ const annotations = getAnnotations(target);
30
+ const annotation = annotations.find((annotation) => annotation.type === 'collection');
31
+ return annotation.name;
32
+ }
33
+ const Firestore = {
34
+ Collection: createClassDecorator('collection'),
35
+ ID: ID,
36
+ String: String$1,
37
+ Null: Null,
38
+ Geopoint: Geopoint,
39
+ Boolean: Boolean$1,
40
+ Timestamp: Timestamp,
41
+ Number: Number$1,
42
+ Map: Map$1,
43
+ Array: Array$1,
44
+ Reference: Reference,
45
+ };
46
+
47
+ class DateAdapter {
48
+ isDate(value) {
49
+ return value instanceof Date || dayjs.isDayjs(value);
50
+ }
51
+ isValid(value) {
52
+ return this.isDate(value) && !isNaN(value.getTime());
53
+ }
54
+ getJsDate(value) {
55
+ return value;
56
+ }
57
+ convertToDate(value) {
58
+ return value;
59
+ }
60
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DateAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
61
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DateAdapter, providedIn: 'root' }); }
62
+ }
63
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DateAdapter, decorators: [{
64
+ type: Injectable,
65
+ args: [{ providedIn: 'root' }]
66
+ }] });
67
+ class DayjsAdapter {
68
+ constructor(dateAdapter) {
69
+ this.dateAdapter = dateAdapter;
70
+ }
71
+ isDayjs(value) {
72
+ return dayjs.isDayjs(value);
73
+ }
74
+ isValid(value) {
75
+ return this.isDayjs(value) && value.isValid();
76
+ }
77
+ getJsDate(value) {
78
+ return value.toDate();
79
+ }
80
+ convertToDayjs(value) {
81
+ return dayjs(value);
82
+ }
83
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DayjsAdapter, deps: [{ token: DateAdapter }], target: i0.ɵɵFactoryTarget.Injectable }); }
84
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DayjsAdapter, providedIn: 'root' }); }
85
+ }
86
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: DayjsAdapter, decorators: [{
87
+ type: Injectable,
88
+ args: [{ providedIn: 'root' }]
89
+ }], ctorParameters: () => [{ type: DateAdapter }] });
90
+
91
+ class FirestoreAdapter {
92
+ constructor() {
93
+ this.dateAdapter = inject(DateAdapter);
94
+ }
95
+ //** @deprecated */
96
+ toFirestoreData(entity) {
97
+ return Object.entries(entity).reduce((pre, [k, v]) => ({
98
+ ...pre, [k]: this.dateAdapter.isDate(v) ? this.convertDateToTimestamp(v) : v,
99
+ }), {});
100
+ }
101
+ //** @deprecated */
102
+ fromFirestoreData(data) {
103
+ return Object.entries(data).reduce((pre, [k, v]) => ({
104
+ ...pre, [k]: this.isTimestamp(v) ? this.convertTimestampToDate(v) : v,
105
+ }), {});
106
+ }
107
+ toFirestore(obj, Entity) {
108
+ const newObj = {};
109
+ for (const annotation of getFirestoreAnnotations(Entity)) {
110
+ const _value = get(obj, annotation.propName);
111
+ if (_value === null) {
112
+ newObj[annotation.fieldName] = null;
113
+ continue;
114
+ }
115
+ switch (annotation.type) {
116
+ case 'id':
117
+ case 'string':
118
+ case 'number':
119
+ case 'boolean': {
120
+ if (typeof _value === 'undefined')
121
+ break;
122
+ newObj[annotation.fieldName] = _value;
123
+ break;
124
+ }
125
+ case 'timestamp': {
126
+ if (typeof _value === 'undefined')
127
+ break;
128
+ if (!this.dateAdapter.isDate(_value)) {
129
+ console.error('[FirestoreAdapter] _value:', _value);
130
+ throw new Error(`Invalid Date Type: ${_value}`);
131
+ }
132
+ newObj[annotation.fieldName] = this.convertDateToTimestamp(_value);
133
+ break;
134
+ }
135
+ case 'array': {
136
+ if (typeof annotation?.childType === 'undefined') {
137
+ if (_value)
138
+ newObj[annotation.fieldName] = _value;
139
+ }
140
+ else {
141
+ const Type = annotation.childType();
142
+ if (typeof _value === 'undefined')
143
+ break;
144
+ if (!Array.isArray(_value)) {
145
+ newObj[annotation.fieldName] = [];
146
+ break;
147
+ }
148
+ const value = (_value ?? []).map(v => {
149
+ switch (Type) {
150
+ case String:
151
+ case Number:
152
+ return v;
153
+ default:
154
+ return this.toFirestore(v, Type);
155
+ }
156
+ });
157
+ if (value)
158
+ newObj[annotation.fieldName] = value;
159
+ }
160
+ break;
161
+ }
162
+ case 'map': {
163
+ if (typeof _value === 'undefined')
164
+ break;
165
+ if (typeof annotation?.childType === 'undefined') {
166
+ if (_value)
167
+ newObj[annotation.fieldName] = _value;
168
+ }
169
+ else {
170
+ const Type = annotation.childType();
171
+ const value = this.toFirestore(_value, Type);
172
+ if (value)
173
+ newObj[annotation.fieldName] = value;
174
+ }
175
+ break;
176
+ }
177
+ }
178
+ }
179
+ return newObj;
180
+ }
181
+ fromFirestore(data, Entity) {
182
+ const newObj = {};
183
+ for (const annotation of getFirestoreAnnotations(Entity)) {
184
+ const _value = data[annotation.fieldName];
185
+ if (typeof _value === 'undefined')
186
+ continue;
187
+ if (_value === null) {
188
+ newObj[annotation.propName] = null;
189
+ continue;
190
+ }
191
+ switch (annotation.type) {
192
+ case 'id':
193
+ case 'string':
194
+ case 'number':
195
+ case 'boolean': {
196
+ newObj[annotation.propName] = _value;
197
+ break;
198
+ }
199
+ case 'timestamp': {
200
+ try {
201
+ newObj[annotation.propName] = this.convertTimestampToDate(_value);
202
+ }
203
+ catch (e) {
204
+ newObj[annotation.propName] = _value;
205
+ }
206
+ break;
207
+ }
208
+ case 'array': {
209
+ if (typeof annotation?.childType === 'undefined') {
210
+ newObj[annotation.propName] = _value;
211
+ }
212
+ else {
213
+ const Type = annotation.childType();
214
+ const value = _value.map(v => {
215
+ switch (Type) {
216
+ case String:
217
+ case Number:
218
+ return v;
219
+ default:
220
+ return this.fromFirestore(v, Type);
221
+ }
222
+ });
223
+ if (value)
224
+ newObj[annotation.propName] = value;
225
+ }
226
+ break;
227
+ }
228
+ case 'map': {
229
+ if (typeof annotation?.childType === 'undefined') {
230
+ newObj[annotation.propName] = _value;
231
+ }
232
+ else {
233
+ const Type = annotation.childType();
234
+ const value = this.fromFirestore(_value, Type);
235
+ if (value)
236
+ newObj[annotation.propName] = value;
237
+ }
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ return newObj;
243
+ }
244
+ /**
245
+ * @description FirestoreのUpdateは通常のオブジェクトでは孫以下のプロパティが既存データとマージされないので、パスに変換する必要がある。
246
+ */
247
+ flattenForUpdate(obj) {
248
+ const newObj = {};
249
+ walkObj(obj, {
250
+ callback: (paths, value) => newObj[paths.join('.')] = value,
251
+ overwrite: (_, value) => {
252
+ if (Array.isArray(value))
253
+ return [true, value];
254
+ if (this.isTimestamp(value))
255
+ return [true, value];
256
+ if (this.isFieldValue(value))
257
+ return [true, value];
258
+ return [false];
259
+ }
260
+ });
261
+ return newObj;
262
+ }
263
+ convertDateToTimestamp(date) {
264
+ return this.Timestamp.fromDate(date);
265
+ }
266
+ convertTimestampToDate(timestamp) {
267
+ return this.dateAdapter.convertToDate(timestamp.toDate());
268
+ }
269
+ }
270
+ function provideFirestoreAdapter(useClass) {
271
+ return { provide: FirestoreAdapter, useClass };
272
+ }
273
+
274
+ class FirestoreHelper {
275
+ static fromFirestore(doc, Entity, adapter) {
276
+ const data = adapter.fromFirestore({ ...doc.data(), id: doc.id }, Entity);
277
+ return plainToInstanceWithValid(Entity, data);
278
+ }
279
+ }
280
+ class BasicConverter {
281
+ constructor(Entity, adapter) {
282
+ this.Entity = Entity;
283
+ this.adapter = adapter;
284
+ }
285
+ fromFirestore(doc) {
286
+ return this.adapter.fromFirestore({
287
+ ...doc.data(),
288
+ id: doc.id
289
+ }, this.Entity);
290
+ }
291
+ fromFirestoreMany(docs) {
292
+ return docs.map(doc => this.fromFirestore(doc));
293
+ }
294
+ toFirestore(entity) {
295
+ return this.adapter.toFirestore(entity, this.Entity);
296
+ }
297
+ }
298
+ function getConverter(Entity, adapter = inject(FirestoreAdapter)) {
299
+ return new BasicConverter(Entity, adapter);
300
+ }
301
+ function injectConverter(Entity) {
302
+ return getConverter(Entity, inject(FirestoreAdapter));
303
+ }
304
+
305
+ class FirestoreDAO {
306
+ constructor(adapter, pathBuilder) {
307
+ this.adapter = adapter;
308
+ this.pathBuilder = pathBuilder;
309
+ }
310
+ collection(paramMap) {
311
+ const path = this.pathBuilder.collection(paramMap);
312
+ return this.adapter.collection(path);
313
+ }
314
+ collectionGroup() {
315
+ const path = this.pathBuilder.collectionGroup();
316
+ return this.adapter.collectionGroup(path);
317
+ }
318
+ doc(paramMap) {
319
+ const path = this.pathBuilder.doc(paramMap);
320
+ return this.adapter.doc(path);
321
+ }
322
+ getCollection(paramMap) {
323
+ return paramMap ? this.collection(paramMap) : this.collectionGroup();
324
+ }
325
+ }
326
+
327
+ class FirestorePathBuilder {
328
+ doc(param) {
329
+ return resolvePaths(param, this.paths);
330
+ }
331
+ collection(param = {}) {
332
+ return resolvePaths(param, this.paths.slice(0, -1));
333
+ }
334
+ collectionGroup() {
335
+ return this.paths[this.paths.length - 2];
336
+ }
337
+ constructor(paths) {
338
+ this.paths = paths;
339
+ }
340
+ }
341
+ function pathBuilderFactory(path) {
342
+ const paths = parsePath(path);
343
+ return new FirestorePathBuilder(paths);
344
+ }
345
+ function parsePath(path) {
346
+ const paths = path.split('/').filter(p => p.length);
347
+ if (!paths[paths.length - 1].startsWith(':'))
348
+ paths.push(':id');
349
+ return paths;
350
+ }
351
+ const resolvePaths = (obj, paths) => {
352
+ return paths.map((path) => path.startsWith(':') ? resolvePath(obj, path.slice(1)) : path).join('/');
353
+ };
354
+ const resolvePath = (obj, key) => {
355
+ if (!obj?.[key])
356
+ throw new Error(`Invalid key is detected in resolving paths, key: \`${key}\`, obj: \`${JSON.stringify(obj)}\``);
357
+ return obj?.[key];
358
+ };
359
+
360
+ let FirestoreQuery$1 = class FirestoreQuery extends FirestoreDAO {
361
+ constructor(adapter, converter, pathBuilder) {
362
+ super(adapter, pathBuilder);
363
+ this.converter = converter;
364
+ this.pathBuilder = pathBuilder;
365
+ this.collection$ = new ReplaySubject(1);
366
+ this.list$ = this.collection$.pipe(switchMap((collection) => this.listChangesByCollectionRef(collection).pipe(catchError(error => throwError(() => error)))), shareReplay(1));
367
+ this.switchCollectionRef();
368
+ }
369
+ /** @deprecated */
370
+ listChanges(paramMap, options = { switch: true }) {
371
+ return of(paramMap).pipe(tap((paramMap) => options?.switch && this.switchCollectionRef(paramMap)), switchMap(() => this.list$));
372
+ }
373
+ listChangesV2(paramMap) {
374
+ return of(paramMap).pipe(tap((paramMap) => paramMap && this.switchCollectionRef(paramMap)), switchMap(() => this.list$));
375
+ }
376
+ listChangesAfter(updatedAt, { limit } = {}) {
377
+ const entitiesMap = new Map();
378
+ const _updatedAt = dayjs.isDayjs(updatedAt) ? this.adapter.convertDateToTimestamp(updatedAt) : updatedAt;
379
+ return this.adapter.query(this.collection(), ...[
380
+ this.adapter.where('updatedAt', '>', _updatedAt),
381
+ this.adapter.orderBy('updatedAt', 'asc'),
382
+ limit ? this.adapter.limit(limit) : undefined,
383
+ ].filter(Boolean)).stateChanges().pipe(
384
+ // bufferTime(1_000),
385
+ // map(zippedActions => zippedActions.flat()),
386
+ tap(actions => actions.forEach(({ type, payload: { doc } }) => {
387
+ if (new Set(['added', 'modified']).has(type)) {
388
+ entitiesMap.set(doc.id, this.converter.fromFirestore(doc));
389
+ }
390
+ else if (type === 'removed') {
391
+ entitiesMap.delete(doc.id);
392
+ }
393
+ })), map(() => [...entitiesMap.values()]));
394
+ }
395
+ changes({ id }) {
396
+ if (!id)
397
+ throw new Error(`Invalid Id. it must be Truthy`);
398
+ return this.list$.pipe(map(entities => entities.find(entity => entity.id === id)), distinctUntilChanged((pre, cur) => JSON.stringify(pre) === JSON.stringify(cur)));
399
+ }
400
+ changesV2(params) {
401
+ const docRef = this.doc(params);
402
+ return docRef.stateChanges().pipe(map((doc) => this.converter.fromFirestore(doc)));
403
+ }
404
+ get({ id }) {
405
+ return lastValueFrom(this.list$.pipe(take(1), map(entities => entities.find(entity => entity.id === id))));
406
+ }
407
+ list(paramMap) {
408
+ this.switchCollectionRef(paramMap);
409
+ return lastValueFrom(this.list$.pipe(take(1)));
410
+ }
411
+ listChangesByCollectionRef(collection) {
412
+ const entitiesMap = new Map();
413
+ return collection.stateChanges().pipe(tap(actions => actions.forEach(({ type, payload: { doc } }) => {
414
+ if (new Set(['added', 'modified']).has(type)) {
415
+ entitiesMap.set(doc.id, this.converter.fromFirestore(doc));
416
+ }
417
+ else if (type === 'removed') {
418
+ entitiesMap.delete(doc.id);
419
+ }
420
+ })), map(() => [...entitiesMap.values()]));
421
+ }
422
+ switchCollectionRef(paramMap) {
423
+ const collection = this.getCollection(paramMap);
424
+ this.switchCollection(collection);
425
+ }
426
+ switchCollection(collection) {
427
+ this.collection$.next(collection);
428
+ }
429
+ };
430
+
431
+ class BaseFirestoreQuery {
432
+ count(paramMap) {
433
+ return this.dao.getCollection(paramMap).count();
434
+ }
435
+ changes(params) {
436
+ const docRef = this.dao.doc(params);
437
+ return docRef.stateChanges().pipe(map$1((doc) => this.converter.fromFirestore(doc)));
438
+ }
439
+ get(paramMap) {
440
+ return lastValueFrom(this.changes(paramMap).pipe(take$1(1)));
441
+ }
442
+ list(paramMap) {
443
+ return lastValueFrom(this.listChanges(paramMap).pipe(take$1(1)));
444
+ }
445
+ listChanges(paramMap) {
446
+ return of(paramMap).pipe(map$1((paramMap) => this.dao.getCollection(paramMap)), switchMap$1((collection) => {
447
+ return this.getListChanges$ByCollectionLike(collection);
448
+ }));
449
+ }
450
+ getChanges$ByDocument(doc) {
451
+ return doc.stateChanges().pipe(map$1((doc) => this.converter.fromFirestore(doc)), catchError$1((error) => {
452
+ console.error(error);
453
+ return NEVER;
454
+ }));
455
+ }
456
+ getListChanges$ByCollectionLike(collection) {
457
+ const entitiesMap = new Map();
458
+ return collection.stateChanges().pipe(tap$1(actions => actions.forEach(({ type, payload: { doc } }) => {
459
+ if (new Set(['added', 'modified']).has(type)) {
460
+ entitiesMap.set(doc.id, this.converter.fromFirestore(doc));
461
+ }
462
+ else if (type === 'removed') {
463
+ entitiesMap.delete(doc.id);
464
+ }
465
+ })), map$1(() => [...entitiesMap.values()]), catchError$1((error) => {
466
+ console.error(error);
467
+ return NEVER;
468
+ }));
469
+ }
470
+ }
471
+ class FirestoreQuery extends BaseFirestoreQuery {
472
+ constructor() {
473
+ super(...arguments);
474
+ this.adapter = inject(FirestoreAdapter);
475
+ }
476
+ get pathBuilder() {
477
+ return pathBuilderFactory(this.collectionPath);
478
+ }
479
+ get dao() {
480
+ return new FirestoreDAO(this.adapter, this.pathBuilder);
481
+ }
482
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreQuery, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
483
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreQuery }); }
484
+ }
485
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreQuery, decorators: [{
486
+ type: Injectable
487
+ }] });
488
+ function getFirestoreQuery(entity, adapter = inject(FirestoreAdapter)) {
489
+ class FirestoreQuery extends BaseFirestoreQuery {
490
+ constructor(converter, collectionPath, pathBuilder, adapter, dao) {
491
+ super();
492
+ this.converter = converter;
493
+ this.collectionPath = collectionPath;
494
+ this.pathBuilder = pathBuilder;
495
+ this.adapter = adapter;
496
+ this.dao = dao;
497
+ }
498
+ }
499
+ return new FirestoreQuery(getConverter(entity, adapter), getCollection(entity), pathBuilderFactory(getCollection(entity)), adapter, new FirestoreDAO(adapter, pathBuilderFactory(getCollection(entity))));
500
+ }
501
+ function injectFirestoreQuery(entity) {
502
+ return getFirestoreQuery(entity, inject(FirestoreAdapter));
503
+ }
504
+
505
+ function fromDexie(observable) {
506
+ return new Observable(subscriber => observable.subscribe(subscriber));
507
+ }
508
+ class DexieAdapter extends Dexie {
509
+ constructor() {
510
+ super('@nx-ddd/firestore/cached-query');
511
+ this.version(3).stores({ entities: '++id' });
512
+ }
513
+ count$() {
514
+ return fromDexie(liveQuery(() => this.entities.count()));
515
+ }
516
+ getLastUpdatedAt$(collectionPath) {
517
+ return of(null).pipe(switchMap$1((() => fromDexie(liveQuery(() => this.entities.toArray())))), map$1((entities) => entities.filter(entity => entity.collectionPath === collectionPath)), map$1(entities => entities.sort((a, b) => {
518
+ if (!a?.updatedAt)
519
+ return -1;
520
+ if (!b?.updatedAt)
521
+ return 1;
522
+ return dayjs(a?.updatedAt).diff(dayjs(b?.updatedAt));
523
+ }).at(-1)), map$1(entity => entity?.updatedAt ? dayjs(entity?.updatedAt) : dayjs('1970-01-01')), distinctUntilChanged$1((pre, cur) => pre.isSame(cur)));
524
+ }
525
+ async bulkPut(collectionPath, entities) {
526
+ return this.entities.bulkPut(entities.map(entity => {
527
+ const key = `${entity.id}`;
528
+ return {
529
+ id: key,
530
+ collectionPath,
531
+ value: JSON.stringify(entity),
532
+ updatedAt: entity.updatedAt.toISOString(),
533
+ };
534
+ }));
535
+ }
536
+ listChanges(collectionPath) {
537
+ return of(null).pipe(switchMap$1(() => fromDexie(liveQuery(() => this.entities.toArray()))), map$1((entities) => entities.filter(entity => entity.collectionPath === collectionPath)), map$1((entities) => entities.map(entity => JSON.parse(entity.value))));
538
+ }
539
+ }
540
+ class FirestoreIndexedDbCached {
541
+ constructor(collectionPath) {
542
+ this.collectionPath = collectionPath;
543
+ this.adapter = new DexieAdapter();
544
+ }
545
+ getLastUpdatedAt$() {
546
+ return this.adapter.getLastUpdatedAt$(this.collectionPath);
547
+ }
548
+ async setMany(entities) {
549
+ return this.adapter.bulkPut(this.collectionPath, entities);
550
+ }
551
+ listChanges() {
552
+ return this.adapter.listChanges(this.collectionPath);
553
+ }
554
+ }
555
+ class CachedFirestoreQuery extends FirestoreQuery {
556
+ constructor() {
557
+ super(...arguments);
558
+ this.config = {
559
+ throttleTime: 5_000,
560
+ loadChunkSize: 1_000,
561
+ };
562
+ }
563
+ get cached() {
564
+ return this._cached ??= new FirestoreIndexedDbCached(this.collectionPath);
565
+ }
566
+ listChanges(paramMap) {
567
+ const collection = this.dao.getCollection(paramMap);
568
+ return this.cached.getLastUpdatedAt$().pipe(switchMap$1((lastUpdatedAt) => this._listChangesAfter(collection, lastUpdatedAt, {
569
+ limit: this.config.loadChunkSize,
570
+ }).pipe(retry(3), tap$1((updated) => this.cached.setMany(updated)), tap$1((updated) => console.log('updated:', updated)), catchError$1((error) => (console.error(error), NEVER)))), switchMap$1(() => this.cached.listChanges()), map$1((entities) => entities.map(entity => this.converter.fromObject(entity))), throttleTime(this.config.throttleTime));
571
+ }
572
+ _listChange(paramMap) {
573
+ return of(paramMap).pipe(map$1((paramMap) => this.dao.getCollection(paramMap)), map$1((collection) => this.adapter.query(collection, this.adapter.limit(10))), switchMap$1((collection) => this.getListChanges$ByCollectionLike(collection)));
574
+ }
575
+ _listChangesAfter(collection, updatedAt, { limit } = {}) {
576
+ const updatedAtTs = dayjs.isDayjs(updatedAt) ? this.adapter.convertDateToTimestamp(updatedAt) : updatedAt;
577
+ const query = this.adapter.query(collection, ...[
578
+ this.adapter.where('updatedAt', '>', updatedAtTs),
579
+ this.adapter.orderBy('updatedAt', 'asc'),
580
+ limit ? this.adapter.limit(limit) : undefined,
581
+ ].filter(Boolean));
582
+ return this.getListChanges$ByCollectionLike(query);
583
+ }
584
+ progressChanges() {
585
+ return combineLatest({
586
+ cloud: this.count(),
587
+ local: this.listChanges().pipe(map$1(items => items.length)),
588
+ });
589
+ }
590
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: CachedFirestoreQuery, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
591
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: CachedFirestoreQuery }); }
592
+ }
593
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: CachedFirestoreQuery, decorators: [{
594
+ type: Injectable
595
+ }] });
596
+
597
+ const toPromise = callback => new Promise((resolve, reject) => {
598
+ try {
599
+ resolve(callback());
600
+ }
601
+ catch (error) {
602
+ reject(error);
603
+ }
604
+ });
605
+ function doesExist(doc) {
606
+ if (typeof doc.exists === 'function') {
607
+ return doc.exists();
608
+ }
609
+ return doc.exists;
610
+ }
611
+ class BaseFirestoreRepository extends Repository {
612
+ constructor() {
613
+ super(...arguments);
614
+ this.genId = () => generateId();
615
+ }
616
+ async list(paramMap, query = q => q) {
617
+ const collection = paramMap ? this.collection(paramMap) : this.collectionGroup();
618
+ return this._list(query(collection));
619
+ }
620
+ listChanges() {
621
+ throw new Error('Method not implemented.');
622
+ }
623
+ async count(paramMap) {
624
+ return this.collection(paramMap).count();
625
+ }
626
+ async get(paramMap) {
627
+ return this._get(this.doc(paramMap));
628
+ }
629
+ async save(entity) {
630
+ const docRef = this.doc({ ...entity, id: entity?.id || this.genId() });
631
+ return this._save(docRef, entity);
632
+ }
633
+ async saveMany(entities) {
634
+ return this.bulkWrite(entities);
635
+ }
636
+ async create(entity) {
637
+ const obj = toObject(entity);
638
+ const id = obj?.id || this.genId();
639
+ return toPromise(() => this.doc({ ...obj, id })).then(doc => this._create(doc, entity));
640
+ }
641
+ async createMany(entities) {
642
+ return Promise.all(entities.map(entity => this.create(entity)));
643
+ }
644
+ async update(entity) {
645
+ return this.doc(entity).update(this.adapter.flattenForUpdate({
646
+ ...this.converter.toFirestore(entity),
647
+ ...this.buildServerTimestampObject(['updatedAt']),
648
+ })).then(() => { });
649
+ }
650
+ async updateMany(data) {
651
+ return this.bulkUpdate(data);
652
+ }
653
+ delete(paramMap) {
654
+ return this.doc(paramMap).delete();
655
+ }
656
+ async deleteMany(params) {
657
+ return params.reduce((batch, param) => {
658
+ const doc = this.doc(param).__ref;
659
+ return batch.delete(doc);
660
+ }, this.adapter.batch()).commit().then(() => params.length);
661
+ }
662
+ async deleteAll() {
663
+ throw new Error('Method not implemented.');
664
+ }
665
+ async bulkWrite(entities, timestamps = []) {
666
+ return entities.reduce((batch, entity) => {
667
+ const doc = this.doc(entity).__ref;
668
+ return batch.set(doc, {
669
+ ...this.converter.toFirestore(entity),
670
+ ...this.buildServerTimestampObject(timestamps),
671
+ });
672
+ }, this.adapter.batch()).commit();
673
+ }
674
+ async bulkCreate(entities) {
675
+ // TODO(nontangent): add maximum 500 record validation
676
+ return entities.reduce((batch, entity) => {
677
+ const doc = this.doc(entity).__ref;
678
+ return batch.create(doc, {
679
+ ...this.converter.toFirestore(entity),
680
+ ...this.buildServerTimestampObject(['createdAt', 'updatedAt']),
681
+ });
682
+ }, this.adapter.batch()).commit();
683
+ }
684
+ async bulkUpdate(entities) {
685
+ // TODO(nontangent): add maximum 500 record validation
686
+ return entities.reduce((batch, entity) => {
687
+ const doc = this.doc(entity).__ref;
688
+ return batch.update(doc, {
689
+ ...this.converter.toFirestore(entity),
690
+ ...this.buildServerTimestampObject(['updatedAt']),
691
+ });
692
+ }, this.adapter.batch()).commit();
693
+ }
694
+ async bulkDelete(paramMaps) {
695
+ // MEMO(@nontangent): 500件以上の削除は未対応
696
+ return paramMaps.reduce((batch, paramMap) => {
697
+ const doc = this.doc(paramMap).__ref;
698
+ return batch.delete(doc);
699
+ }, this.adapter.batch()).commit();
700
+ }
701
+ collection(paramMap) {
702
+ const path = this.pathBuilder.collection(paramMap);
703
+ return this.adapter.collection(path);
704
+ }
705
+ collectionGroup() {
706
+ const path = this.pathBuilder.collectionGroup();
707
+ return this.adapter.collectionGroup(path);
708
+ }
709
+ doc(paramMap) {
710
+ const path = this.pathBuilder.doc(paramMap);
711
+ return this.adapter.doc(path);
712
+ }
713
+ _list(collection) {
714
+ return collection.get().then(({ docs }) => docs.map(doc => {
715
+ if (!doesExist(doc))
716
+ return null;
717
+ return this.converter.fromFirestore(doc);
718
+ }));
719
+ }
720
+ _get(doc) {
721
+ return doc.get().then((doc => {
722
+ if (!doc.data())
723
+ return null;
724
+ return this.converter.fromFirestore(doc);
725
+ }));
726
+ }
727
+ async _save(doc, entity) {
728
+ const exists = await doc.exists();
729
+ if (exists) {
730
+ return this._set(doc, entity).then(doc => [this.converter.fromFirestore(doc), false]);
731
+ }
732
+ else {
733
+ return this._create(doc, entity).then(entity => [entity, true]);
734
+ }
735
+ }
736
+ async _create(doc, entity) {
737
+ const exists = await doc.exists().catch((error) => {
738
+ // MEMO(@nontangent): bunで実行するときだけこのエラーがでるかもしれない。
739
+ if (`${error}`.startsWith('Error: Did not receive document for'))
740
+ return false;
741
+ throw error;
742
+ });
743
+ if (exists)
744
+ throw new Error('Document already exists');
745
+ return doc.set({
746
+ ...this.converter.toFirestore(entity),
747
+ ...this.buildServerTimestampObject(['createdAt', 'updatedAt']),
748
+ }).then(() => doc.get()).then(doc => this.converter.fromFirestore(doc));
749
+ }
750
+ _set(doc, entity, isUpdate = true) {
751
+ const data = this.converter.toFirestore(entity);
752
+ return doc.set({
753
+ ...this.converter.toFirestore(entity),
754
+ ...this.buildServerTimestampObject(isUpdate ? ['updatedAt'] : []),
755
+ }, { merge: isUpdate }).then(() => doc.get());
756
+ }
757
+ buildServerTimestampObject(keys = ['createdAt', 'updatedAt']) {
758
+ return keys.reduce((m, k) => ({ ...m, [k]: this.adapter.FieldValue.serverTimestamp() }), {});
759
+ }
760
+ }
761
+ class FirestoreRepository extends BaseFirestoreRepository {
762
+ constructor() {
763
+ super(...arguments);
764
+ this.adapter = inject(FirestoreAdapter);
765
+ }
766
+ get pathBuilder() {
767
+ return pathBuilderFactory(this.collectionPath);
768
+ }
769
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreRepository, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
770
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreRepository }); }
771
+ }
772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.5", ngImport: i0, type: FirestoreRepository, decorators: [{
773
+ type: Injectable
774
+ }] });
775
+ function getFirestoreRepository(entity, adapter = inject(FirestoreAdapter)) {
776
+ class FirestoreRepository extends BaseFirestoreRepository {
777
+ constructor(converter, collectionPath, pathBuilder, adapter) {
778
+ super();
779
+ this.converter = converter;
780
+ this.collectionPath = collectionPath;
781
+ this.pathBuilder = pathBuilder;
782
+ this.adapter = adapter;
783
+ }
784
+ }
785
+ return new FirestoreRepository(getConverter(entity, adapter), getCollection(entity), pathBuilderFactory(getCollection(entity)), adapter);
786
+ }
787
+ function injectFirestoreRepository(entity) {
788
+ return getFirestoreRepository(entity, inject(FirestoreAdapter));
789
+ }
790
+
791
+ /**
792
+ * Generated bundle index. Do not edit.
793
+ */
794
+
795
+ export { BaseFirestoreQuery, BaseFirestoreRepository, CachedFirestoreQuery, FIRESTORE_ANNOTATIONS, Firestore, FirestoreAdapter, FirestoreDAO, FirestorePathBuilder, FirestoreQuery$1 as FirestoreQuery, FirestoreQuery as FirestoreQueryV2, FirestoreRepository, createClassDecorator, createDecorator, createPropsDecorator, getAnnotations, getCollection, getConverter, getFirestoreAnnotations, getFirestoreQuery, getFirestoreRepository, injectConverter, injectFirestoreQuery, injectFirestoreRepository, parsePath, pathBuilderFactory, provideFirestoreAdapter, resolvePaths };
796
+ //# sourceMappingURL=nx-ddd-firestore.mjs.map