@dereekb/firebase 13.41.0 → 13.43.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/eslint/index.esm.js +145 -170
- package/eslint/package.json +3 -3
- package/index.esm.js +7207 -2997
- package/package.json +5 -5
- package/src/lib/common/firestore/snapshot/snapshot.field.d.ts +4 -0
- package/src/lib/common/storage/context.d.ts +9 -1
- package/src/lib/common/storage/driver/accessor.d.ts +12 -0
- package/src/lib/common/storage/index.d.ts +1 -0
- package/src/lib/common/storage/storage.url.d.ts +69 -0
- package/src/lib/model/calendar/calendar.action.d.ts +34 -0
- package/src/lib/model/calendar/calendar.api.d.ts +147 -0
- package/src/lib/model/calendar/calendar.api.error.d.ts +24 -0
- package/src/lib/model/calendar/calendar.d.ts +460 -0
- package/src/lib/model/calendar/calendar.expand.d.ts +95 -0
- package/src/lib/model/calendar/calendar.ics.d.ts +322 -0
- package/src/lib/model/calendar/calendar.id.d.ts +110 -0
- package/src/lib/model/calendar/calendar.processing.d.ts +77 -0
- package/src/lib/model/calendar/calendar.query.d.ts +76 -0
- package/src/lib/model/calendar/calendar.schedule.d.ts +82 -0
- package/src/lib/model/calendar/calendar.type.d.ts +188 -0
- package/src/lib/model/calendar/calendar.util.d.ts +485 -0
- package/src/lib/model/calendar/index.d.ts +12 -0
- package/src/lib/model/formspace/formspace.access.d.ts +139 -0
- package/src/lib/model/formspace/formspace.action.d.ts +34 -0
- package/src/lib/model/formspace/formspace.api.d.ts +216 -0
- package/src/lib/model/formspace/formspace.api.error.d.ts +71 -0
- package/src/lib/model/formspace/formspace.d.ts +390 -0
- package/src/lib/model/formspace/formspace.id.d.ts +60 -0
- package/src/lib/model/formspace/formspace.permission.d.ts +47 -0
- package/src/lib/model/formspace/formspace.processing.d.ts +86 -0
- package/src/lib/model/formspace/formspace.query.d.ts +110 -0
- package/src/lib/model/formspace/formspace.task.d.ts +75 -0
- package/src/lib/model/formspace/formspace.type.d.ts +267 -0
- package/src/lib/model/formspace/formspace.upload.d.ts +204 -0
- package/src/lib/model/formspace/formspace.util.d.ts +348 -0
- package/src/lib/model/formspace/index.d.ts +13 -0
- package/src/lib/model/index.d.ts +2 -0
- package/src/lib/model/notification/notification.message.d.ts +83 -0
- package/src/lib/model/notification/notification.query.d.ts +41 -0
- package/src/lib/model/oidcmodel/oidcmodel.query.d.ts +36 -0
- package/src/lib/model/storagefile/storagefile.api.d.ts +42 -2
- package/src/lib/model/storagefile/storagefile.create.d.ts +14 -3
- package/src/lib/model/storagefile/storagefile.file.d.ts +42 -2
- package/src/lib/model/storagefile/storagefile.query.d.ts +36 -0
- package/src/lib/model/storagefile/storagefile.upload.d.ts +30 -0
- package/src/lib/model/system/index.d.ts +1 -0
- package/src/lib/model/system/system.scheduler.d.ts +235 -0
- package/test/index.esm.js +24 -1
- package/test/package.json +6 -6
|
@@ -2,6 +2,7 @@ import { type Type } from 'arktype';
|
|
|
2
2
|
import { type TargetModelParams, type OnCallCreateModelResult, type FirestoreModelKey } from '../../common';
|
|
3
3
|
import { type ModelFirebaseCrudFunction, type FirebaseFunctionTypeConfigMap, type ModelFirebaseCrudFunctionConfigMap, type ModelFirebaseFunctionMap, type ModelFirebaseCreateFunction } from '../../client';
|
|
4
4
|
import { type StorageFileSignedDownloadUrl, type StorageFileTypes } from './storagefile';
|
|
5
|
+
import { type StorageFileUploadScope } from './storagefile.upload';
|
|
5
6
|
import { type StorageFileKey, type StorageFileId, type StorageFilePurpose } from './storagefile.id';
|
|
6
7
|
import { type StorageBucketId, type StorageMetadata, type StoragePath, type StorageSlashPath } from '../../common/storage';
|
|
7
8
|
import { type ContentDispositionString, type ContentTypeMimeType, type Maybe, type Milliseconds, type SlashPath, type SlashPathFile, type UnixDateTimeMillisecondsNumber, type UnixDateTimeSecondsNumber } from '@dereekb/util';
|
|
@@ -26,6 +27,14 @@ export interface InitializeAllStorageFilesFromUploadsParams {
|
|
|
26
27
|
readonly maxFilesToInitialize?: Maybe<number>;
|
|
27
28
|
readonly folderPath?: Maybe<StorageSlashPath>;
|
|
28
29
|
readonly overrideUploadsFolderPath?: Maybe<StorageSlashPath>;
|
|
30
|
+
/**
|
|
31
|
+
* Whether to expedite processing of each initialized file that ends up queued for it.
|
|
32
|
+
*
|
|
33
|
+
* The same option {@link InitializeStorageFileFromUploadParams} already offers for a single file. Without
|
|
34
|
+
* it a file initialized by this sweep waits for the next `processAllQueuedStorageFiles` pass, which is a
|
|
35
|
+
* whole scheduling tick of latency for a purpose whose processing the uploader is waiting on.
|
|
36
|
+
*/
|
|
37
|
+
readonly expediteProcessing?: Maybe<boolean>;
|
|
29
38
|
}
|
|
30
39
|
export declare const initializeAllStorageFilesFromUploadsParamsType: Type<InitializeAllStorageFilesFromUploadsParams>;
|
|
31
40
|
/**
|
|
@@ -51,13 +60,34 @@ export declare const initializeStorageFileFromUploadParamsType: Type<InitializeS
|
|
|
51
60
|
/**
|
|
52
61
|
* Parameters for triggering processing of a specific StorageFile.
|
|
53
62
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
63
|
+
* Which flag is required depends on the file's current processing state. A `FAILED` file
|
|
64
|
+
* restarts with no flag, while a `SUCCESS` file needs `processAgainIfSuccessful` (or
|
|
65
|
+
* `forceRestartProcessing`) — note that a file whose processor ran to completion is `SUCCESS`
|
|
66
|
+
* even when the outcome was a rejection, so re-validating a rejected file normally needs one of
|
|
67
|
+
* those flags. `ARCHIVED` and `DO_NOT_PROCESS` files cannot be processed at all.
|
|
68
|
+
*
|
|
69
|
+
* Validated with {@link processStorageFileParamsType}.
|
|
56
70
|
*/
|
|
57
71
|
export interface ProcessStorageFileParams extends TargetModelParams {
|
|
72
|
+
/**
|
|
73
|
+
* Runs the first step of the processing task inline instead of waiting for the scheduled task runner.
|
|
74
|
+
*/
|
|
58
75
|
readonly runImmediately?: Maybe<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* Checks an in-flight `PROCESSING` task immediately, instead of waiting for it to age past the
|
|
78
|
+
* stuck-check throttle.
|
|
79
|
+
*/
|
|
59
80
|
readonly checkRetryProcessing?: Maybe<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* Abandons the file's existing processing task and begins a new one, clearing the completed
|
|
83
|
+
* checkpoints so the flow runs again from the start. For a `PROCESSING` file this is only applied
|
|
84
|
+
* once the retry check runs, so pair it with `checkRetryProcessing` to force a restart while the
|
|
85
|
+
* existing task is still within the stuck-check throttle.
|
|
86
|
+
*/
|
|
60
87
|
readonly forceRestartProcessing?: Maybe<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* Allows processing a file that has already finished processing and is in the `SUCCESS` state.
|
|
90
|
+
*/
|
|
61
91
|
readonly processAgainIfSuccessful?: Maybe<boolean>;
|
|
62
92
|
}
|
|
63
93
|
export declare const processStorageFileParamsType: Type<ProcessStorageFileParams>;
|
|
@@ -322,7 +352,17 @@ export interface CreateStorageFileSignedUploadUrlParams {
|
|
|
322
352
|
* when omitted.
|
|
323
353
|
*/
|
|
324
354
|
readonly expiresInMs?: Maybe<Milliseconds>;
|
|
355
|
+
/**
|
|
356
|
+
* The model, and optionally the slot within it, this upload belongs to. Required when the resolved
|
|
357
|
+
* policy sets `requiresScopeInput: true` (e.g. a FormSpace upload, which is keyed by space and slot
|
|
358
|
+
* rather than by the uid alone).
|
|
359
|
+
*/
|
|
360
|
+
readonly scope?: Maybe<StorageFileUploadScope>;
|
|
325
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Arktype for a {@link StorageFileUploadScope}.
|
|
364
|
+
*/
|
|
365
|
+
export declare const storageFileUploadScopeType: Type<StorageFileUploadScope>;
|
|
326
366
|
export declare const createStorageFileSignedUploadUrlParamsType: Type<CreateStorageFileSignedUploadUrlParams>;
|
|
327
367
|
/**
|
|
328
368
|
* Result of creating a signed upload URL.
|
|
@@ -5,7 +5,7 @@ import { type FirestoreDocumentAccessor } from '../../common/firestore/accessor/
|
|
|
5
5
|
import { type FirebaseStorageAccessorFile } from '../../common/storage/driver/accessor';
|
|
6
6
|
import { type StoragePathRef, type StoragePath } from '../../common/storage/storage';
|
|
7
7
|
import { type FirebaseAuthOwnershipKey, type FirebaseAuthUserId } from '../../common/auth/auth';
|
|
8
|
-
import { type StorageFilePurposeSubgroup, type StorageFileGroupId, type StorageFileGroupRelatedStorageFilePurpose, type StorageFileMetadata, type StorageFilePurpose } from './storagefile.id';
|
|
8
|
+
import { type StorageFilePurposeSubgroup, type StorageFileGroupId, type StorageFileGroupRelatedStorageFilePurpose, type StorageFileId, type StorageFileMetadata, type StorageFilePurpose } from './storagefile.id';
|
|
9
9
|
import { type ReadFirestoreModelKeyInput } from '../../common';
|
|
10
10
|
/**
|
|
11
11
|
* Input for creating a StorageFile document paired with its storage path.
|
|
@@ -13,8 +13,8 @@ import { type ReadFirestoreModelKeyInput } from '../../common';
|
|
|
13
13
|
* Provides all the fields needed to create a fully-configured {@link StorageFile} Firestore document,
|
|
14
14
|
* including ownership, purpose, group membership, and processing state.
|
|
15
15
|
*
|
|
16
|
-
* Either a `file`, `storagePathRef`, or `
|
|
17
|
-
* Either an `accessor` or `context` must be provided for Firestore document creation.
|
|
16
|
+
* Either a `file`, `storagePathRef`, `storagePath`, or `storagePathFactory` must be provided to identify the
|
|
17
|
+
* storage location. Either an `accessor` or `context` must be provided for Firestore document creation.
|
|
18
18
|
*
|
|
19
19
|
* @template M - type of arbitrary metadata stored in the `d` field
|
|
20
20
|
*/
|
|
@@ -43,6 +43,17 @@ export interface CreateStorageFileDocumentPairInput<M extends StorageFileMetadat
|
|
|
43
43
|
* File to use when creating the StorageFile.
|
|
44
44
|
*/
|
|
45
45
|
readonly file?: FirebaseStorageAccessorFile;
|
|
46
|
+
/**
|
|
47
|
+
* Builds the storage path from the id of the StorageFile document being created.
|
|
48
|
+
*
|
|
49
|
+
* Use this when the path should be keyed by the StorageFile itself rather than by the model it belongs
|
|
50
|
+
* to. Two StorageFiles then can never resolve to the same path, so replacing one cannot delete the
|
|
51
|
+
* other's content, and the path is not guessable from the owning model's id.
|
|
52
|
+
*
|
|
53
|
+
* Only consulted when no `file`, `storagePathRef`, or `storagePath` is provided, and only for a
|
|
54
|
+
* DIRECTLY_CREATED StorageFile — a FOR_STORAGE_FILE_GROUP file has a deterministic id by design.
|
|
55
|
+
*/
|
|
56
|
+
readonly storagePathFactory?: Maybe<(storageFileId: StorageFileId) => StoragePath>;
|
|
46
57
|
/**
|
|
47
58
|
* The display name of the StorageFile.
|
|
48
59
|
*
|
|
@@ -1,11 +1,51 @@
|
|
|
1
|
-
import { type Factory, type FactoryWithRequiredInput, type Maybe, type SlashPathDetails } from '@dereekb/util';
|
|
2
|
-
import { type StoragePath } from '../../common/storage/storage';
|
|
1
|
+
import { type ContentTypeMimeType, type Factory, type FactoryWithRequiredInput, type Maybe, type SlashPathDetails, type SlashPathFile } from '@dereekb/util';
|
|
2
|
+
import { type StoragePath, type StorageSlashPath } from '../../common/storage/storage';
|
|
3
|
+
import { type StorageFileDisplayName } from './storagefile.id';
|
|
3
4
|
import { type StorageCustomMetadata } from '../../common/storage/types';
|
|
4
5
|
import { type FirebaseStorageAccessorFile } from '../../common/storage/driver/accessor';
|
|
5
6
|
/**
|
|
6
7
|
* Input for a {@link StoredFileReader}, carrying the storage bucket and path of the file.
|
|
7
8
|
*/
|
|
8
9
|
export type StoredFileReaderInput = StoragePath;
|
|
10
|
+
/**
|
|
11
|
+
* Input for {@link storageFileDisplayFileName}.
|
|
12
|
+
*/
|
|
13
|
+
export interface StorageFileDisplayFileNameInput {
|
|
14
|
+
/**
|
|
15
|
+
* The StorageFile's own `n`, which is UNTYPED by contract (see {@link StorageFileDisplayName}).
|
|
16
|
+
*/
|
|
17
|
+
readonly displayName?: Maybe<StorageFileDisplayName>;
|
|
18
|
+
/**
|
|
19
|
+
* The object's path — a StorageFile's `pathString`, or the `name` off its storage metadata.
|
|
20
|
+
*/
|
|
21
|
+
readonly pathString?: Maybe<StorageSlashPath>;
|
|
22
|
+
/**
|
|
23
|
+
* The object's content type, used when the path names no extension.
|
|
24
|
+
*/
|
|
25
|
+
readonly contentType?: Maybe<ContentTypeMimeType>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Composes the name a stored file should be presented to a user under.
|
|
29
|
+
*
|
|
30
|
+
* THE one place that answers "what is this file called". A StorageFile's `n` is untyped by contract, so
|
|
31
|
+
* the extension always comes from the object's own path — which is why a purpose whose destination is not
|
|
32
|
+
* name-keyed (a FormSpace file lives at `.../{index}.{ext}`) still downloads and zips under the name its
|
|
33
|
+
* uploader gave it.
|
|
34
|
+
*
|
|
35
|
+
* Falls back to the path's own leaf when there is no display name, so a StorageFile that never had one
|
|
36
|
+
* behaves exactly as it did before.
|
|
37
|
+
*
|
|
38
|
+
* @param input - The display name, the object path, and the content type.
|
|
39
|
+
* @returns The composed file name, or null when the input names neither.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* storageFileDisplayFileName({ displayName: 'resume', pathString: '/fsp/f1/resume/0.pdf' }); // 'resume.pdf'
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @__NO_SIDE_EFFECTS__
|
|
47
|
+
*/
|
|
48
|
+
export declare function storageFileDisplayFileName(input: StorageFileDisplayFileNameInput): Maybe<SlashPathFile>;
|
|
9
49
|
/**
|
|
10
50
|
* Factory that creates a {@link StoredFileReader} from a {@link FirebaseStorageAccessorFile}.
|
|
11
51
|
*
|
|
@@ -9,6 +9,11 @@ import { type FirebaseAuthUserId } from '../../common/auth/auth';
|
|
|
9
9
|
*
|
|
10
10
|
* @returns Firestore query constraints for StorageFiles queued for processing.
|
|
11
11
|
*
|
|
12
|
+
* @dbxModelFirebaseIndex
|
|
13
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
14
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
15
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
16
|
+
*
|
|
12
17
|
* @example
|
|
13
18
|
* ```ts
|
|
14
19
|
* const constraints = storageFilesQueuedForProcessingQuery();
|
|
@@ -24,6 +29,11 @@ export declare function storageFilesQueuedForProcessingQuery(): FirestoreQueryCo
|
|
|
24
29
|
* @param now - Reference time for comparison; defaults to current time.
|
|
25
30
|
* @returns Firestore query constraints for StorageFiles whose scheduled delete date has passed.
|
|
26
31
|
*
|
|
32
|
+
* @dbxModelFirebaseIndex
|
|
33
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
34
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
35
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
36
|
+
*
|
|
27
37
|
* @example
|
|
28
38
|
* ```ts
|
|
29
39
|
* const constraints = storageFilesQueuedForDeleteQuery();
|
|
@@ -48,6 +58,12 @@ export interface StorageFilePurposeAndUserQueryInput {
|
|
|
48
58
|
* @param input - The user, purpose, and optional subgroup to filter by.
|
|
49
59
|
* @returns Firestore query constraints for the given purpose and user.
|
|
50
60
|
*
|
|
61
|
+
* @dbxModelFirebaseIndex
|
|
62
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
63
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
64
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
65
|
+
* @dbxModelFirebaseIndexSkip true
|
|
66
|
+
*
|
|
51
67
|
* @example
|
|
52
68
|
* @example
|
|
53
69
|
* ```ts
|
|
@@ -65,6 +81,11 @@ export declare function storageFilePurposeAndUserQuery(input: StorageFilePurpose
|
|
|
65
81
|
*
|
|
66
82
|
* @returns Firestore query constraints for StorageFiles flagged for group synchronization.
|
|
67
83
|
*
|
|
84
|
+
* @dbxModelFirebaseIndex
|
|
85
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
86
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
87
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
88
|
+
*
|
|
68
89
|
* @example
|
|
69
90
|
* @example
|
|
70
91
|
* ```ts
|
|
@@ -79,6 +100,11 @@ export declare function storageFileFlaggedForSyncWithGroupsQuery(): FirestoreQue
|
|
|
79
100
|
*
|
|
80
101
|
* @returns Firestore query constraints for StorageFileGroups needing initialization.
|
|
81
102
|
*
|
|
103
|
+
* @dbxModelFirebaseIndex
|
|
104
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
105
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
106
|
+
* @dbxModelFirebaseIndexCategory init
|
|
107
|
+
*
|
|
82
108
|
* @example
|
|
83
109
|
* @example
|
|
84
110
|
* ```ts
|
|
@@ -91,6 +117,11 @@ export declare function storageFileGroupsFlaggedForNeedsInitializationQuery(): F
|
|
|
91
117
|
*
|
|
92
118
|
* @returns Firestore query constraints for StorageFileGroups flagged for content regeneration.
|
|
93
119
|
*
|
|
120
|
+
* @dbxModelFirebaseIndex
|
|
121
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
122
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
123
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
124
|
+
*
|
|
94
125
|
* @example
|
|
95
126
|
* @example
|
|
96
127
|
* ```ts
|
|
@@ -105,6 +136,11 @@ export declare function storageFileGroupsFlaggedForContentRegenerationQuery(): F
|
|
|
105
136
|
*
|
|
106
137
|
* @returns Firestore query constraints for StorageFileGroups flagged as invalid.
|
|
107
138
|
*
|
|
139
|
+
* @dbxModelFirebaseIndex
|
|
140
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
141
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
142
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
143
|
+
*
|
|
108
144
|
* @example
|
|
109
145
|
* @example
|
|
110
146
|
* ```ts
|
|
@@ -105,6 +105,28 @@ export type StorageFileInitializeFromUploadResultType = 'success' | 'no_determin
|
|
|
105
105
|
export interface StorageFilePurposeUploadPolicyBuildPathInput {
|
|
106
106
|
readonly uid: FirebaseAuthUserId;
|
|
107
107
|
readonly filename?: Maybe<SlashPathFile>;
|
|
108
|
+
/**
|
|
109
|
+
* The model the upload is scoped to, for a purpose whose destination is not derivable from the uid alone.
|
|
110
|
+
*
|
|
111
|
+
* Required when the policy sets {@link StorageFilePurposeUploadPolicy.requiresScopeInput}.
|
|
112
|
+
*/
|
|
113
|
+
readonly scope?: Maybe<StorageFileUploadScope>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Names the specific model, and optionally the slot within it, that an upload belongs to.
|
|
117
|
+
*
|
|
118
|
+
* This is what lets ONE purpose serve many destinations: a FormSpace upload is `{ id: formSpaceId,
|
|
119
|
+
* subgroup: slot }` under a single `form_space` purpose, rather than a purpose per form type.
|
|
120
|
+
*/
|
|
121
|
+
export interface StorageFileUploadScope {
|
|
122
|
+
/**
|
|
123
|
+
* Id of the model the upload is scoped to.
|
|
124
|
+
*/
|
|
125
|
+
readonly id: string;
|
|
126
|
+
/**
|
|
127
|
+
* Slot/subgroup within the scoped model, when the model has more than one.
|
|
128
|
+
*/
|
|
129
|
+
readonly subgroup?: Maybe<string>;
|
|
108
130
|
}
|
|
109
131
|
/**
|
|
110
132
|
* Per-purpose constraints for generating short-lived signed upload URLs.
|
|
@@ -128,4 +150,12 @@ export interface StorageFilePurposeUploadPolicy {
|
|
|
128
150
|
* When false (e.g. avatar), the path is derived solely from the uid.
|
|
129
151
|
*/
|
|
130
152
|
readonly requiresFilenameInput: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* When true, the caller MUST provide a {@link StorageFileUploadScope} for `buildUploadPath`.
|
|
155
|
+
*
|
|
156
|
+
* Set by a purpose whose destination folder is keyed by a model rather than by the uid alone. The scope
|
|
157
|
+
* is only a PATH input — it is not itself authorization; the purpose's initializer is what loads the
|
|
158
|
+
* scoped model and decides whether this uploader may write into it.
|
|
159
|
+
*/
|
|
160
|
+
readonly requiresScopeInput?: boolean;
|
|
131
161
|
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module system.scheduler
|
|
3
|
+
*
|
|
4
|
+
* Declares the framework-owned `scheduler` {@link SystemState} type, plus the pure predicates and
|
|
5
|
+
* the moment-bound read object that make up the "has this schedule already run in this hour?" gate.
|
|
6
|
+
*
|
|
7
|
+
* The gate exists so a cron that fires every hour can run a body that should only run every Nth
|
|
8
|
+
* hour, without each individual task having to carry a throttle of its own. Evaluate it ONCE at the
|
|
9
|
+
* top of a schedule function: pass, run the work; fail, return.
|
|
10
|
+
*
|
|
11
|
+
* The stateful half — read, evaluate, and claim the hour against Firestore — lives in
|
|
12
|
+
* `@dereekb/firebase-server/model` as `schedulerSystemStateAccessorFactory()`. Only the shape, the
|
|
13
|
+
* predicates, and {@link schedulerSystemStateRead} live here, so the evaluation can be exercised
|
|
14
|
+
* without an emulator and the converter can be registered from an app's client-shared converter
|
|
15
|
+
* map.
|
|
16
|
+
*/
|
|
17
|
+
import { type HourOfDay, type Hours, type Maybe } from '@dereekb/util';
|
|
18
|
+
import { type FirestoreDocument, type FirestoreDocumentAccessor } from '../../common';
|
|
19
|
+
import { type SystemState, type SystemStateDocument, type SystemStateStoredData, type SystemStateStoredDataFieldConverterConfig } from './system';
|
|
20
|
+
/**
|
|
21
|
+
* {@link SystemState} type identifier for the scheduler's run-gate state.
|
|
22
|
+
*
|
|
23
|
+
* Also the document id, per the SystemState singleton convention — so this state lives at
|
|
24
|
+
* `sys/scheduler`.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SCHEDULER_SYSTEM_STATE_TYPE = "scheduler";
|
|
27
|
+
/**
|
|
28
|
+
* Scheduler state for gating scheduled work to at most one run per hour window.
|
|
29
|
+
*
|
|
30
|
+
* ONE gate, ONE `lat`. This is deliberately not per-task and not per-table state: it answers
|
|
31
|
+
* "has the scheduler already run its Nth-hour body during this hour?" for the app as a whole. Two
|
|
32
|
+
* callers sharing the document with different `everyNHours` values will have whichever one passes
|
|
33
|
+
* first claim the hour for both.
|
|
34
|
+
*
|
|
35
|
+
* @dbxModelSubObject
|
|
36
|
+
*/
|
|
37
|
+
export interface SchedulerSystemData extends SystemStateStoredData {
|
|
38
|
+
/**
|
|
39
|
+
* Last run at. The single gate anchor — the moment the most recent passing check claimed its hour.
|
|
40
|
+
*
|
|
41
|
+
* @dbxModelVariable lastRunAt
|
|
42
|
+
*/
|
|
43
|
+
lat?: Maybe<Date>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Firestore field converter for {@link SchedulerSystemData}.
|
|
47
|
+
*
|
|
48
|
+
* Register it in the app's `SystemStateStoredDataConverterMap` under
|
|
49
|
+
* {@link SCHEDULER_SYSTEM_STATE_TYPE}. This is NOT optional: without it the collection falls back to
|
|
50
|
+
* the pass-through converter, `lat` reads back as a raw Firestore `Timestamp` instead of a `Date`,
|
|
51
|
+
* and {@link hasRunInCurrentHour} can never match — so the gate would open on every single call.
|
|
52
|
+
*/
|
|
53
|
+
export declare const schedulerSystemDataConverter: SystemStateStoredDataFieldConverterConfig<SchedulerSystemData>;
|
|
54
|
+
/**
|
|
55
|
+
* Loads the {@link SystemStateDocument} that stores {@link SchedulerSystemData}, using
|
|
56
|
+
* {@link SCHEDULER_SYSTEM_STATE_TYPE} as the document id.
|
|
57
|
+
*
|
|
58
|
+
* @param accessor - The document accessor for the SystemState collection.
|
|
59
|
+
* @returns The SystemState document for the scheduler state.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const doc = loadSchedulerSystemState(systemStateCollection.documentAccessor());
|
|
64
|
+
* const data = await doc.snapshotData();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadSchedulerSystemState<D extends FirestoreDocument<SystemState<SystemStateStoredData>>>(accessor: FirestoreDocumentAccessor<SystemState<SystemStateStoredData>, D>): SystemStateDocument<SchedulerSystemData>;
|
|
68
|
+
/**
|
|
69
|
+
* Whether the date's hour-of-day is an Nth hour, in the ambient timezone.
|
|
70
|
+
*
|
|
71
|
+
* This is a modulo against the hour-of-day, NOT an interval since some epoch — so at hour 12 it is
|
|
72
|
+
* true for N = 1, 2, 3, 4, 6, and 12, and false for N = 5. Only divisors of 24 divide the day
|
|
73
|
+
* evenly; a non-divisor (5, 7, 9, …) will still fire, just with a short window across midnight.
|
|
74
|
+
*
|
|
75
|
+
* An `everyNHours` of zero or less has no meaningful Nth hour and returns false rather than
|
|
76
|
+
* dividing by zero.
|
|
77
|
+
*
|
|
78
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
79
|
+
* @param date - Moment to test; defaults to now.
|
|
80
|
+
* @returns True when the date's hour-of-day is an Nth hour.
|
|
81
|
+
*
|
|
82
|
+
* @dbxUtil
|
|
83
|
+
* @dbxUtilCategory date
|
|
84
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, modulo
|
|
85
|
+
* @dbxUtilRelated has-run-in-current-hour
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* isNthHourOfDay(3, new Date('2024-01-01T12:30:00')); // true (12 % 3 === 0)
|
|
90
|
+
* isNthHourOfDay(5, new Date('2024-01-01T12:30:00')); // false (12 % 5 === 2)
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function isNthHourOfDay(everyNHours: Hours, date?: Maybe<Date>): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the last run falls inside the same hour as now.
|
|
96
|
+
*
|
|
97
|
+
* This is a same-hour-bucket compare, not an elapsed-time compare: a run at 12:59 and a `now` of
|
|
98
|
+
* 13:01 are two minutes apart but in different hours, so this returns false. That is what makes the
|
|
99
|
+
* gate track the hourly cron rather than drift with it.
|
|
100
|
+
*
|
|
101
|
+
* A null `lastRunAt` means nothing has run, so it returns false.
|
|
102
|
+
*
|
|
103
|
+
* @param lastRunAt - Moment the work last ran.
|
|
104
|
+
* @param now - Moment to compare against; defaults to now.
|
|
105
|
+
* @returns True when both moments fall in the same hour.
|
|
106
|
+
*
|
|
107
|
+
* @dbxUtil
|
|
108
|
+
* @dbxUtilCategory date
|
|
109
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, window
|
|
110
|
+
* @dbxUtilRelated is-nth-hour-of-day, round-down-to-hour
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* hasRunInCurrentHour(new Date('2024-01-01T12:05:00'), new Date('2024-01-01T12:55:00')); // true
|
|
115
|
+
* hasRunInCurrentHour(new Date('2024-01-01T12:59:00'), new Date('2024-01-01T13:01:00')); // false
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function hasRunInCurrentHour(lastRunAt: Maybe<Date>, now?: Maybe<Date>): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* The zero-based index of the date's hour within the day's every-N-hours schedule, or null when the
|
|
121
|
+
* hour is not an Nth hour.
|
|
122
|
+
*
|
|
123
|
+
* At hour 12 with N=3 the day's matching hours are 0, 3, 6, 9, 12 — so the index is 4. Use it to
|
|
124
|
+
* rotate work across a day's windows ("do the expensive pass only on index 0") without each caller
|
|
125
|
+
* re-deriving the arithmetic from the hour-of-day.
|
|
126
|
+
*
|
|
127
|
+
* Returns null rather than a number for a non-matching hour, so a falsy check cannot confuse
|
|
128
|
+
* "index 0" — the first window of the day — with "no window".
|
|
129
|
+
*
|
|
130
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
131
|
+
* @param date - Moment to test; defaults to now.
|
|
132
|
+
* @returns The zero-based window index, or null when the hour is not an Nth hour.
|
|
133
|
+
*
|
|
134
|
+
* @dbxUtil
|
|
135
|
+
* @dbxUtilCategory date
|
|
136
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, index, window
|
|
137
|
+
* @dbxUtilRelated is-nth-hour-of-day
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* nthHourOfDayIndex(3, new Date('2024-01-01T12:30:00')); // 4 (0, 3, 6, 9, 12)
|
|
142
|
+
* nthHourOfDayIndex(3, new Date('2024-01-01T13:30:00')); // null (13 % 3 !== 0)
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function nthHourOfDayIndex(everyNHours: Hours, date?: Maybe<Date>): Maybe<number>;
|
|
146
|
+
/**
|
|
147
|
+
* A read of the scheduler's gate state, evaluated against a single moment.
|
|
148
|
+
*
|
|
149
|
+
* Holds the `now` and the `lastRunAt` it was built from, so every question below is answered against
|
|
150
|
+
* the same moment. Asking about N=2 and then N=3 cannot straddle an hour boundary mid-evaluation,
|
|
151
|
+
* and no question re-reads the document.
|
|
152
|
+
*
|
|
153
|
+
* This is the pure half of the gate. The Firestore-backed half — read, evaluate, and CLAIM the hour
|
|
154
|
+
* in a transaction — is `schedulerSystemStateAccessorFactory()` in `@dereekb/firebase-server/model`.
|
|
155
|
+
*/
|
|
156
|
+
export interface SchedulerSystemStateRead {
|
|
157
|
+
/**
|
|
158
|
+
* The moment this read was taken at.
|
|
159
|
+
*/
|
|
160
|
+
readonly now: Date;
|
|
161
|
+
/**
|
|
162
|
+
* The hour-of-day of {@link SchedulerSystemStateRead.now}, in the ambient timezone.
|
|
163
|
+
*/
|
|
164
|
+
readonly hourOfDay: HourOfDay;
|
|
165
|
+
/**
|
|
166
|
+
* The moment the most recent passing check claimed its hour, or null if the gate has never been
|
|
167
|
+
* claimed.
|
|
168
|
+
*/
|
|
169
|
+
readonly lastRunAt: Maybe<Date>;
|
|
170
|
+
/**
|
|
171
|
+
* Whether {@link SchedulerSystemStateRead.lastRunAt} falls inside the same hour as `now` — that is,
|
|
172
|
+
* whether the gate has already been claimed during this hour.
|
|
173
|
+
*/
|
|
174
|
+
readonly hasRunInCurrentHour: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Whether the gate is open for the given interval: `now` is an Nth hour of the day AND the gate
|
|
177
|
+
* has not already been claimed during this hour.
|
|
178
|
+
*
|
|
179
|
+
* This is a pure evaluation — it does NOT claim the hour. Use
|
|
180
|
+
* `SchedulerSystemStateAccessor.checkAndClaim()` to actually gate work.
|
|
181
|
+
*
|
|
182
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
183
|
+
* @returns True when the gate is open.
|
|
184
|
+
*/
|
|
185
|
+
isOpen(everyNHours: Hours): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* {@link isNthHourOfDay} bound to this read's `now`.
|
|
188
|
+
*
|
|
189
|
+
* Unlike {@link SchedulerSystemStateRead.isOpen} this ignores `lastRunAt` entirely, which is what
|
|
190
|
+
* makes it the right sub-gate for work running INSIDE an already-claimed hour: the hour is spent
|
|
191
|
+
* either way, so each task only needs to ask whether this is its hour.
|
|
192
|
+
*
|
|
193
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
194
|
+
* @returns True when this read's hour-of-day is an Nth hour.
|
|
195
|
+
*/
|
|
196
|
+
isNthHourOfDay(everyNHours: Hours): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* {@link nthHourOfDayIndex} bound to this read's `now`.
|
|
199
|
+
*
|
|
200
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
201
|
+
* @returns The zero-based window index, or null when this read's hour is not an Nth hour.
|
|
202
|
+
*/
|
|
203
|
+
nthHourOfDayIndex(everyNHours: Hours): Maybe<number>;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Configuration for {@link schedulerSystemStateRead}.
|
|
207
|
+
*/
|
|
208
|
+
export interface SchedulerSystemStateReadConfig {
|
|
209
|
+
/**
|
|
210
|
+
* The moment to evaluate the gate against. Defaults to the current time.
|
|
211
|
+
*/
|
|
212
|
+
readonly now?: Maybe<Date>;
|
|
213
|
+
/**
|
|
214
|
+
* The `lat` read off the scheduler document, or null/undefined when it has never been claimed.
|
|
215
|
+
*/
|
|
216
|
+
readonly lastRunAt: Maybe<Date>;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Creates a {@link SchedulerSystemStateRead} from a moment and a last-run.
|
|
220
|
+
*
|
|
221
|
+
* Every predicate on the result closes over the single `now` resolved here, so a caller can ask
|
|
222
|
+
* about any number of intervals off one read without drifting across an hour boundary.
|
|
223
|
+
*
|
|
224
|
+
* @param config - The moment and the document's last-run.
|
|
225
|
+
* @returns The evaluated read.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* const read = schedulerSystemStateRead({ now, lastRunAt });
|
|
230
|
+
*
|
|
231
|
+
* read.isOpen(3); // should the every-3-hours body run?
|
|
232
|
+
* read.isNthHourOfDay(6); // is this also a 6th hour, for a sub-gated task?
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export declare function schedulerSystemStateRead(config: SchedulerSystemStateReadConfig): SchedulerSystemStateRead;
|
package/test/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { performAsyncTasks, cachedGetter, bitwiseObjectDencoder, modelFieldConversions, compareStrings, waitForMs, isEvenNumber, unique, arrayFactory, mapGetter, randomNumberFactory, randomFromArrayFactory, idBatchFactory, arrayContainsDuplicateValue, useCallback, readableStreamToBuffer, SLASH_PATH_SEPARATOR } from '@dereekb/util';
|
|
2
2
|
import { initializeTestEnvironment } from '@firebase/rules-unit-testing';
|
|
3
3
|
export { assertFails, assertSucceeds } from '@firebase/rules-unit-testing';
|
|
4
|
-
import { firebaseStorageContextFactory, firestoreContextFactory, firebaseFirestoreClientDrivers, firebaseStorageClientDrivers, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, defaultPagedItemPageDataConverter, AbstractFirestoreDocument, AbstractFirestoreDocumentWithParent, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, limit, orderBy, limitToLast, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, streamFromOnSnapshot, latestSnapshotsFromDocuments, newDocuments, loadDocumentsForDocumentReferences, loadDocumentsForDocumentReferencesFromValues, loadDocumentsForKeys, loadDocumentsForKeysFromValues, loadDocumentsForIds, loadDocumentsForIdsFromValues, firestoreDocumentLoader, setIdAndKeyFromKeyIdRefOnDocumentData, firestoreModelIdFromDocument, firestoreModelIdsFromDocuments, firestoreModelKeyFromDocument, firestoreModelKeysFromDocuments, documentReferenceFromDocument, documentReferencesFromDocuments, limitedFirestoreDocumentAccessorSnapshotCache, mapLatestSnapshotsFromDocuments, streamDocumentSnapshotsData, dataFromDocumentSnapshots, streamDocumentSnapshotDataPairs, streamDocumentSnapshotDataPairsWithData, getDocumentSnapshots, getDocumentSnapshotPair, getDocumentSnapshotDataPair, getDocumentSnapshotDataPairs, getDocumentSnapshotDataPairsWithData, getDocumentSnapshotDataTuples, getDocumentSnapshotData, getDocumentSnapshotsData, getDataFromDocumentSnapshots, loadDocumentsForSnapshots, firestoreDocumentSnapshotPairsLoader, documentData, documentDataFunction, documentDataWithIdAndKey, setIdAndKeyFromSnapshotOnDocumentData, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
|
|
4
|
+
import { firebaseStorageContextFactory, firestoreContextFactory, firebaseFirestoreClientDrivers, firebaseStorageClientDrivers, firestoreModelIdentity, snapshotConverterFunctions, firestoreBoolean, optionalFirestoreNumber, optionalFirestoreDate, optionalFirestoreArray, optionalFirestoreString, firestoreDate, firestoreBitwiseObjectMap, firestoreUniqueStringArray, firestoreNumber, firestoreString, firestoreUID, copyUserRelatedDataAccessorFactoryFunction, firestoreSubObject, defaultPagedItemPageDataConverter, AbstractFirestoreDocument, AbstractFirestoreDocumentWithParent, firebaseModelServiceFactory, grantFullAccessIfAdmin, firebaseModelsService, systemStateFirestoreCollection, allChildDocumentsUnderParent, where, makeDocuments, getDocumentSnapshotPairs, useDocumentSnapshot, useDocumentSnapshotData, firestoreIdBatchVerifierFactory, limit, orderBy, limitToLast, whereDocumentId, loadAllFirestoreDocumentSnapshotPairs, loadAllFirestoreDocumentSnapshot, iterateFirestoreDocumentSnapshotPairs, iterateFirestoreDocumentSnapshots, iterateFirestoreDocumentSnapshotPairBatches, iterateFirestoreDocumentSnapshotBatches, whereStringHasRootIdentityModelKey, whereStringValueHasPrefix, whereDateIsAfterWithSort, whereDateIsBeforeWithSort, whereDateIsOnOrAfterWithSort, whereDateIsOnOrBeforeWithSort, whereDateIsInRange, whereDateIsBetween, startAt, orderByDocumentId, startAtValue, startAfter, endAt, endAtValue, endBefore, streamFromOnSnapshot, latestSnapshotsFromDocuments, newDocuments, loadDocumentsForDocumentReferences, loadDocumentsForDocumentReferencesFromValues, loadDocumentsForKeys, loadDocumentsForKeysFromValues, loadDocumentsForIds, loadDocumentsForIdsFromValues, firestoreDocumentLoader, setIdAndKeyFromKeyIdRefOnDocumentData, firestoreModelIdFromDocument, firestoreModelIdsFromDocuments, firestoreModelKeyFromDocument, firestoreModelKeysFromDocuments, documentReferenceFromDocument, documentReferencesFromDocuments, limitedFirestoreDocumentAccessorSnapshotCache, mapLatestSnapshotsFromDocuments, streamDocumentSnapshotsData, dataFromDocumentSnapshots, streamDocumentSnapshotDataPairs, streamDocumentSnapshotDataPairsWithData, getDocumentSnapshots, getDocumentSnapshotPair, getDocumentSnapshotDataPair, getDocumentSnapshotDataPairs, getDocumentSnapshotDataPairsWithData, getDocumentSnapshotDataTuples, getDocumentSnapshotData, getDocumentSnapshotsData, getDataFromDocumentSnapshots, loadDocumentsForSnapshots, firestoreDocumentSnapshotPairsLoader, documentData, documentDataFunction, documentDataWithIdAndKey, setIdAndKeyFromSnapshotOnDocumentData, firebaseQuerySnapshotAccumulator, firebaseQueryItemAccumulator, storagePublicDownloadUrl, uploadFileWithStream, iterateStorageListFilesByEachFile } from '@dereekb/firebase';
|
|
5
5
|
import { setLogLevel } from 'firebase/firestore';
|
|
6
6
|
import { AbstractTestContextFixture, instanceWrapTestContextFactory, AbstractWrappedFixtureWithInstance, itShouldFail, callbackTest, expectFail } from '@dereekb/util/test';
|
|
7
7
|
import { existsSync, readFileSync, createReadStream } from 'node:fs';
|
|
@@ -13221,6 +13221,29 @@ function _ts_generator(thisArg, body) {
|
|
|
13221
13221
|
})();
|
|
13222
13222
|
});
|
|
13223
13223
|
});
|
|
13224
|
+
describe('getPublicUrl()', function() {
|
|
13225
|
+
it('should be reproducible by storagePublicDownloadUrl() from the bucket and path alone.', function() {
|
|
13226
|
+
// The point of this case: the CLIENT derives a public url with storagePublicDownloadUrl() rather
|
|
13227
|
+
// than calling anything, and that derivation is only useful if it matches what the SERVER actually
|
|
13228
|
+
// produces. Only the server driver implements getPublicUrl(), so this pins the shared function
|
|
13229
|
+
// against the real `@google-cloud/storage` output instead of against a reading of its source.
|
|
13230
|
+
if (existsFile.getPublicUrl) {
|
|
13231
|
+
var publicUrl = existsFile.getPublicUrl();
|
|
13232
|
+
var _existsFile_storagePath = existsFile.storagePath, bucketId = _existsFile_storagePath.bucketId, pathString = _existsFile_storagePath.pathString;
|
|
13233
|
+
// split off the origin the server resolved (localhost in the emulator, GCS in production) and
|
|
13234
|
+
// hand it back, so what is under test is the bucket + path half of the url
|
|
13235
|
+
var apiEndpoint = publicUrl.slice(0, publicUrl.indexOf("/".concat(bucketId, "/")));
|
|
13236
|
+
expect(apiEndpoint).toBeTruthy();
|
|
13237
|
+
expect(storagePublicDownloadUrl({
|
|
13238
|
+
apiEndpoint: apiEndpoint,
|
|
13239
|
+
storagePath: {
|
|
13240
|
+
bucketId: bucketId,
|
|
13241
|
+
pathString: pathString
|
|
13242
|
+
}
|
|
13243
|
+
})).toBe(publicUrl);
|
|
13244
|
+
}
|
|
13245
|
+
});
|
|
13246
|
+
});
|
|
13224
13247
|
// Cannot be tested, will throw "Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information."
|
|
13225
13248
|
describe('getSignedUrl()', function() {
|
|
13226
13249
|
it('should return the signed read url.', function() {
|
package/test/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase/test",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.43.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/model": "13.
|
|
9
|
-
"@dereekb/rxjs": "13.
|
|
10
|
-
"@dereekb/util": "13.
|
|
6
|
+
"@dereekb/date": "13.43.0",
|
|
7
|
+
"@dereekb/firebase": "13.43.0",
|
|
8
|
+
"@dereekb/model": "13.43.0",
|
|
9
|
+
"@dereekb/rxjs": "13.43.0",
|
|
10
|
+
"@dereekb/util": "13.43.0",
|
|
11
11
|
"@firebase/rules-unit-testing": "5.0.0",
|
|
12
12
|
"date-fns": "^4.1.0",
|
|
13
13
|
"firebase": "^12.12.1",
|