@dereekb/firebase 13.40.0 → 13.42.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 +2865 -551
- 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/index.d.ts +1 -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 +23 -2
- package/src/lib/model/storagefile/storagefile.create.d.ts +14 -3
- package/src/lib/model/storagefile/storagefile.query.d.ts +36 -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
|
@@ -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[];
|
|
@@ -51,13 +51,34 @@ export declare const initializeStorageFileFromUploadParamsType: Type<InitializeS
|
|
|
51
51
|
/**
|
|
52
52
|
* Parameters for triggering processing of a specific StorageFile.
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Which flag is required depends on the file's current processing state. A `FAILED` file
|
|
55
|
+
* restarts with no flag, while a `SUCCESS` file needs `processAgainIfSuccessful` (or
|
|
56
|
+
* `forceRestartProcessing`) — note that a file whose processor ran to completion is `SUCCESS`
|
|
57
|
+
* even when the outcome was a rejection, so re-validating a rejected file normally needs one of
|
|
58
|
+
* those flags. `ARCHIVED` and `DO_NOT_PROCESS` files cannot be processed at all.
|
|
59
|
+
*
|
|
60
|
+
* Validated with {@link processStorageFileParamsType}.
|
|
56
61
|
*/
|
|
57
62
|
export interface ProcessStorageFileParams extends TargetModelParams {
|
|
63
|
+
/**
|
|
64
|
+
* Runs the first step of the processing task inline instead of waiting for the scheduled task runner.
|
|
65
|
+
*/
|
|
58
66
|
readonly runImmediately?: Maybe<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Checks an in-flight `PROCESSING` task immediately, instead of waiting for it to age past the
|
|
69
|
+
* stuck-check throttle.
|
|
70
|
+
*/
|
|
59
71
|
readonly checkRetryProcessing?: Maybe<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Abandons the file's existing processing task and begins a new one, clearing the completed
|
|
74
|
+
* checkpoints so the flow runs again from the start. For a `PROCESSING` file this is only applied
|
|
75
|
+
* once the retry check runs, so pair it with `checkRetryProcessing` to force a restart while the
|
|
76
|
+
* existing task is still within the stuck-check throttle.
|
|
77
|
+
*/
|
|
60
78
|
readonly forceRestartProcessing?: Maybe<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Allows processing a file that has already finished processing and is in the `SUCCESS` state.
|
|
81
|
+
*/
|
|
61
82
|
readonly processAgainIfSuccessful?: Maybe<boolean>;
|
|
62
83
|
}
|
|
63
84
|
export declare const processStorageFileParamsType: Type<ProcessStorageFileParams>;
|
|
@@ -5,7 +5,7 @@ import { type FirestoreDocumentAccessor } from '../../common/firestore/accessor/
|
|
|
5
5
|
import { type FirebaseStorageAccessorFile } from '../../common/storage/driver/accessor';
|
|
6
6
|
import { type StoragePathRef, type StoragePath } from '../../common/storage/storage';
|
|
7
7
|
import { type FirebaseAuthOwnershipKey, type FirebaseAuthUserId } from '../../common/auth/auth';
|
|
8
|
-
import { type StorageFilePurposeSubgroup, type StorageFileGroupId, type StorageFileGroupRelatedStorageFilePurpose, type StorageFileMetadata, type StorageFilePurpose } from './storagefile.id';
|
|
8
|
+
import { type StorageFilePurposeSubgroup, type StorageFileGroupId, type StorageFileGroupRelatedStorageFilePurpose, type StorageFileId, type StorageFileMetadata, type StorageFilePurpose } from './storagefile.id';
|
|
9
9
|
import { type ReadFirestoreModelKeyInput } from '../../common';
|
|
10
10
|
/**
|
|
11
11
|
* Input for creating a StorageFile document paired with its storage path.
|
|
@@ -13,8 +13,8 @@ import { type ReadFirestoreModelKeyInput } from '../../common';
|
|
|
13
13
|
* Provides all the fields needed to create a fully-configured {@link StorageFile} Firestore document,
|
|
14
14
|
* including ownership, purpose, group membership, and processing state.
|
|
15
15
|
*
|
|
16
|
-
* Either a `file`, `storagePathRef`, or `
|
|
17
|
-
* Either an `accessor` or `context` must be provided for Firestore document creation.
|
|
16
|
+
* Either a `file`, `storagePathRef`, `storagePath`, or `storagePathFactory` must be provided to identify the
|
|
17
|
+
* storage location. Either an `accessor` or `context` must be provided for Firestore document creation.
|
|
18
18
|
*
|
|
19
19
|
* @template M - type of arbitrary metadata stored in the `d` field
|
|
20
20
|
*/
|
|
@@ -43,6 +43,17 @@ export interface CreateStorageFileDocumentPairInput<M extends StorageFileMetadat
|
|
|
43
43
|
* File to use when creating the StorageFile.
|
|
44
44
|
*/
|
|
45
45
|
readonly file?: FirebaseStorageAccessorFile;
|
|
46
|
+
/**
|
|
47
|
+
* Builds the storage path from the id of the StorageFile document being created.
|
|
48
|
+
*
|
|
49
|
+
* Use this when the path should be keyed by the StorageFile itself rather than by the model it belongs
|
|
50
|
+
* to. Two StorageFiles then can never resolve to the same path, so replacing one cannot delete the
|
|
51
|
+
* other's content, and the path is not guessable from the owning model's id.
|
|
52
|
+
*
|
|
53
|
+
* Only consulted when no `file`, `storagePathRef`, or `storagePath` is provided, and only for a
|
|
54
|
+
* DIRECTLY_CREATED StorageFile — a FOR_STORAGE_FILE_GROUP file has a deterministic id by design.
|
|
55
|
+
*/
|
|
56
|
+
readonly storagePathFactory?: Maybe<(storageFileId: StorageFileId) => StoragePath>;
|
|
46
57
|
/**
|
|
47
58
|
* The display name of the StorageFile.
|
|
48
59
|
*
|
|
@@ -9,6 +9,11 @@ import { type FirebaseAuthUserId } from '../../common/auth/auth';
|
|
|
9
9
|
*
|
|
10
10
|
* @returns Firestore query constraints for StorageFiles queued for processing.
|
|
11
11
|
*
|
|
12
|
+
* @dbxModelFirebaseIndex
|
|
13
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
14
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
15
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
16
|
+
*
|
|
12
17
|
* @example
|
|
13
18
|
* ```ts
|
|
14
19
|
* const constraints = storageFilesQueuedForProcessingQuery();
|
|
@@ -24,6 +29,11 @@ export declare function storageFilesQueuedForProcessingQuery(): FirestoreQueryCo
|
|
|
24
29
|
* @param now - Reference time for comparison; defaults to current time.
|
|
25
30
|
* @returns Firestore query constraints for StorageFiles whose scheduled delete date has passed.
|
|
26
31
|
*
|
|
32
|
+
* @dbxModelFirebaseIndex
|
|
33
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
34
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
35
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
36
|
+
*
|
|
27
37
|
* @example
|
|
28
38
|
* ```ts
|
|
29
39
|
* const constraints = storageFilesQueuedForDeleteQuery();
|
|
@@ -48,6 +58,12 @@ export interface StorageFilePurposeAndUserQueryInput {
|
|
|
48
58
|
* @param input - The user, purpose, and optional subgroup to filter by.
|
|
49
59
|
* @returns Firestore query constraints for the given purpose and user.
|
|
50
60
|
*
|
|
61
|
+
* @dbxModelFirebaseIndex
|
|
62
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
63
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
64
|
+
* @dbxModelFirebaseIndexCategory lookup
|
|
65
|
+
* @dbxModelFirebaseIndexSkip true
|
|
66
|
+
*
|
|
51
67
|
* @example
|
|
52
68
|
* @example
|
|
53
69
|
* ```ts
|
|
@@ -65,6 +81,11 @@ export declare function storageFilePurposeAndUserQuery(input: StorageFilePurpose
|
|
|
65
81
|
*
|
|
66
82
|
* @returns Firestore query constraints for StorageFiles flagged for group synchronization.
|
|
67
83
|
*
|
|
84
|
+
* @dbxModelFirebaseIndex
|
|
85
|
+
* @dbxModelFirebaseIndexModel StorageFile
|
|
86
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
87
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
88
|
+
*
|
|
68
89
|
* @example
|
|
69
90
|
* @example
|
|
70
91
|
* ```ts
|
|
@@ -79,6 +100,11 @@ export declare function storageFileFlaggedForSyncWithGroupsQuery(): FirestoreQue
|
|
|
79
100
|
*
|
|
80
101
|
* @returns Firestore query constraints for StorageFileGroups needing initialization.
|
|
81
102
|
*
|
|
103
|
+
* @dbxModelFirebaseIndex
|
|
104
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
105
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
106
|
+
* @dbxModelFirebaseIndexCategory init
|
|
107
|
+
*
|
|
82
108
|
* @example
|
|
83
109
|
* @example
|
|
84
110
|
* ```ts
|
|
@@ -91,6 +117,11 @@ export declare function storageFileGroupsFlaggedForNeedsInitializationQuery(): F
|
|
|
91
117
|
*
|
|
92
118
|
* @returns Firestore query constraints for StorageFileGroups flagged for content regeneration.
|
|
93
119
|
*
|
|
120
|
+
* @dbxModelFirebaseIndex
|
|
121
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
122
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
123
|
+
* @dbxModelFirebaseIndexCategory sweep
|
|
124
|
+
*
|
|
94
125
|
* @example
|
|
95
126
|
* @example
|
|
96
127
|
* ```ts
|
|
@@ -105,6 +136,11 @@ export declare function storageFileGroupsFlaggedForContentRegenerationQuery(): F
|
|
|
105
136
|
*
|
|
106
137
|
* @returns Firestore query constraints for StorageFileGroups flagged as invalid.
|
|
107
138
|
*
|
|
139
|
+
* @dbxModelFirebaseIndex
|
|
140
|
+
* @dbxModelFirebaseIndexModel StorageFileGroup
|
|
141
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
142
|
+
* @dbxModelFirebaseIndexCategory cleanup
|
|
143
|
+
*
|
|
108
144
|
* @example
|
|
109
145
|
* @example
|
|
110
146
|
* ```ts
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module system.scheduler
|
|
3
|
+
*
|
|
4
|
+
* Declares the framework-owned `scheduler` {@link SystemState} type, plus the pure predicates and
|
|
5
|
+
* the moment-bound read object that make up the "has this schedule already run in this hour?" gate.
|
|
6
|
+
*
|
|
7
|
+
* The gate exists so a cron that fires every hour can run a body that should only run every Nth
|
|
8
|
+
* hour, without each individual task having to carry a throttle of its own. Evaluate it ONCE at the
|
|
9
|
+
* top of a schedule function: pass, run the work; fail, return.
|
|
10
|
+
*
|
|
11
|
+
* The stateful half — read, evaluate, and claim the hour against Firestore — lives in
|
|
12
|
+
* `@dereekb/firebase-server/model` as `schedulerSystemStateAccessorFactory()`. Only the shape, the
|
|
13
|
+
* predicates, and {@link schedulerSystemStateRead} live here, so the evaluation can be exercised
|
|
14
|
+
* without an emulator and the converter can be registered from an app's client-shared converter
|
|
15
|
+
* map.
|
|
16
|
+
*/
|
|
17
|
+
import { type HourOfDay, type Hours, type Maybe } from '@dereekb/util';
|
|
18
|
+
import { type FirestoreDocument, type FirestoreDocumentAccessor } from '../../common';
|
|
19
|
+
import { type SystemState, type SystemStateDocument, type SystemStateStoredData, type SystemStateStoredDataFieldConverterConfig } from './system';
|
|
20
|
+
/**
|
|
21
|
+
* {@link SystemState} type identifier for the scheduler's run-gate state.
|
|
22
|
+
*
|
|
23
|
+
* Also the document id, per the SystemState singleton convention — so this state lives at
|
|
24
|
+
* `sys/scheduler`.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SCHEDULER_SYSTEM_STATE_TYPE = "scheduler";
|
|
27
|
+
/**
|
|
28
|
+
* Scheduler state for gating scheduled work to at most one run per hour window.
|
|
29
|
+
*
|
|
30
|
+
* ONE gate, ONE `lat`. This is deliberately not per-task and not per-table state: it answers
|
|
31
|
+
* "has the scheduler already run its Nth-hour body during this hour?" for the app as a whole. Two
|
|
32
|
+
* callers sharing the document with different `everyNHours` values will have whichever one passes
|
|
33
|
+
* first claim the hour for both.
|
|
34
|
+
*
|
|
35
|
+
* @dbxModelSubObject
|
|
36
|
+
*/
|
|
37
|
+
export interface SchedulerSystemData extends SystemStateStoredData {
|
|
38
|
+
/**
|
|
39
|
+
* Last run at. The single gate anchor — the moment the most recent passing check claimed its hour.
|
|
40
|
+
*
|
|
41
|
+
* @dbxModelVariable lastRunAt
|
|
42
|
+
*/
|
|
43
|
+
lat?: Maybe<Date>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Firestore field converter for {@link SchedulerSystemData}.
|
|
47
|
+
*
|
|
48
|
+
* Register it in the app's `SystemStateStoredDataConverterMap` under
|
|
49
|
+
* {@link SCHEDULER_SYSTEM_STATE_TYPE}. This is NOT optional: without it the collection falls back to
|
|
50
|
+
* the pass-through converter, `lat` reads back as a raw Firestore `Timestamp` instead of a `Date`,
|
|
51
|
+
* and {@link hasRunInCurrentHour} can never match — so the gate would open on every single call.
|
|
52
|
+
*/
|
|
53
|
+
export declare const schedulerSystemDataConverter: SystemStateStoredDataFieldConverterConfig<SchedulerSystemData>;
|
|
54
|
+
/**
|
|
55
|
+
* Loads the {@link SystemStateDocument} that stores {@link SchedulerSystemData}, using
|
|
56
|
+
* {@link SCHEDULER_SYSTEM_STATE_TYPE} as the document id.
|
|
57
|
+
*
|
|
58
|
+
* @param accessor - The document accessor for the SystemState collection.
|
|
59
|
+
* @returns The SystemState document for the scheduler state.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const doc = loadSchedulerSystemState(systemStateCollection.documentAccessor());
|
|
64
|
+
* const data = await doc.snapshotData();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadSchedulerSystemState<D extends FirestoreDocument<SystemState<SystemStateStoredData>>>(accessor: FirestoreDocumentAccessor<SystemState<SystemStateStoredData>, D>): SystemStateDocument<SchedulerSystemData>;
|
|
68
|
+
/**
|
|
69
|
+
* Whether the date's hour-of-day is an Nth hour, in the ambient timezone.
|
|
70
|
+
*
|
|
71
|
+
* This is a modulo against the hour-of-day, NOT an interval since some epoch — so at hour 12 it is
|
|
72
|
+
* true for N = 1, 2, 3, 4, 6, and 12, and false for N = 5. Only divisors of 24 divide the day
|
|
73
|
+
* evenly; a non-divisor (5, 7, 9, …) will still fire, just with a short window across midnight.
|
|
74
|
+
*
|
|
75
|
+
* An `everyNHours` of zero or less has no meaningful Nth hour and returns false rather than
|
|
76
|
+
* dividing by zero.
|
|
77
|
+
*
|
|
78
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
79
|
+
* @param date - Moment to test; defaults to now.
|
|
80
|
+
* @returns True when the date's hour-of-day is an Nth hour.
|
|
81
|
+
*
|
|
82
|
+
* @dbxUtil
|
|
83
|
+
* @dbxUtilCategory date
|
|
84
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, modulo
|
|
85
|
+
* @dbxUtilRelated has-run-in-current-hour
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* isNthHourOfDay(3, new Date('2024-01-01T12:30:00')); // true (12 % 3 === 0)
|
|
90
|
+
* isNthHourOfDay(5, new Date('2024-01-01T12:30:00')); // false (12 % 5 === 2)
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function isNthHourOfDay(everyNHours: Hours, date?: Maybe<Date>): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the last run falls inside the same hour as now.
|
|
96
|
+
*
|
|
97
|
+
* This is a same-hour-bucket compare, not an elapsed-time compare: a run at 12:59 and a `now` of
|
|
98
|
+
* 13:01 are two minutes apart but in different hours, so this returns false. That is what makes the
|
|
99
|
+
* gate track the hourly cron rather than drift with it.
|
|
100
|
+
*
|
|
101
|
+
* A null `lastRunAt` means nothing has run, so it returns false.
|
|
102
|
+
*
|
|
103
|
+
* @param lastRunAt - Moment the work last ran.
|
|
104
|
+
* @param now - Moment to compare against; defaults to now.
|
|
105
|
+
* @returns True when both moments fall in the same hour.
|
|
106
|
+
*
|
|
107
|
+
* @dbxUtil
|
|
108
|
+
* @dbxUtilCategory date
|
|
109
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, window
|
|
110
|
+
* @dbxUtilRelated is-nth-hour-of-day, round-down-to-hour
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* hasRunInCurrentHour(new Date('2024-01-01T12:05:00'), new Date('2024-01-01T12:55:00')); // true
|
|
115
|
+
* hasRunInCurrentHour(new Date('2024-01-01T12:59:00'), new Date('2024-01-01T13:01:00')); // false
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare function hasRunInCurrentHour(lastRunAt: Maybe<Date>, now?: Maybe<Date>): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* The zero-based index of the date's hour within the day's every-N-hours schedule, or null when the
|
|
121
|
+
* hour is not an Nth hour.
|
|
122
|
+
*
|
|
123
|
+
* At hour 12 with N=3 the day's matching hours are 0, 3, 6, 9, 12 — so the index is 4. Use it to
|
|
124
|
+
* rotate work across a day's windows ("do the expensive pass only on index 0") without each caller
|
|
125
|
+
* re-deriving the arithmetic from the hour-of-day.
|
|
126
|
+
*
|
|
127
|
+
* Returns null rather than a number for a non-matching hour, so a falsy check cannot confuse
|
|
128
|
+
* "index 0" — the first window of the day — with "no window".
|
|
129
|
+
*
|
|
130
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
131
|
+
* @param date - Moment to test; defaults to now.
|
|
132
|
+
* @returns The zero-based window index, or null when the hour is not an Nth hour.
|
|
133
|
+
*
|
|
134
|
+
* @dbxUtil
|
|
135
|
+
* @dbxUtilCategory date
|
|
136
|
+
* @dbxUtilTags schedule, hour, throttle, cron, gate, index, window
|
|
137
|
+
* @dbxUtilRelated is-nth-hour-of-day
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* nthHourOfDayIndex(3, new Date('2024-01-01T12:30:00')); // 4 (0, 3, 6, 9, 12)
|
|
142
|
+
* nthHourOfDayIndex(3, new Date('2024-01-01T13:30:00')); // null (13 % 3 !== 0)
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function nthHourOfDayIndex(everyNHours: Hours, date?: Maybe<Date>): Maybe<number>;
|
|
146
|
+
/**
|
|
147
|
+
* A read of the scheduler's gate state, evaluated against a single moment.
|
|
148
|
+
*
|
|
149
|
+
* Holds the `now` and the `lastRunAt` it was built from, so every question below is answered against
|
|
150
|
+
* the same moment. Asking about N=2 and then N=3 cannot straddle an hour boundary mid-evaluation,
|
|
151
|
+
* and no question re-reads the document.
|
|
152
|
+
*
|
|
153
|
+
* This is the pure half of the gate. The Firestore-backed half — read, evaluate, and CLAIM the hour
|
|
154
|
+
* in a transaction — is `schedulerSystemStateAccessorFactory()` in `@dereekb/firebase-server/model`.
|
|
155
|
+
*/
|
|
156
|
+
export interface SchedulerSystemStateRead {
|
|
157
|
+
/**
|
|
158
|
+
* The moment this read was taken at.
|
|
159
|
+
*/
|
|
160
|
+
readonly now: Date;
|
|
161
|
+
/**
|
|
162
|
+
* The hour-of-day of {@link SchedulerSystemStateRead.now}, in the ambient timezone.
|
|
163
|
+
*/
|
|
164
|
+
readonly hourOfDay: HourOfDay;
|
|
165
|
+
/**
|
|
166
|
+
* The moment the most recent passing check claimed its hour, or null if the gate has never been
|
|
167
|
+
* claimed.
|
|
168
|
+
*/
|
|
169
|
+
readonly lastRunAt: Maybe<Date>;
|
|
170
|
+
/**
|
|
171
|
+
* Whether {@link SchedulerSystemStateRead.lastRunAt} falls inside the same hour as `now` — that is,
|
|
172
|
+
* whether the gate has already been claimed during this hour.
|
|
173
|
+
*/
|
|
174
|
+
readonly hasRunInCurrentHour: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Whether the gate is open for the given interval: `now` is an Nth hour of the day AND the gate
|
|
177
|
+
* has not already been claimed during this hour.
|
|
178
|
+
*
|
|
179
|
+
* This is a pure evaluation — it does NOT claim the hour. Use
|
|
180
|
+
* `SchedulerSystemStateAccessor.checkAndClaim()` to actually gate work.
|
|
181
|
+
*
|
|
182
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
183
|
+
* @returns True when the gate is open.
|
|
184
|
+
*/
|
|
185
|
+
isOpen(everyNHours: Hours): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* {@link isNthHourOfDay} bound to this read's `now`.
|
|
188
|
+
*
|
|
189
|
+
* Unlike {@link SchedulerSystemStateRead.isOpen} this ignores `lastRunAt` entirely, which is what
|
|
190
|
+
* makes it the right sub-gate for work running INSIDE an already-claimed hour: the hour is spent
|
|
191
|
+
* either way, so each task only needs to ask whether this is its hour.
|
|
192
|
+
*
|
|
193
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
194
|
+
* @returns True when this read's hour-of-day is an Nth hour.
|
|
195
|
+
*/
|
|
196
|
+
isNthHourOfDay(everyNHours: Hours): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* {@link nthHourOfDayIndex} bound to this read's `now`.
|
|
199
|
+
*
|
|
200
|
+
* @param everyNHours - Run every Nth hour of the day.
|
|
201
|
+
* @returns The zero-based window index, or null when this read's hour is not an Nth hour.
|
|
202
|
+
*/
|
|
203
|
+
nthHourOfDayIndex(everyNHours: Hours): Maybe<number>;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Configuration for {@link schedulerSystemStateRead}.
|
|
207
|
+
*/
|
|
208
|
+
export interface SchedulerSystemStateReadConfig {
|
|
209
|
+
/**
|
|
210
|
+
* The moment to evaluate the gate against. Defaults to the current time.
|
|
211
|
+
*/
|
|
212
|
+
readonly now?: Maybe<Date>;
|
|
213
|
+
/**
|
|
214
|
+
* The `lat` read off the scheduler document, or null/undefined when it has never been claimed.
|
|
215
|
+
*/
|
|
216
|
+
readonly lastRunAt: Maybe<Date>;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Creates a {@link SchedulerSystemStateRead} from a moment and a last-run.
|
|
220
|
+
*
|
|
221
|
+
* Every predicate on the result closes over the single `now` resolved here, so a caller can ask
|
|
222
|
+
* about any number of intervals off one read without drifting across an hour boundary.
|
|
223
|
+
*
|
|
224
|
+
* @param config - The moment and the document's last-run.
|
|
225
|
+
* @returns The evaluated read.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* const read = schedulerSystemStateRead({ now, lastRunAt });
|
|
230
|
+
*
|
|
231
|
+
* read.isOpen(3); // should the every-3-hours body run?
|
|
232
|
+
* read.isNthHourOfDay(6); // is this also a 6th hour, for a sub-gated task?
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
export declare function schedulerSystemStateRead(config: SchedulerSystemStateReadConfig): SchedulerSystemStateRead;
|