@dereekb/firebase 13.6.6 → 13.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +769 -53
- package/index.esm.js +759 -55
- package/package.json +5 -5
- package/src/lib/client/firestore/firestore.d.ts +6 -1
- package/src/lib/common/firestore/accessor/document.d.ts +43 -9
- package/src/lib/common/firestore/cache/cache.d.ts +365 -0
- package/src/lib/common/firestore/cache/cache.memory.d.ts +187 -0
- package/src/lib/common/firestore/cache/index.d.ts +2 -0
- package/src/lib/common/firestore/collection/collection.d.ts +3 -2
- package/src/lib/common/firestore/collection/collection.group.d.ts +2 -1
- package/src/lib/common/firestore/context.d.ts +23 -5
- package/src/lib/common/firestore/index.d.ts +1 -0
- package/test/index.cjs.js +145 -77
- package/test/index.esm.js +144 -79
- package/test/package.json +6 -6
- package/test/src/lib/client/firebase.authorized.d.ts +28 -2
- package/test/src/lib/client/firebase.d.ts +9 -1
- package/test/src/lib/client/firestore.mock.item.fixture.authorized.d.ts +3 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { type Observable } from 'rxjs';
|
|
2
|
+
import { type Maybe, type Milliseconds } from '@dereekb/util';
|
|
3
|
+
import { type FirestoreCollectionType } from '../collection/collection';
|
|
4
|
+
import { type FirestoreModelKey } from '../collection/collection';
|
|
5
|
+
import { type FirestoreContextCacheFactory, type FirestoreCacheEntry, type FirestoreCacheEvent, type FirestoreContextCache } from './cache';
|
|
6
|
+
/**
|
|
7
|
+
* Default TTL used by the in-memory cache when no TTL is specified.
|
|
8
|
+
*
|
|
9
|
+
* Set to 5 minutes.
|
|
10
|
+
*/
|
|
11
|
+
export declare const IN_MEMORY_CACHE_DEFAULT_TTL: Milliseconds;
|
|
12
|
+
/**
|
|
13
|
+
* Delegate that handles actual cache storage for a single collection.
|
|
14
|
+
*
|
|
15
|
+
* Implementations provide the storage mechanism (e.g. in-memory Map, no storage for logging).
|
|
16
|
+
* The shared {@link makeFirestoreCollectionCache} handles TTL checking, event emission,
|
|
17
|
+
* and enabled/disabled state around the delegate.
|
|
18
|
+
*
|
|
19
|
+
* @template T - The document data type
|
|
20
|
+
*/
|
|
21
|
+
export interface FirestoreCollectionCacheDelegate<T> {
|
|
22
|
+
/**
|
|
23
|
+
* Gets a raw cached entry by key, without TTL or enabled checks.
|
|
24
|
+
*/
|
|
25
|
+
get(key: FirestoreModelKey): Maybe<FirestoreCacheEntry<T>>;
|
|
26
|
+
/**
|
|
27
|
+
* Stores a cache entry by key.
|
|
28
|
+
*/
|
|
29
|
+
set(key: FirestoreModelKey, entry: FirestoreCacheEntry<T>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Deletes a cache entry by key.
|
|
32
|
+
*/
|
|
33
|
+
delete(key: FirestoreModelKey): void;
|
|
34
|
+
/**
|
|
35
|
+
* Clears all entries.
|
|
36
|
+
*/
|
|
37
|
+
clear(): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates an in-memory {@link FirestoreCollectionCacheDelegate} backed by a Map.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* const delegate = inMemoryFirestoreCollectionCacheDelegate<UserData>();
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function inMemoryFirestoreCollectionCacheDelegate<T>(): FirestoreCollectionCacheDelegate<T>;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a no-storage {@link FirestoreCollectionCacheDelegate} that discards all data.
|
|
50
|
+
*
|
|
51
|
+
* Used by {@link readLoggingFirestoreContextCache} where only event emission matters.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const delegate = noopFirestoreCollectionCacheDelegate<UserData>();
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function noopFirestoreCollectionCacheDelegate<T>(): FirestoreCollectionCacheDelegate<T>;
|
|
59
|
+
/**
|
|
60
|
+
* Configuration for {@link makeFirestoreContextCache}.
|
|
61
|
+
*/
|
|
62
|
+
export interface MakeFirestoreContextCacheConfig {
|
|
63
|
+
/**
|
|
64
|
+
* Default TTL applied to all collections unless overridden per-collection.
|
|
65
|
+
*/
|
|
66
|
+
readonly defaultTtl: Milliseconds;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a delegate for a new collection cache.
|
|
69
|
+
*
|
|
70
|
+
* Called once per collection type when the cache is first requested.
|
|
71
|
+
*/
|
|
72
|
+
readonly createDelegate: <T>(collectionType: FirestoreCollectionType) => FirestoreCollectionCacheDelegate<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Optional function that transforms the raw events observable before it is exposed as `events$`.
|
|
75
|
+
*
|
|
76
|
+
* Can be used to filter, map, or augment events. For example, the read-logging cache
|
|
77
|
+
* uses this to filter out 'miss' events since they are expected and not meaningful.
|
|
78
|
+
*
|
|
79
|
+
* When not provided, the raw events observable is used directly.
|
|
80
|
+
*/
|
|
81
|
+
readonly mapEvents$?: (events$: Observable<FirestoreCacheEvent>) => Observable<FirestoreCacheEvent>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates a {@link FirestoreContextCache} with shared event handling, enable/disable controls,
|
|
85
|
+
* and per-type management. The actual cache storage for each collection is handled by
|
|
86
|
+
* delegates created via {@link MakeFirestoreContextCacheConfig.createDelegate}.
|
|
87
|
+
*
|
|
88
|
+
* This is the shared foundation used by both {@link inMemoryFirestoreContextCache} and
|
|
89
|
+
* {@link readLoggingFirestoreContextCache}.
|
|
90
|
+
*
|
|
91
|
+
* @param config - Configuration including default TTL and delegate factory
|
|
92
|
+
* @returns A new context cache
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const contextCache = makeFirestoreContextCache({
|
|
97
|
+
* defaultTtl: 60000,
|
|
98
|
+
* createDelegate: () => inMemoryFirestoreCollectionCacheDelegate()
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function makeFirestoreContextCache(config: MakeFirestoreContextCacheConfig): FirestoreContextCache;
|
|
103
|
+
/**
|
|
104
|
+
* Creates a {@link FirestoreContextCacheFactory} that stores cached documents in memory.
|
|
105
|
+
*
|
|
106
|
+
* Uses simple Map-based storage with TTL-based expiration. Suitable for
|
|
107
|
+
* client-side caching where the application lifecycle matches the cache lifecycle.
|
|
108
|
+
*
|
|
109
|
+
* @returns A factory function that creates in-memory context caches
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const factory = inMemoryFirestoreContextCacheFactory();
|
|
114
|
+
* const contextCache = factory({ defaultTtl: 60000 });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function inMemoryFirestoreContextCacheFactory(): FirestoreContextCacheFactory;
|
|
118
|
+
/**
|
|
119
|
+
* Configuration for creating an in-memory {@link FirestoreContextCache}.
|
|
120
|
+
*/
|
|
121
|
+
export interface InMemoryFirestoreContextCacheConfig {
|
|
122
|
+
/**
|
|
123
|
+
* Default TTL applied to all collections unless overridden per-collection.
|
|
124
|
+
*/
|
|
125
|
+
readonly defaultTtl?: Milliseconds;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Creates an in-memory {@link FirestoreContextCache} that manages per-collection
|
|
129
|
+
* caches with TTL-based expiration, per-type enable/disable, and event streaming.
|
|
130
|
+
*
|
|
131
|
+
* @param config - Optional configuration
|
|
132
|
+
* @returns A new context cache
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const contextCache = inMemoryFirestoreContextCache({ defaultTtl: 60000 });
|
|
137
|
+
* const userCache = contextCache.cacheForCollection<UserData>('user', { defaultTtl: 30000 });
|
|
138
|
+
* userCache.set('users/abc', { data: userData });
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function inMemoryFirestoreContextCache(config?: InMemoryFirestoreContextCacheConfig): FirestoreContextCache;
|
|
142
|
+
/**
|
|
143
|
+
* Creates a {@link FirestoreContextCacheFactory} that emits events for all cache interactions
|
|
144
|
+
* but does not actually store any data.
|
|
145
|
+
*
|
|
146
|
+
* Useful for analytics, debugging, and monitoring read patterns without the
|
|
147
|
+
* memory overhead of actual caching.
|
|
148
|
+
*
|
|
149
|
+
* @returns A factory function that creates read-logging context caches
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* const factory = readLoggingFirestoreContextCacheFactory();
|
|
154
|
+
* const contextCache = factory();
|
|
155
|
+
* contextCache.events$.subscribe((event) => console.log(event));
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export declare function readLoggingFirestoreContextCacheFactory(): FirestoreContextCacheFactory;
|
|
159
|
+
/**
|
|
160
|
+
* Configuration for creating a read-logging {@link FirestoreContextCache}.
|
|
161
|
+
*/
|
|
162
|
+
export interface ReadLoggingFirestoreContextCacheConfig {
|
|
163
|
+
/**
|
|
164
|
+
* Default TTL applied to all collections. Since no data is stored,
|
|
165
|
+
* this only affects what gets reported as a "miss" vs "hit" (always miss).
|
|
166
|
+
*/
|
|
167
|
+
readonly defaultTtl?: Milliseconds;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Creates a {@link FirestoreContextCache} that emits events for all cache interactions
|
|
171
|
+
* but does not actually store any data. Every `get()` call results in a 'miss' event.
|
|
172
|
+
*
|
|
173
|
+
* Useful for tracking read patterns, debugging, and monitoring which documents
|
|
174
|
+
* are accessed and how often, without the memory overhead of actual caching.
|
|
175
|
+
*
|
|
176
|
+
* @param config - Optional configuration
|
|
177
|
+
* @returns A new read-logging context cache
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* const contextCache = readLoggingFirestoreContextCache();
|
|
182
|
+
* contextCache.events$.subscribe((event) => {
|
|
183
|
+
* console.log(`${event.type}: ${event.collectionType} ${event.key}`);
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export declare function readLoggingFirestoreContextCache(config?: ReadLoggingFirestoreContextCacheConfig): FirestoreContextCache;
|
|
@@ -3,6 +3,7 @@ import { type FirestoreDocument, type FirestoreDocumentAccessorFactory, type Fir
|
|
|
3
3
|
import { type FirestoreItemPageIterationBaseConfig, type FirestoreItemPageIterationFactory } from '../query/iterator';
|
|
4
4
|
import { type FirestoreQueryFactory } from '../query/query';
|
|
5
5
|
import { type FirestoreDrivers } from '../driver/driver';
|
|
6
|
+
import { type FirestoreCollectionCacheRef, type FirestoreCollectionCacheConfig } from '../cache/cache';
|
|
6
7
|
import { type FirestoreCollectionQueryFactory } from './collection.query';
|
|
7
8
|
import { type ArrayOrValue, type Maybe, type ModelKey, type ModelTypeString } from '@dereekb/util';
|
|
8
9
|
/**
|
|
@@ -570,12 +571,12 @@ export interface FirestoreModelKeyRef {
|
|
|
570
571
|
* @template D - The concrete FirestoreDocument subclass
|
|
571
572
|
* @template A - The accessor type (limited or full)
|
|
572
573
|
*/
|
|
573
|
-
export interface FirestoreCollectionLike<T, D extends FirestoreDocument<T> = FirestoreDocument<T>, A extends LimitedFirestoreDocumentAccessor<T, D> = LimitedFirestoreDocumentAccessor<T, D>> extends FirestoreContextReference, FirestoreModelIdentityRef, QueryLikeReferenceRef<T>, FirestoreItemPageIterationFactory<T>, FirestoreQueryFactory<T>, LimitedFirestoreDocumentAccessorFactory<T, D, A>, LimitedFirestoreDocumentAccessorForTransactionFactory<T, D, A>, LimitedFirestoreDocumentAccessorForWriteBatchFactory<T, D, A>, FirestoreCollectionQueryFactory<T, D> {
|
|
574
|
+
export interface FirestoreCollectionLike<T, D extends FirestoreDocument<T> = FirestoreDocument<T>, A extends LimitedFirestoreDocumentAccessor<T, D> = LimitedFirestoreDocumentAccessor<T, D>> extends FirestoreContextReference, FirestoreModelIdentityRef, QueryLikeReferenceRef<T>, FirestoreItemPageIterationFactory<T>, FirestoreQueryFactory<T>, LimitedFirestoreDocumentAccessorFactory<T, D, A>, LimitedFirestoreDocumentAccessorForTransactionFactory<T, D, A>, LimitedFirestoreDocumentAccessorForWriteBatchFactory<T, D, A>, FirestoreCollectionQueryFactory<T, D>, FirestoreCollectionCacheRef<T> {
|
|
574
575
|
}
|
|
575
576
|
/**
|
|
576
577
|
* FirestoreCollection configuration
|
|
577
578
|
*/
|
|
578
|
-
export interface FirestoreCollectionConfig<T, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends FirestoreContextReference, FirestoreDrivers, Omit<FirestoreItemPageIterationBaseConfig<T>, 'queryLike'>, Partial<QueryLikeReferenceRef<T>>, FirestoreDocumentAccessorFactoryConfig<T, D> {
|
|
579
|
+
export interface FirestoreCollectionConfig<T, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends FirestoreContextReference, FirestoreDrivers, Omit<FirestoreItemPageIterationBaseConfig<T>, 'queryLike'>, Partial<QueryLikeReferenceRef<T>>, FirestoreDocumentAccessorFactoryConfig<T, D>, Partial<FirestoreCollectionCacheConfig> {
|
|
579
580
|
}
|
|
580
581
|
/**
|
|
581
582
|
* Full Firestore collection interface with document CRUD, querying, iteration, and context support.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FirestoreDocument, type LimitedFirestoreDocumentAccessorFactoryConfig } from '../accessor/document';
|
|
2
|
+
import { type FirestoreCollectionCacheConfig } from '../cache/cache';
|
|
2
3
|
import { type FirestoreItemPageIterationBaseConfig } from '../query/iterator';
|
|
3
4
|
import { type FirestoreContextReference } from '../reference';
|
|
4
5
|
import { type FirestoreDrivers } from '../driver/driver';
|
|
@@ -14,7 +15,7 @@ import { type FirestoreCollectionLike } from './collection';
|
|
|
14
15
|
* @template T - The data type of the documents in the collection group
|
|
15
16
|
* @template D - The FirestoreDocument type that wraps the data, defaults to FirestoreDocument<T>
|
|
16
17
|
*/
|
|
17
|
-
export interface FirestoreCollectionGroupConfig<T, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends FirestoreContextReference, FirestoreDrivers, FirestoreItemPageIterationBaseConfig<T>, LimitedFirestoreDocumentAccessorFactoryConfig<T, D> {
|
|
18
|
+
export interface FirestoreCollectionGroupConfig<T, D extends FirestoreDocument<T> = FirestoreDocument<T>> extends FirestoreContextReference, FirestoreDrivers, FirestoreItemPageIterationBaseConfig<T>, LimitedFirestoreDocumentAccessorFactoryConfig<T, D>, Partial<FirestoreCollectionCacheConfig> {
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Interface for working with documents across a Firestore collection group.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FirestoreDocument, type SingleItemFirestoreCollectionDocumentIdentifierRef } from './accessor/document';
|
|
2
2
|
import { type FirestoreCollection, type FirestoreCollectionConfig, type FirestoreCollectionWithParent, type SingleItemFirestoreCollection, type FirestoreCollectionGroup, type RootSingleItemFirestoreCollection } from './collection';
|
|
3
|
+
import { type FirestoreContextCacheFactoryRef, type FirestoreContextCacheRef } from './cache/cache';
|
|
3
4
|
import { type FirestoreDrivers } from './driver/driver';
|
|
4
5
|
import { type WriteBatchFactoryReference, type RunTransactionFactoryReference } from './driver';
|
|
5
6
|
import { type DocumentReference, type CollectionReference, type DocumentData, type Firestore, type CollectionGroup } from './types';
|
|
@@ -17,7 +18,7 @@ import { type QueryLikeReferenceRef } from './reference';
|
|
|
17
18
|
*
|
|
18
19
|
* @template F - The Firestore implementation type (defaults to standard Firestore)
|
|
19
20
|
*/
|
|
20
|
-
export interface FirestoreContext<F extends Firestore = Firestore> extends RunTransactionFactoryReference, WriteBatchFactoryReference {
|
|
21
|
+
export interface FirestoreContext<F extends Firestore = Firestore> extends RunTransactionFactoryReference, WriteBatchFactoryReference, FirestoreContextCacheRef {
|
|
21
22
|
/**
|
|
22
23
|
* The underlying Firestore instance.
|
|
23
24
|
*/
|
|
@@ -113,7 +114,7 @@ export interface FirestoreContext<F extends Firestore = Firestore> extends RunTr
|
|
|
113
114
|
* @template T - The document data type in the collection
|
|
114
115
|
* @template D - The FirestoreDocument implementation type
|
|
115
116
|
*/
|
|
116
|
-
export type FirestoreContextFirestoreCollectionConfig<T, D extends FirestoreDocument<T>> = Omit<FirestoreCollectionConfig<T, D>, 'firestoreDriverIdentifier' | 'firestoreDriverType' | 'firestoreQueryDriver' | 'firestoreAccessorDriver'>;
|
|
117
|
+
export type FirestoreContextFirestoreCollectionConfig<T, D extends FirestoreDocument<T>> = Omit<FirestoreCollectionConfig<T, D>, 'firestoreDriverIdentifier' | 'firestoreDriverType' | 'firestoreQueryDriver' | 'firestoreAccessorDriver' | 'cache'>;
|
|
117
118
|
/**
|
|
118
119
|
* Configuration for creating a RootSingleItemFirestoreCollection through a FirestoreContext.
|
|
119
120
|
*
|
|
@@ -165,17 +166,34 @@ export interface FirestoreContextFirestoreCollectionWithParentConfig<T, PT, D ex
|
|
|
165
166
|
*/
|
|
166
167
|
export interface FirestoreContextSingleItemFirestoreCollectionConfig<T, PT, D extends FirestoreDocument<T> = FirestoreDocument<T>, PD extends FirestoreDocument<PT> = FirestoreDocument<PT>> extends FirestoreContextFirestoreCollectionWithParentConfig<T, PT, D, PD>, Partial<SingleItemFirestoreCollectionDocumentIdentifierRef> {
|
|
167
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Optional parameters for {@link FirestoreContextFactory} that configure context-level
|
|
171
|
+
* features like caching. These are separate from the platform drivers because they
|
|
172
|
+
* are app-level concerns, not platform-level concerns.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* const params: FirestoreContextFactoryParams = {
|
|
177
|
+
* firestoreContextCacheFactory: inMemoryFirestoreContextCacheFactory()
|
|
178
|
+
* };
|
|
179
|
+
* const context = contextFactory(firestore, params);
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export interface FirestoreContextFactoryParams extends FirestoreContextCacheFactoryRef {
|
|
183
|
+
}
|
|
168
184
|
/**
|
|
169
185
|
* Factory function type for creating FirestoreContext instances.
|
|
170
186
|
*
|
|
171
|
-
* Takes a Firestore instance and returns a fully configured
|
|
172
|
-
*
|
|
187
|
+
* Takes a Firestore instance and optional params, and returns a fully configured
|
|
188
|
+
* FirestoreContext. The params allow app-level configuration (e.g. caching) to be
|
|
189
|
+
* provided at the DI injection site rather than at driver creation time.
|
|
173
190
|
*
|
|
174
191
|
* @template F - The Firestore implementation type
|
|
175
192
|
* @param firestore - The Firestore instance to wrap
|
|
193
|
+
* @param params - Optional context-level configuration (e.g. cache factory)
|
|
176
194
|
* @returns A FirestoreContext instance for the specified Firestore
|
|
177
195
|
*/
|
|
178
|
-
export type FirestoreContextFactory<F extends Firestore = Firestore> = (firestore: F) => FirestoreContext;
|
|
196
|
+
export type FirestoreContextFactory<F extends Firestore = Firestore> = (firestore: F, params?: FirestoreContextFactoryParams) => FirestoreContext;
|
|
179
197
|
/**
|
|
180
198
|
* Creates a factory function for generating FirestoreContext instances.
|
|
181
199
|
*
|
package/test/index.cjs.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var util = require('@dereekb/util');
|
|
4
|
-
var test = require('@dereekb/util/test');
|
|
5
4
|
var rulesUnitTesting = require('@firebase/rules-unit-testing');
|
|
6
5
|
var firebase = require('@dereekb/firebase');
|
|
7
6
|
var firestore = require('firebase/firestore');
|
|
7
|
+
var test = require('@dereekb/util/test');
|
|
8
8
|
var rxjs$1 = require('rxjs');
|
|
9
9
|
var rxjs = require('@dereekb/rxjs');
|
|
10
10
|
var dateFns = require('date-fns');
|
|
@@ -938,71 +938,113 @@ function _ts_generator$5(thisArg, body) {
|
|
|
938
938
|
* A TestContextBuilderFunction for building firebase test context factories using @firebase/firebase and @firebase/rules-unit-testing. This means CLIENT TESTING ONLY. For server testing, look at @dereekb/firestore-server.
|
|
939
939
|
*
|
|
940
940
|
* This can be used to easily build a testing context that sets up RulesTestEnvironment for tests that sets itself up and tears itself down.
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
941
|
+
*
|
|
942
|
+
* The {@link RulesTestEnvironment} is initialized once per test suite (`beforeAll`) and cleaned up
|
|
943
|
+
* once (`afterAll`), while fresh drivers and a {@link RulesTestContext} are created per test (`beforeEach`). This avoids
|
|
944
|
+
* repeated calls to `initializeTestEnvironment` (which hits the emulator's `PUT /internal/setRules` endpoint),
|
|
945
|
+
* preventing interference between parallel workers sharing the same Firebase Storage emulator. The Storage
|
|
946
|
+
* emulator maintains rules globally (not per-project), so concurrent `setRules` calls from multiple workers
|
|
947
|
+
* can momentarily leave the emulator in a transitional state that causes `storage/unauthorized` errors.
|
|
948
|
+
*/ var firebaseRulesUnitTestBuilder = function firebaseRulesUnitTestBuilder(inputConfig) {
|
|
949
|
+
var _ref;
|
|
950
|
+
var config = {
|
|
951
|
+
testEnvironment: (_ref = inputConfig === null || inputConfig === void 0 ? void 0 : inputConfig.testEnvironment) !== null && _ref !== void 0 ? _ref : {},
|
|
952
|
+
rulesContext: inputConfig === null || inputConfig === void 0 ? void 0 : inputConfig.rulesContext
|
|
953
|
+
};
|
|
954
|
+
return function(buildTests) {
|
|
955
|
+
var fixture = new RulesUnitTestFirebaseTestingContextFixture();
|
|
956
|
+
var rulesTestEnvironment;
|
|
957
|
+
// Initialize the emulator environment once per test suite.
|
|
958
|
+
// This is the expensive operation that hits the emulator's REST API (e.g. PUT /internal/setRules).
|
|
959
|
+
beforeAll(function() {
|
|
960
|
+
return _async_to_generator$5(function() {
|
|
961
|
+
return _ts_generator$5(this, function(_state) {
|
|
962
|
+
switch(_state.label){
|
|
963
|
+
case 0:
|
|
964
|
+
return [
|
|
965
|
+
4,
|
|
966
|
+
rulesUnitTesting.initializeTestEnvironment(config.testEnvironment)
|
|
967
|
+
];
|
|
968
|
+
case 1:
|
|
969
|
+
rulesTestEnvironment = _state.sent();
|
|
970
|
+
return [
|
|
971
|
+
2
|
|
972
|
+
];
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
})();
|
|
976
|
+
});
|
|
977
|
+
// Clean up the emulator environment once after all tests complete.
|
|
978
|
+
afterAll(function() {
|
|
979
|
+
return _async_to_generator$5(function() {
|
|
980
|
+
return _ts_generator$5(this, function(_state) {
|
|
981
|
+
switch(_state.label){
|
|
982
|
+
case 0:
|
|
983
|
+
if (!rulesTestEnvironment) return [
|
|
984
|
+
3,
|
|
985
|
+
2
|
|
986
|
+
];
|
|
987
|
+
return [
|
|
988
|
+
4,
|
|
989
|
+
rulesTestEnvironment.cleanup().catch(function(e) {
|
|
990
|
+
console.warn('firebaseRulesUnitTestBuilder(): Failed to cleanup rules test environment', e);
|
|
991
|
+
throw e;
|
|
992
|
+
})
|
|
993
|
+
];
|
|
994
|
+
case 1:
|
|
995
|
+
_state.sent();
|
|
996
|
+
_state.label = 2;
|
|
997
|
+
case 2:
|
|
998
|
+
return [
|
|
999
|
+
2
|
|
1000
|
+
];
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
})();
|
|
1004
|
+
});
|
|
1005
|
+
// Create fresh drivers and RulesTestContext per test.
|
|
1006
|
+
// Drivers are recreated per test because the Firestore testing driver fuzzes collection names
|
|
1007
|
+
// (via makeTestingFirestoreAccesorDriver) to provide data isolation between tests.
|
|
1008
|
+
var clearInstance = null;
|
|
1009
|
+
beforeEach(function() {
|
|
1010
|
+
return _async_to_generator$5(function() {
|
|
1011
|
+
var drivers, rulesTestContext, instance;
|
|
1012
|
+
return _ts_generator$5(this, function(_state) {
|
|
1013
|
+
try {
|
|
959
1014
|
drivers = _object_spread$1({}, makeTestingFirestoreDrivers(firebase.firebaseFirestoreClientDrivers()), makeTestingFirebaseStorageDrivers(firebase.firebaseStorageClientDrivers(), {
|
|
960
1015
|
useTestDefaultBucket: true
|
|
961
1016
|
}));
|
|
962
|
-
testEnvironment = config.testEnvironment;
|
|
963
1017
|
if (config.testEnvironment.collectionNames) {
|
|
964
1018
|
drivers.firestoreAccessorDriver.initWithCollectionNames(config.testEnvironment.collectionNames);
|
|
965
|
-
testEnvironment = _object_spread_props(_object_spread$1({}, testEnvironment), {
|
|
966
|
-
firestore: rewriteEmulatorConfigRulesForFuzzedCollectionNames(testEnvironment.firestore)
|
|
967
|
-
});
|
|
968
1019
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
_state.sent();
|
|
998
|
-
return [
|
|
999
|
-
2
|
|
1000
|
-
];
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
})();
|
|
1004
|
-
}
|
|
1005
|
-
});
|
|
1020
|
+
rulesTestContext = rulesTestContextForConfig(rulesTestEnvironment, config.rulesContext);
|
|
1021
|
+
instance = new RulesUnitTestTestFirebaseInstance(drivers, rulesTestEnvironment, rulesTestContext);
|
|
1022
|
+
clearInstance = fixture.setInstance(instance);
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
console.error('firebaseRulesUnitTestBuilder(): Failed building a test instance. Error: ', e);
|
|
1025
|
+
if (clearInstance) {
|
|
1026
|
+
clearInstance();
|
|
1027
|
+
clearInstance = null;
|
|
1028
|
+
}
|
|
1029
|
+
throw e;
|
|
1030
|
+
}
|
|
1031
|
+
return [
|
|
1032
|
+
2
|
|
1033
|
+
];
|
|
1034
|
+
});
|
|
1035
|
+
})();
|
|
1036
|
+
});
|
|
1037
|
+
// Declare tests
|
|
1038
|
+
buildTests(fixture);
|
|
1039
|
+
// Clear the instance reference after each test.
|
|
1040
|
+
afterEach(function() {
|
|
1041
|
+
if (clearInstance) {
|
|
1042
|
+
clearInstance();
|
|
1043
|
+
clearInstance = null;
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
};
|
|
1047
|
+
};
|
|
1006
1048
|
// MARK: Internal
|
|
1007
1049
|
function rulesTestContextForConfig(rulesTestEnv, testingRulesConfig) {
|
|
1008
1050
|
var rulesTestContext;
|
|
@@ -1014,18 +1056,6 @@ function rulesTestContextForConfig(rulesTestEnv, testingRulesConfig) {
|
|
|
1014
1056
|
}
|
|
1015
1057
|
return rulesTestContext;
|
|
1016
1058
|
}
|
|
1017
|
-
function rewriteEmulatorConfigRulesForFuzzedCollectionNames(config, fuzzedCollectionNamesMap) {
|
|
1018
|
-
if (config && config.rules) {
|
|
1019
|
-
config = _object_spread_props(_object_spread$1({}, config), {
|
|
1020
|
-
rules: rewriteRulesForFuzzedCollectionNames(config.rules)
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
return config;
|
|
1024
|
-
}
|
|
1025
|
-
function rewriteRulesForFuzzedCollectionNames(rules, fuzzedCollectionNamesMap) {
|
|
1026
|
-
// TODO: rewrite the rules using regex matching/replacement.
|
|
1027
|
-
return rules;
|
|
1028
|
-
}
|
|
1029
1059
|
// MARK: Utility
|
|
1030
1060
|
/**
|
|
1031
1061
|
* Registers `beforeAll`/`afterAll` hooks to suppress verbose Firestore log output during tests.
|
|
@@ -1044,24 +1074,56 @@ function rewriteRulesForFuzzedCollectionNames(rules, fuzzedCollectionNamesMap) {
|
|
|
1044
1074
|
/**
|
|
1045
1075
|
* Default user ID used for the authenticated context in client-side Firebase tests.
|
|
1046
1076
|
*/ var TESTING_AUTHORIZED_FIREBASE_USER_ID = '0';
|
|
1077
|
+
/**
|
|
1078
|
+
* Permissive Firestore security rules that allow all reads and writes.
|
|
1079
|
+
*/ var AUTHORIZED_FIRESTORE_RULES = "\nrules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if true;\n }\n }\n}\n";
|
|
1080
|
+
/**
|
|
1081
|
+
* Permissive Firebase Storage security rules that allow all reads and writes.
|
|
1082
|
+
*/ var AUTHORIZED_STORAGE_RULES = "\nrules_version = '2';\nservice firebase.storage {\n match /b/{bucket}/o {\n match /{allPaths=**} {\n allow read, write: if true;\n }\n }\n}\n";
|
|
1047
1083
|
/**
|
|
1048
1084
|
* Pre-configured {@link FirebaseTestContextFactory} that provides a fully authorized (all reads/writes allowed)
|
|
1049
1085
|
* Firebase emulator context for both Firestore and Storage.
|
|
1050
1086
|
*
|
|
1051
1087
|
* Uses permissive security rules and authenticates as {@link TESTING_AUTHORIZED_FIREBASE_USER_ID}.
|
|
1052
|
-
*
|
|
1088
|
+
*
|
|
1089
|
+
* **Important:** Only use this factory for tests that actually need Firebase Storage access.
|
|
1090
|
+
* Firestore-only tests should use {@link authorizedFirestoreOnlyFactory} instead to avoid
|
|
1091
|
+
* interfering with the storage emulator's global rules endpoint during parallel test execution.
|
|
1053
1092
|
*
|
|
1054
1093
|
* @example
|
|
1055
1094
|
* ```ts
|
|
1056
|
-
* const f =
|
|
1095
|
+
* const f = testWithMockItemStorageFixture()(authorizedFirebaseFactory);
|
|
1057
1096
|
* ```
|
|
1058
1097
|
*/ var authorizedFirebaseFactory = firebaseRulesUnitTestBuilder({
|
|
1059
1098
|
testEnvironment: {
|
|
1060
1099
|
firestore: {
|
|
1061
|
-
rules:
|
|
1100
|
+
rules: AUTHORIZED_FIRESTORE_RULES
|
|
1062
1101
|
},
|
|
1063
1102
|
storage: {
|
|
1064
|
-
rules:
|
|
1103
|
+
rules: AUTHORIZED_STORAGE_RULES
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
rulesContext: {
|
|
1107
|
+
userId: TESTING_AUTHORIZED_FIREBASE_USER_ID
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
/**
|
|
1111
|
+
* Pre-configured {@link FirebaseTestContextFactory} that provides a fully authorized Firestore-only
|
|
1112
|
+
* emulator context without configuring Storage rules.
|
|
1113
|
+
*
|
|
1114
|
+
* Use this for Firestore-only tests when running with parallel workers. The Firebase Storage
|
|
1115
|
+
* emulator maintains rules globally (not per-project like Firestore), so `initializeTestEnvironment`
|
|
1116
|
+
* calls that include storage rules from non-storage workers will hit `PUT /internal/setRules`
|
|
1117
|
+
* concurrently, which can cause transient `storage/unauthorized` errors in the storage test worker.
|
|
1118
|
+
*
|
|
1119
|
+
* @example
|
|
1120
|
+
* ```ts
|
|
1121
|
+
* const f = testWithMockItemCollectionFixture()(authorizedFirestoreOnlyFactory);
|
|
1122
|
+
* ```
|
|
1123
|
+
*/ var authorizedFirestoreOnlyFactory = firebaseRulesUnitTestBuilder({
|
|
1124
|
+
testEnvironment: {
|
|
1125
|
+
firestore: {
|
|
1126
|
+
rules: AUTHORIZED_FIRESTORE_RULES
|
|
1065
1127
|
}
|
|
1066
1128
|
},
|
|
1067
1129
|
rulesContext: {
|
|
@@ -2135,8 +2197,11 @@ function _is_native_reflect_construct$2() {
|
|
|
2135
2197
|
/**
|
|
2136
2198
|
* Convenience mock instance for collection tests within an authorized firebase context.
|
|
2137
2199
|
*
|
|
2200
|
+
* Uses {@link authorizedFirestoreOnlyFactory} to avoid touching the Storage emulator's
|
|
2201
|
+
* global rules endpoint, preventing interference with parallel storage test workers.
|
|
2202
|
+
*
|
|
2138
2203
|
* Uses @firebase/firestore. This is ONLY for the client.
|
|
2139
|
-
*/ var authorizedTestWithMockItemCollection = testWithMockItemCollectionFixture()(
|
|
2204
|
+
*/ var authorizedTestWithMockItemCollection = testWithMockItemCollectionFixture()(authorizedFirestoreOnlyFactory);
|
|
2140
2205
|
/**
|
|
2141
2206
|
* Convenience mock instance for storage tests within an authorized firebase context.
|
|
2142
2207
|
*
|
|
@@ -12483,6 +12548,8 @@ function _ts_generator(thisArg, body) {
|
|
|
12483
12548
|
});
|
|
12484
12549
|
}
|
|
12485
12550
|
|
|
12551
|
+
exports.AUTHORIZED_FIRESTORE_RULES = AUTHORIZED_FIRESTORE_RULES;
|
|
12552
|
+
exports.AUTHORIZED_STORAGE_RULES = AUTHORIZED_STORAGE_RULES;
|
|
12486
12553
|
exports.MOCK_FIREBASE_MODEL_SERVICE_FACTORIES = MOCK_FIREBASE_MODEL_SERVICE_FACTORIES;
|
|
12487
12554
|
exports.MOCK_SYSTEM_STATE_TYPE = MOCK_SYSTEM_STATE_TYPE;
|
|
12488
12555
|
exports.MockItemCollectionFixture = MockItemCollectionFixture;
|
|
@@ -12506,6 +12573,7 @@ exports.TestFirestoreContextFixture = TestFirestoreContextFixture;
|
|
|
12506
12573
|
exports.TestFirestoreInstance = TestFirestoreInstance;
|
|
12507
12574
|
exports.allChildMockItemSubItemDeepsWithinMockItem = allChildMockItemSubItemDeepsWithinMockItem;
|
|
12508
12575
|
exports.authorizedFirebaseFactory = authorizedFirebaseFactory;
|
|
12576
|
+
exports.authorizedFirestoreOnlyFactory = authorizedFirestoreOnlyFactory;
|
|
12509
12577
|
exports.authorizedTestWithMockItemCollection = authorizedTestWithMockItemCollection;
|
|
12510
12578
|
exports.authorizedTestWithMockItemStorage = authorizedTestWithMockItemStorage;
|
|
12511
12579
|
exports.changeFirestoreLogLevelBeforeAndAfterTests = changeFirestoreLogLevelBeforeAndAfterTests;
|