@dereekb/firebase-server 13.42.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.
@@ -0,0 +1,174 @@
1
+ import { type AppFormSpaceTypeConfigService, type FormSpace, type FormSpaceDocument, type FormSpaceFile, type FormSpaceFileSlot, type FormSpaceFileSlotConfig, type FormSpaceFileValidationSubtask, type FormSpaceFileValidationSubtaskMetadata, type FormSpaceFirestoreCollections, type FormSpaceType, type StorageFileDocument, type StorageFileFirestoreCollections, type StorageFileMetadata, type StoredFileReader } from '@dereekb/firebase';
2
+ import { type Getter, type Maybe, type Milliseconds } from '@dereekb/util';
3
+ import { type StorageFileProcessingPurposeSubtaskProcessorConfigWithTarget } from '../storagefile/storagefile.task.service.handler';
4
+ /**
5
+ * @module formspace.validation
6
+ *
7
+ * SERVER-ONLY registration of what a FormSpace slot's uploaded file must actually contain.
8
+ *
9
+ * The split mirrors the one the type registry already uses. Pure data — which mime types, how large, how
10
+ * many, whether validation is required at all — lives in `@dereekb/firebase` so the client pre-checks with
11
+ * the same rules the server enforces. The CHECK itself is a function, so it lives here: a validator may read
12
+ * the file's bytes, call out to a model, or consult another collection, none of which belongs in a bundle
13
+ * shipped to a browser.
14
+ *
15
+ * Registration is keyed by `(FormSpaceType, slot)` and consumed by one processor targeting the single
16
+ * `form_space` StorageFilePurpose, because the subtask framework dispatches on one target and that target is
17
+ * already spent on the purpose.
18
+ */
19
+ /**
20
+ * How long a `pending` verdict waits before the validator is asked again, when it names no delay itself.
21
+ */
22
+ export declare const DEFAULT_FORM_SPACE_FILE_VALIDATION_RETRY_DELAY: Milliseconds;
23
+ /**
24
+ * How many times a validator may answer `pending` before the file is failed.
25
+ *
26
+ * Bounds a validator whose external dependency never resolves. A validator that legitimately needs longer
27
+ * should return a longer `retryIn` rather than more attempts.
28
+ */
29
+ export declare const DEFAULT_FORM_SPACE_FILE_VALIDATION_MAX_ATTEMPTS = 10;
30
+ /**
31
+ * What a {@link FormSpaceFileValidator} is handed.
32
+ */
33
+ export interface FormSpaceFileValidatorInput {
34
+ /**
35
+ * The FormSpace the file was uploaded into.
36
+ */
37
+ readonly formSpaceDocument: FormSpaceDocument;
38
+ /**
39
+ * Loads the FormSpace, memoized for the duration of one task run.
40
+ */
41
+ readonly loadFormSpace: Getter<Promise<FormSpace>>;
42
+ /**
43
+ * The StorageFile holding the bytes.
44
+ */
45
+ readonly storageFileDocument: StorageFileDocument;
46
+ /**
47
+ * Reads the stored file's bytes, stream, and metadata.
48
+ */
49
+ readonly fileDetailsAccessor: StoredFileReader;
50
+ /**
51
+ * The entry on the FormSpace this validation is for.
52
+ */
53
+ readonly formSpaceFile: FormSpaceFile;
54
+ /**
55
+ * The slot the file fills.
56
+ */
57
+ readonly slot: FormSpaceFileSlot;
58
+ /**
59
+ * The slot's configuration, when its type declares one.
60
+ */
61
+ readonly slotConfig: Maybe<FormSpaceFileSlotConfig>;
62
+ /**
63
+ * How many times the validator has already been asked about this file, starting at 0.
64
+ */
65
+ readonly attempt: number;
66
+ }
67
+ /**
68
+ * A validator's answer about one file.
69
+ *
70
+ * `pending` is the third outcome rather than an error: a validator waiting on something external has not
71
+ * failed, and reporting it as a failure would consume the file's retry budget on a check still in flight.
72
+ */
73
+ export type FormSpaceFileValidationVerdict = 'valid' | 'invalid' | 'pending';
74
+ /**
75
+ * The result of validating one file.
76
+ */
77
+ export interface FormSpaceFileValidationResult {
78
+ readonly verdict: FormSpaceFileValidationVerdict;
79
+ /**
80
+ * Why the file was judged invalid, written for the OWNER to read and act on.
81
+ *
82
+ * Free text rather than a code because a content rejection cannot be enumerated in advance — "the document
83
+ * expired in 2019" and "this is a photo of a receipt" are both correct answers from the same validator.
84
+ */
85
+ readonly reason?: Maybe<string>;
86
+ /**
87
+ * How long to wait before asking again. `pending` only; defaults to
88
+ * {@link DEFAULT_FORM_SPACE_FILE_VALIDATION_RETRY_DELAY}.
89
+ */
90
+ readonly retryIn?: Maybe<Milliseconds>;
91
+ /**
92
+ * Metadata to write onto the StorageFile's `d`, REPLACING the default verdict metadata.
93
+ */
94
+ readonly metadata?: Maybe<StorageFileMetadata>;
95
+ }
96
+ /**
97
+ * Decides whether one uploaded file satisfies one FormSpace slot.
98
+ */
99
+ export type FormSpaceFileValidator = (input: FormSpaceFileValidatorInput) => Promise<FormSpaceFileValidationResult>;
100
+ /**
101
+ * Registers a {@link FormSpaceFileValidator} for one {@link FormSpaceType}.
102
+ */
103
+ export interface FormSpaceFileValidatorConfig {
104
+ /**
105
+ * The type this validator applies to.
106
+ */
107
+ readonly formSpaceType: FormSpaceType;
108
+ /**
109
+ * The slot this validator applies to. When absent it applies to every slot of the type that does not
110
+ * declare a validator of its own.
111
+ */
112
+ readonly slot?: Maybe<FormSpaceFileSlot>;
113
+ /**
114
+ * The check.
115
+ */
116
+ readonly validate: FormSpaceFileValidator;
117
+ }
118
+ /**
119
+ * Configuration for {@link formSpaceFileValidationStorageFileProcessor}.
120
+ */
121
+ export interface FormSpaceFileValidationStorageFileProcessorConfig {
122
+ readonly formSpaceFirestoreCollections: FormSpaceFirestoreCollections;
123
+ /**
124
+ * Accessor for the StorageFile collection, used to flag a file the register step supersedes.
125
+ */
126
+ readonly storageFileFirestoreCollections: StorageFileFirestoreCollections;
127
+ /**
128
+ * The registry the processor resolves each file's slot rules from.
129
+ */
130
+ readonly appFormSpaceTypeConfigService: AppFormSpaceTypeConfigService;
131
+ /**
132
+ * The registered validators.
133
+ */
134
+ readonly validators: FormSpaceFileValidatorConfig[];
135
+ /**
136
+ * How many `pending` verdicts a file may collect before it is failed. Defaults to
137
+ * {@link DEFAULT_FORM_SPACE_FILE_VALIDATION_MAX_ATTEMPTS}.
138
+ */
139
+ readonly maxAttempts?: Maybe<number>;
140
+ /**
141
+ * When true, asserts at wiring time that every slot declaring `validationRequired` in
142
+ * `appFormSpaceTypeConfigService` has a validator registered here.
143
+ *
144
+ * On by default. A slot that asks for validation and gets none would otherwise pass every file silently,
145
+ * which is the one failure mode a validator exists to prevent.
146
+ */
147
+ readonly validateCoverage?: Maybe<boolean>;
148
+ }
149
+ /**
150
+ * Builds the `form_space` purpose's subtask processor: the thing that actually runs a slot's validator.
151
+ *
152
+ * ONE processor covers every form type and every slot, the same way one upload initializer does — the
153
+ * per-slot rules come from the registry keyed off the loaded space, not from the processor's registration.
154
+ *
155
+ * The verdict is written to BOTH the FormSpace's `f` entry and the StorageFile. The FormSpace copy is the
156
+ * one that matters: the owner can read their own space (and list it), but cannot list StorageFiles, so a
157
+ * verdict that never reaches `f` is a verdict the user never sees.
158
+ *
159
+ * @param config - The FormSpace collection, type registry, and registered validators.
160
+ * @returns The processor config, for the `processors` array of the storage-file processing handler.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const processors = [
165
+ * formSpaceFileValidationStorageFileProcessor({
166
+ * formSpaceFirestoreCollections: context,
167
+ * storageFileFirestoreCollections: context,
168
+ * appFormSpaceTypeConfigService: appFormSpaceTypeConfigService(formSpaceTypeConfigRecord(APP_FORM_SPACE_TYPE_CONFIGS)),
169
+ * validators: [MY_RESUME_VALIDATOR]
170
+ * })
171
+ * ];
172
+ * ```
173
+ */
174
+ export declare function formSpaceFileValidationStorageFileProcessor(config: FormSpaceFileValidationStorageFileProcessorConfig): StorageFileProcessingPurposeSubtaskProcessorConfigWithTarget<FormSpaceFileValidationSubtaskMetadata, FormSpaceFileValidationSubtask>;
@@ -0,0 +1,6 @@
1
+ export * from './formspace.action.server';
2
+ export * from './formspace.error';
3
+ export * from './formspace.module';
4
+ export * from './formspace.task.service.handler';
5
+ export * from './formspace.upload.initializer';
6
+ export * from './formspace.validation';
@@ -1,4 +1,5 @@
1
1
  export * from './calendar';
2
+ export * from './formspace';
2
3
  export * from './mailgun';
3
4
  export * from './notification';
4
5
  export * from './storagefile';
@@ -210,20 +210,27 @@ export interface ProcessStorageFileInTransactionInput {
210
210
  * Creates or restarts a notification task for the file based on its current processing state,
211
211
  * handling stuck-processing detection, forced restarts, and re-processing of already-successful files.
212
212
  *
213
- * @param context - The storage file server actions context.
213
+ * Takes the BASE context rather than the full one so a sibling model's actions (Calendar's ICS re-flag,
214
+ * for instance) can build this without standing up the upload service and signed-upload policy registry
215
+ * it has no use for.
216
+ *
217
+ * @param context - The base storage file server actions context.
214
218
  * @returns An async function that processes a storage file within a transaction.
215
219
  */
216
- export declare function _processStorageFileInTransactionFactory(context: StorageFileServerActionsContext): (input: ProcessStorageFileInTransactionInput, transaction: Transaction) => Promise<void>;
220
+ export declare function _processStorageFileInTransactionFactory(context: BaseStorageFileServerActionsContext): (input: ProcessStorageFileInTransactionInput, transaction: Transaction) => Promise<void>;
217
221
  /**
218
222
  * Factory for the `processStorageFile` action.
219
223
  *
220
224
  * Processes a single {@link StorageFile} by creating a notification task for it
221
225
  * and marking it as processing. Validates the file is in a valid state for processing.
222
226
  *
223
- * @param context - The storage file server actions context.
227
+ * Takes the BASE context, so a sibling model that needs to re-flag a StorageFile it owns can build this
228
+ * action directly instead of injecting the whole {@link StorageFileServerActions}.
229
+ *
230
+ * @param context - The base storage file server actions context.
224
231
  * @returns An async transform-and-validate function that processes a single StorageFile.
225
232
  */
226
- export declare function processStorageFileFactory(context: StorageFileServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<ProcessStorageFileParams, (storageFileDocument: StorageFileDocument) => Promise<ProcessStorageFileResult>, object, unknown>;
233
+ export declare function processStorageFileFactory(context: BaseStorageFileServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<ProcessStorageFileParams, (storageFileDocument: StorageFileDocument) => Promise<ProcessStorageFileResult>, object, unknown>;
227
234
  /**
228
235
  * Factory for the `deleteAllQueuedStorageFiles` action.
229
236
  *
@@ -74,6 +74,22 @@ export interface StorageFileInitializeFromUploadServiceInitializerStorageFileDoc
74
74
  * @returns A permanent failure result with the error and optional created file reference.
75
75
  */
76
76
  export declare function storageFileInitializeFromUploadServiceInitializerResultPermanentFailure(error: unknown, createdFile?: Maybe<StoragePathRef>): StorageFileInitializeFromUploadServiceInitializerResult;
77
+ /**
78
+ * Convenience factory for creating a TRANSIENT failure result, indicating the initializer could not
79
+ * finish for a reason that may not recur.
80
+ *
81
+ * The difference from a permanent failure is what happens to the SOURCE upload: a permanent failure
82
+ * discards it, while a transient one leaves it in place for the next sweep to retry. Any intermediate
83
+ * file named by `createdFile` is deleted either way, so a retry never finds a half-written destination.
84
+ *
85
+ * Use this for infrastructure — a contended transaction, a storage blip — and reserve the permanent
86
+ * result for a decision that no retry will reverse.
87
+ *
88
+ * @param error - The error that caused the failure.
89
+ * @param createdFile - Optional path to a file that was created before the error and should be deleted.
90
+ * @returns A transient failure result with the error and optional created file reference.
91
+ */
92
+ export declare function storageFileInitializeFromUploadServiceInitializerResultTransientFailure(error: unknown, createdFile?: Maybe<StoragePathRef>): StorageFileInitializeFromUploadServiceInitializerResult;
77
93
  /**
78
94
  * Processes the input details accessor and returns the results.
79
95
  */
package/oidc/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.42.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
- "@dereekb/analytics": "13.42.0",
7
- "@dereekb/date": "13.42.0",
8
- "@dereekb/firebase": "13.42.0",
9
- "@dereekb/firebase-server": "13.42.0",
10
- "@dereekb/model": "13.42.0",
11
- "@dereekb/nestjs": "13.42.0",
12
- "@dereekb/rxjs": "13.42.0",
13
- "@dereekb/util": "13.42.0",
14
- "@dereekb/zoho": "13.42.0",
6
+ "@dereekb/analytics": "13.43.0",
7
+ "@dereekb/date": "13.43.0",
8
+ "@dereekb/firebase": "13.43.0",
9
+ "@dereekb/firebase-server": "13.43.0",
10
+ "@dereekb/model": "13.43.0",
11
+ "@dereekb/nestjs": "13.43.0",
12
+ "@dereekb/rxjs": "13.43.0",
13
+ "@dereekb/util": "13.43.0",
14
+ "@dereekb/zoho": "13.43.0",
15
15
  "@nestjs/common": "^11.1.19",
16
16
  "@nestjs/config": "^4.0.4",
17
17
  "express": "^5.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "13.42.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -58,17 +58,17 @@
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@cantoo/pdf-lib": "^2.6.5",
61
- "@dereekb/analytics": "13.42.0",
62
- "@dereekb/calcom": "13.42.0",
63
- "@dereekb/date": "13.42.0",
64
- "@dereekb/dbx-core": "13.42.0",
65
- "@dereekb/discord": "13.42.0",
66
- "@dereekb/firebase": "13.42.0",
67
- "@dereekb/model": "13.42.0",
68
- "@dereekb/nestjs": "13.42.0",
69
- "@dereekb/rxjs": "13.42.0",
70
- "@dereekb/util": "13.42.0",
71
- "@dereekb/zoho": "13.42.0",
61
+ "@dereekb/analytics": "13.43.0",
62
+ "@dereekb/calcom": "13.43.0",
63
+ "@dereekb/date": "13.43.0",
64
+ "@dereekb/dbx-core": "13.43.0",
65
+ "@dereekb/discord": "13.43.0",
66
+ "@dereekb/firebase": "13.43.0",
67
+ "@dereekb/model": "13.43.0",
68
+ "@dereekb/nestjs": "13.43.0",
69
+ "@dereekb/rxjs": "13.43.0",
70
+ "@dereekb/util": "13.43.0",
71
+ "@dereekb/zoho": "13.43.0",
72
72
  "@google-cloud/firestore": "^7.11.6",
73
73
  "@google-cloud/storage": "^7.19.0",
74
74
  "@modelcontextprotocol/node": "2.0.0",
package/test/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/test",
3
- "version": "13.42.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
- "@dereekb/analytics": "13.42.0",
7
- "@dereekb/date": "13.42.0",
8
- "@dereekb/firebase": "13.42.0",
9
- "@dereekb/firebase-server": "13.42.0",
10
- "@dereekb/firebase-server/oidc": "13.42.0",
11
- "@dereekb/model": "13.42.0",
12
- "@dereekb/nestjs": "13.42.0",
13
- "@dereekb/rxjs": "13.42.0",
14
- "@dereekb/util": "13.42.0",
6
+ "@dereekb/analytics": "13.43.0",
7
+ "@dereekb/date": "13.43.0",
8
+ "@dereekb/firebase": "13.43.0",
9
+ "@dereekb/firebase-server": "13.43.0",
10
+ "@dereekb/firebase-server/oidc": "13.43.0",
11
+ "@dereekb/model": "13.43.0",
12
+ "@dereekb/nestjs": "13.43.0",
13
+ "@dereekb/rxjs": "13.43.0",
14
+ "@dereekb/util": "13.43.0",
15
15
  "@google-cloud/firestore": "^7.11.6",
16
16
  "@google-cloud/storage": "^7.19.0",
17
17
  "@nestjs/common": "^11.1.19",
@@ -24,7 +24,7 @@
24
24
  "supertest": "^7.2.2"
25
25
  },
26
26
  "devDependencies": {
27
- "@dereekb/nestjs": "13.42.0"
27
+ "@dereekb/nestjs": "13.43.0"
28
28
  },
29
29
  "exports": {
30
30
  "./package.json": "./package.json",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/twilio",
3
- "version": "13.42.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
- "@dereekb/date": "13.42.0",
7
- "@dereekb/firebase": "13.42.0",
8
- "@dereekb/firebase-server": "13.42.0",
9
- "@dereekb/model": "13.42.0",
10
- "@dereekb/nestjs": "13.42.0",
11
- "@dereekb/rxjs": "13.42.0",
12
- "@dereekb/util": "13.42.0"
6
+ "@dereekb/date": "13.43.0",
7
+ "@dereekb/firebase": "13.43.0",
8
+ "@dereekb/firebase-server": "13.43.0",
9
+ "@dereekb/model": "13.43.0",
10
+ "@dereekb/nestjs": "13.43.0",
11
+ "@dereekb/rxjs": "13.43.0",
12
+ "@dereekb/util": "13.43.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",
package/zoho/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/zoho",
3
- "version": "13.42.0",
3
+ "version": "13.43.0",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
- "@dereekb/analytics": "13.42.0",
7
- "@dereekb/date": "13.42.0",
8
- "@dereekb/model": "13.42.0",
9
- "@dereekb/nestjs": "13.42.0",
10
- "@dereekb/rxjs": "13.42.0",
11
- "@dereekb/firebase": "13.42.0",
12
- "@dereekb/firebase-server": "13.42.0",
13
- "@dereekb/util": "13.42.0",
14
- "@dereekb/zoho": "13.42.0",
6
+ "@dereekb/analytics": "13.43.0",
7
+ "@dereekb/date": "13.43.0",
8
+ "@dereekb/model": "13.43.0",
9
+ "@dereekb/nestjs": "13.43.0",
10
+ "@dereekb/rxjs": "13.43.0",
11
+ "@dereekb/firebase": "13.43.0",
12
+ "@dereekb/firebase-server": "13.43.0",
13
+ "@dereekb/util": "13.43.0",
14
+ "@dereekb/zoho": "13.43.0",
15
15
  "@nestjs/common": "^11.1.19",
16
16
  "@nestjs/config": "^4.0.4",
17
17
  "express": "^5.2.1"