@dereekb/firebase-server 13.42.0 → 14.0.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 (45) hide show
  1. package/calcom/index.esm.js +57 -96
  2. package/calcom/package.json +11 -11
  3. package/discord/index.esm.js +48 -74
  4. package/discord/package.json +11 -11
  5. package/index.esm.js +336 -583
  6. package/mailgun/index.esm.js +20 -32
  7. package/mailgun/package.json +9 -9
  8. package/mcp/index.esm.js +131 -185
  9. package/mcp/package.json +14 -14
  10. package/model/index.esm.js +19560 -16566
  11. package/model/package.json +14 -14
  12. package/model/src/lib/calendar/calendar.action.server.d.ts +9 -7
  13. package/model/src/lib/calendar/calendar.module.d.ts +1 -4
  14. package/model/src/lib/formspace/formspace.action.server.d.ts +266 -0
  15. package/model/src/lib/formspace/formspace.error.d.ts +112 -0
  16. package/model/src/lib/formspace/formspace.module.d.ts +57 -0
  17. package/model/src/lib/formspace/formspace.task.service.handler.d.ts +98 -0
  18. package/model/src/lib/formspace/formspace.upload.initializer.d.ts +89 -0
  19. package/model/src/lib/formspace/formspace.validation.d.ts +174 -0
  20. package/model/src/lib/formspace/index.d.ts +6 -0
  21. package/model/src/lib/index.d.ts +1 -0
  22. package/model/src/lib/storagefile/storagefile.action.server.d.ts +11 -4
  23. package/model/src/lib/storagefile/storagefile.task.service.handler.d.ts +3 -3
  24. package/model/src/lib/storagefile/storagefile.upload.service.initializer.d.ts +16 -0
  25. package/oidc/index.esm.js +144 -251
  26. package/oidc/package.json +14 -14
  27. package/package.json +24 -24
  28. package/src/lib/function/type.d.ts +0 -9
  29. package/src/lib/nest/app.d.ts +0 -4
  30. package/src/lib/nest/development/development.app.function.d.ts +2 -2
  31. package/src/lib/nest/model/analytics.handler.d.ts +0 -8
  32. package/src/lib/nest/model/api.details.d.ts +0 -8
  33. package/src/lib/nest/nest.provider.d.ts +0 -8
  34. package/test/index.esm.js +239 -395
  35. package/test/package.json +17 -17
  36. package/test/src/lib/firebase/firebase.admin.auth.d.ts +4 -25
  37. package/test/src/lib/firebase/firebase.admin.nest.d.ts +0 -4
  38. package/test/src/lib/firebase/firebase.admin.nest.function.d.ts +7 -7
  39. package/test/src/lib/firebase/firebase.function.d.ts +20 -81
  40. package/test/src/lib/firebase/firebase.test.d.ts +0 -9
  41. package/twilio/index.esm.js +11 -14
  42. package/twilio/package.json +8 -8
  43. package/zoho/index.esm.js +72 -132
  44. package/zoho/package.json +12 -12
  45. package/zoho/src/lib/zoho.accounts.firebase.system.d.ts +0 -10
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.42.0",
3
+ "version": "14.0.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
- "@nestjs/common": "^11.1.19",
15
- "@nestjs/config": "^4.0.4",
16
- "archiver": "^7.0.1",
6
+ "@dereekb/analytics": "14.0.0",
7
+ "@dereekb/date": "14.0.0",
8
+ "@dereekb/firebase": "14.0.0",
9
+ "@dereekb/firebase-server": "14.0.0",
10
+ "@dereekb/model": "14.0.0",
11
+ "@dereekb/nestjs": "14.0.0",
12
+ "@dereekb/rxjs": "14.0.0",
13
+ "@dereekb/util": "14.0.0",
14
+ "@nestjs/common": "^12.0.1",
15
+ "@nestjs/config": "^12.0.0",
16
+ "archiver": "^8.0.0",
17
17
  "date-fns": "^4.1.0",
18
18
  "express": "^5.2.1",
19
- "firebase-functions": "^7.2.5",
19
+ "firebase-functions": "^7.3.2",
20
20
  "make-error": "^1.3.6",
21
21
  "@cantoo/pdf-lib": "^2.6.5",
22
- "sharp": "0.35.2"
22
+ "sharp": "0.35.4"
23
23
  },
24
24
  "exports": {
25
25
  "./package.json": "./package.json",
@@ -1,8 +1,7 @@
1
- import { type AppCalendarTypeConfigServiceRef, type CalendarDocument, type CalendarFirestoreCollections, type FirestoreContextReference, type FlagStaleCalendarsForSyncParams, type FlagStaleCalendarsForSyncResult, type RotateCalendarIcsParams, type RotateCalendarIcsResult, type StorageFileFirestoreCollections, type SyncAllFlaggedCalendarsParams, type SyncAllFlaggedCalendarsResult, type SyncCalendarParams, type SyncCalendarResult } from '@dereekb/firebase';
2
- import { type FirebaseServerActionsContext, type FirebaseServerStorageServiceRef } from '@dereekb/firebase-server';
1
+ import { type AppCalendarTypeConfigServiceRef, type CalendarDocument, type CalendarFirestoreCollections, type FlagStaleCalendarsForSyncParams, type FlagStaleCalendarsForSyncResult, type RotateCalendarIcsParams, type RotateCalendarIcsResult, type SyncAllFlaggedCalendarsParams, type SyncAllFlaggedCalendarsResult, type SyncCalendarParams, type SyncCalendarResult } from '@dereekb/firebase';
3
2
  import { type TransformAndValidateFunctionResult } from '@dereekb/model';
4
3
  import { type InjectionToken } from '@nestjs/common';
5
- import { type StorageFileServerActions } from '../storagefile/storagefile.action.server';
4
+ import { type BaseStorageFileServerActionsContext } from '../storagefile/storagefile.action.server';
6
5
  /**
7
6
  * NestJS injection token for the {@link BaseCalendarServerActionsContext}.
8
7
  */
@@ -14,15 +13,18 @@ export declare const CALENDAR_SERVER_ACTION_CONTEXT_TOKEN: InjectionToken;
14
13
  /**
15
14
  * Minimal context providing the Firebase infrastructure, storage and Firestore collections every Calendar
16
15
  * server action needs.
16
+ *
17
+ * Extends {@link BaseStorageFileServerActionsContext} because a Calendar's ICS feed IS a StorageFile: the
18
+ * sync and rotate paths re-flag it for processing, which builds a notification task. Taking the base
19
+ * context lets those paths build `processStorageFile` locally rather than injecting the whole
20
+ * StorageFileServerActions — and so keeps the Calendar module free of a StorageFileModule import.
17
21
  */
18
- export interface BaseCalendarServerActionsContext extends FirebaseServerActionsContext, CalendarFirestoreCollections, StorageFileFirestoreCollections, FirebaseServerStorageServiceRef, FirestoreContextReference {
22
+ export interface BaseCalendarServerActionsContext extends BaseStorageFileServerActionsContext, CalendarFirestoreCollections {
19
23
  }
20
24
  /**
21
- * Full context for the Calendar server actions, adding the type registry and the StorageFile actions the
22
- * sweep re-flags through.
25
+ * Full context for the Calendar server actions, adding the type registry.
23
26
  */
24
27
  export interface CalendarServerActionsContext extends BaseCalendarServerActionsContext, AppCalendarTypeConfigServiceRef {
25
- readonly storageFileServerActions: StorageFileServerActions;
26
28
  }
27
29
  /**
28
30
  * The publish-side server actions for the Calendar model.
@@ -2,7 +2,6 @@ import { type InjectionToken, type ModuleMetadata } from '@nestjs/common';
2
2
  import { type Maybe } from '@dereekb/util';
3
3
  import { AppCalendarTypeConfigService, type CalendarTypeConfig } from '@dereekb/firebase';
4
4
  import { type BaseCalendarServerActionsContext, CalendarServerActions, type CalendarServerActionsContext } from './calendar.action.server';
5
- import { StorageFileServerActions } from '../storagefile/storagefile.action.server';
6
5
  /**
7
6
  * NestJS injection token for the app's `CalendarTypeConfig[]` registry.
8
7
  *
@@ -25,10 +24,9 @@ export declare function appCalendarTypeConfigServiceFactory(calendarTypeConfigs:
25
24
  *
26
25
  * @param context - The base context providing Firebase infrastructure and collections.
27
26
  * @param appCalendarTypeConfigServiceInstance - The app's calendar type registry service.
28
- * @param storageFileServerActions - The StorageFile actions the sweep re-flags through.
29
27
  * @returns The fully assembled context.
30
28
  */
31
- export declare function calendarServerActionsContextFactory(context: BaseCalendarServerActionsContext, appCalendarTypeConfigServiceInstance: AppCalendarTypeConfigService, storageFileServerActions: StorageFileServerActions): CalendarServerActionsContext;
29
+ export declare function calendarServerActionsContextFactory(context: BaseCalendarServerActionsContext, appCalendarTypeConfigServiceInstance: AppCalendarTypeConfigService): CalendarServerActionsContext;
32
30
  /**
33
31
  * Factory that creates a {@link CalendarServerActions} instance from the assembled context.
34
32
  *
@@ -40,7 +38,6 @@ export interface ProvideAppCalendarMetadataConfig extends Pick<ModuleMetadata, '
40
38
  /**
41
39
  * The AppCalendarModule requires the following dependencies in order to initialize properly:
42
40
  * - BaseCalendarServerActionsContext (BASE_CALENDAR_SERVER_ACTION_CONTEXT_TOKEN)
43
- * - StorageFileServerActions
44
41
  *
45
42
  * This module declaration makes it easier to import a module that exports those dependencies.
46
43
  */
@@ -0,0 +1,266 @@
1
+ import { type AppFormSpaceTypeConfigServiceRef, type DeleteFormSpaceParams, type ExpireAllExpiredFormSpacesParams, type ExpireAllExpiredFormSpacesResult, type FirebaseAuthUserId, type FirestoreContextReference, type FormSpaceDocument, type FormSpaceFirestoreCollections, type FormSpaceId, type LockFormSpaceParams, type NotificationFirestoreCollections, type ProcessAllQueuedFormSpacesParams, type ProcessAllQueuedFormSpacesResult, type RemoveFormSpaceFileParams, type ReopenFormSpaceParams, type StorageFileFirestoreCollections, type SubmitFormSpaceParams, type SubmitFormSpaceResult, type CreateFormSpaceParams, type UpdateFormSpaceParams } from '@dereekb/firebase';
2
+ import { type FirebaseServerActionsContext, type FirebaseServerAuthServiceRef } from '@dereekb/firebase-server';
3
+ import { type Maybe, type Milliseconds } from '@dereekb/util';
4
+ import { type TransformAndValidateFunctionResult } from '@dereekb/model';
5
+ import { type InjectionToken } from '@nestjs/common';
6
+ import { type NotificationExpediteServiceRef } from '../notification/notification.expedite.service';
7
+ /**
8
+ * NestJS injection token for the {@link BaseFormSpaceServerActionsContext}.
9
+ */
10
+ export declare const BASE_FORM_SPACE_SERVER_ACTION_CONTEXT_TOKEN: InjectionToken;
11
+ /**
12
+ * NestJS injection token for the fully assembled {@link FormSpaceServerActionsContext}.
13
+ */
14
+ export declare const FORM_SPACE_SERVER_ACTION_CONTEXT_TOKEN: InjectionToken;
15
+ /**
16
+ * Default page size for {@link expireAllExpiredFormSpacesFactory}.
17
+ */
18
+ export declare const DEFAULT_FORM_SPACE_EXPIRATION_SWEEP_PAGE_SIZE = 50;
19
+ /**
20
+ * Default wall-clock budget for {@link expireAllExpiredFormSpacesFactory}: one minute.
21
+ */
22
+ export declare const DEFAULT_FORM_SPACE_EXPIRATION_SWEEP_MAX_RUN_TIME: Milliseconds;
23
+ /**
24
+ * Minimal context providing the Firebase infrastructure and Firestore collections every FormSpace server
25
+ * action needs.
26
+ */
27
+ export interface BaseFormSpaceServerActionsContext extends FirebaseServerActionsContext, FormSpaceFirestoreCollections, StorageFileFirestoreCollections, NotificationFirestoreCollections, NotificationExpediteServiceRef, FirebaseServerAuthServiceRef, FirestoreContextReference {
28
+ }
29
+ /**
30
+ * Full context for the FormSpace server actions, adding the type registry.
31
+ */
32
+ export interface FormSpaceServerActionsContext extends BaseFormSpaceServerActionsContext, AppFormSpaceTypeConfigServiceRef {
33
+ }
34
+ /**
35
+ * Extra input for {@link FormSpaceServerActions.createFormSpace}, supplied by the callable rather than by
36
+ * the caller: the space's owner is WHO IS CALLING, never a value in the request body.
37
+ */
38
+ export interface CreateFormSpaceActionInput {
39
+ readonly uid: string;
40
+ readonly ownerKey?: Maybe<string>;
41
+ /**
42
+ * Create the space at THIS id rather than at a generated one.
43
+ *
44
+ * For the SHARED shape — one space per target model, reached by everyone who reaches the target — a
45
+ * generated id lets two concurrent callers each mint one, and neither sees the other's files. Deriving
46
+ * the id with {@link formSpaceIdForModel} makes the create idempotent and lets a client read the space
47
+ * with a plain `get` before it has ever called create.
48
+ */
49
+ readonly formSpaceId?: Maybe<FormSpaceId>;
50
+ /**
51
+ * Return the existing space when {@link formSpaceId} is already taken, instead of failing.
52
+ *
53
+ * Only meaningful alongside {@link formSpaceId} — a generated id can never collide.
54
+ */
55
+ readonly getOrCreate?: Maybe<boolean>;
56
+ }
57
+ /**
58
+ * Extra input for {@link FormSpaceServerActions.removeFormSpaceFile}, supplied by the callable rather than by
59
+ * the caller: WHO is removing decides which files they may remove, so it can never be a value in the request
60
+ * body.
61
+ */
62
+ export interface RemoveFormSpaceFileActionInput {
63
+ /**
64
+ * The uid of the caller.
65
+ *
66
+ * A REQUIRED key with a nullable value, so a call site has to state it rather than inherit an unrestricted
67
+ * remove by forgetting an optional argument. A null uid is refused by any slot whose
68
+ * {@link FormSpaceFileAccess} is `'uploader'`, which is the fail-closed direction.
69
+ */
70
+ readonly uid: Maybe<string>;
71
+ }
72
+ /**
73
+ * Extra input for {@link FormSpaceServerActions.reopenFormSpace}, supplied by the callable rather than by the
74
+ * caller: WHO reopened is recorded on `rby`, so it can never be a value in the request body.
75
+ */
76
+ export interface ReopenFormSpaceActionInput {
77
+ /**
78
+ * The uid of the caller.
79
+ */
80
+ readonly uid?: Maybe<FirebaseAuthUserId>;
81
+ }
82
+ /**
83
+ * Extra input for {@link FormSpaceServerActions.lockFormSpace}, on the same terms: `lby` records who made
84
+ * the submission final.
85
+ */
86
+ export interface LockFormSpaceActionInput {
87
+ /**
88
+ * The uid of the caller.
89
+ */
90
+ readonly uid?: Maybe<FirebaseAuthUserId>;
91
+ }
92
+ /**
93
+ * The server actions for the FormSpace model.
94
+ *
95
+ * @see {@link formSpaceServerActions} for the concrete implementation factory.
96
+ */
97
+ export declare abstract class FormSpaceServerActions {
98
+ abstract createFormSpace(params: CreateFormSpaceParams): Promise<TransformAndValidateFunctionResult<CreateFormSpaceParams, (input: CreateFormSpaceActionInput) => Promise<FormSpaceDocument>>>;
99
+ abstract updateFormSpace(params: UpdateFormSpaceParams): Promise<TransformAndValidateFunctionResult<UpdateFormSpaceParams, (formSpaceDocument: FormSpaceDocument) => Promise<FormSpaceDocument>>>;
100
+ abstract submitFormSpace(params: SubmitFormSpaceParams): Promise<TransformAndValidateFunctionResult<SubmitFormSpaceParams, (formSpaceDocument: FormSpaceDocument) => Promise<SubmitFormSpaceResult>>>;
101
+ abstract reopenFormSpace(params: ReopenFormSpaceParams): Promise<TransformAndValidateFunctionResult<ReopenFormSpaceParams, (formSpaceDocument: FormSpaceDocument, input: ReopenFormSpaceActionInput) => Promise<FormSpaceDocument>>>;
102
+ abstract lockFormSpace(params: LockFormSpaceParams): Promise<TransformAndValidateFunctionResult<LockFormSpaceParams, (formSpaceDocument: FormSpaceDocument, input: LockFormSpaceActionInput) => Promise<FormSpaceDocument>>>;
103
+ abstract removeFormSpaceFile(params: RemoveFormSpaceFileParams): Promise<TransformAndValidateFunctionResult<RemoveFormSpaceFileParams, (formSpaceDocument: FormSpaceDocument, input: RemoveFormSpaceFileActionInput) => Promise<FormSpaceDocument>>>;
104
+ abstract deleteFormSpace(params: DeleteFormSpaceParams): Promise<TransformAndValidateFunctionResult<DeleteFormSpaceParams, (formSpaceDocument: FormSpaceDocument) => Promise<void>>>;
105
+ abstract processAllQueuedFormSpaces(params: ProcessAllQueuedFormSpacesParams): Promise<TransformAndValidateFunctionResult<ProcessAllQueuedFormSpacesParams, () => Promise<ProcessAllQueuedFormSpacesResult>>>;
106
+ abstract expireAllExpiredFormSpaces(params: ExpireAllExpiredFormSpacesParams): Promise<TransformAndValidateFunctionResult<ExpireAllExpiredFormSpacesParams, () => Promise<ExpireAllExpiredFormSpacesResult>>>;
107
+ }
108
+ /**
109
+ * Creates a concrete {@link FormSpaceServerActions} implementation from the given context.
110
+ *
111
+ * @param context - The fully assembled FormSpace server actions context.
112
+ * @returns The server actions.
113
+ */
114
+ export declare function formSpaceServerActions(context: FormSpaceServerActionsContext): FormSpaceServerActions;
115
+ /**
116
+ * Factory for the `createFormSpace` action.
117
+ *
118
+ * Rejects an unregistered {@link FormSpaceType} outright. This is the one place the registry is STRICT —
119
+ * a sweep over existing documents falls back to the default config so one bad document cannot take the
120
+ * pass down, but a space that could never be filled in or submitted should not be created at all.
121
+ *
122
+ * @param context - The FormSpace server actions context.
123
+ * @returns An async transform-and-validate function that creates a FormSpace.
124
+ */
125
+ export declare function createFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<CreateFormSpaceParams<import("@dereekb/firebase").FormSpaceData>, ({ uid, ownerKey, formSpaceId, getOrCreate }: CreateFormSpaceActionInput) => Promise<FormSpaceDocument>, object, unknown>;
126
+ /**
127
+ * Factory for the `updateFormSpace` action.
128
+ *
129
+ * `data` REPLACES the stored JSON rather than merging into it — the client owns the whole form, and a
130
+ * merge would make clearing a field impossible to express.
131
+ *
132
+ * @param context - The FormSpace server actions context.
133
+ * @returns An async transform-and-validate function that updates a draft FormSpace.
134
+ */
135
+ export declare function updateFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<UpdateFormSpaceParams<import("@dereekb/firebase").FormSpaceData>, (formSpaceDocument: FormSpaceDocument) => Promise<FormSpaceDocument>, object, unknown>;
136
+ /**
137
+ * Factory for the `submitFormSpace` action.
138
+ *
139
+ * The LOCK is taken in a transaction; the processing task is created afterwards. That split is deliberate:
140
+ * `createOrRunUniqueNotificationDocument()` does not accept a transaction, since running a task inside one
141
+ * would hold the lock across the whole handler. A crash in between leaves the space in
142
+ * QUEUED_FOR_PROCESSING with no task, which {@link processAllQueuedFormSpacesFactory} is the backstop for.
143
+ *
144
+ * Also serves a RESUBMISSION after a reopen, unchanged: the space is a draft again, so the same editable
145
+ * check and the same submit blockers apply. The only difference is that the task is keyed by the space's
146
+ * reopen count, so it gets a document and a checkpoint flow of its own instead of colliding with the
147
+ * finished task of the attempt before it.
148
+ *
149
+ * @param context - The FormSpace server actions context.
150
+ * @returns An async transform-and-validate function that submits a FormSpace.
151
+ */
152
+ export declare function submitFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<SubmitFormSpaceParams, (formSpaceDocument: FormSpaceDocument) => Promise<SubmitFormSpaceResult>, object, unknown>;
153
+ /**
154
+ * Factory for the `reopenFormSpace` action.
155
+ *
156
+ * The inverse of `submitFormSpace`, and deliberately NOT a widening of `isFormSpaceEditable`: it restores
157
+ * every condition that predicate tests instead, so update, upload, `removeFile` and a resubmit all come
158
+ * back with no further change anywhere.
159
+ *
160
+ * The reopen task story is handled by KEYING rather than cleanup. The superseded attempt's task document is
161
+ * left where it is: it is either already done and waiting for the notification cleanup sweep, or still
162
+ * pending and about to be fenced off by its own handler, which compares the count it was created with
163
+ * against the space's. Deleting it here would mean reaching into a Notification subcollection whose parent
164
+ * this action cannot reconstruct once the task's own cleanup has nulled `pn` — for no gain, since the
165
+ * resubmission keys a document of its own either way.
166
+ *
167
+ * @param context - The FormSpace server actions context.
168
+ * @returns An async transform-and-validate function that reopens a submitted FormSpace.
169
+ */
170
+ export declare function reopenFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<ReopenFormSpaceParams, (formSpaceDocument: FormSpaceDocument, input: ReopenFormSpaceActionInput) => Promise<FormSpaceDocument>, object, unknown>;
171
+ /**
172
+ * Factory for the `lockFormSpace` action.
173
+ *
174
+ * Not a state transition. The space stays exactly where it is and its processing is untouched; the only
175
+ * thing that moves is `lat`, brought forward to now so every reopen predicate answers false from here on.
176
+ *
177
+ * Accepts a reopened DRAFT as well as a SUBMITTED space — locking a draft that has been submitted before is
178
+ * how a reviewer says "the next submission is the last one", and it survives the resubmit because
179
+ * {@link submitFormSpaceTemplate} only writes `lat` for a space that has never been submitted. A space with
180
+ * no submission at all is the one case refused.
181
+ *
182
+ * @param context - The FormSpace server actions context.
183
+ * @returns An async transform-and-validate function that locks a FormSpace's submission.
184
+ */
185
+ export declare function lockFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<LockFormSpaceParams, (formSpaceDocument: FormSpaceDocument, input: LockFormSpaceActionInput) => Promise<FormSpaceDocument>, object, unknown>;
186
+ /**
187
+ * Factory for the `removeFormSpaceFile` action.
188
+ *
189
+ * Drops one file from a slot. The StorageFile is FLAGGED, never deleted inline — the StorageFile delete
190
+ * sweep owns removing the object from GCS, and a second code path that removed it here is how an orphaned
191
+ * object gets left behind.
192
+ *
193
+ * `uc` is deliberately NOT decremented: it counts uploads ACCEPTED over the space's lifetime, and letting a
194
+ * remove refund it would turn `maxUploads` from a bound on work done into a bound on files retained, which
195
+ * an upload/remove loop could then evade entirely.
196
+ *
197
+ * The caller's `removeFile` ROLE opened the door; the type's {@link FormSpaceFileAccess} decides which files
198
+ * inside it are theirs. That second check is made HERE rather than in the callable because it is per-file
199
+ * and needs the space's own `f` array — the same snapshot the removal writes, read under the same
200
+ * transaction, so a concurrent supersede cannot slip a different file under the decision.
201
+ *
202
+ * @param context - The FormSpace server actions context.
203
+ * @returns An async transform-and-validate function that removes one file from a draft FormSpace.
204
+ */
205
+ export declare function removeFormSpaceFileFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<RemoveFormSpaceFileParams, (formSpaceDocument: FormSpaceDocument, input: RemoveFormSpaceFileActionInput) => Promise<FormSpaceDocument>, object, unknown>;
206
+ /**
207
+ * Input for the function {@link _queueFormSpaceForProcessingFactory} returns.
208
+ */
209
+ export interface QueueFormSpaceForProcessingInput {
210
+ readonly formSpaceDocument: FormSpaceDocument;
211
+ /**
212
+ * The space's reopen count — the submission ATTEMPT to key the task by. Defaults to 0.
213
+ *
214
+ * Getting this wrong is not cosmetic: a resubmission keyed to attempt 0 resolves to the finished task of
215
+ * the first submission, which `createOrRunUniqueNotificationDocument` leaves exactly as it found it.
216
+ */
217
+ readonly reopenCount?: Maybe<number>;
218
+ readonly runImmediately?: Maybe<boolean>;
219
+ }
220
+ /**
221
+ * Creates (or re-runs) the unique submission task for one ATTEMPT of a FormSpace and records its key on `pn`.
222
+ *
223
+ * Shared by `submitFormSpace` and the queued backstop sweep, which is what makes the backstop safe to run
224
+ * against a space whose task already exists: the task is unique per attempt, so a second call for the same
225
+ * attempt resolves to the same document rather than racing a second processor.
226
+ *
227
+ * @param context - The FormSpace server actions context.
228
+ * @returns Queues one FormSpace, reporting the task key and whether this call created it.
229
+ */
230
+ export declare function _queueFormSpaceForProcessingFactory(context: FormSpaceServerActionsContext): (input: QueueFormSpaceForProcessingInput) => Promise<SubmitFormSpaceResult>;
231
+ /**
232
+ * Factory for the `deleteFormSpace` action.
233
+ *
234
+ * Files are FLAGGED, not deleted: the StorageFile delete sweep owns removing the object from GCS, and
235
+ * duplicating that here would mean a second code path that can leave an orphaned object behind.
236
+ *
237
+ * @param context - The FormSpace server actions context.
238
+ * @returns An async transform-and-validate function that deletes a FormSpace and flags its files.
239
+ */
240
+ export declare function deleteFormSpaceFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<DeleteFormSpaceParams, (formSpaceDocument: FormSpaceDocument) => Promise<void>, object, unknown>;
241
+ /**
242
+ * Factory for the `processAllQueuedFormSpaces` action.
243
+ *
244
+ * The BACKSTOP for a submission whose task creation was lost between the lock transaction and the task
245
+ * write. It is safe to run against a space whose task already exists because the task is unique per space.
246
+ *
247
+ * @param context - The FormSpace server actions context.
248
+ * @returns An async transform-and-validate function that returns batch processing results.
249
+ */
250
+ export declare function processAllQueuedFormSpacesFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<ProcessAllQueuedFormSpacesParams, () => Promise<ProcessAllQueuedFormSpacesResult>, object, unknown>;
251
+ /**
252
+ * Factory for the `expireAllExpiredFormSpaces` action.
253
+ *
254
+ * A PAGED sweep with a pinned cutoff and a hard time budget, copying `openRouterRunTaskExpirationSweep`.
255
+ * `firestoreDate` persists an ISO8601 string, so Firestore's native TTL cannot be pointed at `eat` — this
256
+ * sweep is the mechanism, not a stopgap.
257
+ *
258
+ * No cursor is needed and one would be meaningless: expiring a page clears its `eat`, so the page no
259
+ * longer matches and re-running the query IS the next page. An empty page is therefore the only "done"
260
+ * signal, which is why the cutoff must be pinned — a cutoff advancing with the clock would let a space
261
+ * that ages mid-sweep join a page not yet reached, making the pass unbounded.
262
+ *
263
+ * @param context - The FormSpace server actions context.
264
+ * @returns An async transform-and-validate function that expires due FormSpaces.
265
+ */
266
+ export declare function expireAllExpiredFormSpacesFactory(context: FormSpaceServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<ExpireAllExpiredFormSpacesParams, () => Promise<ExpireAllExpiredFormSpacesResult>, object, unknown>;
@@ -0,0 +1,112 @@
1
+ import { type FormSpaceFileSlot, type FormSpaceId, type FormSpaceSubmitBlocker, type FormSpaceType, type FormSpaceUploadRejectionReason } from '@dereekb/firebase';
2
+ /**
3
+ * Creates an error indicating the app never registered the requested {@link FormSpaceType}.
4
+ *
5
+ * @param formSpaceType - The unregistered type.
6
+ * @returns A bad-request HttpsError with the FORM_SPACE_TYPE_NOT_REGISTERED error code.
7
+ */
8
+ export declare function formSpaceTypeNotRegisteredError(formSpaceType: FormSpaceType): import("firebase-functions/https").HttpsError;
9
+ /**
10
+ * Creates an error indicating the FormSpace is no longer a draft, so it cannot be changed.
11
+ *
12
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_NOT_EDITABLE error code.
13
+ */
14
+ export declare function formSpaceNotEditableError(): import("firebase-functions/https").HttpsError;
15
+ /**
16
+ * Creates an error indicating the FormSpace has never been submitted, so there is no submission to lock.
17
+ *
18
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_NOT_SUBMITTED error code.
19
+ */
20
+ export declare function formSpaceNotSubmittedError(): import("firebase-functions/https").HttpsError;
21
+ /**
22
+ * Creates an error indicating the FormSpace's submission is final, so it cannot be reopened.
23
+ *
24
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_NOT_REOPENABLE error code.
25
+ */
26
+ export declare function formSpaceNotReopenableError(): import("firebase-functions/https").HttpsError;
27
+ /**
28
+ * Creates an error indicating the FormSpace's submission is being processed right now.
29
+ *
30
+ * Deliberately separate from {@link formSpaceNotReopenableError}: this one clears on its own, and telling
31
+ * a caller their submission is final when the processor is simply mid-run would be wrong.
32
+ *
33
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_PROCESSING_IN_PROGRESS error code.
34
+ */
35
+ export declare function formSpaceProcessingInProgressError(): import("firebase-functions/https").HttpsError;
36
+ /**
37
+ * Creates an error indicating a required upload slot is still empty at submission time.
38
+ *
39
+ * @param slots - The unsatisfied slots.
40
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_REQUIRED_SLOT_MISSING error code.
41
+ */
42
+ export declare function formSpaceRequiredSlotMissingError(slots: FormSpaceFileSlot[]): import("firebase-functions/https").HttpsError;
43
+ /**
44
+ * Creates an error indicating a slot holds a file that failed validation.
45
+ *
46
+ * Carries the per-file reasons rather than only the slot names: "the slot is not acceptable" is not
47
+ * actionable, "scan.png is not a readable PDF" is.
48
+ *
49
+ * @param blockers - The invalid-file blockers reported by `formSpaceSubmitBlockers`.
50
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_HAS_INVALID_FILES error code.
51
+ */
52
+ export declare function formSpaceHasInvalidFilesError(blockers: FormSpaceSubmitBlocker[]): import("firebase-functions/https").HttpsError;
53
+ /**
54
+ * Creates an error indicating a slot is still awaiting a validation verdict.
55
+ *
56
+ * @param slots - The slots still being validated.
57
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_VALIDATION_PENDING error code.
58
+ */
59
+ export declare function formSpaceValidationPendingError(slots: FormSpaceFileSlot[]): import("firebase-functions/https").HttpsError;
60
+ /**
61
+ * Creates an error indicating the slot does not hold the file that was asked to be removed.
62
+ *
63
+ * @param slot - The slot that was targeted.
64
+ * @returns A bad-request HttpsError with the FORM_SPACE_FILE_NOT_FOUND error code.
65
+ */
66
+ export declare function formSpaceFileNotFoundError(slot: FormSpaceFileSlot): import("firebase-functions/https").HttpsError;
67
+ /**
68
+ * Creates an error indicating the caller may reach the FormSpace but not this file of it.
69
+ *
70
+ * Deliberately NOT a plain forbidden: the caller passed the space-level role check, and what refused them is
71
+ * the type's `FormSpaceFileAccess` narrowing to the file's own uploader. A distinct code lets a client say
72
+ * "that is someone else's file" rather than "you cannot edit this form".
73
+ *
74
+ * @param slot - The slot holding the file.
75
+ * @returns A bad-request HttpsError with the FORM_SPACE_FILE_ACCESS_DENIED error code.
76
+ */
77
+ export declare function formSpaceFileAccessDeniedError(slot: FormSpaceFileSlot): import("firebase-functions/https").HttpsError;
78
+ /**
79
+ * Creates an error indicating the upload was rejected by the type's rules.
80
+ *
81
+ * @param reason - Why the upload was rejected.
82
+ * @returns A bad-request HttpsError with the FORM_SPACE_UPLOAD_NOT_ALLOWED error code.
83
+ */
84
+ export declare function formSpaceUploadNotAllowedError(reason: FormSpaceUploadRejectionReason): import("firebase-functions/https").HttpsError;
85
+ /**
86
+ * Creates an error indicating the uploader is not the user the FormSpace belongs to.
87
+ *
88
+ * @returns A bad-request HttpsError with the FORM_SPACE_UPLOAD_USER_MISMATCH error code.
89
+ */
90
+ export declare function formSpaceUploadUserMismatchError(): import("firebase-functions/https").HttpsError;
91
+ /**
92
+ * Creates an error indicating the referenced FormSpace does not exist.
93
+ *
94
+ * @returns An unavailable HttpsError with the FORM_SPACE_NOT_FOUND error code.
95
+ */
96
+ export declare function formSpaceNotFoundError(): import("firebase-functions/https").HttpsError;
97
+ /**
98
+ * Creates an error indicating an explicit FormSpace id is already taken.
99
+ *
100
+ * @param formSpaceId - The id that already exists.
101
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_ALREADY_EXISTS error code.
102
+ */
103
+ export declare function formSpaceAlreadyExistsError(formSpaceId: FormSpaceId): import("firebase-functions/https").HttpsError;
104
+ /**
105
+ * Creates an error indicating a get-or-create resolved to a space of a different type.
106
+ *
107
+ * @param formSpaceId - The id that resolved.
108
+ * @param expected - The type that was asked for.
109
+ * @param found - The type the existing space actually carries.
110
+ * @returns A precondition-conflict HttpsError with the FORM_SPACE_TYPE_MISMATCH error code.
111
+ */
112
+ export declare function formSpaceTypeMismatchError(formSpaceId: FormSpaceId, expected: FormSpaceType, found: FormSpaceType): import("firebase-functions/https").HttpsError;
@@ -0,0 +1,57 @@
1
+ import { type InjectionToken, type ModuleMetadata } from '@nestjs/common';
2
+ import { type Maybe } from '@dereekb/util';
3
+ import { AppFormSpaceTypeConfigService, type FormSpaceTypeConfig } from '@dereekb/firebase';
4
+ import { type BaseFormSpaceServerActionsContext, FormSpaceServerActions, type FormSpaceServerActionsContext } from './formspace.action.server';
5
+ /**
6
+ * NestJS injection token for the app's `FormSpaceTypeConfig[]` registry.
7
+ *
8
+ * Apps bind this token via {@link appFormSpaceModuleMetadata} by passing `formSpaceTypeConfigs`.
9
+ */
10
+ export declare const FORM_SPACE_TYPE_CONFIGS_TOKEN: InjectionToken;
11
+ /**
12
+ * Factory that builds the app's {@link AppFormSpaceTypeConfigService} from its registered configs.
13
+ *
14
+ * @param formSpaceTypeConfigs - The app's form space type registry.
15
+ * @returns The service.
16
+ */
17
+ export declare function appFormSpaceTypeConfigServiceFactory(formSpaceTypeConfigs: FormSpaceTypeConfig[]): AppFormSpaceTypeConfigService;
18
+ /**
19
+ * Factory that assembles the full {@link FormSpaceServerActionsContext}.
20
+ *
21
+ * @param context - The base context providing Firebase infrastructure and collections.
22
+ * @param appFormSpaceTypeConfigServiceInstance - The app's form space type registry service.
23
+ * @returns The fully assembled context.
24
+ */
25
+ export declare function formSpaceServerActionsContextFactory(context: BaseFormSpaceServerActionsContext, appFormSpaceTypeConfigServiceInstance: AppFormSpaceTypeConfigService): FormSpaceServerActionsContext;
26
+ /**
27
+ * Factory that creates a {@link FormSpaceServerActions} instance from the assembled context.
28
+ *
29
+ * @param context - The fully assembled form space server actions context.
30
+ * @returns The server actions.
31
+ */
32
+ export declare function formSpaceServerActionsFactory(context: FormSpaceServerActionsContext): FormSpaceServerActions;
33
+ export interface ProvideAppFormSpaceMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
34
+ /**
35
+ * The AppFormSpaceModule requires the following dependencies in order to initialize properly:
36
+ * - BaseFormSpaceServerActionsContext (BASE_FORM_SPACE_SERVER_ACTION_CONTEXT_TOKEN)
37
+ *
38
+ * This module declaration makes it easier to import a module that exports those dependencies.
39
+ */
40
+ readonly dependencyModule?: Maybe<Required<ModuleMetadata>['imports']['0']>;
41
+ /**
42
+ * The app's {@link FormSpaceType} registry. Bound to {@link FORM_SPACE_TYPE_CONFIGS_TOKEN}.
43
+ */
44
+ readonly formSpaceTypeConfigs: FormSpaceTypeConfig[];
45
+ }
46
+ /**
47
+ * Convenience function used to generate ModuleMetadata for an app's FormSpaceModule.
48
+ *
49
+ * By default this module exports:
50
+ * - FormSpaceServerActionsContext (FORM_SPACE_SERVER_ACTION_CONTEXT_TOKEN)
51
+ * - FormSpaceServerActions
52
+ * - AppFormSpaceTypeConfigService
53
+ *
54
+ * @param config - The module configuration.
55
+ * @returns The assembled {@link ModuleMetadata} for the form space module.
56
+ */
57
+ export declare function appFormSpaceModuleMetadata(config: ProvideAppFormSpaceMetadataConfig): ModuleMetadata;
@@ -0,0 +1,98 @@
1
+ import { type FormSpace, type FormSpaceDocument, type FormSpaceFirestoreCollections, FormSpaceProcessingState, type FormSpaceSubmissionNotificationTaskData, type FormSpaceSubmissionSubtask, type FormSpaceSubmissionSubtaskMetadata } from '@dereekb/firebase';
2
+ import { type Getter, type Maybe } from '@dereekb/util';
3
+ import { type NotificationTaskServiceTaskHandlerConfig } from '../notification/notification.task.service.handler';
4
+ import { type NotificationTaskSubtaskCleanupInstructions, type NotificationTaskSubtaskFlowEntry, type NotificationTaskSubtaskInput, type NotificationTaskSubtaskResult, type NotificationTaskSubtaskNotificationTaskHandlerConfig, type NotificationTaskSubtaskProcessorConfig } from '../notification/notification.task.subtask.handler';
5
+ /**
6
+ * @module formspace.task.service.handler
7
+ *
8
+ * The submission-processing handler: one NotificationTask type dispatching, by {@link FormSpaceType}, to
9
+ * the app's registered processors.
10
+ *
11
+ * This is a thin specialization of {@link notificationTaskSubtaskNotificationTaskHandlerFactory}, exactly
12
+ * as `storageFileProcessingNotificationTaskHandler` is — the checkpoint / retry / delay semantics are the
13
+ * framework's, and the only thing FormSpace supplies is "how to load the space" and "what to write when
14
+ * processing concludes".
15
+ */
16
+ /**
17
+ * Input handed to every FormSpace submission subtask.
18
+ *
19
+ * @template M - subtask metadata type
20
+ * @template S - subtask checkpoint string type
21
+ */
22
+ export interface FormSpaceSubmissionSubtaskInput<M extends FormSpaceSubmissionSubtaskMetadata = FormSpaceSubmissionSubtaskMetadata, S extends FormSpaceSubmissionSubtask = FormSpaceSubmissionSubtask> extends NotificationTaskSubtaskInput<FormSpaceSubmissionNotificationTaskData<M, S>, M, S> {
23
+ /**
24
+ * The FormSpaceDocument being processed.
25
+ */
26
+ readonly formSpaceDocument: FormSpaceDocument;
27
+ /**
28
+ * Loads the FormSpace, memoized for the duration of one task run.
29
+ *
30
+ * Still a getter rather than the value, so a processor reads it the same way whether or not the handler
31
+ * happened to have it already. It resolves the snapshot the run's attempt fence was checked against, so
32
+ * every checkpoint in one run sees one consistent space.
33
+ */
34
+ readonly loadFormSpace: Getter<Promise<FormSpace>>;
35
+ }
36
+ /**
37
+ * Result of a FormSpace submission subtask.
38
+ */
39
+ export type FormSpaceSubmissionSubtaskResult<M extends FormSpaceSubmissionSubtaskMetadata = FormSpaceSubmissionSubtaskMetadata, S extends FormSpaceSubmissionSubtask = FormSpaceSubmissionSubtask> = NotificationTaskSubtaskResult<M, S>;
40
+ /**
41
+ * One entry in a FormSpace submission processor's checkpoint flow.
42
+ */
43
+ export type FormSpaceSubmissionSubtaskFlowEntry<M extends FormSpaceSubmissionSubtaskMetadata = FormSpaceSubmissionSubtaskMetadata, S extends FormSpaceSubmissionSubtask = FormSpaceSubmissionSubtask> = NotificationTaskSubtaskFlowEntry<FormSpaceSubmissionSubtaskInput<M, S>, FormSpaceSubmissionNotificationTaskData<M, S>, M, S>;
44
+ /**
45
+ * What a FormSpace submission processor asks the cleanup step to write.
46
+ */
47
+ export interface FormSpaceSubmissionSubtaskCleanupOutput extends NotificationTaskSubtaskCleanupInstructions {
48
+ /**
49
+ * The processing state to leave the FormSpace in. Defaults to SUCCESS.
50
+ */
51
+ readonly nextProcessingState?: Maybe<FormSpaceProcessingState>;
52
+ /**
53
+ * Whether to move the space to ARCHIVED as part of cleanup. Defaults to false.
54
+ */
55
+ readonly archive?: Maybe<boolean>;
56
+ }
57
+ /**
58
+ * A processor for one {@link FormSpaceType}, keyed by that type as its subtask target.
59
+ *
60
+ * @template M - subtask metadata type
61
+ * @template S - subtask checkpoint string type
62
+ */
63
+ export type FormSpaceSubmissionProcessorConfig<M extends FormSpaceSubmissionSubtaskMetadata = FormSpaceSubmissionSubtaskMetadata, S extends FormSpaceSubmissionSubtask = FormSpaceSubmissionSubtask> = NotificationTaskSubtaskProcessorConfig<FormSpaceSubmissionSubtaskInput<M, S>, FormSpaceSubmissionSubtaskCleanupOutput, FormSpaceSubmissionNotificationTaskData<M, S>, M, S>;
64
+ /**
65
+ * Configuration for {@link formSpaceSubmissionNotificationTaskHandler}.
66
+ */
67
+ export interface FormSpaceSubmissionNotificationTaskHandlerConfig extends Omit<NotificationTaskSubtaskNotificationTaskHandlerConfig<FormSpaceSubmissionSubtaskInput, FormSpaceSubmissionSubtaskCleanupOutput, FormSpaceSubmissionNotificationTaskData>, 'processors'> {
68
+ /**
69
+ * The per-type processors.
70
+ */
71
+ readonly processors: FormSpaceSubmissionProcessorConfig[];
72
+ /**
73
+ * Accessor for the FormSpace collection.
74
+ */
75
+ readonly formSpaceFirestoreCollections: FormSpaceFirestoreCollections;
76
+ }
77
+ /**
78
+ * The cleanup instructions applied when a processor asks for none: mark the submission successful.
79
+ *
80
+ * @returns The default cleanup instructions.
81
+ */
82
+ export declare const formSpaceSubmissionNotificationTaskHandlerDefaultCleanup: () => FormSpaceSubmissionSubtaskCleanupOutput;
83
+ /**
84
+ * Creates the {@link NotificationTaskServiceTaskHandlerConfig} that processes FormSpace submissions.
85
+ *
86
+ * @param config - Handler configuration including the per-type processors and the FormSpace collection.
87
+ * @returns A NotificationTaskServiceTaskHandlerConfig wired for FormSpace submission processing.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const handler = formSpaceSubmissionNotificationTaskHandler({
92
+ * processors: [demoExampleFormSpaceProcessor],
93
+ * validate: DEMO_FORM_SPACE_TYPE_CONFIGS.map((x) => x.formSpaceType),
94
+ * formSpaceFirestoreCollections: context
95
+ * });
96
+ * ```
97
+ */
98
+ export declare function formSpaceSubmissionNotificationTaskHandler(config: FormSpaceSubmissionNotificationTaskHandlerConfig): NotificationTaskServiceTaskHandlerConfig<FormSpaceSubmissionNotificationTaskData>;