@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.
Files changed (49) hide show
  1. package/eslint/index.esm.js +145 -170
  2. package/eslint/package.json +3 -3
  3. package/index.esm.js +7207 -2997
  4. package/package.json +5 -5
  5. package/src/lib/common/firestore/snapshot/snapshot.field.d.ts +4 -0
  6. package/src/lib/common/storage/context.d.ts +9 -1
  7. package/src/lib/common/storage/driver/accessor.d.ts +12 -0
  8. package/src/lib/common/storage/index.d.ts +1 -0
  9. package/src/lib/common/storage/storage.url.d.ts +69 -0
  10. package/src/lib/model/calendar/calendar.action.d.ts +34 -0
  11. package/src/lib/model/calendar/calendar.api.d.ts +147 -0
  12. package/src/lib/model/calendar/calendar.api.error.d.ts +24 -0
  13. package/src/lib/model/calendar/calendar.d.ts +460 -0
  14. package/src/lib/model/calendar/calendar.expand.d.ts +95 -0
  15. package/src/lib/model/calendar/calendar.ics.d.ts +322 -0
  16. package/src/lib/model/calendar/calendar.id.d.ts +110 -0
  17. package/src/lib/model/calendar/calendar.processing.d.ts +77 -0
  18. package/src/lib/model/calendar/calendar.query.d.ts +76 -0
  19. package/src/lib/model/calendar/calendar.schedule.d.ts +82 -0
  20. package/src/lib/model/calendar/calendar.type.d.ts +188 -0
  21. package/src/lib/model/calendar/calendar.util.d.ts +485 -0
  22. package/src/lib/model/calendar/index.d.ts +12 -0
  23. package/src/lib/model/formspace/formspace.access.d.ts +139 -0
  24. package/src/lib/model/formspace/formspace.action.d.ts +34 -0
  25. package/src/lib/model/formspace/formspace.api.d.ts +216 -0
  26. package/src/lib/model/formspace/formspace.api.error.d.ts +71 -0
  27. package/src/lib/model/formspace/formspace.d.ts +390 -0
  28. package/src/lib/model/formspace/formspace.id.d.ts +60 -0
  29. package/src/lib/model/formspace/formspace.permission.d.ts +47 -0
  30. package/src/lib/model/formspace/formspace.processing.d.ts +86 -0
  31. package/src/lib/model/formspace/formspace.query.d.ts +110 -0
  32. package/src/lib/model/formspace/formspace.task.d.ts +75 -0
  33. package/src/lib/model/formspace/formspace.type.d.ts +267 -0
  34. package/src/lib/model/formspace/formspace.upload.d.ts +204 -0
  35. package/src/lib/model/formspace/formspace.util.d.ts +348 -0
  36. package/src/lib/model/formspace/index.d.ts +13 -0
  37. package/src/lib/model/index.d.ts +2 -0
  38. package/src/lib/model/notification/notification.message.d.ts +83 -0
  39. package/src/lib/model/notification/notification.query.d.ts +41 -0
  40. package/src/lib/model/oidcmodel/oidcmodel.query.d.ts +36 -0
  41. package/src/lib/model/storagefile/storagefile.api.d.ts +42 -2
  42. package/src/lib/model/storagefile/storagefile.create.d.ts +14 -3
  43. package/src/lib/model/storagefile/storagefile.file.d.ts +42 -2
  44. package/src/lib/model/storagefile/storagefile.query.d.ts +36 -0
  45. package/src/lib/model/storagefile/storagefile.upload.d.ts +30 -0
  46. package/src/lib/model/system/index.d.ts +1 -0
  47. package/src/lib/model/system/system.scheduler.d.ts +235 -0
  48. package/test/index.esm.js +24 -1
  49. 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';
@@ -1,4 +1,6 @@
1
1
  export * from './external';
2
+ export * from './calendar';
3
+ export * from './formspace';
2
4
  export * from './user';
3
5
  export * from './notification';
4
6
  export * from './oidcmodel';
@@ -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[];