@dereekb/firebase 13.41.0 → 13.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint/index.esm.js +145 -170
- package/eslint/package.json +3 -3
- package/index.esm.js +7207 -2997
- package/package.json +5 -5
- package/src/lib/common/firestore/snapshot/snapshot.field.d.ts +4 -0
- package/src/lib/common/storage/context.d.ts +9 -1
- package/src/lib/common/storage/driver/accessor.d.ts +12 -0
- package/src/lib/common/storage/index.d.ts +1 -0
- package/src/lib/common/storage/storage.url.d.ts +69 -0
- package/src/lib/model/calendar/calendar.action.d.ts +34 -0
- package/src/lib/model/calendar/calendar.api.d.ts +147 -0
- package/src/lib/model/calendar/calendar.api.error.d.ts +24 -0
- package/src/lib/model/calendar/calendar.d.ts +460 -0
- package/src/lib/model/calendar/calendar.expand.d.ts +95 -0
- package/src/lib/model/calendar/calendar.ics.d.ts +322 -0
- package/src/lib/model/calendar/calendar.id.d.ts +110 -0
- package/src/lib/model/calendar/calendar.processing.d.ts +77 -0
- package/src/lib/model/calendar/calendar.query.d.ts +76 -0
- package/src/lib/model/calendar/calendar.schedule.d.ts +82 -0
- package/src/lib/model/calendar/calendar.type.d.ts +188 -0
- package/src/lib/model/calendar/calendar.util.d.ts +485 -0
- package/src/lib/model/calendar/index.d.ts +12 -0
- package/src/lib/model/formspace/formspace.access.d.ts +139 -0
- package/src/lib/model/formspace/formspace.action.d.ts +34 -0
- package/src/lib/model/formspace/formspace.api.d.ts +216 -0
- package/src/lib/model/formspace/formspace.api.error.d.ts +71 -0
- package/src/lib/model/formspace/formspace.d.ts +390 -0
- package/src/lib/model/formspace/formspace.id.d.ts +60 -0
- package/src/lib/model/formspace/formspace.permission.d.ts +47 -0
- package/src/lib/model/formspace/formspace.processing.d.ts +86 -0
- package/src/lib/model/formspace/formspace.query.d.ts +110 -0
- package/src/lib/model/formspace/formspace.task.d.ts +75 -0
- package/src/lib/model/formspace/formspace.type.d.ts +267 -0
- package/src/lib/model/formspace/formspace.upload.d.ts +204 -0
- package/src/lib/model/formspace/formspace.util.d.ts +348 -0
- package/src/lib/model/formspace/index.d.ts +13 -0
- package/src/lib/model/index.d.ts +2 -0
- package/src/lib/model/notification/notification.message.d.ts +83 -0
- package/src/lib/model/notification/notification.query.d.ts +41 -0
- package/src/lib/model/oidcmodel/oidcmodel.query.d.ts +36 -0
- package/src/lib/model/storagefile/storagefile.api.d.ts +42 -2
- package/src/lib/model/storagefile/storagefile.create.d.ts +14 -3
- package/src/lib/model/storagefile/storagefile.file.d.ts +42 -2
- package/src/lib/model/storagefile/storagefile.query.d.ts +36 -0
- package/src/lib/model/storagefile/storagefile.upload.d.ts +30 -0
- package/src/lib/model/system/index.d.ts +1 -0
- package/src/lib/model/system/system.scheduler.d.ts +235 -0
- package/test/index.esm.js +24 -1
- package/test/package.json +6 -6
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { type ContentTypeMimeType, type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type FirebaseAuthOwnershipKey, type FirebaseAuthUserId } from '../../common/auth/auth';
|
|
3
|
+
import { type FirestoreModelKey } from '../../common/firestore/collection/collection';
|
|
4
|
+
import { type StorageFileGroupId } from '../storagefile/storagefile.id';
|
|
5
|
+
import { type FormSpace, type FormSpaceData, type FormSpaceFile } from './formspace';
|
|
6
|
+
import { type FormSpaceFileSlot, type FormSpaceKey, type FormSpaceType } from './formspace.id';
|
|
7
|
+
import { type FormSpaceFileSlotConfig, type FormSpaceTypeConfig } from './formspace.type';
|
|
8
|
+
/**
|
|
9
|
+
* @module formspace.util
|
|
10
|
+
*
|
|
11
|
+
* Pure helpers shared by the client and the server: the write templates for each lifecycle transition, and
|
|
12
|
+
* the upload predicate.
|
|
13
|
+
*
|
|
14
|
+
* {@link assertFormSpaceUploadAllowed} in particular is deliberately PURE and lives here rather than in
|
|
15
|
+
* `firebase-server`: the client pre-checks a file with it before asking for a signed URL, and the server's
|
|
16
|
+
* upload initializer enforces the very same function afterwards. One rule, two callers — a client-side copy
|
|
17
|
+
* that drifted would show the user an accept for a file the server then silently discards.
|
|
18
|
+
*
|
|
19
|
+
* A file's NAME is not one of the rules. It used to be: two files of one name in a slot resolved to the
|
|
20
|
+
* same destination object, so the second silently overwrote the first. The destination is now keyed by the
|
|
21
|
+
* space's `fi` index instead, so two files of one name are two objects, and the name is free to be
|
|
22
|
+
* whatever the user uploaded.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Returns the {@link StorageFileGroupId} that owns every file uploaded into a FormSpace.
|
|
26
|
+
*
|
|
27
|
+
* The group is keyed by the FormSpace's own model key, so the existing sync machinery creates it on the
|
|
28
|
+
* first upload and the existing zip / cleanup machinery applies with no FormSpace-specific code.
|
|
29
|
+
*
|
|
30
|
+
* @param formSpaceKey - The FormSpace's model key.
|
|
31
|
+
* @returns The group id.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const groupId = formSpaceStorageFileGroupId('fsp/abc123'); // 'fsp_abc123'
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function formSpaceStorageFileGroupId(formSpaceKey: FormSpaceKey): StorageFileGroupId;
|
|
39
|
+
/**
|
|
40
|
+
* Input for {@link resolveFormSpaceExpiresAt}.
|
|
41
|
+
*/
|
|
42
|
+
export interface ResolveFormSpaceExpiresAtInput {
|
|
43
|
+
readonly config: FormSpaceTypeConfig;
|
|
44
|
+
/**
|
|
45
|
+
* The instant the space is being created at. Defaults to now.
|
|
46
|
+
*/
|
|
47
|
+
readonly now?: Maybe<Date>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns the instant a newly created FormSpace of the given type expires at, or null when its type never
|
|
51
|
+
* expires.
|
|
52
|
+
*
|
|
53
|
+
* Null is meaningful rather than merely absent: it is what leaves `eat` unwritten, and an unwritten `eat`
|
|
54
|
+
* is what excludes the space from the sweep's inequality query.
|
|
55
|
+
*
|
|
56
|
+
* @param input - The type config and creation instant.
|
|
57
|
+
* @returns The expiration instant, or null when the type does not expire.
|
|
58
|
+
*
|
|
59
|
+
* @__NO_SIDE_EFFECTS__
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveFormSpaceExpiresAt(input: ResolveFormSpaceExpiresAtInput): Maybe<Date>;
|
|
62
|
+
/**
|
|
63
|
+
* Input for {@link formSpaceTemplate}.
|
|
64
|
+
*/
|
|
65
|
+
export interface FormSpaceTemplateInput<T extends FormSpaceData = FormSpaceData> {
|
|
66
|
+
readonly formSpaceType: FormSpaceType;
|
|
67
|
+
readonly uid: FirebaseAuthUserId;
|
|
68
|
+
readonly ownerKey?: Maybe<FirebaseAuthOwnershipKey>;
|
|
69
|
+
readonly targetModelKey?: Maybe<FirestoreModelKey>;
|
|
70
|
+
readonly displayName?: Maybe<string>;
|
|
71
|
+
readonly data?: Maybe<T>;
|
|
72
|
+
readonly expiresAt?: Maybe<Date>;
|
|
73
|
+
/**
|
|
74
|
+
* The creation instant. Defaults to now.
|
|
75
|
+
*/
|
|
76
|
+
readonly now?: Maybe<Date>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Builds the complete document template for a newly created FormSpace.
|
|
80
|
+
*
|
|
81
|
+
* @param input - The type, owner, and initial content of the space.
|
|
82
|
+
* @returns The FormSpace template.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* const template = formSpaceTemplate({ formSpaceType: 'demo_example', uid: 'user123' });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @__NO_SIDE_EFFECTS__
|
|
90
|
+
*/
|
|
91
|
+
export declare function formSpaceTemplate<T extends FormSpaceData = FormSpaceData>(input: FormSpaceTemplateInput<T>): FormSpace<T>;
|
|
92
|
+
/**
|
|
93
|
+
* Builds the update template that submits a FormSpace.
|
|
94
|
+
*
|
|
95
|
+
* Clearing `eat` is not tidiness: a submitted space that kept its expiration instant would still match the
|
|
96
|
+
* expiration sweep and be retired out from under the processing task.
|
|
97
|
+
*
|
|
98
|
+
* @param now - The submission instant. Defaults to now.
|
|
99
|
+
* @returns The update template.
|
|
100
|
+
*
|
|
101
|
+
* @__NO_SIDE_EFFECTS__
|
|
102
|
+
*/
|
|
103
|
+
export declare function submitFormSpaceTemplate(now?: Maybe<Date>): Partial<FormSpace>;
|
|
104
|
+
/**
|
|
105
|
+
* Builds the update template that expires a FormSpace.
|
|
106
|
+
*
|
|
107
|
+
* @param now - The expiration instant. Defaults to now.
|
|
108
|
+
* @returns The update template.
|
|
109
|
+
*
|
|
110
|
+
* @__NO_SIDE_EFFECTS__
|
|
111
|
+
*/
|
|
112
|
+
export declare function expireFormSpaceTemplate(now?: Maybe<Date>): Partial<FormSpace>;
|
|
113
|
+
/**
|
|
114
|
+
* Input for {@link isFormSpaceEditable}.
|
|
115
|
+
*/
|
|
116
|
+
export interface IsFormSpaceEditableInput {
|
|
117
|
+
readonly formSpace: Pick<FormSpace, 's' | 'sat' | 'eat'>;
|
|
118
|
+
/**
|
|
119
|
+
* The instant to judge against. Defaults to now.
|
|
120
|
+
*/
|
|
121
|
+
readonly now?: Maybe<Date>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns true when a FormSpace may still be edited or uploaded into.
|
|
125
|
+
*
|
|
126
|
+
* Checks the expiration instant as well as the state, so a space whose sweep has not run yet is already
|
|
127
|
+
* closed. The sweep is what RETIRES the document; it is not what makes it un-editable.
|
|
128
|
+
*
|
|
129
|
+
* @param input - The space and the instant to judge against.
|
|
130
|
+
* @returns True when the space is editable.
|
|
131
|
+
*
|
|
132
|
+
* @__NO_SIDE_EFFECTS__
|
|
133
|
+
*/
|
|
134
|
+
export declare function isFormSpaceEditable(input: IsFormSpaceEditableInput): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Returns the slot config a type declares for the given slot, or null when it declares none.
|
|
137
|
+
*
|
|
138
|
+
* @param config - The type config.
|
|
139
|
+
* @param slot - The slot to look up.
|
|
140
|
+
* @returns The slot config, or null.
|
|
141
|
+
*
|
|
142
|
+
* @__NO_SIDE_EFFECTS__
|
|
143
|
+
*/
|
|
144
|
+
export declare function formSpaceFileSlotConfig(config: FormSpaceTypeConfig, slot: FormSpaceFileSlot): Maybe<FormSpaceFileSlotConfig>;
|
|
145
|
+
/**
|
|
146
|
+
* Returns the human-readable name of a slot, falling back to the slot key itself.
|
|
147
|
+
*
|
|
148
|
+
* The key is a reasonable fallback rather than a placeholder: a slot is named `resume` or `cover` precisely
|
|
149
|
+
* because that is what it holds, so a type that declared no `name` still reads as something.
|
|
150
|
+
*
|
|
151
|
+
* @param config - The type config.
|
|
152
|
+
* @param slot - The slot to name.
|
|
153
|
+
* @returns The slot's name.
|
|
154
|
+
*
|
|
155
|
+
* @__NO_SIDE_EFFECTS__
|
|
156
|
+
*/
|
|
157
|
+
export declare function formSpaceFileSlotName(config: FormSpaceTypeConfig, slot: FormSpaceFileSlot): string;
|
|
158
|
+
/**
|
|
159
|
+
* Returns how many files a slot may hold at once.
|
|
160
|
+
*
|
|
161
|
+
* @param slotConfig - The slot config, or null for an undeclared slot.
|
|
162
|
+
* @returns The slot's file capacity.
|
|
163
|
+
*
|
|
164
|
+
* @__NO_SIDE_EFFECTS__
|
|
165
|
+
*/
|
|
166
|
+
export declare function formSpaceSlotMaxFiles(slotConfig: Maybe<FormSpaceFileSlotConfig>): number;
|
|
167
|
+
/**
|
|
168
|
+
* Returns how many files a slot must hold before the space may be submitted.
|
|
169
|
+
*
|
|
170
|
+
* `required` is the older, coarser spelling of the same idea, so it resolves to 1 when `minFiles` is absent.
|
|
171
|
+
*
|
|
172
|
+
* @param slotConfig - The slot config, or null for an undeclared slot.
|
|
173
|
+
* @returns The slot's minimum file count.
|
|
174
|
+
*
|
|
175
|
+
* @__NO_SIDE_EFFECTS__
|
|
176
|
+
*/
|
|
177
|
+
export declare function formSpaceSlotMinFiles(slotConfig: Maybe<FormSpaceFileSlotConfig>): number;
|
|
178
|
+
/**
|
|
179
|
+
* Returns the files a FormSpace currently holds in one slot.
|
|
180
|
+
*
|
|
181
|
+
* @param formSpace - The space to read.
|
|
182
|
+
* @param slot - The slot to filter by.
|
|
183
|
+
* @returns The slot's files, in the order the space stores them.
|
|
184
|
+
*
|
|
185
|
+
* @__NO_SIDE_EFFECTS__
|
|
186
|
+
*/
|
|
187
|
+
export declare function formSpaceFilesInSlot(formSpace: Pick<FormSpace, 'f'>, slot: FormSpaceFileSlot): FormSpaceFile[];
|
|
188
|
+
/**
|
|
189
|
+
* Every slot a type requires be filled before its spaces may be submitted.
|
|
190
|
+
*
|
|
191
|
+
* A CLIENT-side convenience for labelling a form's required slots. The submit gate itself uses
|
|
192
|
+
* {@link formSpaceSubmitBlockers}, which also understands `minFiles` and validation state.
|
|
193
|
+
*
|
|
194
|
+
* @param config - The type config.
|
|
195
|
+
* @returns The required slots.
|
|
196
|
+
*
|
|
197
|
+
* @__NO_SIDE_EFFECTS__
|
|
198
|
+
*/
|
|
199
|
+
export declare function requiredFormSpaceFileSlots(config: FormSpaceTypeConfig): FormSpaceFileSlot[];
|
|
200
|
+
/**
|
|
201
|
+
* Why {@link assertFormSpaceUploadAllowed} rejected an upload.
|
|
202
|
+
*
|
|
203
|
+
* A discriminated reason rather than a bare false: the caller turns it into an error code, and a client
|
|
204
|
+
* pre-check turns it into a message the user can act on.
|
|
205
|
+
*/
|
|
206
|
+
export type FormSpaceUploadRejectionReason = 'not_editable' | 'unknown_slot' | 'max_uploads_reached' | 'slot_full' | 'invalid_mime_type' | 'file_too_large';
|
|
207
|
+
/**
|
|
208
|
+
* Result of {@link assertFormSpaceUploadAllowed}.
|
|
209
|
+
*/
|
|
210
|
+
export interface FormSpaceUploadAllowedResult {
|
|
211
|
+
readonly allowed: boolean;
|
|
212
|
+
readonly reason?: Maybe<FormSpaceUploadRejectionReason>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Input for {@link assertFormSpaceUploadAllowed}.
|
|
216
|
+
*/
|
|
217
|
+
export interface AssertFormSpaceUploadAllowedInput {
|
|
218
|
+
readonly formSpace: Pick<FormSpace, 's' | 'sat' | 'eat' | 'uc' | 'f'>;
|
|
219
|
+
readonly config: FormSpaceTypeConfig;
|
|
220
|
+
readonly slot: FormSpaceFileSlot;
|
|
221
|
+
readonly mimeType: ContentTypeMimeType;
|
|
222
|
+
readonly sizeBytes: number;
|
|
223
|
+
/**
|
|
224
|
+
* The instant to judge editability against. Defaults to now.
|
|
225
|
+
*/
|
|
226
|
+
readonly now?: Maybe<Date>;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Decides whether one file may be uploaded into one slot of one FormSpace.
|
|
230
|
+
*
|
|
231
|
+
* THE single upload rule. The client calls it to pre-check before requesting a signed URL, and the server's
|
|
232
|
+
* upload initializer calls it again — authoritatively, after loading the space — before creating any
|
|
233
|
+
* StorageFile. The client call is a courtesy; only the server call is a control.
|
|
234
|
+
*
|
|
235
|
+
* @param input - The space, its type config, and the candidate file.
|
|
236
|
+
* @returns Whether the upload is allowed, and why not when it is not.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```ts
|
|
240
|
+
* const result = assertFormSpaceUploadAllowed({ formSpace, config, slot: 'resume', mimeType: 'application/pdf', sizeBytes: 4096 });
|
|
241
|
+
* ```
|
|
242
|
+
*
|
|
243
|
+
* @__NO_SIDE_EFFECTS__
|
|
244
|
+
*/
|
|
245
|
+
export declare function assertFormSpaceUploadAllowed(input: AssertFormSpaceUploadAllowedInput): FormSpaceUploadAllowedResult;
|
|
246
|
+
/**
|
|
247
|
+
* Why a FormSpace cannot be submitted yet.
|
|
248
|
+
*
|
|
249
|
+
* Per-slot rather than a bare list of slot names, because "you have not uploaded a second document" and "the
|
|
250
|
+
* document you uploaded was rejected" want different words in front of the user.
|
|
251
|
+
*/
|
|
252
|
+
export interface FormSpaceSubmitBlocker {
|
|
253
|
+
readonly slot: FormSpaceFileSlot;
|
|
254
|
+
/**
|
|
255
|
+
* `missing_files` — the slot holds fewer than its `minFiles`.
|
|
256
|
+
* `invalid_file` — the slot holds a file validation judged INVALID.
|
|
257
|
+
* `pending_validation` — the slot holds a file whose validation has not concluded.
|
|
258
|
+
*/
|
|
259
|
+
readonly reason: 'missing_files' | 'invalid_file' | 'pending_validation';
|
|
260
|
+
/**
|
|
261
|
+
* The offending files, for `invalid_file` and `pending_validation`.
|
|
262
|
+
*/
|
|
263
|
+
readonly files?: Maybe<FormSpaceFile[]>;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Returns every reason a FormSpace may not be submitted yet, or an empty array when it may.
|
|
267
|
+
*
|
|
268
|
+
* Reads the space's own `f` array rather than querying its StorageFiles. That array is written in the
|
|
269
|
+
* accept transaction, so unlike a query it is correct immediately after an upload — and unlike a query it
|
|
270
|
+
* can be read inside the transaction that takes the submit lock.
|
|
271
|
+
*
|
|
272
|
+
* @param formSpace - The space to check.
|
|
273
|
+
* @param config - Its type config.
|
|
274
|
+
* @returns The blockers, empty when the space may be submitted.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const blockers = formSpaceSubmitBlockers(formSpace, config);
|
|
279
|
+
*
|
|
280
|
+
* if (blockers.length > 0) {
|
|
281
|
+
* throw formSpaceRequiredSlotMissingError(blockers.map((x) => x.slot));
|
|
282
|
+
* }
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* @__NO_SIDE_EFFECTS__
|
|
286
|
+
*/
|
|
287
|
+
export declare function formSpaceSubmitBlockers(formSpace: Pick<FormSpace, 'f'>, config: FormSpaceTypeConfig): FormSpaceSubmitBlocker[];
|
|
288
|
+
/**
|
|
289
|
+
* What one slot of a FormSpace currently holds, and whether that satisfies the slot's own requirement.
|
|
290
|
+
*
|
|
291
|
+
* The per-slot view of {@link formSpaceSubmitBlockers}, for a UI that labels each slot individually rather
|
|
292
|
+
* than reporting one verdict for the whole space.
|
|
293
|
+
*/
|
|
294
|
+
export interface FormSpaceSlotStatus {
|
|
295
|
+
readonly slot: FormSpaceFileSlot;
|
|
296
|
+
/**
|
|
297
|
+
* The files the slot currently holds.
|
|
298
|
+
*/
|
|
299
|
+
readonly files: FormSpaceFile[];
|
|
300
|
+
readonly minFiles: number;
|
|
301
|
+
readonly maxFiles: number;
|
|
302
|
+
/**
|
|
303
|
+
* Whether the space cannot be submitted while this slot is empty, i.e. {@link minFiles} is above zero.
|
|
304
|
+
*/
|
|
305
|
+
readonly required: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* Every reason this slot blocks submission. Empty when it does not.
|
|
308
|
+
*/
|
|
309
|
+
readonly blockers: FormSpaceSubmitBlocker[];
|
|
310
|
+
/**
|
|
311
|
+
* Whether this slot blocks submission. An OPTIONAL EMPTY slot is satisfied — it is holding up nothing.
|
|
312
|
+
*/
|
|
313
|
+
readonly satisfied: boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Whether the slot is satisfied AND holds something.
|
|
316
|
+
*
|
|
317
|
+
* The distinction from {@link satisfied} is what an optional slot needs: an empty one blocks nothing, but
|
|
318
|
+
* marking it DONE claims the user dealt with it when they have not touched it. So this is the narrower
|
|
319
|
+
* predicate — "there is something here and it is fine" — and it is what a checkmark belongs next to.
|
|
320
|
+
*/
|
|
321
|
+
readonly complete: boolean;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Input for {@link formSpaceSlotStatus}.
|
|
325
|
+
*/
|
|
326
|
+
export interface FormSpaceSlotStatusInput {
|
|
327
|
+
readonly formSpace: Pick<FormSpace, 'f'>;
|
|
328
|
+
readonly config: FormSpaceTypeConfig;
|
|
329
|
+
readonly slot: FormSpaceFileSlot;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Returns what one slot holds and whether that satisfies the slot's requirement.
|
|
333
|
+
*
|
|
334
|
+
* Derived from {@link formSpaceSubmitBlockers} rather than re-deriving the rule, so a slot a UI marks done is
|
|
335
|
+
* exactly a slot the server's submit gate would not object to.
|
|
336
|
+
*
|
|
337
|
+
* @param input - The space, its type config, and the slot to report on.
|
|
338
|
+
* @returns The slot's status.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* const status = formSpaceSlotStatus({ formSpace, config, slot: 'resume' });
|
|
343
|
+
* const showCheck = status.complete;
|
|
344
|
+
* ```
|
|
345
|
+
*
|
|
346
|
+
* @__NO_SIDE_EFFECTS__
|
|
347
|
+
*/
|
|
348
|
+
export declare function formSpaceSlotStatus(input: FormSpaceSlotStatusInput): FormSpaceSlotStatus;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './formspace.access';
|
|
2
|
+
export * from './formspace.action';
|
|
3
|
+
export * from './formspace.api.error';
|
|
4
|
+
export * from './formspace.api';
|
|
5
|
+
export * from './formspace.id';
|
|
6
|
+
export * from './formspace.permission';
|
|
7
|
+
export * from './formspace.processing';
|
|
8
|
+
export * from './formspace.query';
|
|
9
|
+
export * from './formspace.task';
|
|
10
|
+
export * from './formspace.type';
|
|
11
|
+
export * from './formspace.upload';
|
|
12
|
+
export * from './formspace.util';
|
|
13
|
+
export * from './formspace';
|
package/src/lib/model/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* before dispatching them through the configured delivery channels.
|
|
10
10
|
*/
|
|
11
11
|
import { type PromiseOrValue, type Maybe, type WebsiteUrl, type NameEmailPair, type ArrayOrValue } from '@dereekb/util';
|
|
12
|
+
import { type ICalendarIcsString, type ICalendarMethod } from '@dereekb/date';
|
|
12
13
|
import { type NotificationRecipient, type NotificationRecipientWithConfig } from './notification.config';
|
|
13
14
|
import { type NotificationSendFlags, type Notification, type NotificationBox } from './notification';
|
|
14
15
|
import { type NotificationItem, type NotificationItemMetadata } from './notification.item';
|
|
@@ -102,6 +103,80 @@ export interface NotificationMessageContent {
|
|
|
102
103
|
*/
|
|
103
104
|
readonly templateVariables?: Maybe<NotificationMessageTemplateVariables>;
|
|
104
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* The iTIP method a {@link NotificationMessageCalendarAttachment} may carry.
|
|
108
|
+
*
|
|
109
|
+
* Deliberately the whole of {@link ICalendarMethod}, including its open string branch. A notification
|
|
110
|
+
* USUALLY speaks as the organizer -- PUBLISH for an informational copy, REQUEST for an invitation or an
|
|
111
|
+
* update to one, ADD for extra instances of a recurring event, CANCEL to withdraw one, and DECLINECOUNTER
|
|
112
|
+
* to reject a proposed change -- but an app that sends ON BEHALF of an attendee has an equally real use
|
|
113
|
+
* for the attendee-to-organizer methods: REPLY to RSVP, REFRESH to ask for the latest copy, and COUNTER
|
|
114
|
+
* to propose one. Narrowing to the organizer set would put that behind a library change, and closing the
|
|
115
|
+
* union would also drop the `X-` extension methods {@link ICalendarMethod} intentionally leaves room for.
|
|
116
|
+
*
|
|
117
|
+
* The invariant worth enforcing is not WHICH method but that it agrees with the METHOD property inside
|
|
118
|
+
* the payload, which no type can express -- so this alias exists to document the choice rather than to
|
|
119
|
+
* constrain it.
|
|
120
|
+
*/
|
|
121
|
+
export type NotificationMessageCalendarAttachmentMethod = ICalendarMethod;
|
|
122
|
+
/**
|
|
123
|
+
* A rendered iTIP calendar payload for a single recipient, for the sending service to bundle as a calendar
|
|
124
|
+
* MIME part on the outgoing email.
|
|
125
|
+
*
|
|
126
|
+
* Produced by a {@link NotificationMessageCalendarAttachmentFactory} at SEND time and never stored: it is
|
|
127
|
+
* not on `NotificationItem.d`, because the item is re-embedded verbatim into `NotificationSummary.n[]`
|
|
128
|
+
* (capped at 1000 items) and `NotificationWeek.n[]`, which each share a single 1 MiB document — an ICS
|
|
129
|
+
* blob in `d` would consume the summary's whole budget. Store the IDENTIFIERS in `d` and render the ICS
|
|
130
|
+
* from the message factory, which is async and holds the notification document.
|
|
131
|
+
*/
|
|
132
|
+
export interface NotificationMessageCalendarAttachment {
|
|
133
|
+
/**
|
|
134
|
+
* The rendered ICS document.
|
|
135
|
+
*/
|
|
136
|
+
readonly ics: ICalendarIcsString;
|
|
137
|
+
/**
|
|
138
|
+
* The iTIP method the document carries. Duplicated onto the part's Content-Type by the sending service,
|
|
139
|
+
* as RFC 6047 requires, and it MUST agree with the METHOD property inside {@link ics}.
|
|
140
|
+
*/
|
|
141
|
+
readonly method: NotificationMessageCalendarAttachmentMethod;
|
|
142
|
+
/**
|
|
143
|
+
* File name of the part. Defaults to "invite.ics" when the sending service is given none.
|
|
144
|
+
*/
|
|
145
|
+
readonly filename?: Maybe<string>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The file name given to a {@link NotificationMessageCalendarAttachment} that carries none.
|
|
149
|
+
*/
|
|
150
|
+
export declare const DEFAULT_NOTIFICATION_MESSAGE_CALENDAR_ATTACHMENT_FILENAME = "invite.ics";
|
|
151
|
+
/**
|
|
152
|
+
* Input for a {@link NotificationMessageCalendarAttachmentFactory}.
|
|
153
|
+
*/
|
|
154
|
+
export interface NotificationMessageCalendarAttachmentFactoryInput {
|
|
155
|
+
/**
|
|
156
|
+
* The message the part is being built for.
|
|
157
|
+
*/
|
|
158
|
+
readonly message: NotificationMessage;
|
|
159
|
+
/**
|
|
160
|
+
* The address the sending service has resolved for this recipient, and the address the payload's
|
|
161
|
+
* ATTENDEE must name.
|
|
162
|
+
*
|
|
163
|
+
* Load-bearing for a REQUEST: a client only renders an invitation inline when it finds ITS OWN address
|
|
164
|
+
* in the ATTENDEE, which is why the payload is built here rather than once per notification.
|
|
165
|
+
*/
|
|
166
|
+
readonly recipient: NameEmailPair;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Renders the iTIP calendar payload for one recipient of a message.
|
|
170
|
+
*
|
|
171
|
+
* A factory rather than a value because the payload is per-recipient and transient: it is never
|
|
172
|
+
* serialized, it is only meaningful to a sending service that delivers email, and the ATTENDEE varies by
|
|
173
|
+
* recipient. Deferring the render to the sending service means one of these can be built once per
|
|
174
|
+
* notification, closing over the event, and the ICS is only produced for the recipients that actually
|
|
175
|
+
* receive an email.
|
|
176
|
+
*
|
|
177
|
+
* Return `undefined` to send the email without a calendar part.
|
|
178
|
+
*/
|
|
179
|
+
export type NotificationMessageCalendarAttachmentFactory = (input: NotificationMessageCalendarAttachmentFactoryInput) => PromiseOrValue<Maybe<NotificationMessageCalendarAttachment>>;
|
|
105
180
|
export interface NotificationMessageEmailContent extends NotificationMessageContent {
|
|
106
181
|
/**
|
|
107
182
|
* Email subject. If not defined, defaults to the title.
|
|
@@ -133,6 +208,14 @@ export interface NotificationMessageEmailContent extends NotificationMessageCont
|
|
|
133
208
|
* If the "replyTo" is present, this value acts as a fallback if the entity key returns no match.
|
|
134
209
|
*/
|
|
135
210
|
readonly replyToEmail?: Maybe<NameEmailPair>;
|
|
211
|
+
/**
|
|
212
|
+
* Renders an iTIP calendar payload to bundle onto the email as a calendar MIME part.
|
|
213
|
+
*
|
|
214
|
+
* Opt-in per sending service, like every other field here: a builder that does not call it simply sends
|
|
215
|
+
* the email without the invite. A builder that DOES call it must give each recipient the payload names a
|
|
216
|
+
* request of its own, since attachments live on the request rather than the recipient.
|
|
217
|
+
*/
|
|
218
|
+
readonly calendarAttachmentFactory?: Maybe<NotificationMessageCalendarAttachmentFactory>;
|
|
136
219
|
}
|
|
137
220
|
export interface NotificationMessageNotificationSummaryContent {
|
|
138
221
|
}
|
|
@@ -13,6 +13,11 @@ import { type ArrayOrValue } from '@dereekb/util';
|
|
|
13
13
|
* Used by the server to discover users whose configs need to be synced to their NotificationBox recipients.
|
|
14
14
|
*
|
|
15
15
|
* @returns Array of Firestore query constraints filtering for users needing sync.
|
|
16
|
+
*
|
|
17
|
+
* @dbxModelFirebaseIndex
|
|
18
|
+
* @dbxModelFirebaseIndexModel NotificationUser
|
|
19
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
20
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
16
21
|
*/
|
|
17
22
|
export declare function notificationUsersFlaggedForNeedsSyncQuery(): FirestoreQueryConstraint[];
|
|
18
23
|
/**
|
|
@@ -20,18 +25,34 @@ export declare function notificationUsersFlaggedForNeedsSyncQuery(): FirestoreQu
|
|
|
20
25
|
*
|
|
21
26
|
* @param exclusionId - One or more box IDs or collection name prefixes to match against.
|
|
22
27
|
* @returns Array of Firestore query constraints filtering for users with matching exclusions.
|
|
28
|
+
*
|
|
29
|
+
* @dbxModelFirebaseIndex
|
|
30
|
+
* @dbxModelFirebaseIndexModel NotificationUser
|
|
31
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
32
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
33
|
+
* @dbxModelFirebaseIndexAllowArrayContainsAny
|
|
23
34
|
*/
|
|
24
35
|
export declare function notificationUserHasExclusionQuery(exclusionId: ArrayOrValue<NotificationBoxSendExclusion>): FirestoreQueryConstraint[];
|
|
25
36
|
/**
|
|
26
37
|
* Query constraints for finding {@link NotificationSummary} documents that need server-side initialization (`s == true`).
|
|
27
38
|
*
|
|
28
39
|
* @returns Array of Firestore query constraints filtering for summaries needing initialization.
|
|
40
|
+
*
|
|
41
|
+
* @dbxModelFirebaseIndex
|
|
42
|
+
* @dbxModelFirebaseIndexModel NotificationSummary
|
|
43
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
44
|
+
* @dbxModelFirebaseIndexCategory init
|
|
29
45
|
*/
|
|
30
46
|
export declare function notificationSummariesFlaggedForNeedsInitializationQuery(): FirestoreQueryConstraint[];
|
|
31
47
|
/**
|
|
32
48
|
* Query constraints for finding {@link NotificationBox} documents that need server-side initialization (`s == true`).
|
|
33
49
|
*
|
|
34
50
|
* @returns Array of Firestore query constraints filtering for boxes needing initialization.
|
|
51
|
+
*
|
|
52
|
+
* @dbxModelFirebaseIndex
|
|
53
|
+
* @dbxModelFirebaseIndexModel NotificationBox
|
|
54
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
55
|
+
* @dbxModelFirebaseIndexCategory init
|
|
35
56
|
*/
|
|
36
57
|
export declare function notificationBoxesFlaggedForNeedsInitializationQuery(): FirestoreQueryConstraint[];
|
|
37
58
|
/**
|
|
@@ -40,6 +61,11 @@ export declare function notificationBoxesFlaggedForNeedsInitializationQuery(): F
|
|
|
40
61
|
* Used by the server to clean up boxes that could not be initialized.
|
|
41
62
|
*
|
|
42
63
|
* @returns Array of Firestore query constraints filtering for boxes flagged as invalid.
|
|
64
|
+
*
|
|
65
|
+
* @dbxModelFirebaseIndex
|
|
66
|
+
* @dbxModelFirebaseIndexModel NotificationBox
|
|
67
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
68
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
43
69
|
*/
|
|
44
70
|
export declare function notificationBoxesFlaggedInvalidQuery(): FirestoreQueryConstraint[];
|
|
45
71
|
/**
|
|
@@ -50,6 +76,11 @@ export declare function notificationBoxesFlaggedInvalidQuery(): FirestoreQueryCo
|
|
|
50
76
|
*
|
|
51
77
|
* @param now - Reference time for the `sat` comparison (defaults to current time)
|
|
52
78
|
* @returns Array of Firestore query constraints filtering for notifications past their scheduled send time.
|
|
79
|
+
*
|
|
80
|
+
* @dbxModelFirebaseIndex
|
|
81
|
+
* @dbxModelFirebaseIndexModel Notification
|
|
82
|
+
* @dbxModelFirebaseIndexScope COLLECTION_GROUP
|
|
83
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
53
84
|
*/
|
|
54
85
|
export declare function notificationsPastSendAtTimeQuery(now?: Date): FirestoreQueryConstraint[];
|
|
55
86
|
/**
|
|
@@ -57,6 +88,11 @@ export declare function notificationsPastSendAtTimeQuery(now?: Date): FirestoreQ
|
|
|
57
88
|
* and ready to be archived to {@link NotificationWeek} and then deleted.
|
|
58
89
|
*
|
|
59
90
|
* @returns Array of Firestore query constraints filtering for completed notifications ready to archive.
|
|
91
|
+
*
|
|
92
|
+
* @dbxModelFirebaseIndex
|
|
93
|
+
* @dbxModelFirebaseIndexModel Notification
|
|
94
|
+
* @dbxModelFirebaseIndexScope COLLECTION_GROUP
|
|
95
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
60
96
|
*/
|
|
61
97
|
export declare function notificationsReadyForCleanupQuery(): FirestoreQueryConstraint[];
|
|
62
98
|
/**
|
|
@@ -74,5 +110,10 @@ export declare function notificationsReadyForCleanupQuery(): FirestoreQueryConst
|
|
|
74
110
|
* @param retentionDays - Number of days of history to retain; days strictly older than `now - retentionDays` match.
|
|
75
111
|
* @param now - Reference time for the cutoff (defaults to current time)
|
|
76
112
|
* @returns Array of Firestore query constraints filtering by the day string.
|
|
113
|
+
*
|
|
114
|
+
* @dbxModelFirebaseIndex
|
|
115
|
+
* @dbxModelFirebaseIndexModel NotificationLoggedEventDay
|
|
116
|
+
* @dbxModelFirebaseIndexScope COLLECTION_GROUP
|
|
117
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
77
118
|
*/
|
|
78
119
|
export declare function notificationLoggedEventDaysOlderThanQuery(retentionDays: number, now?: Date): FirestoreQueryConstraint[];
|
|
@@ -5,6 +5,11 @@ import { type OidcEntryType } from './oidcmodel';
|
|
|
5
5
|
*
|
|
6
6
|
* @param type - The OIDC entry type to filter by.
|
|
7
7
|
* @returns Firestore query constraints for the given type.
|
|
8
|
+
*
|
|
9
|
+
* @dbxModelFirebaseIndex
|
|
10
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
11
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
12
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
8
13
|
*/
|
|
9
14
|
export declare function oidcEntriesWithTypeQuery(type: OidcEntryType): FirestoreQueryConstraint[];
|
|
10
15
|
/**
|
|
@@ -13,6 +18,11 @@ export declare function oidcEntriesWithTypeQuery(type: OidcEntryType): Firestore
|
|
|
13
18
|
* @param type - The OIDC entry type to filter by.
|
|
14
19
|
* @param userCode - The user code to match.
|
|
15
20
|
* @returns Firestore query constraints for the given type and userCode.
|
|
21
|
+
*
|
|
22
|
+
* @dbxModelFirebaseIndex
|
|
23
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
24
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
25
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
16
26
|
*/
|
|
17
27
|
export declare function oidcEntriesByUserCodeQuery(type: OidcEntryType, userCode: string): FirestoreQueryConstraint[];
|
|
18
28
|
/**
|
|
@@ -21,6 +31,11 @@ export declare function oidcEntriesByUserCodeQuery(type: OidcEntryType, userCode
|
|
|
21
31
|
* @param type - The OIDC entry type to filter by.
|
|
22
32
|
* @param uid - The Firebase user ID to match.
|
|
23
33
|
* @returns Firestore query constraints for the given type and uid.
|
|
34
|
+
*
|
|
35
|
+
* @dbxModelFirebaseIndex
|
|
36
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
37
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
38
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
24
39
|
*/
|
|
25
40
|
export declare function oidcEntriesByUidQuery(type: OidcEntryType, uid: FirebaseAuthUserId): FirestoreQueryConstraint[];
|
|
26
41
|
/**
|
|
@@ -29,6 +44,11 @@ export declare function oidcEntriesByUidQuery(type: OidcEntryType, uid: Firebase
|
|
|
29
44
|
* @param type - The OIDC entry type to filter by.
|
|
30
45
|
* @param grantId - The grant ID to match.
|
|
31
46
|
* @returns Firestore query constraints for the given type and grantId.
|
|
47
|
+
*
|
|
48
|
+
* @dbxModelFirebaseIndex
|
|
49
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
50
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
51
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
32
52
|
*/
|
|
33
53
|
export declare function oidcEntriesByGrantIdQuery(type: OidcEntryType, grantId: string): FirestoreQueryConstraint[];
|
|
34
54
|
/**
|
|
@@ -37,6 +57,11 @@ export declare function oidcEntriesByGrantIdQuery(type: OidcEntryType, grantId:
|
|
|
37
57
|
* @param type - The OIDC entry type to filter by.
|
|
38
58
|
* @param clientId - The OAuth client ID to match.
|
|
39
59
|
* @returns Firestore query constraints for the given type and clientId.
|
|
60
|
+
*
|
|
61
|
+
* @dbxModelFirebaseIndex
|
|
62
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
63
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
64
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
40
65
|
*/
|
|
41
66
|
export declare function oidcEntriesByClientIdQuery(type: OidcEntryType, clientId: string): FirestoreQueryConstraint[];
|
|
42
67
|
/**
|
|
@@ -44,6 +69,11 @@ export declare function oidcEntriesByClientIdQuery(type: OidcEntryType, clientId
|
|
|
44
69
|
*
|
|
45
70
|
* @param ownershipKey - The ownership key identifying the owner.
|
|
46
71
|
* @returns Firestore query constraints for Client entries matching the ownership key.
|
|
72
|
+
*
|
|
73
|
+
* @dbxModelFirebaseIndex
|
|
74
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
75
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
76
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
47
77
|
*/
|
|
48
78
|
export declare function oidcClientEntriesByOwnerQuery(ownershipKey: FirebaseAuthOwnershipKey): FirestoreQueryConstraint[];
|
|
49
79
|
/**
|
|
@@ -54,5 +84,11 @@ export declare function oidcClientEntriesByOwnerQuery(ownershipKey: FirebaseAuth
|
|
|
54
84
|
*
|
|
55
85
|
* @param uid - The Firebase user id the grants were issued to.
|
|
56
86
|
* @returns Firestore query constraints for Grant entries matching the uid.
|
|
87
|
+
*
|
|
88
|
+
* @dbxModelFirebaseIndex
|
|
89
|
+
* @dbxModelFirebaseIndexModel OidcEntry
|
|
90
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
91
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
92
|
+
* @dbxModelFirebaseIndexDispatcher true
|
|
57
93
|
*/
|
|
58
94
|
export declare function oidcGrantEntriesByUidQuery(uid: FirebaseAuthUserId): FirestoreQueryConstraint[];
|