@dereekb/firebase 13.0.6 → 13.1.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.
@@ -1,6 +1,7 @@
1
- import { TargetModelParams, OnCallCreateModelResult, FirestoreModelKey } from '../../common';
2
- import { type ModelFirebaseCrudFunction, type FirebaseFunctionTypeConfigMap, type ModelFirebaseCrudFunctionConfigMap, type ModelFirebaseFunctionMap, ModelFirebaseCreateFunction } from '../../client';
3
- import { StorageFileSignedDownloadUrl, StorageFileTypes } from './storagefile';
1
+ import { type Type } from 'arktype';
2
+ import { type TargetModelParams, type OnCallCreateModelResult, type FirestoreModelKey } from '../../common';
3
+ import { type ModelFirebaseCrudFunction, type FirebaseFunctionTypeConfigMap, type ModelFirebaseCrudFunctionConfigMap, type ModelFirebaseFunctionMap, type ModelFirebaseCreateFunction } from '../../client';
4
+ import { type StorageFileSignedDownloadUrl, type StorageFileTypes } from './storagefile';
4
5
  import { type StorageBucketId, type StoragePath, type StorageSlashPath } from '../../common/storage';
5
6
  import { type ContentDispositionString, type ContentTypeMimeType, type Maybe, type Milliseconds, type UnixDateTimeSecondsNumber } from '@dereekb/util';
6
7
  import { type StorageFileId } from './storagefile.id';
@@ -8,25 +9,18 @@ import { type SendNotificationResult } from '../notification/notification.api';
8
9
  /**
9
10
  * Used for directly create a new StorageFile.
10
11
  */
11
- export declare class CreateStorageFileParams {
12
+ export interface CreateStorageFileParams {
12
13
  }
14
+ export declare const createStorageFileParamsType: Type<CreateStorageFileParams>;
13
15
  /**
14
16
  * Initializes all StorageFiles in the uploads folder.
15
17
  */
16
- export declare class InitializeAllStorageFilesFromUploadsParams {
17
- /**
18
- * The maximum number of files to initialize at once.
19
- */
20
- maxFilesToInitialize?: Maybe<number>;
21
- /**
22
- * The specific folder under the uploads folder to search for files and initialize
23
- */
24
- folderPath?: Maybe<StorageSlashPath>;
25
- /**
26
- * Overrides the default uploads folder path.
27
- */
28
- overrideUploadsFolderPath?: Maybe<StorageSlashPath>;
18
+ export interface InitializeAllStorageFilesFromUploadsParams {
19
+ readonly maxFilesToInitialize?: Maybe<number>;
20
+ readonly folderPath?: Maybe<StorageSlashPath>;
21
+ readonly overrideUploadsFolderPath?: Maybe<StorageSlashPath>;
29
22
  }
23
+ export declare const initializeAllStorageFilesFromUploadsParamsType: Type<InitializeAllStorageFilesFromUploadsParams>;
30
24
  export interface InitializeAllStorageFilesFromUploadsResult extends OnCallCreateModelResult {
31
25
  readonly filesVisited: number;
32
26
  readonly initializationsSuccessCount: number;
@@ -35,146 +29,68 @@ export interface InitializeAllStorageFilesFromUploadsResult extends OnCallCreate
35
29
  /**
36
30
  * Initializes a StorageFile from the document at the given path.
37
31
  */
38
- export declare class InitializeStorageFileFromUploadParams implements Pick<StoragePath, 'pathString'> {
39
- /**
40
- * Specific bucketId to use.
41
- *
42
- * If not defined, the default bucket will be used.
43
- */
44
- bucketId?: Maybe<StorageBucketId>;
45
- pathString: StorageSlashPath;
46
- /**
47
- * Whether or not to attempt to expedite the processing of the created StorageFile, if it is queued for processing.
48
- *
49
- * If it cannot be processed, this argument will have no effect.
50
- */
51
- expediteProcessing?: boolean;
52
- }
53
- export declare class ProcessStorageFileParams extends TargetModelParams {
54
- /**
55
- * If set, will start/run the processing immediately instead of waiting for the next scheduled run.
56
- */
57
- runImmediately?: Maybe<boolean>;
58
- /**
59
- * If set, will check and retry processing if the StorageFile is in a failed processing state.
60
- */
61
- checkRetryProcessing?: Maybe<boolean>;
62
- /**
63
- * Used with checkRetryProcessing.
64
- *
65
- * If set, will forcibly create a new processing task even if the existing processing task appears to be ok, or if processing was already marked complete.
66
- */
67
- forceRestartProcessing?: Maybe<boolean>;
68
- /**
69
- * If set, will start the processing again if the StorageFile is in a successful processing state.
70
- */
71
- processAgainIfSuccessful?: Maybe<boolean>;
72
- }
32
+ export interface InitializeStorageFileFromUploadParams extends Pick<StoragePath, 'pathString'> {
33
+ readonly bucketId?: Maybe<StorageBucketId>;
34
+ readonly pathString: StorageSlashPath;
35
+ readonly expediteProcessing?: boolean;
36
+ }
37
+ export declare const initializeStorageFileFromUploadParamsType: Type<InitializeStorageFileFromUploadParams>;
38
+ export interface ProcessStorageFileParams extends TargetModelParams {
39
+ readonly runImmediately?: Maybe<boolean>;
40
+ readonly checkRetryProcessing?: Maybe<boolean>;
41
+ readonly forceRestartProcessing?: Maybe<boolean>;
42
+ readonly processAgainIfSuccessful?: Maybe<boolean>;
43
+ }
44
+ export declare const processStorageFileParamsType: Type<ProcessStorageFileParams>;
73
45
  export interface ProcessStorageFileResult {
74
- /**
75
- * Whether or not the StorageFile was run immediately.
76
- */
77
46
  readonly runImmediately: boolean;
78
- /**
79
- * The expedite result, if runImmediately returned true.
80
- */
81
47
  readonly expediteResult: Maybe<SendNotificationResult>;
82
48
  }
83
49
  /**
84
50
  * Processes all StorageFiles that are queued for processing.
85
51
  */
86
- export declare class ProcessAllQueuedStorageFilesParams {
52
+ export interface ProcessAllQueuedStorageFilesParams {
87
53
  }
54
+ export declare const processAllQueuedStorageFilesParamsType: Type<ProcessAllQueuedStorageFilesParams>;
88
55
  export interface ProcessAllQueuedStorageFilesResult {
89
- /**
90
- * The total number of StorageFiles visited.
91
- */
92
56
  readonly storageFilesVisited: number;
93
- /**
94
- * The total number of StorageFiles that started processing.
95
- */
96
57
  readonly storageFilesProcessStarted: number;
97
- /**
98
- * The total number of StorageFiles that failed to start processing.
99
- */
100
58
  readonly storageFilesFailedStarting: number;
101
59
  }
102
- export declare class UpdateStorageFileParams extends TargetModelParams {
103
- /**
104
- * Sets the delete at time for the given StorageFileDocument, and queues the file for deletion.
105
- */
106
- sdat?: Maybe<Date>;
60
+ export interface UpdateStorageFileParams extends TargetModelParams {
61
+ readonly sdat?: Maybe<Date>;
107
62
  }
108
- export declare class DeleteStorageFileParams extends TargetModelParams {
109
- /**
110
- * If true, will force the deletion of the StorageFile even if it is not queued for deletion.
111
- */
112
- force?: Maybe<boolean>;
63
+ export declare const updateStorageFileParamsType: Type<UpdateStorageFileParams>;
64
+ export interface DeleteStorageFileParams extends TargetModelParams {
65
+ readonly force?: Maybe<boolean>;
113
66
  }
67
+ export declare const deleteStorageFileParamsType: Type<DeleteStorageFileParams>;
114
68
  /**
115
69
  * Processes all StorageFiles that are queued for processing.
116
70
  */
117
- export declare class DeleteAllQueuedStorageFilesParams {
71
+ export interface DeleteAllQueuedStorageFilesParams {
118
72
  }
73
+ export declare const deleteAllQueuedStorageFilesParamsType: Type<DeleteAllQueuedStorageFilesParams>;
119
74
  export interface DeleteAllQueuedStorageFilesResult {
120
- /**
121
- * The total number of StorageFiles visited.
122
- */
123
75
  readonly storageFilesVisited: number;
124
- /**
125
- * The total number of StorageFiles that were deleted.
126
- */
127
76
  readonly storageFilesDeleted: number;
128
- /**
129
- * The total number of StorageFiles that failed to delete.
130
- */
131
77
  readonly storageFilesFailedDeleting: number;
132
78
  }
133
- export declare class DownloadStorageFileParams extends TargetModelParams {
134
- /**
135
- * Date to expire the download URL.
136
- */
137
- expiresAt?: Maybe<Date>;
138
- /**
139
- * Duration in milliseconds to expire the download URL from now.
140
- */
141
- expiresIn?: Maybe<Milliseconds>;
142
- /**
143
- * The content disposition for the response to use.
144
- */
145
- responseDisposition?: Maybe<ContentDispositionString>;
146
- /**
147
- * The content type for the response to use.
148
- *
149
- * Only available to admins.
150
- */
151
- responseContentType?: Maybe<ContentTypeMimeType>;
152
- /**
153
- * Whether or not an admin is creating the link.
154
- *
155
- * Allows a longer expiration.
156
- */
157
- asAdmin?: Maybe<boolean>;
79
+ export interface DownloadStorageFileParams extends TargetModelParams {
80
+ readonly expiresAt?: Maybe<Date>;
81
+ readonly expiresIn?: Maybe<Milliseconds>;
82
+ readonly responseDisposition?: Maybe<ContentDispositionString>;
83
+ readonly responseContentType?: Maybe<ContentTypeMimeType>;
84
+ readonly asAdmin?: Maybe<boolean>;
158
85
  }
86
+ export declare const downloadStorageFileParamsType: Type<DownloadStorageFileParams>;
159
87
  /**
160
88
  * Result of downloading a StorageFile.
161
89
  */
162
90
  export interface DownloadStorageFileResult {
163
- /**
164
- * The download URL.
165
- */
166
91
  readonly url: StorageFileSignedDownloadUrl;
167
- /**
168
- * The name of the StorageFile, if available.
169
- */
170
92
  readonly fileName?: Maybe<string>;
171
- /**
172
- * The mime type of the StorageFile, if available.
173
- */
174
93
  readonly mimeType?: Maybe<ContentTypeMimeType>;
175
- /**
176
- * Expiration time as a UnixDateTimeSecondsNumber value.
177
- */
178
94
  readonly expiresAt?: Maybe<UnixDateTimeSecondsNumber>;
179
95
  }
180
96
  /**
@@ -184,93 +100,60 @@ export interface DownloadStorageFileResult {
184
100
  *
185
101
  * The preferred way is to create a StorageFileGroup through a StorageFile.
186
102
  */
187
- export declare class CreateStorageFileGroupParams {
188
- /**
189
- * ModelKey to use for creating the StorageFileGroup.
190
- */
191
- model?: Maybe<FirestoreModelKey>;
192
- /**
193
- * StorageFileId to use for creating the StorageFileGroup.
194
- */
195
- storageFileId?: Maybe<StorageFileId>;
103
+ export interface CreateStorageFileGroupParams {
104
+ readonly model?: Maybe<FirestoreModelKey>;
105
+ readonly storageFileId?: Maybe<StorageFileId>;
196
106
  }
197
- export declare class SyncStorageFileWithGroupsParams extends TargetModelParams {
198
- /**
199
- * If true, will force syncing even if the StorageFile is not flagged for a resync.
200
- */
201
- force?: boolean;
107
+ export declare const createStorageFileGroupParamsType: Type<CreateStorageFileGroupParams>;
108
+ export interface SyncStorageFileWithGroupsParams extends TargetModelParams {
109
+ readonly force?: boolean;
202
110
  }
111
+ export declare const syncStorageFileWithGroupsParamsType: Type<SyncStorageFileWithGroupsParams>;
203
112
  export interface SyncStorageFileWithGroupsResult {
204
- /**
205
- * The number of StorageFileGroups that were created.
206
- */
207
113
  readonly storageFilesGroupsCreated: number;
208
- /**
209
- * The number of StorageFileGroups that were updated.
210
- */
211
114
  readonly storageFilesGroupsUpdated: number;
212
115
  }
213
- export declare class SyncAllFlaggedStorageFilesWithGroupsParams {
116
+ export interface SyncAllFlaggedStorageFilesWithGroupsParams {
214
117
  }
118
+ export declare const syncAllFlaggedStorageFilesWithGroupsParamsType: Type<SyncAllFlaggedStorageFilesWithGroupsParams>;
215
119
  export interface SyncAllFlaggedStorageFilesWithGroupsResult {
216
- /**
217
- * The total number of StorageFiles that were synced.
218
- */
219
120
  readonly storageFilesSynced: number;
220
- /**
221
- * The total number of StorageFileGroups that were created.
222
- */
223
121
  readonly storageFilesGroupsCreated: number;
224
- /**
225
- * The total number of StorageFileGroups that were updated.
226
- */
227
122
  readonly storageFilesGroupsUpdated: number;
228
123
  }
229
- export declare class UpdateStorageFileGroupParams extends TargetModelParams {
230
- /**
231
- * Entries to update, if selected.
232
- */
233
- entries?: Maybe<UpdateStorageFileGroupEntryParams[]>;
124
+ export interface UpdateStorageFileGroupEntryParams {
125
+ readonly s: StorageFileId;
126
+ readonly n?: Maybe<string>;
234
127
  }
235
- export declare class UpdateStorageFileGroupEntryParams {
236
- s: StorageFileId;
237
- n?: Maybe<string>;
128
+ export declare const updateStorageFileGroupEntryParamsType: Type<UpdateStorageFileGroupEntryParams>;
129
+ export interface UpdateStorageFileGroupParams extends TargetModelParams {
130
+ readonly entries?: Maybe<UpdateStorageFileGroupEntryParams[]>;
238
131
  }
239
- export declare class RegenerateStorageFileGroupContentParams extends TargetModelParams {
240
- /**
241
- * If true, will force syncing even if the StorageFile is not flagged for a resync.
242
- */
243
- force?: boolean;
132
+ export declare const updateStorageFileGroupParamsType: Type<UpdateStorageFileGroupParams>;
133
+ export interface RegenerateStorageFileGroupContentParams extends TargetModelParams {
134
+ readonly force?: boolean;
244
135
  }
136
+ export declare const regenerateStorageFileGroupContentParamsType: Type<RegenerateStorageFileGroupContentParams>;
245
137
  export interface RegenerateStorageFileGroupContentResult {
246
- /**
247
- * The total number of "content" StorageFiles that were flagged for processing again.
248
- */
249
138
  readonly contentStorageFilesFlaggedForProcessing: number;
250
139
  }
251
- export declare class RegenerateAllFlaggedStorageFileGroupsContentParams {
140
+ export interface RegenerateAllFlaggedStorageFileGroupsContentParams {
252
141
  }
142
+ export declare const regenerateAllFlaggedStorageFileGroupsContentParamsType: Type<RegenerateAllFlaggedStorageFileGroupsContentParams>;
253
143
  export interface RegenerateAllFlaggedStorageFileGroupsContentResult {
254
- /**
255
- * The number of StorageFileGroups that were updated.
256
- */
257
144
  readonly storageFileGroupsUpdated: number;
258
- /**
259
- * The number of "content" StorageFiles that were flagged for processing again.
260
- */
261
145
  readonly contentStorageFilesFlaggedForProcessing: number;
262
146
  }
263
147
  /**
264
148
  * Used for initializing an uninitialized model like NotificationBox or NotificationSummary.
265
149
  */
266
- export declare class InitializeStorageFileModelParams extends TargetModelParams {
267
- /**
268
- * Whether or not to throw an error if the notification has already been sent or is being sent.
269
- */
270
- throwErrorIfAlreadyInitialized?: boolean;
150
+ export interface InitializeStorageFileModelParams extends TargetModelParams {
151
+ readonly throwErrorIfAlreadyInitialized?: boolean;
271
152
  }
272
- export declare class InitializeAllApplicableStorageFileGroupsParams {
153
+ export declare const initializeStorageFileModelParamsType: Type<InitializeStorageFileModelParams>;
154
+ export interface InitializeAllApplicableStorageFileGroupsParams {
273
155
  }
156
+ export declare const initializeAllApplicableStorageFileGroupsParamsType: Type<InitializeAllApplicableStorageFileGroupsParams>;
274
157
  export interface InitializeAllApplicableStorageFileGroupsResult {
275
158
  readonly storageFileGroupsVisited: number;
276
159
  readonly storageFileGroupsSucceeded: number;
package/test/index.cjs.js CHANGED
@@ -3485,6 +3485,87 @@ function describeFirestoreDocumentUtilityTests(f) {
3485
3485
  });
3486
3486
  });
3487
3487
  });
3488
+ // MARK: limitedFirestoreDocumentAccessorSnapshotCache
3489
+ describe('limitedFirestoreDocumentAccessorSnapshotCache()', () => {
3490
+ it('should expose the underlying accessor', () => {
3491
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3492
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3493
+ expect(cache.accessor).toBe(accessor);
3494
+ });
3495
+ describe('getDocumentSnapshotDataPairForKey()', () => {
3496
+ it('should return a snapshot data pair for an existing document', async () => {
3497
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3498
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3499
+ const pair = await cache.getDocumentSnapshotDataPairForKey(items[0].key);
3500
+ expect(pair.document).toBeDefined();
3501
+ expect(pair.document.key).toBe(items[0].key);
3502
+ expect(pair.snapshot).toBeDefined();
3503
+ expect(pair.data).toBeDefined();
3504
+ expect(pair.data.id).toBe(items[0].id);
3505
+ expect(pair.data.key).toBe(items[0].key);
3506
+ });
3507
+ it('should return the same promise for the same key', async () => {
3508
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3509
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3510
+ const promise1 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3511
+ const promise2 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3512
+ expect(promise1).toBe(promise2);
3513
+ });
3514
+ it('should return undefined data for a non-existent document', async () => {
3515
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3516
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3517
+ const newDoc = firebase.newDocuments(accessor, 1)[0];
3518
+ const pair = await cache.getDocumentSnapshotDataPairForKey(newDoc.key);
3519
+ expect(pair.document).toBeDefined();
3520
+ expect(pair.data).toBeUndefined();
3521
+ });
3522
+ });
3523
+ describe('getDocumentSnapshotDataPairsForKeys()', () => {
3524
+ it('should return pairs for all keys in order', async () => {
3525
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3526
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3527
+ const keys = items.map((x) => x.key);
3528
+ const pairs = await cache.getDocumentSnapshotDataPairsForKeys(keys);
3529
+ expect(pairs.length).toBe(testDocumentCount);
3530
+ pairs.forEach((pair, i) => {
3531
+ expect(pair.document.key).toBe(items[i].key);
3532
+ expect(pair.data).toBeDefined();
3533
+ expect(pair.data.id).toBe(items[i].id);
3534
+ });
3535
+ });
3536
+ it('should use the cache for duplicate keys', async () => {
3537
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3538
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3539
+ const key = items[0].key;
3540
+ const [pair1] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3541
+ const [pair2] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3542
+ expect(pair1).toBe(pair2);
3543
+ });
3544
+ });
3545
+ describe('getDocumentSnapshotDataPairsWithDataForKeys()', () => {
3546
+ it('should return pairs for all existing documents', async () => {
3547
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3548
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3549
+ const keys = items.map((x) => x.key);
3550
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3551
+ expect(pairs.length).toBe(testDocumentCount);
3552
+ pairs.forEach((pair) => {
3553
+ expect(pair.data).toBeDefined();
3554
+ expect(pair.data.id).toBeDefined();
3555
+ expect(pair.data.key).toBeDefined();
3556
+ });
3557
+ });
3558
+ it('should filter out non-existent documents', async () => {
3559
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3560
+ const cache = firebase.limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3561
+ const newDoc = firebase.newDocuments(accessor, 1)[0];
3562
+ const keys = [...items.map((x) => x.key), newDoc.key];
3563
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3564
+ expect(pairs.length).toBe(testDocumentCount);
3565
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3566
+ });
3567
+ });
3568
+ });
3488
3569
  });
3489
3570
  describe('document.rxjs.ts', () => {
3490
3571
  // MARK: latestSnapshotsFromDocuments
@@ -3532,10 +3613,10 @@ function describeFirestoreDocumentUtilityTests(f) {
3532
3613
  });
3533
3614
  }));
3534
3615
  });
3535
- // MARK: latestDataFromDocuments
3536
- describe('latestDataFromDocuments()', () => {
3616
+ // MARK: streamDocumentSnapshotsData
3617
+ describe('streamDocumentSnapshotsData()', () => {
3537
3618
  it('should emit data with id/key for all documents', test.callbackTest((done) => {
3538
- sub.subscription = firebase.latestDataFromDocuments(items)
3619
+ sub.subscription = firebase.streamDocumentSnapshotsData(items)
3539
3620
  .pipe(rxjs$1.first())
3540
3621
  .subscribe((data) => {
3541
3622
  expect(data.length).toBe(testDocumentCount);
@@ -3548,7 +3629,7 @@ function describeFirestoreDocumentUtilityTests(f) {
3548
3629
  });
3549
3630
  }));
3550
3631
  it('should emit an empty array for empty input', test.callbackTest((done) => {
3551
- sub.subscription = firebase.latestDataFromDocuments([])
3632
+ sub.subscription = firebase.streamDocumentSnapshotsData([])
3552
3633
  .pipe(rxjs$1.first())
3553
3634
  .subscribe((data) => {
3554
3635
  expect(data.length).toBe(0);
package/test/index.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { performAsyncTasks, cachedGetter, bitwiseObjectDencoder, modelFieldConversions, isEvenNumber, arrayFactory, mapGetter, randomNumberFactory, randomFromArrayFactory, idBatchFactory, unique, waitForMs, arrayContainsDuplicateValue, useCallback, readableStreamToBuffer, SLASH_PATH_SEPARATOR } from '@dereekb/util';
2
2
  import { AbstractTestContextFixture, testContextBuilder, instanceWrapTestContextFactory, AbstractWrappedFixtureWithInstance, itShouldFail, expectFail, callbackTest } from '@dereekb/util/test';
3
3
  import { initializeTestEnvironment } from '@firebase/rules-unit-testing';
4
- import { firebaseStorageClientDrivers, firebaseFirestoreClientDrivers, firestoreContextFactory, firebaseStorageContextFactory, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, AbstractFirestoreDocumentWithParent, AbstractFirestoreDocument, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, limit, orderBy, limitToLast, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, streamFromOnSnapshot, latestSnapshotsFromDocuments, newDocuments, getDocumentSnapshots, getDocumentSnapshotPair, getDocumentSnapshotDataPair, getDocumentSnapshotDataPairs, getDocumentSnapshotDataPairsWithData, getDocumentSnapshotDataTuples, getDocumentSnapshotData, getDocumentSnapshotsData, getDataFromDocumentSnapshots, loadDocumentsForSnapshots, loadDocumentsForDocumentReferences, loadDocumentsForDocumentReferencesFromValues, loadDocumentsForKeys, loadDocumentsForKeysFromValues, loadDocumentsForIds, loadDocumentsForIdsFromValues, firestoreDocumentLoader, firestoreDocumentSnapshotPairsLoader, documentData, documentDataFunction, documentDataWithIdAndKey, setIdAndKeyFromSnapshotOnDocumentData, setIdAndKeyFromKeyIdRefOnDocumentData, firestoreModelIdFromDocument, firestoreModelIdsFromDocuments, firestoreModelKeyFromDocument, firestoreModelKeysFromDocuments, documentReferenceFromDocument, documentReferencesFromDocuments, mapLatestSnapshotsFromDocuments, latestDataFromDocuments, dataFromDocumentSnapshots, streamDocumentSnapshotDataPairs, streamDocumentSnapshotDataPairsWithData, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
4
+ import { firebaseStorageClientDrivers, firebaseFirestoreClientDrivers, firestoreContextFactory, firebaseStorageContextFactory, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, AbstractFirestoreDocumentWithParent, AbstractFirestoreDocument, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, limit, orderBy, limitToLast, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, streamFromOnSnapshot, latestSnapshotsFromDocuments, newDocuments, getDocumentSnapshots, getDocumentSnapshotPair, getDocumentSnapshotDataPair, getDocumentSnapshotDataPairs, getDocumentSnapshotDataPairsWithData, getDocumentSnapshotDataTuples, getDocumentSnapshotData, getDocumentSnapshotsData, getDataFromDocumentSnapshots, loadDocumentsForSnapshots, loadDocumentsForDocumentReferences, loadDocumentsForDocumentReferencesFromValues, loadDocumentsForKeys, loadDocumentsForKeysFromValues, loadDocumentsForIds, loadDocumentsForIdsFromValues, firestoreDocumentLoader, firestoreDocumentSnapshotPairsLoader, documentData, documentDataFunction, documentDataWithIdAndKey, setIdAndKeyFromSnapshotOnDocumentData, setIdAndKeyFromKeyIdRefOnDocumentData, firestoreModelIdFromDocument, firestoreModelIdsFromDocuments, firestoreModelKeyFromDocument, firestoreModelKeysFromDocuments, documentReferenceFromDocument, documentReferencesFromDocuments, limitedFirestoreDocumentAccessorSnapshotCache, mapLatestSnapshotsFromDocuments, streamDocumentSnapshotsData, dataFromDocumentSnapshots, streamDocumentSnapshotDataPairs, streamDocumentSnapshotDataPairsWithData, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
5
5
  import { setLogLevel } from 'firebase/firestore';
6
6
  import { firstValueFrom, filter, skip, from, first, map, switchMap } from 'rxjs';
7
7
  import { SubscriptionObject, iteratorNextPageUntilPage, flattenAccumulatorResultItemArray, accumulatorCurrentPageListLoadingState, isLoadingStateFinishedLoading, accumulatorFlattenPageListLoadingState } from '@dereekb/rxjs';
@@ -3483,6 +3483,87 @@ function describeFirestoreDocumentUtilityTests(f) {
3483
3483
  });
3484
3484
  });
3485
3485
  });
3486
+ // MARK: limitedFirestoreDocumentAccessorSnapshotCache
3487
+ describe('limitedFirestoreDocumentAccessorSnapshotCache()', () => {
3488
+ it('should expose the underlying accessor', () => {
3489
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3490
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3491
+ expect(cache.accessor).toBe(accessor);
3492
+ });
3493
+ describe('getDocumentSnapshotDataPairForKey()', () => {
3494
+ it('should return a snapshot data pair for an existing document', async () => {
3495
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3496
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3497
+ const pair = await cache.getDocumentSnapshotDataPairForKey(items[0].key);
3498
+ expect(pair.document).toBeDefined();
3499
+ expect(pair.document.key).toBe(items[0].key);
3500
+ expect(pair.snapshot).toBeDefined();
3501
+ expect(pair.data).toBeDefined();
3502
+ expect(pair.data.id).toBe(items[0].id);
3503
+ expect(pair.data.key).toBe(items[0].key);
3504
+ });
3505
+ it('should return the same promise for the same key', async () => {
3506
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3507
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3508
+ const promise1 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3509
+ const promise2 = cache.getDocumentSnapshotDataPairForKey(items[0].key);
3510
+ expect(promise1).toBe(promise2);
3511
+ });
3512
+ it('should return undefined data for a non-existent document', async () => {
3513
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3514
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3515
+ const newDoc = newDocuments(accessor, 1)[0];
3516
+ const pair = await cache.getDocumentSnapshotDataPairForKey(newDoc.key);
3517
+ expect(pair.document).toBeDefined();
3518
+ expect(pair.data).toBeUndefined();
3519
+ });
3520
+ });
3521
+ describe('getDocumentSnapshotDataPairsForKeys()', () => {
3522
+ it('should return pairs for all keys in order', async () => {
3523
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3524
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3525
+ const keys = items.map((x) => x.key);
3526
+ const pairs = await cache.getDocumentSnapshotDataPairsForKeys(keys);
3527
+ expect(pairs.length).toBe(testDocumentCount);
3528
+ pairs.forEach((pair, i) => {
3529
+ expect(pair.document.key).toBe(items[i].key);
3530
+ expect(pair.data).toBeDefined();
3531
+ expect(pair.data.id).toBe(items[i].id);
3532
+ });
3533
+ });
3534
+ it('should use the cache for duplicate keys', async () => {
3535
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3536
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3537
+ const key = items[0].key;
3538
+ const [pair1] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3539
+ const [pair2] = await cache.getDocumentSnapshotDataPairsForKeys([key]);
3540
+ expect(pair1).toBe(pair2);
3541
+ });
3542
+ });
3543
+ describe('getDocumentSnapshotDataPairsWithDataForKeys()', () => {
3544
+ it('should return pairs for all existing documents', async () => {
3545
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3546
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3547
+ const keys = items.map((x) => x.key);
3548
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3549
+ expect(pairs.length).toBe(testDocumentCount);
3550
+ pairs.forEach((pair) => {
3551
+ expect(pair.data).toBeDefined();
3552
+ expect(pair.data.id).toBeDefined();
3553
+ expect(pair.data.key).toBeDefined();
3554
+ });
3555
+ });
3556
+ it('should filter out non-existent documents', async () => {
3557
+ const accessor = f.instance.mockItemCollection.documentAccessor();
3558
+ const cache = limitedFirestoreDocumentAccessorSnapshotCache(accessor);
3559
+ const newDoc = newDocuments(accessor, 1)[0];
3560
+ const keys = [...items.map((x) => x.key), newDoc.key];
3561
+ const pairs = await cache.getDocumentSnapshotDataPairsWithDataForKeys(keys);
3562
+ expect(pairs.length).toBe(testDocumentCount);
3563
+ expect(pairs.every((p) => p.data != null)).toBe(true);
3564
+ });
3565
+ });
3566
+ });
3486
3567
  });
3487
3568
  describe('document.rxjs.ts', () => {
3488
3569
  // MARK: latestSnapshotsFromDocuments
@@ -3530,10 +3611,10 @@ function describeFirestoreDocumentUtilityTests(f) {
3530
3611
  });
3531
3612
  }));
3532
3613
  });
3533
- // MARK: latestDataFromDocuments
3534
- describe('latestDataFromDocuments()', () => {
3614
+ // MARK: streamDocumentSnapshotsData
3615
+ describe('streamDocumentSnapshotsData()', () => {
3535
3616
  it('should emit data with id/key for all documents', callbackTest((done) => {
3536
- sub.subscription = latestDataFromDocuments(items)
3617
+ sub.subscription = streamDocumentSnapshotsData(items)
3537
3618
  .pipe(first())
3538
3619
  .subscribe((data) => {
3539
3620
  expect(data.length).toBe(testDocumentCount);
@@ -3546,7 +3627,7 @@ function describeFirestoreDocumentUtilityTests(f) {
3546
3627
  });
3547
3628
  }));
3548
3629
  it('should emit an empty array for empty input', callbackTest((done) => {
3549
- sub.subscription = latestDataFromDocuments([])
3630
+ sub.subscription = streamDocumentSnapshotsData([])
3550
3631
  .pipe(first())
3551
3632
  .subscribe((data) => {
3552
3633
  expect(data.length).toBe(0);
package/test/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/firebase/test",
3
- "version": "13.0.6",
3
+ "version": "13.1.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.0.6",
6
- "@dereekb/firebase": "13.0.6",
7
- "@dereekb/model": "13.0.6",
8
- "@dereekb/rxjs": "13.0.6",
9
- "@dereekb/util": "13.0.6",
5
+ "@dereekb/date": "13.1.0",
6
+ "@dereekb/firebase": "13.1.0",
7
+ "@dereekb/model": "13.1.0",
8
+ "@dereekb/rxjs": "13.1.0",
9
+ "@dereekb/util": "13.1.0",
10
10
  "@firebase/rules-unit-testing": "5.0.0",
11
11
  "date-fns": "^4.0.0",
12
12
  "firebase": "^12.0.0",