@dereekb/firebase 13.6.17 → 13.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase",
3
- "version": "13.6.17",
3
+ "version": "13.8.0",
4
4
  "exports": {
5
5
  "./test": {
6
6
  "module": "./test/index.esm.js",
@@ -17,14 +17,14 @@
17
17
  }
18
18
  },
19
19
  "peerDependencies": {
20
- "@dereekb/util": "13.6.17",
21
- "@dereekb/date": "13.6.17",
22
- "@dereekb/model": "13.6.17",
23
- "@dereekb/rxjs": "13.6.17",
20
+ "@dereekb/util": "13.8.0",
21
+ "@dereekb/date": "13.8.0",
22
+ "@dereekb/model": "13.8.0",
23
+ "@dereekb/rxjs": "13.8.0",
24
24
  "@firebase/rules-unit-testing": "5.0.0",
25
25
  "arktype": "^2.2.0",
26
26
  "date-fns": "^4.0.0",
27
- "firebase": "^12.0.0",
27
+ "firebase": "^12.12.0",
28
28
  "make-error": "^1.3.0",
29
29
  "rxjs": "^7.8.0",
30
30
  "ts-essentials": "^10.0.0"
@@ -48,6 +48,13 @@ export declare const FIREBASE_AUTH_CREDENTIAL_ALREADY_IN_USE_ERROR = "auth/crede
48
48
  * Error code when an email address is already in use by another account during linking.
49
49
  */
50
50
  export declare const FIREBASE_AUTH_EMAIL_ALREADY_IN_USE_ERROR = "auth/email-already-in-use";
51
+ /**
52
+ * Error code when the quota for updating account information has been exceeded.
53
+ *
54
+ * This typically occurs when too many account update operations (e.g., updateUser, setCustomUserClaims)
55
+ * are performed in a short time window.
56
+ */
57
+ export declare const FIREBASE_AUTH_QUOTA_EXCEEDED_ERROR = "auth/quota-exceeded";
51
58
  /**
52
59
  * Converts a {@link FirebaseAuthError} into a user-friendly {@link ReadableError} with a human-readable message.
53
60
  *
@@ -10,3 +10,19 @@ export declare const DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE = "NO_AUTH";
10
10
  * This can happen with anonymous or malformed tokens.
11
11
  */
12
12
  export declare const DBX_FIREBASE_SERVER_NO_UID_ERROR_CODE = "NO_USER_UID";
13
+ /**
14
+ * Error code used when the provided password reset code is invalid or expired.
15
+ */
16
+ export declare const DBX_FIREBASE_SERVER_PASSWORD_RESET_INVALID_CODE_ERROR_CODE = "PASSWORD_RESET_INVALID_CODE";
17
+ /**
18
+ * Error code used when a password reset is attempted but no active reset exists for the user.
19
+ */
20
+ export declare const DBX_FIREBASE_SERVER_PASSWORD_RESET_NO_CONFIG_ERROR_CODE = "PASSWORD_RESET_NO_CONFIG";
21
+ /**
22
+ * Error code used when a password reset email send is throttled due to a recent send.
23
+ */
24
+ export declare const DBX_FIREBASE_SERVER_PASSWORD_RESET_THROTTLE_ERROR_CODE = "PASSWORD_RESET_THROTTLE";
25
+ /**
26
+ * Error code used when a password reset email has already been sent and the send-once constraint is active.
27
+ */
28
+ export declare const DBX_FIREBASE_SERVER_PASSWORD_RESET_SEND_ONCE_ERROR_CODE = "PASSWORD_RESET_SEND_ONCE";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Error code for unauthenticated (401) requests.
3
+ */
4
+ export declare const UNAUTHENTICATED_ERROR_CODE = "UNAUTHENTICATED";
5
+ /**
6
+ * Error code for forbidden (403) requests.
7
+ */
8
+ export declare const FORBIDDEN_ERROR_CODE = "FORBIDDEN";
9
+ /**
10
+ * Error code for permission-denied (403) requests.
11
+ */
12
+ export declare const PERMISSION_DENIED_ERROR_CODE = "PERMISSION_DENIED";
13
+ /**
14
+ * Error code for not-found (404) requests.
15
+ */
16
+ export declare const NOT_FOUND_ERROR_CODE = "NOT_FOUND";
17
+ /**
18
+ * Error code for a Firestore document that does not exist (404).
19
+ */
20
+ export declare const MODEL_NOT_AVAILABLE_ERROR_CODE = "MODEL_NOT_AVAILABLE";
21
+ /**
22
+ * Error code for bad-request (400) responses.
23
+ */
24
+ export declare const BAD_REQUEST_ERROR_CODE = "BAD_REQUEST";
25
+ /**
26
+ * Error code for precondition-conflict (409) responses.
27
+ */
28
+ export declare const CONFLICT_ERROR_CODE = "CONFLICT";
29
+ /**
30
+ * Error code for already-exists (409) responses.
31
+ */
32
+ export declare const ALREADY_EXISTS_ERROR_CODE = "ALREADY_EXISTS";
33
+ /**
34
+ * Error code for unavailable (503) responses.
35
+ */
36
+ export declare const UNAVAILABLE_ERROR_CODE = "UNAVAILABLE";
37
+ /**
38
+ * Error code for deactivated or unavailable functions (501).
39
+ */
40
+ export declare const UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE = "UNAVAILABLE_OR_DEACTIVATED_FUNCTION";
41
+ /**
42
+ * Error code for internal server errors (500).
43
+ */
44
+ export declare const INTERNAL_SERVER_ERROR_CODE = "INTERNAL_ERROR";
@@ -1 +1,2 @@
1
1
  export * from './action';
2
+ export * from './error';
@@ -4,7 +4,7 @@ import { type FirestoreModelKey, type FirestoreModelType, type FirestoreModelTyp
4
4
  /**
5
5
  * Standard CRUD call types used by the `callModel` Firebase function pattern.
6
6
  */
7
- export type KnownOnCallFunctionType = 'create' | 'read' | 'update' | 'delete';
7
+ export type KnownOnCallFunctionType = 'create' | 'read' | 'update' | 'delete' | 'query';
8
8
  /**
9
9
  * Call type identifier — one of the standard CRUD types or a custom string.
10
10
  */
@@ -82,6 +82,10 @@ export declare const onCallUpdateModelParams: OnCallTypeModelParamsFunction;
82
82
  * Pre-configured OnCallTypedModelParamsFunctions for 'delete'
83
83
  */
84
84
  export declare const onCallDeleteModelParams: OnCallTypeModelParamsFunction;
85
+ /**
86
+ * Pre-configured OnCallTypedModelParamsFunctions for 'query'
87
+ */
88
+ export declare const onCallQueryModelParams: OnCallTypeModelParamsFunction;
85
89
  /**
86
90
  * Key used on the front-end and backend that refers to the call function.
87
91
  */
@@ -102,6 +106,80 @@ export type OnCallUpdateModelParams<T = unknown> = OnCallTypedModelParams<T>;
102
106
  * OnCallTypedModelParams for Delete calls.
103
107
  */
104
108
  export type OnCallDeleteModelParams<T = unknown> = OnCallTypedModelParams<T>;
109
+ /**
110
+ * OnCallTypedModelParams for Query calls.
111
+ */
112
+ export type OnCallQueryModelParams<T extends OnCallQueryModelRequestParams = OnCallQueryModelRequestParams> = OnCallTypedModelParams<T>;
113
+ /**
114
+ * Default maximum items per page for query operations.
115
+ */
116
+ export declare const DEFAULT_ON_CALL_QUERY_MODEL_LIMIT = 50;
117
+ /**
118
+ * Absolute maximum items per page for query operations. Prevents unbounded queries.
119
+ */
120
+ export declare const MAX_ON_CALL_QUERY_MODEL_LIMIT = 200;
121
+ /**
122
+ * Base request parameters that all query handler input types must include.
123
+ *
124
+ * Provides cursor-based pagination fields. Custom query handlers extend this
125
+ * with additional filter fields specific to their model type.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * interface QueryGuestbooksParams extends OnCallQueryModelRequestParams {
130
+ * readonly name?: string;
131
+ * readonly published?: boolean;
132
+ * }
133
+ * ```
134
+ */
135
+ export interface OnCallQueryModelRequestParams {
136
+ /**
137
+ * Maximum number of items to return per page.
138
+ *
139
+ * Clamped server-side to {@link MAX_ON_CALL_QUERY_MODEL_LIMIT}.
140
+ * Defaults to {@link DEFAULT_ON_CALL_QUERY_MODEL_LIMIT} when omitted.
141
+ */
142
+ readonly limit?: number;
143
+ /**
144
+ * Firestore model key of the last document from the previous page.
145
+ *
146
+ * When provided, the query resumes after the document at this key.
147
+ * Obtained from {@link OnCallQueryModelResult.cursorDocumentKey}.
148
+ */
149
+ readonly cursorDocumentKey?: FirestoreModelKey;
150
+ }
151
+ /**
152
+ * Standard result returned by model query operations.
153
+ *
154
+ * Includes the matched documents and cursor information for pagination.
155
+ *
156
+ * @typeParam T - The document data type.
157
+ */
158
+ export interface OnCallQueryModelResult<T = unknown> {
159
+ /**
160
+ * The matched documents' data for this page.
161
+ */
162
+ readonly results: readonly T[];
163
+ /**
164
+ * The keys of the matched documents, in the same order as {@link results}.
165
+ */
166
+ readonly keys: readonly FirestoreModelKey[];
167
+ /**
168
+ * Total number of results returned in this page.
169
+ */
170
+ readonly count: number;
171
+ /**
172
+ * Firestore model key of the last document in this page.
173
+ *
174
+ * Pass this value as the `cursorDocumentKey` field in the next query request to fetch the next page.
175
+ * Undefined when there are no more results.
176
+ */
177
+ readonly cursorDocumentKey?: FirestoreModelKey;
178
+ /**
179
+ * Whether there are more results after this page.
180
+ */
181
+ readonly hasMore: boolean;
182
+ }
105
183
  /**
106
184
  * Standard result returned by model create operations, containing the key(s) of the created document(s).
107
185
  */
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Error code for a model type string that has no registered handler.
3
+ */
4
+ export declare const UNKNOWN_MODEL_TYPE_ERROR_CODE = "UNKNOWN_MODEL_TYPE";
5
+ /**
6
+ * Error code for an invalid or inaccessible cursor document key in a query request.
7
+ */
8
+ export declare const BAD_DOCUMENT_QUERY_CURSOR_ERROR_CODE = "BAD_DOCUMENT_QUERY_CURSOR";
@@ -2,4 +2,5 @@ export * from './model';
2
2
  export * from './permission';
3
3
  export * from './context';
4
4
  export * from './function';
5
+ export * from './function.error';
5
6
  export * from './model.service';
@@ -27,7 +27,7 @@ export declare const oidcEntryIdentity: import("../..").RootFirestoreModelIdenti
27
27
  *
28
28
  * Used as the discriminator in the {@link OidcEntry.type} field.
29
29
  */
30
- export type OidcEntryType = 'Session' | 'AccessToken' | 'AuthorizationCode' | 'RefreshToken' | 'DeviceCode' | 'ClientCredentials' | 'Client' | 'InitialAccessToken' | 'RegistrationAccessToken' | 'Interaction' | 'ReplayDetection' | 'PushedAuthorizationRequest' | 'Grant' | 'BackchannelAuthenticationRequest' | (string & {});
30
+ export type OidcEntryType = 'Session' | 'AccessToken' | 'AuthorizationCode' | 'RefreshToken' | 'DeviceCode' | 'ClientCredentials' | 'Client' | 'InitialAccessToken' | 'RegistrationAccessToken' | 'Interaction' | 'ReplayDetection' | 'PushedAuthorizationRequest' | 'Grant' | 'BackchannelAuthenticationRequest' | string;
31
31
  /**
32
32
  * Type value for Client adapter entries.
33
33
  */
package/test/index.cjs.js CHANGED
@@ -10,7 +10,7 @@ var rxjs = require('@dereekb/rxjs');
10
10
  var dateFns = require('date-fns');
11
11
  var date = require('@dereekb/date');
12
12
  var stream = require('stream');
13
- var fs = require('fs');
13
+ var node_fs = require('node:fs');
14
14
 
15
15
  function _array_like_to_array$2(arr, len) {
16
16
  if (len == null || len > arr.length) len = arr.length;
@@ -10032,6 +10032,56 @@ function _ts_generator$1(thisArg, body) {
10032
10032
  done();
10033
10033
  });
10034
10034
  }));
10035
+ it('should reach end when limit equals total document count.', test.callbackTest(function(done) {
10036
+ var limit = testDocumentCount; // exactly matches item count
10037
+ var iteration = firestoreIteration({
10038
+ limit: limit
10039
+ });
10040
+ var accumulator = firebase.firebaseQuerySnapshotAccumulator(iteration);
10041
+ // First page should return all items but not yet know it's the end
10042
+ iteration.nextPage().then(function() {
10043
+ // Load the next page which should discover end via empty snapshot
10044
+ iteration.nextPage().then(function() {
10045
+ sub.subscription = iteration.latestState$.pipe(rxjs$1.filter(function(x) {
10046
+ return rxjs.isLoadingStateFinishedLoading(x);
10047
+ }), rxjs$1.first()).subscribe(function(state) {
10048
+ expect(state.hasNextPage).toBe(false);
10049
+ // Verify all items loaded without duplicates
10050
+ rxjs.flattenAccumulatorResultItemArray(accumulator).pipe(rxjs$1.first()).subscribe(function(values) {
10051
+ expect(values.length).toBe(testDocumentCount);
10052
+ expect(util.arrayContainsDuplicateValue(values.map(function(x) {
10053
+ return x.id;
10054
+ }))).toBe(false);
10055
+ done();
10056
+ });
10057
+ });
10058
+ });
10059
+ });
10060
+ }));
10061
+ it('should reach end when loading all pages with limit as a divisor of total count.', test.callbackTest(function(done) {
10062
+ var limit = 5; // divides evenly into testDocumentCount (10)
10063
+ var expectedPages = testDocumentCount / limit; // 2 full pages + 1 empty to discover end
10064
+ var iteration = firestoreIteration({
10065
+ limit: limit
10066
+ });
10067
+ var accumulator = firebase.firebaseQuerySnapshotAccumulator(iteration);
10068
+ // Load pages until the iterator reaches the end
10069
+ rxjs.iteratorNextPageUntilPage(iteration, expectedPages + 1).then(function(page) {
10070
+ sub.subscription = iteration.latestState$.pipe(rxjs$1.filter(function(x) {
10071
+ return rxjs.isLoadingStateFinishedLoading(x);
10072
+ }), rxjs$1.first()).subscribe(function(state) {
10073
+ expect(state.hasNextPage).toBe(false);
10074
+ // Verify all items loaded without duplicates
10075
+ rxjs.flattenAccumulatorResultItemArray(accumulator).pipe(rxjs$1.first()).subscribe(function(values) {
10076
+ expect(values.length).toBe(testDocumentCount);
10077
+ expect(util.arrayContainsDuplicateValue(values.map(function(x) {
10078
+ return x.id;
10079
+ }))).toBe(false);
10080
+ done();
10081
+ });
10082
+ });
10083
+ });
10084
+ }));
10035
10085
  });
10036
10086
  describe('with inferEndOfResultsFromPageSize=false', function() {
10037
10087
  it('should not mark the result as the end even when fewer items are returned than the limit.', test.callbackTest(function(done) {
@@ -11108,7 +11158,7 @@ function _ts_generator(thisArg, body) {
11108
11158
  ];
11109
11159
  testFilePath = "".concat(__dirname, "/assets/testpng.png");
11110
11160
  contentType = 'image/png';
11111
- testFileStream = fs.createReadStream(testFilePath, {});
11161
+ testFileStream = node_fs.createReadStream(testFilePath, {});
11112
11162
  return [
11113
11163
  4,
11114
11164
  firebase.uploadFileWithStream(uploadFile, testFileStream, {
package/test/index.esm.js CHANGED
@@ -4,11 +4,11 @@ import { firebaseStorageContextFactory, firestoreContextFactory, firebaseFiresto
4
4
  import { setLogLevel } from 'firebase/firestore';
5
5
  import { AbstractTestContextFixture, instanceWrapTestContextFactory, AbstractWrappedFixtureWithInstance, itShouldFail, expectFail, callbackTest } from '@dereekb/util/test';
6
6
  import { firstValueFrom, filter, skip, from, first, map, switchMap } from 'rxjs';
7
- import { SubscriptionObject, iteratorNextPageUntilPage, flattenAccumulatorResultItemArray, accumulatorCurrentPageListLoadingState, isLoadingStateFinishedLoading, accumulatorFlattenPageListLoadingState } from '@dereekb/rxjs';
7
+ import { SubscriptionObject, isLoadingStateFinishedLoading, flattenAccumulatorResultItemArray, iteratorNextPageUntilPage, accumulatorCurrentPageListLoadingState, accumulatorFlattenPageListLoadingState } from '@dereekb/rxjs';
8
8
  import { addDays, startOfDay, addHours } from 'date-fns';
9
9
  import { DateRangeType } from '@dereekb/date';
10
10
  import { Readable } from 'stream';
11
- import { createReadStream } from 'fs';
11
+ import { createReadStream } from 'node:fs';
12
12
 
13
13
  function _array_like_to_array$2(arr, len) {
14
14
  if (len == null || len > arr.length) len = arr.length;
@@ -10030,6 +10030,56 @@ function _ts_generator$1(thisArg, body) {
10030
10030
  done();
10031
10031
  });
10032
10032
  }));
10033
+ it('should reach end when limit equals total document count.', callbackTest(function(done) {
10034
+ var limit = testDocumentCount; // exactly matches item count
10035
+ var iteration = firestoreIteration({
10036
+ limit: limit
10037
+ });
10038
+ var accumulator = firebaseQuerySnapshotAccumulator(iteration);
10039
+ // First page should return all items but not yet know it's the end
10040
+ iteration.nextPage().then(function() {
10041
+ // Load the next page which should discover end via empty snapshot
10042
+ iteration.nextPage().then(function() {
10043
+ sub.subscription = iteration.latestState$.pipe(filter(function(x) {
10044
+ return isLoadingStateFinishedLoading(x);
10045
+ }), first()).subscribe(function(state) {
10046
+ expect(state.hasNextPage).toBe(false);
10047
+ // Verify all items loaded without duplicates
10048
+ flattenAccumulatorResultItemArray(accumulator).pipe(first()).subscribe(function(values) {
10049
+ expect(values.length).toBe(testDocumentCount);
10050
+ expect(arrayContainsDuplicateValue(values.map(function(x) {
10051
+ return x.id;
10052
+ }))).toBe(false);
10053
+ done();
10054
+ });
10055
+ });
10056
+ });
10057
+ });
10058
+ }));
10059
+ it('should reach end when loading all pages with limit as a divisor of total count.', callbackTest(function(done) {
10060
+ var limit = 5; // divides evenly into testDocumentCount (10)
10061
+ var expectedPages = testDocumentCount / limit; // 2 full pages + 1 empty to discover end
10062
+ var iteration = firestoreIteration({
10063
+ limit: limit
10064
+ });
10065
+ var accumulator = firebaseQuerySnapshotAccumulator(iteration);
10066
+ // Load pages until the iterator reaches the end
10067
+ iteratorNextPageUntilPage(iteration, expectedPages + 1).then(function(page) {
10068
+ sub.subscription = iteration.latestState$.pipe(filter(function(x) {
10069
+ return isLoadingStateFinishedLoading(x);
10070
+ }), first()).subscribe(function(state) {
10071
+ expect(state.hasNextPage).toBe(false);
10072
+ // Verify all items loaded without duplicates
10073
+ flattenAccumulatorResultItemArray(accumulator).pipe(first()).subscribe(function(values) {
10074
+ expect(values.length).toBe(testDocumentCount);
10075
+ expect(arrayContainsDuplicateValue(values.map(function(x) {
10076
+ return x.id;
10077
+ }))).toBe(false);
10078
+ done();
10079
+ });
10080
+ });
10081
+ });
10082
+ }));
10033
10083
  });
10034
10084
  describe('with inferEndOfResultsFromPageSize=false', function() {
10035
10085
  it('should not mark the result as the end even when fewer items are returned than the limit.', callbackTest(function(done) {
package/test/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase/test",
3
- "version": "13.6.17",
3
+ "version": "13.8.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.17",
6
- "@dereekb/firebase": "13.6.17",
7
- "@dereekb/model": "13.6.17",
8
- "@dereekb/rxjs": "13.6.17",
9
- "@dereekb/util": "13.6.17",
5
+ "@dereekb/date": "13.8.0",
6
+ "@dereekb/firebase": "13.8.0",
7
+ "@dereekb/model": "13.8.0",
8
+ "@dereekb/rxjs": "13.8.0",
9
+ "@dereekb/util": "13.8.0",
10
10
  "@firebase/rules-unit-testing": "5.0.0",
11
11
  "date-fns": "^4.0.0",
12
- "firebase": "^12.0.0",
12
+ "firebase": "^12.12.0",
13
13
  "rxjs": "^7.8.0"
14
14
  },
15
15
  "exports": {