@dereekb/firebase-server 12.4.4 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/mailgun/package.json +1 -1
  3. package/model/package.json +1 -1
  4. package/model/src/lib/index.d.ts +1 -0
  5. package/model/src/lib/index.js +1 -0
  6. package/model/src/lib/index.js.map +1 -1
  7. package/model/src/lib/notification/notification.action.server.d.ts +2 -2
  8. package/model/src/lib/notification/notification.action.server.js +142 -93
  9. package/model/src/lib/notification/notification.action.server.js.map +1 -1
  10. package/model/src/lib/notification/notification.task.service.d.ts +1 -1
  11. package/model/src/lib/notification/notification.task.service.handler.d.ts +5 -1
  12. package/model/src/lib/notification/notification.task.service.handler.js +5 -1
  13. package/model/src/lib/notification/notification.task.service.handler.js.map +1 -1
  14. package/model/src/lib/notification/notification.task.service.util.d.ts +9 -0
  15. package/model/src/lib/notification/notification.task.service.util.js +27 -0
  16. package/model/src/lib/notification/notification.task.service.util.js.map +1 -0
  17. package/model/src/lib/storagefile/index.d.ts +7 -0
  18. package/model/src/lib/storagefile/index.js +11 -0
  19. package/model/src/lib/storagefile/index.js.map +1 -0
  20. package/model/src/lib/storagefile/storagefile.action.server.d.ts +41 -0
  21. package/model/src/lib/storagefile/storagefile.action.server.js +392 -0
  22. package/model/src/lib/storagefile/storagefile.action.server.js.map +1 -0
  23. package/model/src/lib/storagefile/storagefile.error.d.ts +11 -0
  24. package/model/src/lib/storagefile/storagefile.error.js +78 -0
  25. package/model/src/lib/storagefile/storagefile.error.js.map +1 -0
  26. package/model/src/lib/storagefile/storagefile.module.d.ts +30 -0
  27. package/model/src/lib/storagefile/storagefile.module.js +50 -0
  28. package/model/src/lib/storagefile/storagefile.module.js.map +1 -0
  29. package/model/src/lib/storagefile/storagefile.task.service.handler.d.ts +163 -0
  30. package/model/src/lib/storagefile/storagefile.task.service.handler.js +286 -0
  31. package/model/src/lib/storagefile/storagefile.task.service.handler.js.map +1 -0
  32. package/model/src/lib/storagefile/storagefile.upload.service.d.ts +57 -0
  33. package/model/src/lib/storagefile/storagefile.upload.service.initializer.d.ts +105 -0
  34. package/model/src/lib/storagefile/storagefile.upload.service.initializer.js +143 -0
  35. package/model/src/lib/storagefile/storagefile.upload.service.initializer.js.map +1 -0
  36. package/model/src/lib/storagefile/storagefile.upload.service.js +10 -0
  37. package/model/src/lib/storagefile/storagefile.upload.service.js.map +1 -0
  38. package/model/src/lib/storagefile/storagefile.util.d.ts +37 -0
  39. package/model/src/lib/storagefile/storagefile.util.js +54 -0
  40. package/model/src/lib/storagefile/storagefile.util.js.map +1 -0
  41. package/package.json +1 -1
  42. package/src/lib/storage/driver.accessor.d.ts +3 -3
  43. package/src/lib/storage/driver.accessor.js +111 -18
  44. package/src/lib/storage/driver.accessor.js.map +1 -1
  45. package/test/package.json +1 -1
  46. package/zoho/package.json +1 -1
@@ -0,0 +1,57 @@
1
+ import { FirebaseStorageAccessorFile, StorageFileDocument, StorageFileInitializeFromUploadResultType, UploadedFileTypeDeterminerResult } from '@dereekb/firebase';
2
+ import { Maybe, PromiseOrValue } from '@dereekb/util';
3
+ /**
4
+ * Provides a reference to a StorageFileInitializeFromUploadService instance.
5
+ */
6
+ export interface StorageFileInitializeFromUploadServiceRef {
7
+ readonly storageFileInitializeFromUploadService: StorageFileInitializeFromUploadService;
8
+ }
9
+ export interface StorageFileInitializeFromUploadInput {
10
+ /**
11
+ * The target file.
12
+ *
13
+ * This file should not be modified (e.g. deleted) during the processor call.
14
+ */
15
+ readonly file: FirebaseStorageAccessorFile;
16
+ }
17
+ export interface StorageFileInitializeFromUploadResult {
18
+ /**
19
+ * Whether or not the initialization was successful.
20
+ */
21
+ readonly resultType: StorageFileInitializeFromUploadResultType;
22
+ /**
23
+ * The initialized StorageFile value, if applicable.
24
+ */
25
+ readonly storageFileDocument?: Maybe<StorageFileDocument>;
26
+ /**
27
+ * Any error that occurred during processing.
28
+ */
29
+ readonly initializationError?: Maybe<unknown>;
30
+ /**
31
+ * Number of StorageFiles that were flagged for deletion.
32
+ *
33
+ * Only set if flagPreviousForDelete was provided.
34
+ */
35
+ readonly previousStorageFilesFlaggedForDeletion?: Maybe<number>;
36
+ }
37
+ /**
38
+ * Service dedicated to initializing a StorageFileDocument value from an uploaded file.
39
+ */
40
+ export declare abstract class StorageFileInitializeFromUploadService {
41
+ /**
42
+ * Returns true if the file is allowed to be initialized.
43
+ *
44
+ * @param file
45
+ */
46
+ abstract checkFileIsAllowedToBeInitialized(file: FirebaseStorageAccessorFile): PromiseOrValue<boolean>;
47
+ /**
48
+ * Used to determine the type of the input file.
49
+ */
50
+ abstract determineUploadFileType(input: StorageFileInitializeFromUploadInput): Promise<Maybe<UploadedFileTypeDeterminerResult>>;
51
+ /**
52
+ * Initializes a StorageFileDocument value from an uploaded file.
53
+ *
54
+ * The input file is unchanged, only new content is created.
55
+ */
56
+ abstract initializeFromUpload(input: StorageFileInitializeFromUploadInput): Promise<StorageFileInitializeFromUploadResult>;
57
+ }
@@ -0,0 +1,105 @@
1
+ import { CombineUploadFileTypeDeterminerConfig, CreateStorageFileDocumentPairResult, FirebaseStorageAccessorFile, StorageFileDocument, StorageFileFirestoreCollection, StorageFilePurposeAndUserQueryInput, StoredFileReader, UploadedFileTypeDeterminer, UploadedFileTypeDeterminerResult, UploadedFileTypeIdentifier } from '@dereekb/firebase';
2
+ import { ArrayOrValue, AsyncDecisionFunction, Maybe } from '@dereekb/util';
3
+ import { StorageFileInitializeFromUploadService } from './storagefile.upload.service';
4
+ export interface StorageFileInitializeFromUploadServiceInitializerInput {
5
+ /**
6
+ * The result of the determiner.
7
+ */
8
+ readonly determinerResult: UploadedFileTypeDeterminerResult;
9
+ /**
10
+ * The uploaded file.
11
+ */
12
+ readonly fileDetailsAccessor: StoredFileReader;
13
+ }
14
+ export type StorageFileInitializeFromUploadServiceInitializerResult = StorageFileInitializeFromUploadServiceInitializerStorageFileErrorResult | StorageFileInitializeFromUploadServiceInitializerCreateStorageFileResult | StorageFileInitializeFromUploadServiceInitializerStorageFileDocumentResult;
15
+ export interface StorageFileInitializeFromUploadServiceInitializerStorageFileErrorResult {
16
+ /**
17
+ * The error thrown initializing.
18
+ */
19
+ readonly error: unknown;
20
+ /**
21
+ * If true, the initializer failed permanently and the file should be deleted.
22
+ */
23
+ readonly permanentFailure?: boolean;
24
+ }
25
+ export interface StorageFileInitializeFromUploadServiceInitializerCreateStorageFileResult {
26
+ /**
27
+ * The result of the createStorageFileDocumentPair function, if a StorageFileDocument was created.
28
+ */
29
+ readonly createStorageFileResult: CreateStorageFileDocumentPairResult;
30
+ /**
31
+ * If set, the initializer will query existing StorageFiles for the user and purpose and flag them for deletion.
32
+ *
33
+ * If true, createStorageFileResult will be used.
34
+ */
35
+ readonly flagPreviousForDelete?: Maybe<boolean | StorageFilePurposeAndUserQueryInput>;
36
+ }
37
+ export interface StorageFileInitializeFromUploadServiceInitializerStorageFileDocumentResult {
38
+ /**
39
+ * The StorageFileDocument, if it was initialized.
40
+ */
41
+ readonly storageFileDocument: StorageFileDocument;
42
+ /**
43
+ * If set, the initializer will query existing StorageFiles for the user and purpose and flag them for deletion.
44
+ *
45
+ * If true, createStorageFileResult will be used.
46
+ */
47
+ readonly flagPreviousForDelete?: Maybe<StorageFilePurposeAndUserQueryInput>;
48
+ }
49
+ export declare function storageFileInitializeFromUploadServiceInitializerResultPermanentFailure(error: unknown): StorageFileInitializeFromUploadServiceInitializerResult;
50
+ /**
51
+ * Processes the input details accessor and returns the results.
52
+ */
53
+ export type StorageFileInitializeFromUploadServiceInitializerFunction = (input: StorageFileInitializeFromUploadServiceInitializerInput) => Promise<StorageFileInitializeFromUploadServiceInitializerResult>;
54
+ export interface StorageFileInitializeFromUploadServiceInitializer {
55
+ /**
56
+ * The file type(s) identifier that this processor handles.
57
+ */
58
+ readonly type: ArrayOrValue<UploadedFileTypeIdentifier>;
59
+ /**
60
+ * A determiner that is paired with the processor.
61
+ *
62
+ * Determiners defined here are modified to ONLY respond to the types specified in the `type` property. This means that
63
+ * if the determiner returns a separate type, that result will be altered to be null, instead of returned as a match.
64
+ */
65
+ readonly determiner?: Maybe<UploadedFileTypeDeterminer>;
66
+ /**
67
+ * Handles the initialization of the StorageFileDocument from the uploaded file.
68
+ */
69
+ readonly initialize: StorageFileInitializeFromUploadServiceInitializerFunction;
70
+ }
71
+ export interface StorageFileInitializeFromUploadServiceConfig {
72
+ /**
73
+ * If true, will validate that all processor file types have at least one corresponding determiner.
74
+ */
75
+ readonly validate?: boolean;
76
+ /**
77
+ * The UploadedFileTypeDeterminer(s) to use for determining the upload type of each uploaded file.
78
+ *
79
+ * They will be combined together with determiners from the processors.
80
+ */
81
+ readonly determiner?: Maybe<ArrayOrValue<UploadedFileTypeDeterminer>>;
82
+ /**
83
+ * StorageFilleFirestoreCollection used for retrieving existing StorageFiles for marking them as deleted.
84
+ */
85
+ readonly storageFileCollection: StorageFileFirestoreCollection;
86
+ /**
87
+ * Configuration for combining the determiners.
88
+ *
89
+ * Defaults to:
90
+ * - completeSearchOnFirstMatch: true
91
+ */
92
+ readonly combineDeterminersConfig?: Maybe<CombineUploadFileTypeDeterminerConfig>;
93
+ /**
94
+ * Optional function to check if the file is allowed to be initialized.
95
+ */
96
+ readonly checkFileIsAllowedToBeInitialized?: Maybe<AsyncDecisionFunction<FirebaseStorageAccessorFile>>;
97
+ /**
98
+ * List of handlers for NotificationTaskTypes.
99
+ */
100
+ readonly initializer: StorageFileInitializeFromUploadServiceInitializer[];
101
+ }
102
+ /**
103
+ * A basic StorageFileInitializeFromUploadService implementation.
104
+ */
105
+ export declare function storageFileInitializeFromUploadService(config: StorageFileInitializeFromUploadServiceConfig): StorageFileInitializeFromUploadService;
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.storageFileInitializeFromUploadServiceInitializerResultPermanentFailure = storageFileInitializeFromUploadServiceInitializerResultPermanentFailure;
4
+ exports.storageFileInitializeFromUploadService = storageFileInitializeFromUploadService;
5
+ const firebase_1 = require("@dereekb/firebase");
6
+ const util_1 = require("@dereekb/util");
7
+ const storagefile_util_1 = require("./storagefile.util");
8
+ function storageFileInitializeFromUploadServiceInitializerResultPermanentFailure(error) {
9
+ return {
10
+ error,
11
+ permanentFailure: true
12
+ };
13
+ }
14
+ /**
15
+ * A basic StorageFileInitializeFromUploadService implementation.
16
+ */
17
+ function storageFileInitializeFromUploadService(config) {
18
+ const { storageFileCollection, initializer: inputInitializers, determiner: inputDeterminers, validate, checkFileIsAllowedToBeInitialized: inputCheckFileIsAllowedToBeInitialized } = config;
19
+ const allDeterminers = [];
20
+ const initializers = {};
21
+ const detailsAccessorFactory = (0, firebase_1.storedFileReaderFactory)();
22
+ if (inputDeterminers) {
23
+ (0, util_1.pushItemOrArrayItemsIntoArray)(allDeterminers, inputDeterminers);
24
+ }
25
+ // iterate initializers
26
+ inputInitializers.forEach((initializer) => {
27
+ const { type: inputTypes, determiner: inputDeterminer } = initializer;
28
+ const types = (0, util_1.asArray)(inputTypes);
29
+ types.forEach((type) => {
30
+ initializers[type] = initializer;
31
+ });
32
+ if (inputDeterminer) {
33
+ const wrappedDeterminer = (0, firebase_1.limitUploadFileTypeDeterminer)(inputDeterminer, types);
34
+ allDeterminers.push(wrappedDeterminer);
35
+ }
36
+ });
37
+ const determiner = (0, firebase_1.combineUploadFileTypeDeterminers)({
38
+ determiners: allDeterminers,
39
+ ...{
40
+ completeSearchOnFirstMatch: true,
41
+ ...config.combineDeterminersConfig
42
+ }
43
+ });
44
+ // validate initializers
45
+ if (validate) {
46
+ const allInitializerTypes = Object.keys(initializers);
47
+ const allDeterminerTypes = new Set(determiner.getPossibleFileTypes());
48
+ // all initializer types should have a corresponding determiner
49
+ allInitializerTypes.forEach((type) => {
50
+ if (!allDeterminerTypes.has(type)) {
51
+ throw new Error(`Initializer type ${type} does not have a corresponding determiner.`);
52
+ }
53
+ });
54
+ }
55
+ async function determineUploadFileType(input) {
56
+ const { file } = input;
57
+ const fileDetailsAccessor = detailsAccessorFactory(file);
58
+ return determiner.determine(fileDetailsAccessor);
59
+ }
60
+ return {
61
+ checkFileIsAllowedToBeInitialized: inputCheckFileIsAllowedToBeInitialized ?? (0, util_1.asDecisionFunction)(true),
62
+ determineUploadFileType,
63
+ initializeFromUpload: async (input) => {
64
+ const determinerResult = await determineUploadFileType(input);
65
+ let resultType;
66
+ let storageFileDocument;
67
+ let processorError;
68
+ let previousStorageFilesFlaggedForDeletion;
69
+ if (determinerResult) {
70
+ const { input: fileDetailsAccessor } = determinerResult;
71
+ resultType = 'success';
72
+ const initializer = initializers[determinerResult.type];
73
+ if (initializer) {
74
+ try {
75
+ const initializerResult = await initializer.initialize({ determinerResult, fileDetailsAccessor });
76
+ if (initializerResult.error) {
77
+ processorError = initializerResult.error;
78
+ if (initializerResult.permanentFailure) {
79
+ resultType = 'permanent_initializer_failure';
80
+ }
81
+ else {
82
+ resultType = 'initializer_error';
83
+ }
84
+ }
85
+ else {
86
+ let flagPreviousForDelete;
87
+ if (initializerResult.createStorageFileResult) {
88
+ const { createStorageFileResult, flagPreviousForDelete: flagPreviousForDeleteResult } = initializerResult;
89
+ storageFileDocument = createStorageFileResult.storageFileDocument;
90
+ if (flagPreviousForDeleteResult) {
91
+ if (typeof flagPreviousForDeleteResult === 'object') {
92
+ flagPreviousForDelete = flagPreviousForDeleteResult;
93
+ }
94
+ else {
95
+ const { p, u } = createStorageFileResult.storageFile;
96
+ if (!p || !u) {
97
+ throw new Error('initializeFromUpload(): flagPreviousForDelete=true requires that the created StorageFile have a purpose (p) and user (u).');
98
+ }
99
+ flagPreviousForDelete = {
100
+ purpose: p,
101
+ user: u
102
+ };
103
+ }
104
+ }
105
+ }
106
+ else {
107
+ storageFileDocument = initializerResult.storageFileDocument;
108
+ flagPreviousForDelete = initializerResult.flagPreviousForDelete;
109
+ }
110
+ // if flagPreviousForDelete is set, flag the previous storage files for deletion
111
+ if (flagPreviousForDelete) {
112
+ const flagForDeleteResult = await (0, storagefile_util_1.queryAndFlagStorageFilesForDelete)({
113
+ storageFileCollection,
114
+ constraints: (0, firebase_1.storageFilePurposeAndUserQuery)(flagPreviousForDelete),
115
+ skipDeleteForKeys: [storageFileDocument.key]
116
+ });
117
+ previousStorageFilesFlaggedForDeletion = flagForDeleteResult.queuedForDeleteCount;
118
+ }
119
+ }
120
+ }
121
+ catch (e) {
122
+ resultType = 'initializer_error';
123
+ processorError = e;
124
+ }
125
+ }
126
+ else {
127
+ resultType = 'no_initializer_configured';
128
+ }
129
+ }
130
+ else {
131
+ resultType = 'no_determiner_match';
132
+ }
133
+ const result = {
134
+ resultType,
135
+ storageFileDocument,
136
+ initializationError: processorError,
137
+ previousStorageFilesFlaggedForDeletion
138
+ };
139
+ return result;
140
+ }
141
+ };
142
+ }
143
+ //# sourceMappingURL=storagefile.upload.service.initializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storagefile.upload.service.initializer.js","sourceRoot":"","sources":["../../../../../../../packages/firebase-server/model/src/lib/storagefile/storagefile.upload.service.initializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StorageFileInitializeFromUploadService = void 0;
4
+ /**
5
+ * Service dedicated to initializing a StorageFileDocument value from an uploaded file.
6
+ */
7
+ class StorageFileInitializeFromUploadService {
8
+ }
9
+ exports.StorageFileInitializeFromUploadService = StorageFileInitializeFromUploadService;
10
+ //# sourceMappingURL=storagefile.upload.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storagefile.upload.service.js","sourceRoot":"","sources":["../../../../../../../packages/firebase-server/model/src/lib/storagefile/storagefile.upload.service.ts"],"names":[],"mappings":";;;AAwCA;;GAEG;AACH,MAAsB,sCAAsC;CAiB3D;AAjBD,wFAiBC"}
@@ -0,0 +1,37 @@
1
+ import { FirestoreQueryConstraint, StorageFile, StorageFileFirestoreCollection, StorageFileKey } from '@dereekb/firebase';
2
+ import { Maybe, Milliseconds, ArrayOrValue } from '@dereekb/util';
3
+ /**
4
+ * Describes when a StorageFile should be queued for deletion.
5
+ *
6
+ * If true, the StorageFile will be queued for deletion immediately.
7
+ * If a number, the StorageFile will be queued for deletion after the specified number of milliseconds.
8
+ * If a Date, the StorageFile will be queued for deletion at the specified date.
9
+ */
10
+ export type StorageFileQueueForDeleteTime = true | Milliseconds | Date;
11
+ export interface QueryAndFlagStorageFilesForDeleteInput {
12
+ readonly storageFileCollection: StorageFileFirestoreCollection;
13
+ readonly constraints: FirestoreQueryConstraint[];
14
+ readonly queuedForDeleteTime?: Maybe<StorageFileQueueForDeleteTime>;
15
+ /**
16
+ * Array of document keys that should be ignored/skipped while flagging.
17
+ */
18
+ readonly skipDeleteForKeys?: ArrayOrValue<StorageFileKey>;
19
+ }
20
+ export interface QueryAndFlagStorageFilesForDeleteResult {
21
+ readonly visitedCount: number;
22
+ readonly queuedForDeleteCount: number;
23
+ }
24
+ /**
25
+ * Performs a query and flags the matching StorageFiles for deletion.
26
+ *
27
+ * @param input The input for the query and flagging.
28
+ * @returns The result of the query and flagging.
29
+ */
30
+ export declare function queryAndFlagStorageFilesForDelete(input: QueryAndFlagStorageFilesForDeleteInput): Promise<QueryAndFlagStorageFilesForDeleteResult>;
31
+ /**
32
+ * Creates a template for updating a StorageFile to be queued for deletion at the input time.
33
+ *
34
+ * @param queueForDeleteTime When to delete the StorageFile. If true or unset, the StorageFile will be flagged to be deleted immediately.
35
+ * @returns The update template for the StorageFile.
36
+ */
37
+ export declare function markStorageFileForDeleteTemplate(queueForDeleteTime?: Maybe<StorageFileQueueForDeleteTime>): Pick<StorageFile, 'sdat' | 'fs'>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryAndFlagStorageFilesForDelete = queryAndFlagStorageFilesForDelete;
4
+ exports.markStorageFileForDeleteTemplate = markStorageFileForDeleteTemplate;
5
+ const firebase_1 = require("@dereekb/firebase");
6
+ const util_1 = require("@dereekb/util");
7
+ /**
8
+ * Performs a query and flags the matching StorageFiles for deletion.
9
+ *
10
+ * @param input The input for the query and flagging.
11
+ * @returns The result of the query and flagging.
12
+ */
13
+ async function queryAndFlagStorageFilesForDelete(input) {
14
+ const { storageFileCollection, constraints, queuedForDeleteTime: inputQueueForDeleteTime, skipDeleteForKeys } = input;
15
+ const queuedForDeleteTime = inputQueueForDeleteTime ?? true;
16
+ const skipDeleteSet = new Set((0, util_1.asArray)(skipDeleteForKeys));
17
+ let visitedCount = 0;
18
+ let queuedForDeleteCount = 0;
19
+ await (0, firebase_1.iterateFirestoreDocumentSnapshotPairs)({
20
+ documentAccessor: storageFileCollection.documentAccessor(),
21
+ iterateSnapshotPair: async (snapshotPair) => {
22
+ const { document, data: storageFile } = snapshotPair;
23
+ if (!storageFile.sdat && !skipDeleteSet.has(storageFile.key)) {
24
+ await document.update(markStorageFileForDeleteTemplate(queuedForDeleteTime));
25
+ queuedForDeleteCount++;
26
+ }
27
+ visitedCount++;
28
+ },
29
+ queryFactory: storageFileCollection,
30
+ constraintsFactory: () => constraints,
31
+ batchSize: undefined,
32
+ performTasksConfig: {
33
+ maxParallelTasks: 20
34
+ }
35
+ });
36
+ return {
37
+ visitedCount,
38
+ queuedForDeleteCount
39
+ };
40
+ }
41
+ /**
42
+ * Creates a template for updating a StorageFile to be queued for deletion at the input time.
43
+ *
44
+ * @param queueForDeleteTime When to delete the StorageFile. If true or unset, the StorageFile will be flagged to be deleted immediately.
45
+ * @returns The update template for the StorageFile.
46
+ */
47
+ function markStorageFileForDeleteTemplate(queueForDeleteTime) {
48
+ const updateTemplate = {
49
+ sdat: queueForDeleteTime === true || queueForDeleteTime == null ? new Date() : (0, util_1.dateFromDateOrTimeNumber)(queueForDeleteTime),
50
+ fs: firebase_1.StorageFileState.QUEUED_FOR_DELETE
51
+ };
52
+ return updateTemplate;
53
+ }
54
+ //# sourceMappingURL=storagefile.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storagefile.util.js","sourceRoot":"","sources":["../../../../../../../packages/firebase-server/model/src/lib/storagefile/storagefile.util.ts"],"names":[],"mappings":";;AAiCA,8EAgCC;AAQD,4EAOC;AAhFD,gDAAmL;AACnL,wCAAqG;AA0BrG;;;;;GAKG;AACI,KAAK,UAAU,iCAAiC,CAAC,KAA6C;IACnG,MAAM,EAAE,qBAAqB,EAAE,WAAW,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC;IACtH,MAAM,mBAAmB,GAAG,uBAAuB,IAAI,IAAI,CAAC;IAC5D,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,IAAA,cAAO,EAAC,iBAAiB,CAAC,CAAC,CAAC;IAE1D,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAE7B,MAAM,IAAA,gDAAqC,EAAC;QAC1C,gBAAgB,EAAE,qBAAqB,CAAC,gBAAgB,EAAE;QAC1D,mBAAmB,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE;YAC1C,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC;YAErD,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7D,MAAM,QAAQ,CAAC,MAAM,CAAC,gCAAgC,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBAC7E,oBAAoB,EAAE,CAAC;YACzB,CAAC;YAED,YAAY,EAAE,CAAC;QACjB,CAAC;QACD,YAAY,EAAE,qBAAqB;QACnC,kBAAkB,EAAE,GAAG,EAAE,CAAC,WAAW;QACrC,SAAS,EAAE,SAAS;QACpB,kBAAkB,EAAE;YAClB,gBAAgB,EAAE,EAAE;SACrB;KACF,CAAC,CAAC;IAEH,OAAO;QACL,YAAY;QACZ,oBAAoB;KACrB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,gCAAgC,CAAC,kBAAyD;IACxG,MAAM,cAAc,GAAqC;QACvD,IAAI,EAAE,kBAAkB,KAAK,IAAI,IAAI,kBAAkB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAA,+BAAwB,EAAC,kBAAkB,CAAC;QAC3H,EAAE,EAAE,2BAAgB,CAAC,iBAAiB;KACvC,CAAC;IAEF,OAAO,cAAc,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "12.4.4",
3
+ "version": "12.5.0",
4
4
  "exports": {
5
5
  ".": {
6
6
  "types": "./src/index.d.ts",
@@ -1,9 +1,9 @@
1
1
  import { type FirebaseStorageAccessorDriver, type FirebaseStorageAccessorFile, type FirebaseStorageAccessorFolder, type StoragePath, type StorageMetadata, type StorageBucketId } from '@dereekb/firebase';
2
2
  import { type SlashPathFolder } from '@dereekb/util';
3
- import { type GetFilesOptions, type Storage as GoogleCloudStorage, type File as GoogleCloudFile, type FileMetadata } from '@google-cloud/storage';
4
- export declare function googleCloudStorageBucketForStorageFilePath(storage: GoogleCloudStorage, path: StoragePath): import("@google-cloud/storage").Bucket;
3
+ import { type GetFilesOptions, type Storage as GoogleCloudStorage, type File as GoogleCloudFile, type FileMetadata, Bucket } from '@google-cloud/storage';
4
+ export declare function googleCloudStorageBucketForStorageFilePath(storage: GoogleCloudStorage, path: StoragePath): Bucket;
5
5
  export declare function googleCloudStorageFileForStorageFilePath(storage: GoogleCloudStorage, path: StoragePath): GoogleCloudFile;
6
- export type GoogleCloudStorageAccessorFile = FirebaseStorageAccessorFile<GoogleCloudFile>;
6
+ export type GoogleCloudStorageAccessorFile = FirebaseStorageAccessorFile<GoogleCloudFile> & Required<Pick<FirebaseStorageAccessorFile<GoogleCloudFile>, 'uploadStream' | 'getStream'>>;
7
7
  export declare function googleCloudFileMetadataToStorageMetadata(file: GoogleCloudFile, metadata: FileMetadata): StorageMetadata;
8
8
  export declare function googleCloudStorageAccessorFile(storage: GoogleCloudStorage, storagePath: StoragePath): GoogleCloudStorageAccessorFile;
9
9
  export type GoogleCloudStorageAccessorFolder = FirebaseStorageAccessorFolder<GoogleCloudFile>;
@@ -9,6 +9,8 @@ exports.googleCloudStorageAccessorFolder = googleCloudStorageAccessorFolder;
9
9
  exports.googleCloudStorageFirebaseStorageAccessorDriver = googleCloudStorageFirebaseStorageAccessorDriver;
10
10
  const firebase_1 = require("@dereekb/firebase");
11
11
  const util_1 = require("@dereekb/util");
12
+ const storage_1 = require("@google-cloud/storage");
13
+ const date_fns_1 = require("date-fns");
12
14
  const types_1 = require("util/types");
13
15
  function googleCloudStorageBucketForStorageFilePath(storage, path) {
14
16
  return storage.bucket(path.bucketId);
@@ -21,6 +23,7 @@ function googleCloudFileMetadataToStorageMetadata(file, metadata) {
21
23
  const generation = String(metadata.generation ?? file.generation);
22
24
  const metageneration = String(metadata.metageneration);
23
25
  const size = Number(metadata.size);
26
+ const customMetadata = metadata.metadata;
24
27
  return {
25
28
  bucket: file.bucket.name,
26
29
  fullPath,
@@ -36,7 +39,7 @@ function googleCloudFileMetadataToStorageMetadata(file, metadata) {
36
39
  contentEncoding: metadata.contentEncoding,
37
40
  contentLanguage: metadata.contentLanguage,
38
41
  contentType: metadata.contentType,
39
- customMetadata: metadata.customMetadata
42
+ customMetadata
40
43
  };
41
44
  }
42
45
  function googleCloudStorageAccessorFile(storage, storagePath) {
@@ -51,26 +54,84 @@ function googleCloudStorageAccessorFile(storage, storagePath) {
51
54
  : undefined)
52
55
  };
53
56
  }
57
+ function _configureMetadata(options) {
58
+ const customMetadata = (0, util_1.filterUndefinedValues)({
59
+ ...options.metadata?.customMetadata,
60
+ ...options?.customMetadata
61
+ });
62
+ return (0, util_1.filterUndefinedValues)({
63
+ cacheControl: options.metadata?.cacheControl,
64
+ contentDisposition: options.metadata?.contentDisposition,
65
+ contentEncoding: options.metadata?.contentEncoding,
66
+ contentLanguage: options.metadata?.contentLanguage,
67
+ contentType: options.metadata?.contentType,
68
+ metadata: !(0, util_1.objectHasNoKeys)(customMetadata) ? customMetadata : undefined
69
+ });
70
+ }
54
71
  function makeUploadOptions(options) {
55
72
  let metadata;
56
- if (options?.contentType) {
57
- metadata = {
58
- contentType: options?.contentType
59
- };
73
+ if (options != null) {
74
+ metadata = _configureMetadata({
75
+ metadata: {
76
+ ...options.metadata,
77
+ contentType: options.contentType ?? options.metadata?.contentType
78
+ },
79
+ customMetadata: options.customMetadata
80
+ });
60
81
  }
61
82
  return {
62
83
  // non-resumable
63
84
  resumable: false,
64
- // add content type
85
+ // add content type and other custom metadata
65
86
  ...(metadata ? { metadata } : undefined)
66
87
  };
67
88
  }
68
- return {
89
+ function asFileMetadata(metadata) {
90
+ return _configureMetadata({ metadata });
91
+ }
92
+ function makeStoragePathForPath(newPath) {
93
+ let path;
94
+ if (typeof newPath === 'string') {
95
+ path = {
96
+ bucketId: file.bucket.name,
97
+ pathString: newPath
98
+ };
99
+ }
100
+ else {
101
+ path = newPath;
102
+ }
103
+ return path;
104
+ }
105
+ async function copy(newPath, options) {
106
+ const newStoragePath = makeStoragePathForPath(newPath);
107
+ const newFile = googleCloudStorageAccessorFile(storage, newStoragePath);
108
+ return _copyWithFile(newFile, options);
109
+ }
110
+ async function _copyWithFile(newFile, options) {
111
+ const copyOptions = {
112
+ ...options
113
+ };
114
+ await file.copy(newFile.reference, copyOptions);
115
+ return newFile;
116
+ }
117
+ const accessorFile = {
69
118
  reference: file,
70
119
  storagePath,
71
120
  exists: async () => file.exists().then((x) => x[0]),
72
121
  getDownloadUrl: async () => file.getMetadata().then((x) => file.publicUrl()),
122
+ getSignedUrl: async (input) => {
123
+ const expires = input?.expiresAt ?? (input?.expiresIn != null ? (0, date_fns_1.addMilliseconds)(new Date(), input.expiresIn) : (0, date_fns_1.addHours)(new Date(), 1));
124
+ const config = {
125
+ ...input,
126
+ action: input?.action ?? 'read',
127
+ expiresIn: undefined,
128
+ expiresAt: undefined,
129
+ expires
130
+ };
131
+ return file.getSignedUrl(config).then((x) => x[0]);
132
+ },
73
133
  getMetadata: () => file.getMetadata().then((x) => googleCloudFileMetadataToStorageMetadata(file, x[0])),
134
+ setMetadata: (metadata) => file.setMetadata(asFileMetadata(metadata)).then((x) => googleCloudFileMetadataToStorageMetadata(file, x[0])),
74
135
  getBytes: (maxDownloadSizeBytes) => file.download(makeDownloadOptions(maxDownloadSizeBytes)).then((x) => x[0]),
75
136
  getStream: (maxDownloadSizeBytes) => file.createReadStream(makeDownloadOptions(maxDownloadSizeBytes)),
76
137
  upload: async (input, options) => {
@@ -105,8 +166,30 @@ function googleCloudStorageAccessorFile(storage, storagePath) {
105
166
  return file.save(data, makeUploadOptions(options));
106
167
  },
107
168
  uploadStream: (options) => file.createWriteStream(makeUploadOptions(options)),
169
+ move: async (newPath, options) => {
170
+ const newStoragePath = makeStoragePathForPath(newPath);
171
+ const newFile = googleCloudStorageAccessorFile(storage, newStoragePath);
172
+ const moveOptions = {
173
+ ...options
174
+ };
175
+ await file.moveFileAtomic(newFile.reference, moveOptions).catch(async (e) => {
176
+ if (e instanceof storage_1.ApiError && e.response?.statusMessage === 'Not Implemented') {
177
+ // NOTE: This is not implemented in storage emulator, so it will fail with this error in testing.
178
+ // https://github.com/firebase/firebase-tools/issues/3751
179
+ // we can perform the same task using copy and then deleting this file.
180
+ await copy(newPath, moveOptions);
181
+ await accessorFile.delete();
182
+ }
183
+ else {
184
+ throw e;
185
+ }
186
+ });
187
+ return newFile;
188
+ },
189
+ copy,
108
190
  delete: (options) => file.delete(options).then((x) => undefined)
109
191
  };
192
+ return accessorFile;
110
193
  }
111
194
  exports.googleCloudStorageListFilesResultFactory = (0, firebase_1.storageListFilesResultFactory)({
112
195
  hasItems(result) {
@@ -115,8 +198,11 @@ exports.googleCloudStorageListFilesResultFactory = (0, firebase_1.storageListFil
115
198
  hasNext: (result) => {
116
199
  return result.nextQuery != null;
117
200
  },
118
- next(storage, folder, result) {
119
- return folder.list(result.nextQuery);
201
+ nextPageTokenFromResult(result) {
202
+ return result.nextQuery?.pageToken;
203
+ },
204
+ next(storage, options, folder, result) {
205
+ return folder.list({ ...options, ...result.nextQuery });
120
206
  },
121
207
  file(storage, fileResult) {
122
208
  return googleCloudStorageAccessorFile(storage, fileResult.storagePath);
@@ -141,17 +227,23 @@ function googleCloudStorageAccessorFolder(storage, storagePath) {
141
227
  storagePath,
142
228
  exists: async () => folder.list({ maxResults: 1 }).then((x) => x.hasItems()),
143
229
  list: (options) => {
144
- return bucket
145
- .getFiles({
146
- ...options,
147
- delimiter: util_1.SLASH_PATH_SEPARATOR,
230
+ const { maxResults, pageToken, includeNestedResults: listAll } = options ?? {};
231
+ const listOptions = {
232
+ maxResults,
233
+ pageToken,
148
234
  autoPaginate: false,
149
235
  versions: false,
150
- maxResults: options?.maxResults,
151
- // includeTrailingDelimiter: true,
152
- prefix: (0, util_1.toRelativeSlashPathStartType)((0, util_1.fixMultiSlashesInSlashPath)(storagePath.pathString + '/')) // make sure the folder always ends with a slash
153
- })
154
- .then((x) => {
236
+ ...(listAll
237
+ ? {
238
+ prefix: (0, util_1.toRelativeSlashPathStartType)((0, util_1.fixMultiSlashesInSlashPath)(storagePath.pathString + '/'))
239
+ }
240
+ : {
241
+ // includeTrailingDelimiter: true,
242
+ delimiter: util_1.SLASH_PATH_SEPARATOR,
243
+ prefix: (0, util_1.toRelativeSlashPathStartType)((0, util_1.fixMultiSlashesInSlashPath)(storagePath.pathString + '/')) // make sure the folder always ends with a slash
244
+ })
245
+ };
246
+ return bucket.getFiles(listOptions).then((x) => {
155
247
  const files = x[0];
156
248
  const nextQuery = x[1];
157
249
  const apiResponse = x[2];
@@ -168,6 +260,7 @@ function googleCloudStorageAccessorFolder(storage, storagePath) {
168
260
  }
169
261
  function googleCloudStorageFirebaseStorageAccessorDriver() {
170
262
  return {
263
+ type: 'server',
171
264
  file: (storage, path) => googleCloudStorageAccessorFile(storage, path),
172
265
  folder: (storage, path) => googleCloudStorageAccessorFolder(storage, path)
173
266
  };