@dereekb/firebase-server 13.31.0 → 13.32.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.
@@ -1,8 +1,9 @@
1
- import { mapObjectKeysToLowercase, multiValueMapBuilder, batch, runAsyncTasksForValues, asArray, pushArrayItemsIntoArray, UNSET_INDEX_NUMBER, areEqualPOJOValues, performAsyncTasks, removeValuesAtIndexesFromArrayCopy, takeFront, makeModelMap, computeNextFreeIndexOnSortedValuesFunction, readIndexNumber, asPromise, dateOrMillisecondsToDate, filterOnlyUndefinedValues, iterablesAreSetEquivalent, mapIdentityFunction, makeValuesGroupMap, filterMaybeArrayValues, cutStringFunction, takeLast, separateValues, unique, addMilliseconds, mergeSlashPaths, ModelRelationUtility, isThrottled, isPast, unixDateTimeSecondsNumberFromDate, slashPathDetails, MS_IN_MINUTE, expirationDetails, dateFromDateOrTimeMillisecondsNumber, cachedGetter, MS_IN_HOUR, documentFileExtensionForMimeType, useCallback, ZIP_FILE_MIME_TYPE, MAP_IDENTITY, pushItemOrArrayItemsIntoArray, asDecisionFunction } from '@dereekb/util';
1
+ import { notificationHealthCheckIssue, KnownNotificationHealthCheckIssueCode, NotificationHealthCheckStatus, MailgunNotificationHealthCheckIssueCode, untrackableNotificationHealthCheckProbe, CREATE_NOTIFICATION_ID_REQUIRED_ERROR_CODE, NOTIFICATION_BOX_DOES_NOT_EXIST_ERROR_CODE, NOTIFICATION_BOX_EXCLUSION_TARGET_INVALID_ERROR_CODE, NOTIFICATION_BOX_EXISTS_FOR_MODEL_ERROR_CODE, NOTIFICATION_BOX_RECIPIENT_DOES_NOT_EXIST_ERROR_CODE, NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE, NOTIFICATION_USER_BLOCKED_FROM_BEING_ADD_TO_RECIPIENTS_ERROR_CODE, NOTIFICATION_USER_HEALTH_CHECK_PROBE_THROTTLED_ERROR_CODE, NOTIFICATION_USER_HEALTH_CHECK_THROTTLED_ERROR_CODE, NOTIFICATION_USER_HEALTH_CHECK_VERIFY_THROTTLED_ERROR_CODE, NOTIFICATION_USER_INVALID_UID_FOR_CREATE_ERROR_CODE, NOTIFICATION_USER_LOCKED_CONFIG_FROM_BEING_UPDATED_ERROR_CODE, notificationUserHealthCheckParamsType, rollupNotificationHealthCheckResultStatus, rollupNotificationDeliveryHealthCheckResultStatus, isPendingNotificationHealthCheckProbe, notificationUserHealthCheckNextProbeAt, notificationUserHealthCheckNextVerifyAt, notificationUserHealthCheckNextRunAt, DEFAULT_NOTIFICATION_TEMPLATE_TYPE, NotificationDeliveryMethod, NotificationBoxRecipientFlag, effectiveNotificationBoxRecipientTemplateConfig, firestoreDummyKey, mergeNotificationBoxRecipients, mergeNotificationUserNotificationBoxRecipientConfigs, applyExclusionsToNotificationUserNotificationBoxRecipientConfigs, mergeNotificationBoxRecipientTemplateConfigs, notificationSendExclusionCanSendFunction, mergeNotificationUserDefaultNotificationBoxRecipientConfig, NotificationRecipientSendFlag, allowedNotificationRecipients, loadDocumentsForIds, getDocumentSnapshotDataPairsWithData, cleanupOldNotificationLoggedEventDaysParamsType, cleanupSentNotificationsParamsType, createNotificationBoxParamsType, createNotificationSummaryParamsType, createNotificationUserParamsType, resyncNotificationUserParamsType, sendNotificationParamsType, sendQueuedNotificationsParamsType, updateNotificationBoxParamsType, updateNotificationBoxRecipientParamsType, updateNotificationSummaryParamsType, updateNotificationUserParamsType, updateNotificationUserDefaultNotificationBoxRecipientConfig, effectiveNotificationBoxRecipientConfig, updateNotificationUserNotificationBoxRecipientConfigs, calculateNsForNotificationUserNotificationBoxRecipientConfigs, getDocumentSnapshotDataPairs, iterateFirestoreDocumentSnapshotPairs, notificationUsersFlaggedForNeedsSyncQuery, notificationSummaryIdForModel, loadNotificationBoxDocumentForReferencePair, updateNotificationUserNotificationSendExclusions, notificationBoxRecipientTemplateConfigArrayToRecord, updateNotificationRecipient, setIdAndKeyFromKeyIdRefOnDocumentData, inferKeyFromTwoWayFlatFirestoreModelKey, NotificationSendType, getDocumentSnapshotData, notificationSendFlagsImplyIsComplete, NotificationSendState, mergeNotificationSendMessagesResult, notificationsPastSendAtTimeQuery, loadDocumentsForDocumentReferencesFromValues, notificationLoggedEventDayId, shouldSaveNotificationToNotificationWeek, notificationsReadyForCleanupQuery, notificationLoggedEventDaysOlderThanQuery, initializeAllApplicableNotificationBoxesParamsType, initializeAllApplicableNotificationSummariesParamsType, initializeNotificationModelParamsType, firestoreModelKeyCollectionName, notificationBoxesFlaggedForNeedsInitializationQuery, notificationSummariesFlaggedForNeedsInitializationQuery, noContentNotificationMessageFunctionFactory, _createNotificationDocumentFromPair, createNotificationDocumentPair, NOTIFICATION_SUMMARY_EMBEDDED_NOTIFICATION_ITEM_SUBJECT_MAX_LENGTH, NOTIFICATION_SUMMARY_EMBEDDED_NOTIFICATION_ITEM_MESSAGE_MAX_LENGTH, sortNotificationItemsFunction, NOTIFICATION_SUMMARY_ITEM_LIMIT, NOTIFICATION_TASK_SUBTASK_CHECKPOINT_PROCESSING, NOTIFICATION_TASK_SUBTASK_CHECKPOINT_CLEANUP, completeSubtaskProcessingAndScheduleCleanupTaskResult, delayCompletion, notificationTaskComplete, notificationTaskDelayRetry, DEFAULT_NOTIFICATION_TASK_SUBTASK_CLEANUP_RETRY_DELAY, DEFAULT_NOTIFICATION_TASK_SUBTASK_CLEANUP_RETRY_ATTEMPTS, STORAGE_FILE_GROUP_CREATE_INPUT_ERROR_CODE, STORAGE_FILE_ALREADY_PROCESSED_ERROR_CODE, STORAGE_FILE_CANNOT_BE_DELETED_YET_ERROR_CODE, STORAGE_FILE_GROUP_QUEUED_FOR_INITIALIZATION_ERROR_CODE, STORAGE_FILE_MODEL_ALREADY_INITIALIZED_ERROR_CODE, STORAGE_FILE_NOT_FLAGGED_FOR_DELETION_ERROR_CODE, STORAGE_FILE_NOT_FLAGGED_FOR_GROUPS_SYNC_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_ALLOWED_FOR_INVALID_STATE_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_AVAILABLE_FOR_TYPE_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_QUEUED_FOR_PROCESSING_ERROR_CODE, UPLOADED_FILE_DOES_NOT_EXIST_ERROR_CODE, UPLOADED_FILE_INITIALIZATION_DISCARDED_ERROR_CODE, UPLOADED_FILE_INITIALIZATION_FAILED_ERROR_CODE, UPLOADED_FILE_NOT_ALLOWED_TO_BE_INITIALIZED_ERROR_CODE, createStorageFileParamsType, createStorageFileGroupParamsType, createStorageFileSignedUploadUrlParamsType, deleteAllQueuedStorageFilesParamsType, deleteStorageFileParamsType, downloadMultipleStorageFilesParamsType, downloadStorageFileParamsType, initializeAllStorageFilesFromUploadsParamsType, initializeStorageFileFromUploadParamsType, processAllQueuedStorageFilesParamsType, processStorageFileParamsType, readMultipleStorageFilesMetadataParamsType, readStorageFileMetadataParamsType, regenerateAllFlaggedStorageFileGroupsContentParamsType, regenerateStorageFileGroupContentParamsType, syncAllFlaggedStorageFilesWithGroupsParamsType, syncStorageFileWithGroupsParamsType, updateStorageFileParamsType, updateStorageFileGroupParamsType, UPLOADS_FOLDER_PATH, iterateStorageListFilesByEachFile, StorageFileProcessingState, storageFilesQueuedForProcessingQuery, STORAGE_FILE_PROCESSING_STUCK_THROTTLE_CHECK_MS, StorageFileState, createNotificationDocument, storageFileProcessingNotificationTaskTemplate, storageFilesQueuedForDeleteQuery, loadStorageFileGroupDocumentForReferencePair, calculateStorageFileGroupEmbeddedFileUpdate, iterateFirestoreDocumentSnapshotPairBatches, storageFileFlaggedForSyncWithGroupsQuery, createStorageFileDocumentPairFactory, StorageFileCreationType, storageFileGroupZipFileStoragePath, STORAGE_FILE_GROUP_ZIP_STORAGE_FILE_PURPOSE, calculateStorageFileGroupRegeneration, getDocumentSnapshotDataPair, storageFileGroupsFlaggedForContentRegenerationQuery, DEFAULT_CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MIN_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MAX_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MAX_FILENAME_LENGTH, initializeAllApplicableStorageFileGroupsParamsType, initializeStorageFileModelParamsType, storageFileGroupsFlaggedForNeedsInitializationQuery, STORAGE_FILE_GROUP_ZIP_STORAGE_FILE_PURPOSE_CREATE_ZIP_SUBTASK, storedFileReaderFactory, STORAGE_FILE_PROCESSING_NOTIFICATION_TASK_TYPE, copyStoragePath, notificationSubtaskComplete, STORAGE_FILE_GROUP_ZIP_INFO_JSON_FILE_NAME, limitUploadFileTypeDeterminer, combineUploadFileTypeDeterminers, STORAGEFILE_RELATED_FILE_METADATA_KEY, storageFilePurposeAndUserQuery } from '@dereekb/firebase';
2
+ import { MailgunEventName, mailgunDomainState, mailgunSuppressionsForRecipient, mailgunRecentEventsForRecipient, mailgunValidateEmail, mailgunDomainEventDate, mailgunDomainEventFailureReason, MailgunEventSeverity, mailgunEventsForMessageId, bareMailgunMessageId } from '@dereekb/nestjs/mailgun';
3
+ import { mapObjectKeysToLowercase, multiValueMapBuilder, batch, runAsyncTasksForValues, asArray, pushArrayItemsIntoArray, filterMaybeArrayValues, takeFront, UNSET_INDEX_NUMBER, areEqualPOJOValues, performAsyncTasks, removeValuesAtIndexesFromArrayCopy, makeModelMap, computeNextFreeIndexOnSortedValuesFunction, readIndexNumber, asPromise, dateOrMillisecondsToDate, filterOnlyUndefinedValues, iterablesAreSetEquivalent, mapIdentityFunction, makeValuesGroupMap, cutStringFunction, takeLast, separateValues, unique, addMilliseconds, mergeSlashPaths, ModelRelationUtility, isThrottled, isPast, unixDateTimeSecondsNumberFromDate, slashPathDetails, MS_IN_MINUTE, expirationDetails, dateFromDateOrTimeMillisecondsNumber, cachedGetter, MS_IN_HOUR, documentFileExtensionForMimeType, useCallback, ZIP_FILE_MIME_TYPE, MAP_IDENTITY, pushItemOrArrayItemsIntoArray, asDecisionFunction } from '@dereekb/util';
2
4
  import { yearWeekCode, isSameDate, findMaxDate, findMinDate } from '@dereekb/date';
3
- import { CREATE_NOTIFICATION_ID_REQUIRED_ERROR_CODE, NOTIFICATION_BOX_DOES_NOT_EXIST_ERROR_CODE, NOTIFICATION_BOX_EXCLUSION_TARGET_INVALID_ERROR_CODE, NOTIFICATION_BOX_EXISTS_FOR_MODEL_ERROR_CODE, NOTIFICATION_BOX_RECIPIENT_DOES_NOT_EXIST_ERROR_CODE, NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE, NOTIFICATION_USER_BLOCKED_FROM_BEING_ADD_TO_RECIPIENTS_ERROR_CODE, NOTIFICATION_USER_INVALID_UID_FOR_CREATE_ERROR_CODE, NOTIFICATION_USER_LOCKED_CONFIG_FROM_BEING_UPDATED_ERROR_CODE, firestoreDummyKey, mergeNotificationBoxRecipients, mergeNotificationUserNotificationBoxRecipientConfigs, applyExclusionsToNotificationUserNotificationBoxRecipientConfigs, mergeNotificationBoxRecipientTemplateConfigs, effectiveNotificationBoxRecipientTemplateConfig, notificationSendExclusionCanSendFunction, mergeNotificationUserDefaultNotificationBoxRecipientConfig, DEFAULT_NOTIFICATION_TEMPLATE_TYPE, NotificationRecipientSendFlag, allowedNotificationRecipients, loadDocumentsForIds, getDocumentSnapshotDataPairsWithData, cleanupOldNotificationLoggedEventDaysParamsType, cleanupSentNotificationsParamsType, createNotificationBoxParamsType, createNotificationSummaryParamsType, createNotificationUserParamsType, resyncNotificationUserParamsType, sendNotificationParamsType, sendQueuedNotificationsParamsType, updateNotificationBoxParamsType, updateNotificationBoxRecipientParamsType, updateNotificationSummaryParamsType, updateNotificationUserParamsType, updateNotificationUserDefaultNotificationBoxRecipientConfig, effectiveNotificationBoxRecipientConfig, updateNotificationUserNotificationBoxRecipientConfigs, calculateNsForNotificationUserNotificationBoxRecipientConfigs, getDocumentSnapshotDataPairs, iterateFirestoreDocumentSnapshotPairs, notificationUsersFlaggedForNeedsSyncQuery, notificationSummaryIdForModel, loadNotificationBoxDocumentForReferencePair, updateNotificationUserNotificationSendExclusions, notificationBoxRecipientTemplateConfigArrayToRecord, updateNotificationRecipient, setIdAndKeyFromKeyIdRefOnDocumentData, inferKeyFromTwoWayFlatFirestoreModelKey, NotificationSendType, getDocumentSnapshotData, notificationSendFlagsImplyIsComplete, NotificationSendState, mergeNotificationSendMessagesResult, notificationsPastSendAtTimeQuery, loadDocumentsForDocumentReferencesFromValues, notificationLoggedEventDayId, shouldSaveNotificationToNotificationWeek, notificationsReadyForCleanupQuery, notificationLoggedEventDaysOlderThanQuery, initializeAllApplicableNotificationBoxesParamsType, initializeAllApplicableNotificationSummariesParamsType, initializeNotificationModelParamsType, firestoreModelKeyCollectionName, notificationBoxesFlaggedForNeedsInitializationQuery, notificationSummariesFlaggedForNeedsInitializationQuery, noContentNotificationMessageFunctionFactory, _createNotificationDocumentFromPair, createNotificationDocumentPair, NOTIFICATION_SUMMARY_EMBEDDED_NOTIFICATION_ITEM_SUBJECT_MAX_LENGTH, NOTIFICATION_SUMMARY_EMBEDDED_NOTIFICATION_ITEM_MESSAGE_MAX_LENGTH, sortNotificationItemsFunction, NOTIFICATION_SUMMARY_ITEM_LIMIT, NOTIFICATION_TASK_SUBTASK_CHECKPOINT_PROCESSING, NOTIFICATION_TASK_SUBTASK_CHECKPOINT_CLEANUP, completeSubtaskProcessingAndScheduleCleanupTaskResult, delayCompletion, notificationTaskComplete, notificationTaskDelayRetry, DEFAULT_NOTIFICATION_TASK_SUBTASK_CLEANUP_RETRY_DELAY, DEFAULT_NOTIFICATION_TASK_SUBTASK_CLEANUP_RETRY_ATTEMPTS, STORAGE_FILE_GROUP_CREATE_INPUT_ERROR_CODE, STORAGE_FILE_ALREADY_PROCESSED_ERROR_CODE, STORAGE_FILE_CANNOT_BE_DELETED_YET_ERROR_CODE, STORAGE_FILE_GROUP_QUEUED_FOR_INITIALIZATION_ERROR_CODE, STORAGE_FILE_MODEL_ALREADY_INITIALIZED_ERROR_CODE, STORAGE_FILE_NOT_FLAGGED_FOR_DELETION_ERROR_CODE, STORAGE_FILE_NOT_FLAGGED_FOR_GROUPS_SYNC_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_ALLOWED_FOR_INVALID_STATE_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_AVAILABLE_FOR_TYPE_ERROR_CODE, STORAGE_FILE_PROCESSING_NOT_QUEUED_FOR_PROCESSING_ERROR_CODE, UPLOADED_FILE_DOES_NOT_EXIST_ERROR_CODE, UPLOADED_FILE_INITIALIZATION_DISCARDED_ERROR_CODE, UPLOADED_FILE_INITIALIZATION_FAILED_ERROR_CODE, UPLOADED_FILE_NOT_ALLOWED_TO_BE_INITIALIZED_ERROR_CODE, createStorageFileParamsType, createStorageFileGroupParamsType, createStorageFileSignedUploadUrlParamsType, deleteAllQueuedStorageFilesParamsType, deleteStorageFileParamsType, downloadMultipleStorageFilesParamsType, downloadStorageFileParamsType, initializeAllStorageFilesFromUploadsParamsType, initializeStorageFileFromUploadParamsType, processAllQueuedStorageFilesParamsType, processStorageFileParamsType, readMultipleStorageFilesMetadataParamsType, readStorageFileMetadataParamsType, regenerateAllFlaggedStorageFileGroupsContentParamsType, regenerateStorageFileGroupContentParamsType, syncAllFlaggedStorageFilesWithGroupsParamsType, syncStorageFileWithGroupsParamsType, updateStorageFileParamsType, updateStorageFileGroupParamsType, UPLOADS_FOLDER_PATH, iterateStorageListFilesByEachFile, StorageFileProcessingState, storageFilesQueuedForProcessingQuery, STORAGE_FILE_PROCESSING_STUCK_THROTTLE_CHECK_MS, StorageFileState, createNotificationDocument, storageFileProcessingNotificationTaskTemplate, storageFilesQueuedForDeleteQuery, loadStorageFileGroupDocumentForReferencePair, calculateStorageFileGroupEmbeddedFileUpdate, iterateFirestoreDocumentSnapshotPairBatches, storageFileFlaggedForSyncWithGroupsQuery, createStorageFileDocumentPairFactory, StorageFileCreationType, storageFileGroupZipFileStoragePath, STORAGE_FILE_GROUP_ZIP_STORAGE_FILE_PURPOSE, calculateStorageFileGroupRegeneration, getDocumentSnapshotDataPair, storageFileGroupsFlaggedForContentRegenerationQuery, DEFAULT_CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MIN_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MAX_EXPIRES_IN_MS, CREATE_STORAGE_FILE_SIGNED_UPLOAD_URL_MAX_FILENAME_LENGTH, initializeAllApplicableStorageFileGroupsParamsType, initializeStorageFileModelParamsType, storageFileGroupsFlaggedForNeedsInitializationQuery, STORAGE_FILE_GROUP_ZIP_STORAGE_FILE_PURPOSE_CREATE_ZIP_SUBTASK, storedFileReaderFactory, STORAGE_FILE_PROCESSING_NOTIFICATION_TASK_TYPE, copyStoragePath, notificationSubtaskComplete, STORAGE_FILE_GROUP_ZIP_INFO_JSON_FILE_NAME, limitUploadFileTypeDeterminer, combineUploadFileTypeDeterminers, STORAGEFILE_RELATED_FILE_METADATA_KEY, storageFilePurposeAndUserQuery } from '@dereekb/firebase';
4
5
  import { preconditionConflictError, assertSnapshotData, internalServerError, badRequestError } from '@dereekb/firebase-server';
5
- import { hoursToMilliseconds, addHours, isFuture, addMinutes, addSeconds, addDays } from 'date-fns';
6
+ import { isAfter, hoursToMilliseconds, addHours, isFuture, addMinutes, addSeconds, addDays } from 'date-fns';
6
7
  import { Optional, Inject, Injectable, Module, Global } from '@nestjs/common';
7
8
  import { ConfigModule } from '@nestjs/config';
8
9
  import { BaseError } from 'make-error';
@@ -11,15 +12,18 @@ import { inflateSync } from 'node:zlib';
11
12
  import { PDFName, PDFDocument, PDFRawStream, PDFArray, PDFNumber, PDFStream, PDFDict, PDFRef } from '@cantoo/pdf-lib';
12
13
  import archiver from 'archiver';
13
14
 
14
- function _array_like_to_array$a(arr, len) {
15
+ function _array_like_to_array$c(arr, len) {
15
16
  if (len == null || len > arr.length) len = arr.length;
16
17
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
17
18
  return arr2;
18
19
  }
19
- function _array_with_holes$4(arr) {
20
+ function _array_with_holes$6(arr) {
20
21
  if (Array.isArray(arr)) return arr;
21
22
  }
22
- function asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, key, arg) {
23
+ function _array_without_holes$9(arr) {
24
+ if (Array.isArray(arr)) return _array_like_to_array$c(arr);
25
+ }
26
+ function asyncGeneratorStep$j(gen, resolve, reject, _next, _throw, key, arg) {
23
27
  try {
24
28
  var info = gen[key](arg);
25
29
  var value = info.value;
@@ -33,22 +37,38 @@ function asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, key, arg) {
33
37
  Promise.resolve(value).then(_next, _throw);
34
38
  }
35
39
  }
36
- function _async_to_generator$h(fn) {
40
+ function _async_to_generator$j(fn) {
37
41
  return function() {
38
42
  var self = this, args = arguments;
39
43
  return new Promise(function(resolve, reject) {
40
44
  var gen = fn.apply(self, args);
41
45
  function _next(value) {
42
- asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, "next", value);
46
+ asyncGeneratorStep$j(gen, resolve, reject, _next, _throw, "next", value);
43
47
  }
44
48
  function _throw(err) {
45
- asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, "throw", err);
49
+ asyncGeneratorStep$j(gen, resolve, reject, _next, _throw, "throw", err);
46
50
  }
47
51
  _next(undefined);
48
52
  });
49
53
  };
50
54
  }
51
- function _iterable_to_array_limit$4(arr, i) {
55
+ function _define_property$g(obj, key, value) {
56
+ if (key in obj) {
57
+ Object.defineProperty(obj, key, {
58
+ value: value,
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true
62
+ });
63
+ } else {
64
+ obj[key] = value;
65
+ }
66
+ return obj;
67
+ }
68
+ function _iterable_to_array$9(iter) {
69
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
70
+ }
71
+ function _iterable_to_array_limit$6(arr, i) {
52
72
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
53
73
  if (_i == null) return;
54
74
  var _arr = [];
@@ -72,21 +92,61 @@ function _iterable_to_array_limit$4(arr, i) {
72
92
  }
73
93
  return _arr;
74
94
  }
75
- function _non_iterable_rest$4() {
95
+ function _non_iterable_rest$6() {
76
96
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
77
97
  }
78
- function _sliced_to_array$4(arr, i) {
79
- return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$a(arr, i) || _non_iterable_rest$4();
98
+ function _non_iterable_spread$9() {
99
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
80
100
  }
81
- function _unsupported_iterable_to_array$a(o, minLen) {
101
+ function _object_spread$f(target) {
102
+ for(var i = 1; i < arguments.length; i++){
103
+ var source = arguments[i] != null ? arguments[i] : {};
104
+ var ownKeys = Object.keys(source);
105
+ if (typeof Object.getOwnPropertySymbols === "function") {
106
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
107
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
108
+ }));
109
+ }
110
+ ownKeys.forEach(function(key) {
111
+ _define_property$g(target, key, source[key]);
112
+ });
113
+ }
114
+ return target;
115
+ }
116
+ function ownKeys$e(object, enumerableOnly) {
117
+ var keys = Object.keys(object);
118
+ if (Object.getOwnPropertySymbols) {
119
+ var symbols = Object.getOwnPropertySymbols(object);
120
+ keys.push.apply(keys, symbols);
121
+ }
122
+ return keys;
123
+ }
124
+ function _object_spread_props$e(target, source) {
125
+ source = source != null ? source : {};
126
+ if (Object.getOwnPropertyDescriptors) {
127
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
128
+ } else {
129
+ ownKeys$e(Object(source)).forEach(function(key) {
130
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
131
+ });
132
+ }
133
+ return target;
134
+ }
135
+ function _sliced_to_array$6(arr, i) {
136
+ return _array_with_holes$6(arr) || _iterable_to_array_limit$6(arr, i) || _unsupported_iterable_to_array$c(arr, i) || _non_iterable_rest$6();
137
+ }
138
+ function _to_consumable_array$9(arr) {
139
+ return _array_without_holes$9(arr) || _iterable_to_array$9(arr) || _unsupported_iterable_to_array$c(arr) || _non_iterable_spread$9();
140
+ }
141
+ function _unsupported_iterable_to_array$c(o, minLen) {
82
142
  if (!o) return;
83
- if (typeof o === "string") return _array_like_to_array$a(o, minLen);
143
+ if (typeof o === "string") return _array_like_to_array$c(o, minLen);
84
144
  var n = Object.prototype.toString.call(o).slice(8, -1);
85
145
  if (n === "Object" && o.constructor) n = o.constructor.name;
86
146
  if (n === "Map" || n === "Set") return Array.from(n);
87
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$a(o, minLen);
147
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$c(o, minLen);
88
148
  }
89
- function _ts_generator$h(thisArg, body) {
149
+ function _ts_generator$j(thisArg, body) {
90
150
  var f, y, t, _ = {
91
151
  label: 0,
92
152
  sent: function() {
@@ -185,283 +245,2089 @@ function _ts_generator$h(thisArg, body) {
185
245
  };
186
246
  }
187
247
  }
188
- var DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST = 50;
189
248
  /**
190
- * Creates a {@link NotificationEmailSendService} that sends notification emails via Mailgun.
249
+ * How long a dispatched probe may stay unresolved before it is reported as failed.
191
250
  *
192
- * Groups messages by their send template name, batches them (up to `maxBatchSizePerRequest`),
193
- * converts each batch to a {@link MailgunTemplateEmailRequest} using the configured template builders,
194
- * and dispatches them through the Mailgun API.
251
+ * Mailgun normally records a `delivered` or `failed` event within seconds; a probe with no event after
252
+ * this long is not going to arrive.
253
+ */ var DEFAULT_MAILGUN_HEALTH_CHECK_PROBE_TIMEOUT_MINUTES = 15;
254
+ /**
255
+ * Creates a {@link NotificationEmailSendServiceHealthCheckService} backed by the Mailgun API.
195
256
  *
196
- * @param config - Service configuration including the Mailgun service, template builders, and batch size.
197
- * @returns A {@link NotificationEmailSendService} that batches and sends emails through Mailgun.
257
+ * Attach the result to a {@link NotificationEmailSendService} as its `healthCheckService` and the
258
+ * notification health check will pick it up automatically.
259
+ *
260
+ * @param config - The Mailgun service plus optional probe builder and inspection limits.
261
+ * @returns A health check service for the email delivery method.
198
262
  *
199
263
  * @example
200
264
  * ```ts
201
- * const emailService = mailgunNotificationEmailSendService({
202
- * mailgunService,
203
- * defaultSendTemplateName: 'notification',
204
- * messageBuilders: {
205
- * notification: buildNotificationTemplate
206
- * }
207
- * });
208
- *
209
- * const sendInstance = await emailService.buildSendInstanceForEmailNotificationMessages(messages);
210
- * const result = await sendInstance();
265
+ * const emailSendService = mailgunNotificationEmailSendService({ mailgunService, messageBuilders });
266
+ *
267
+ * const sendServiceWithHealthCheck: NotificationEmailSendService = {
268
+ * ...emailSendService,
269
+ * healthCheckService: mailgunNotificationEmailSendServiceHealthCheckService({
270
+ * mailgunService,
271
+ * probeBuilder: ({ recipient }) => ({
272
+ * to: recipient,
273
+ * template: 'notificationtemplate',
274
+ * subject: 'Email delivery test'
275
+ * })
276
+ * })
277
+ * };
211
278
  * ```
212
- */ function mailgunNotificationEmailSendService(config) {
213
- var mailgunService = config.mailgunService, defaultSendTemplateName = config.defaultSendTemplateName, inputMaxBatchSizePerRequest = config.maxBatchSizePerRequest, inputMessageBuilders = config.messageBuilders;
214
- var lowercaseKeysMessageBuilders = mapObjectKeysToLowercase(inputMessageBuilders);
215
- var maxBatchSizePerRequest = inputMaxBatchSizePerRequest !== null && inputMaxBatchSizePerRequest !== void 0 ? inputMaxBatchSizePerRequest : DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST;
216
- var sendService = {
217
- buildSendInstanceForEmailNotificationMessages: function buildSendInstanceForEmailNotificationMessages(notificationMessages) {
218
- return _async_to_generator$h(function() {
219
- var templateMap, messageSendBatches, templateRequestArrays, templateRequests, sendFn;
220
- return _ts_generator$h(this, function(_state) {
279
+ */ function mailgunNotificationEmailSendServiceHealthCheckService(config) {
280
+ var mailgunService = config.mailgunService, probeBuilder = config.probeBuilder, recentEventsLimit = config.recentEventsLimit, recentEventsWindowDays = config.recentEventsWindowDays, validateAddress = config.validateAddress, inputProbeTimeoutMinutes = config.probeTimeoutMinutes;
281
+ var probeTimeoutMinutes = inputProbeTimeoutMinutes !== null && inputProbeTimeoutMinutes !== void 0 ? inputProbeTimeoutMinutes : DEFAULT_MAILGUN_HEALTH_CHECK_PROBE_TIMEOUT_MINUTES;
282
+ var mailgunApi = mailgunService.mailgunApi;
283
+ return {
284
+ supportsProbe: probeBuilder != null,
285
+ runHealthCheck: function runHealthCheck(request) {
286
+ return _async_to_generator$j(function() {
287
+ var // MARK: recent activity
288
+ _issues, _issues1, target, uid, sendProbe, pendingProbe, now, begin, _ref, domainState, suppressions, recentEvents, validation, issues, bounce, complaint, unsubscribe, probeResult;
289
+ return _ts_generator$j(this, function(_state) {
221
290
  switch(_state.label){
222
291
  case 0:
223
- templateMap = multiValueMapBuilder();
224
- // group by templates
225
- notificationMessages.forEach(function(x) {
226
- var _ref, _ref1;
227
- var _x_emailContent;
228
- var sendTemplateName = (_ref = (_ref1 = (_x_emailContent = x.emailContent) === null || _x_emailContent === void 0 ? void 0 : _x_emailContent.sendTemplateName) !== null && _ref1 !== void 0 ? _ref1 : x.content.sendTemplateName) !== null && _ref !== void 0 ? _ref : defaultSendTemplateName;
229
- if (sendTemplateName == null) {
230
- throw new Error("mailgunNotificationEmailSendService(): A sendTemplateName for a message was not available and no default was provided. Consider configuring a default send template.");
231
- }
232
- templateMap.add(sendTemplateName, x);
233
- });
234
- // build send batches
235
- messageSendBatches = templateMap.entries().flatMap(function(param) {
236
- var _param = _sliced_to_array$4(param, 2), templateType = _param[0], messages = _param[1];
237
- return batch(messages, maxBatchSizePerRequest).map(function(x) {
238
- return [
239
- templateType,
240
- x
241
- ];
242
- });
243
- });
292
+ target = request.target, uid = request.uid, sendProbe = request.sendProbe, pendingProbe = request.pendingProbe, now = request.now;
293
+ begin = recentEventsWindowDays == null ? undefined : new Date(now.getTime() - recentEventsWindowDays * 24 * 60 * 60 * 1000);
244
294
  return [
245
295
  4,
246
- Promise.all(messageSendBatches.map(function(param) {
247
- var _param = _sliced_to_array$4(param, 2), sendTemplateName = _param[0], messages = _param[1];
248
- return _async_to_generator$h(function() {
249
- var sendTemplateNameToLowercase, builderForKey, input;
250
- return _ts_generator$h(this, function(_state) {
251
- sendTemplateNameToLowercase = sendTemplateName.toLowerCase();
252
- builderForKey = lowercaseKeysMessageBuilders[sendTemplateNameToLowercase];
253
- if (builderForKey) {
254
- input = {
255
- mailgunService: mailgunService,
256
- sendTemplateName: sendTemplateName,
257
- messages: messages
258
- };
259
- return [
260
- 2,
261
- builderForKey(input)
262
- ];
263
- }
264
- throw new Error('mailgunNotificationEmailSendService(): A template builder was not available for template type "'.concat(sendTemplateName, '".'));
265
- });
266
- })();
267
- }))
296
+ Promise.all([
297
+ //
298
+ mailgunDomainState(mailgunApi),
299
+ mailgunSuppressionsForRecipient(mailgunApi, target),
300
+ mailgunRecentEventsForRecipient(mailgunApi, target, {
301
+ limit: recentEventsLimit,
302
+ begin: begin
303
+ }),
304
+ validateAddress ? mailgunValidateEmail(mailgunApi, target) : Promise.resolve(undefined)
305
+ ])
268
306
  ];
269
307
  case 1:
270
- templateRequestArrays = _state.sent();
271
- templateRequests = templateRequestArrays.flat();
272
- sendFn = function sendFn() {
273
- return _async_to_generator$h(function() {
274
- var success, failed, result;
275
- return _ts_generator$h(this, function(_state) {
276
- switch(_state.label){
277
- case 0:
278
- success = [];
279
- failed = [];
280
- // send the template emails
281
- return [
282
- 4,
283
- runAsyncTasksForValues(templateRequests, function(x) {
284
- var recipients = asArray(x.to).map(function(z) {
285
- return z.email;
286
- });
287
- return mailgunService.sendTemplateEmail(x).then(function() {
288
- pushArrayItemsIntoArray(success, recipients);
289
- }).catch(function(e) {
290
- pushArrayItemsIntoArray(failed, recipients);
291
- console.error('mailgunNotificationEmailSendService(): failed sending template emails', e);
292
- // suppress error
293
- });
294
- }, {
295
- maxParallelTasks: 3
296
- })
297
- ];
298
- case 1:
299
- _state.sent();
300
- result = {
301
- success: success,
302
- failed: failed,
303
- ignored: []
304
- };
305
- return [
306
- 2,
307
- result
308
- ];
308
+ _ref = _sliced_to_array$6.apply(void 0, [
309
+ _state.sent(),
310
+ 4
311
+ ]), domainState = _ref[0], suppressions = _ref[1], recentEvents = _ref[2], validation = _ref[3];
312
+ issues = [];
313
+ // MARK: domain
314
+ if (domainState.unknown) {
315
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SEND_SERVICE_HEALTH_CHECK_UNAVAILABLE, NotificationHealthCheckStatus.UNKNOWN, {
316
+ message: 'The email provider could not be reached to confirm the sending domain is healthy.',
317
+ data: {
318
+ domain: domainState.domain
319
+ }
320
+ }));
321
+ } else if (domainState.disabled || domainState.state != null && domainState.state !== 'active') {
322
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.DOMAIN_NOT_ACTIVE, NotificationHealthCheckStatus.ERROR, {
323
+ message: 'The system that sends our email is not currently active, so no email is going out to anyone.',
324
+ fix: 'This is a system-wide problem. Contact support so it can be escalated.',
325
+ data: {
326
+ domain: domainState.domain,
327
+ state: domainState.state,
328
+ disabled: domainState.disabled
329
+ }
330
+ }));
331
+ }
332
+ // MARK: suppressions
333
+ bounce = suppressions.bounce, complaint = suppressions.complaint, unsubscribe = suppressions.unsubscribe;
334
+ if (bounce) {
335
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.SUPPRESSED_BOUNCE, NotificationHealthCheckStatus.ERROR, {
336
+ message: 'Email to this address previously bounced, so our email provider is now blocking every message to it.',
337
+ fix: 'Contact support to have the block removed. If the address has a typo, correct it on your account first.',
338
+ data: {
339
+ address: bounce.address,
340
+ code: bounce.code,
341
+ error: bounce.error,
342
+ createdAt: bounce.created_at
343
+ }
344
+ }));
345
+ }
346
+ if (complaint) {
347
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.SUPPRESSED_COMPLAINT, NotificationHealthCheckStatus.ERROR, {
348
+ message: 'One of our emails was reported as spam from this address, so our email provider is now blocking every message to it.',
349
+ fix: 'Contact support to have the block removed.',
350
+ data: {
351
+ address: complaint.address,
352
+ createdAt: complaint.created_at
353
+ }
354
+ }));
355
+ }
356
+ if (unsubscribe) {
357
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.SUPPRESSED_UNSUBSCRIBE, NotificationHealthCheckStatus.WARNING, {
358
+ message: 'This address has unsubscribed from our email, so most messages will not be delivered to it.',
359
+ fix: 'Contact support to resubscribe this address.',
360
+ data: {
361
+ address: unsubscribe.address,
362
+ tags: unsubscribe.tags,
363
+ createdAt: unsubscribe.created_at
364
+ }
365
+ }));
366
+ }
367
+ (_issues = issues).push.apply(_issues, _to_consumable_array$9(recentEventActivityIssues(recentEvents)));
368
+ // MARK: validation
369
+ if (validation) {
370
+ if (validation.result === 'undeliverable') {
371
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.ADDRESS_UNDELIVERABLE, NotificationHealthCheckStatus.ERROR, {
372
+ message: 'This address does not appear to be able to receive email.',
373
+ fix: 'Check the address for typos and correct it on your account.',
374
+ data: {
375
+ address: validation.address,
376
+ result: validation.result,
377
+ risk: validation.risk,
378
+ reason: validation.reason
309
379
  }
310
- });
311
- })();
312
- };
380
+ }));
381
+ }
382
+ if (validation.is_disposable_address) {
383
+ issues.push(notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.ADDRESS_DISPOSABLE, NotificationHealthCheckStatus.WARNING, {
384
+ message: 'This address belongs to a disposable email service, which often stops accepting mail after a short time.',
385
+ fix: 'Use a permanent email address for notifications.',
386
+ data: {
387
+ address: validation.address
388
+ }
389
+ }));
390
+ }
391
+ }
392
+ return [
393
+ 4,
394
+ resolveProbe({
395
+ mailgunService: mailgunService,
396
+ probeBuilder: probeBuilder,
397
+ probeTimeoutMinutes: probeTimeoutMinutes,
398
+ target: target,
399
+ uid: uid,
400
+ sendProbe: sendProbe,
401
+ pendingProbe: pendingProbe,
402
+ now: now
403
+ })
404
+ ];
405
+ case 2:
406
+ probeResult = _state.sent();
407
+ (_issues1 = issues).push.apply(_issues1, _to_consumable_array$9(probeResult.issues));
313
408
  return [
314
409
  2,
315
- sendFn
410
+ {
411
+ issues: issues,
412
+ probe: probeResult.probe
413
+ }
316
414
  ];
317
415
  }
318
416
  });
319
417
  })();
320
418
  }
321
419
  };
322
- return sendService;
323
- }
324
-
325
- /**
326
- * Creates an error indicating that a required notification ID was missing during creation.
327
- *
328
- * Thrown when attempting to create a {@link Notification} document without providing the mandatory ID field.
329
- *
330
- * @returns A precondition conflict error with the notification ID required error code.
331
- */ function createNotificationIdRequiredError() {
332
- return preconditionConflictError({
333
- message: "The required id was not present when attempting to create a Notification.",
334
- code: CREATE_NOTIFICATION_ID_REQUIRED_ERROR_CODE
335
- });
336
420
  }
337
421
  /**
338
- * Creates an error indicating that a notification model (box or summary) has already been initialized.
339
- *
340
- * Thrown during initialization when `throwErrorIfAlreadyInitialized` is true and the model's
341
- * setup flag (`s`) indicates it was previously initialized.
342
- *
343
- * @returns A precondition conflict error with the already-initialized error code.
344
- */ function notificationModelAlreadyInitializedError() {
345
- return preconditionConflictError({
346
- message: "This model has already been initialized.",
347
- code: NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE
348
- });
349
- }
422
+ * Mailgun event names that settle a message's fate. Anything else (`accepted`, `opened`, …) leaves the
423
+ * outcome still open.
424
+ */ var CONCLUSIVE_MAILGUN_EVENT_NAMES = new Set([
425
+ MailgunEventName.DELIVERED,
426
+ MailgunEventName.FAILED,
427
+ MailgunEventName.REJECTED
428
+ ]);
350
429
  /**
351
- * Creates an error indicating that a {@link NotificationBox} is associated with an unregistered model type.
352
- *
353
- * Thrown when the model key's collection name does not match any registered notification model type.
430
+ * Formats a failure reason as a sentence suffix.
354
431
  *
355
- * @param key - The Firestore model key that has no registered notification model type.
356
- * @returns A precondition conflict error with the unregistered model type error code.
357
- */ function notificationBoxUnregistredModelTypeInitializationError(key) {
358
- return preconditionConflictError({
359
- message: "This NotificationBox is associated with an unregistered model type.",
360
- code: NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE,
361
- data: {
362
- key: key
363
- }
364
- });
432
+ * @param reason - The provider's explanation, when it recorded one.
433
+ * @returns `: <reason>` when a reason is available, otherwise a full stop.
434
+ */ function failureReasonSuffix(reason) {
435
+ return reason ? ": ".concat(reason) : '.';
365
436
  }
366
437
  /**
367
- * Creates an error indicating that no {@link NotificationBox} exists for the target model.
368
- *
369
- * Thrown when an operation requires a NotificationBox but none has been created for the given model key.
370
- *
371
- * @returns A precondition conflict error with the box-not-found error code.
372
- */ function notificationBoxDoesNotExist() {
373
- return preconditionConflictError({
374
- message: "A NotificationBox does not exist for this model.",
375
- code: NOTIFICATION_BOX_DOES_NOT_EXIST_ERROR_CODE
438
+ * Classifies a recipient's recent Mailgun events into findings.
439
+ *
440
+ * The most recent conclusive event is what matters: a delivery after a failure means the problem is
441
+ * already resolved, so an old failure should not be reported as a current one.
442
+ *
443
+ * @param recentEvents - The recipient's recent events, newest first.
444
+ * @returns The findings describing the recipient's recent email activity.
445
+ */ function recentEventActivityIssues(recentEvents) {
446
+ if (!recentEvents.length) {
447
+ return [
448
+ notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.NO_RECENT_ACTIVITY, NotificationHealthCheckStatus.WARNING, {
449
+ message: 'No email has been sent to this address recently, so the problem is more likely to be in what triggers the notifications than in the email itself.',
450
+ fix: 'Check the settings above, and contact support if you expected to receive something.'
451
+ })
452
+ ];
453
+ }
454
+ // events come back newest-first
455
+ var conclusiveEvent = recentEvents.find(function(x) {
456
+ return CONCLUSIVE_MAILGUN_EVENT_NAMES.has(x.event);
376
457
  });
458
+ if (!conclusiveEvent) {
459
+ return [
460
+ notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.NO_RECENT_ACTIVITY, NotificationHealthCheckStatus.UNKNOWN, {
461
+ message: 'Recent email to this address has been accepted for delivery, but no delivery outcome has been recorded yet.',
462
+ data: {
463
+ eventCount: recentEvents.length
464
+ }
465
+ })
466
+ ];
467
+ }
468
+ var eventDate = mailgunDomainEventDate(conclusiveEvent);
469
+ if (conclusiveEvent.event === MailgunEventName.DELIVERED) {
470
+ return [
471
+ notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.RECENT_DELIVERY_SUCCESS, NotificationHealthCheckStatus.OK, {
472
+ message: 'Our email provider successfully delivered email to this address recently.',
473
+ fix: 'If you still cannot find it, check your spam or junk folder and any email filters or rules you have set up.',
474
+ data: {
475
+ deliveredAt: eventDate
476
+ }
477
+ })
478
+ ];
479
+ }
480
+ var reason = mailgunDomainEventFailureReason(conclusiveEvent);
481
+ var isPermanent = conclusiveEvent.severity === MailgunEventSeverity.PERMANENT;
482
+ return [
483
+ notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.RECENT_DELIVERY_FAILURE, isPermanent ? NotificationHealthCheckStatus.ERROR : NotificationHealthCheckStatus.WARNING, {
484
+ message: "The last email we sent to this address did not get through".concat(failureReasonSuffix(reason)),
485
+ fix: isPermanent ? 'This will not resolve on its own. Check the address for typos, then contact support.' : 'This is often temporary. Re-run this check later to see if it clears.',
486
+ data: {
487
+ event: conclusiveEvent.event,
488
+ severity: conclusiveEvent.severity,
489
+ reason: reason,
490
+ at: eventDate
491
+ }
492
+ })
493
+ ];
377
494
  }
378
495
  /**
379
- * Creates an error indicating that the exclusion target is invalid.
496
+ * Resolves an in-flight probe, or dispatches a new one.
380
497
  *
381
- * Thrown when the target recipient on the {@link NotificationBox} does not exist on the box
382
- * or does not have a UID, making it ineligible for exclusion.
498
+ * Resolving takes priority over dispatching: if a probe is already in flight there is no reason to send
499
+ * a second message to the same inbox.
383
500
  *
384
- * @returns A precondition conflict error with the invalid exclusion target error code.
385
- */ function notificationBoxExclusionTargetInvalidError() {
386
- return preconditionConflictError({
387
- message: "The target for exclusion is invalid. The target recipient on the NotificationBox must be exist on the NotificationBox and have a uid to be excluded.",
388
- code: NOTIFICATION_BOX_EXCLUSION_TARGET_INVALID_ERROR_CODE
389
- });
501
+ * @param input - The probe target, whether dispatching is permitted, and any probe already in flight.
502
+ * @returns The probe findings and the resulting probe state.
503
+ */ function resolveProbe(input) {
504
+ return _async_to_generator$j(function() {
505
+ var sendProbe, pendingProbe, result;
506
+ return _ts_generator$j(this, function(_state) {
507
+ switch(_state.label){
508
+ case 0:
509
+ sendProbe = input.sendProbe, pendingProbe = input.pendingProbe;
510
+ if (!pendingProbe) return [
511
+ 3,
512
+ 2
513
+ ];
514
+ return [
515
+ 4,
516
+ resolvePendingProbe(input, pendingProbe)
517
+ ];
518
+ case 1:
519
+ result = _state.sent();
520
+ return [
521
+ 3,
522
+ 5
523
+ ];
524
+ case 2:
525
+ if (!sendProbe) return [
526
+ 3,
527
+ 4
528
+ ];
529
+ return [
530
+ 4,
531
+ dispatchProbe(input)
532
+ ];
533
+ case 3:
534
+ result = _state.sent();
535
+ return [
536
+ 3,
537
+ 5
538
+ ];
539
+ case 4:
540
+ result = {
541
+ issues: []
542
+ };
543
+ _state.label = 5;
544
+ case 5:
545
+ return [
546
+ 2,
547
+ result
548
+ ];
549
+ }
550
+ });
551
+ })();
552
+ }
553
+ /**
554
+ * Looks up the outcome of a probe that was dispatched by an earlier run.
555
+ *
556
+ * An empty event list means Mailgun has not recorded an outcome *yet*, not that the message failed —
557
+ * only once the probe has outlived its timeout is silence treated as a failure.
558
+ *
559
+ * @param input - The probe target and timeout.
560
+ * @param pendingProbe - The probe awaiting an outcome.
561
+ * @returns The probe findings and the probe's updated state.
562
+ */ function resolvePendingProbe(input, pendingProbe) {
563
+ return _async_to_generator$j(function() {
564
+ var mailgunService, probeTimeoutMinutes, target, now, events, delivered, failure, probeAgeMinutes, result, reason;
565
+ return _ts_generator$j(this, function(_state) {
566
+ switch(_state.label){
567
+ case 0:
568
+ mailgunService = input.mailgunService, probeTimeoutMinutes = input.probeTimeoutMinutes, target = input.target, now = input.now;
569
+ return [
570
+ 4,
571
+ mailgunEventsForMessageId(mailgunService.mailgunApi, pendingProbe.id, {
572
+ recipient: target
573
+ })
574
+ ];
575
+ case 1:
576
+ events = _state.sent();
577
+ delivered = events.find(function(x) {
578
+ return x.event === MailgunEventName.DELIVERED;
579
+ });
580
+ failure = events.find(function(x) {
581
+ return x.event === MailgunEventName.FAILED || x.event === MailgunEventName.REJECTED;
582
+ });
583
+ probeAgeMinutes = (now.getTime() - pendingProbe.at.getTime()) / (60 * 1000);
584
+ if (delivered) {
585
+ result = {
586
+ issues: [
587
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_DELIVERED, NotificationHealthCheckStatus.OK, {
588
+ message: 'The test email we sent was delivered successfully.',
589
+ fix: 'If it is not in your inbox, check your spam or junk folder and any email filters you have set up.',
590
+ data: {
591
+ deliveredAt: mailgunDomainEventDate(delivered)
592
+ }
593
+ })
594
+ ],
595
+ probe: _object_spread_props$e(_object_spread$f({}, pendingProbe), {
596
+ s: NotificationHealthCheckStatus.OK,
597
+ d: 'Delivered'
598
+ })
599
+ };
600
+ } else if (failure) {
601
+ reason = mailgunDomainEventFailureReason(failure);
602
+ result = {
603
+ issues: [
604
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_FAILED, NotificationHealthCheckStatus.ERROR, {
605
+ message: "The test email we sent did not get through".concat(failureReasonSuffix(reason)),
606
+ fix: 'Check the address for typos, then contact support with this report.',
607
+ data: {
608
+ event: failure.event,
609
+ severity: failure.severity,
610
+ reason: reason
611
+ }
612
+ })
613
+ ],
614
+ probe: _object_spread_props$e(_object_spread$f({}, pendingProbe), {
615
+ s: NotificationHealthCheckStatus.ERROR,
616
+ d: reason !== null && reason !== void 0 ? reason : 'Delivery failed'
617
+ })
618
+ };
619
+ } else if (probeAgeMinutes > probeTimeoutMinutes) {
620
+ result = {
621
+ issues: [
622
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_FAILED, NotificationHealthCheckStatus.ERROR, {
623
+ message: 'The test email we sent never reached a delivery result, which means it did not arrive.',
624
+ fix: 'Contact support with this report.',
625
+ data: {
626
+ dispatchedAt: pendingProbe.at,
627
+ probeTimeoutMinutes: probeTimeoutMinutes
628
+ }
629
+ })
630
+ ],
631
+ probe: _object_spread_props$e(_object_spread$f({}, pendingProbe), {
632
+ s: NotificationHealthCheckStatus.ERROR,
633
+ d: 'No delivery result recorded'
634
+ })
635
+ };
636
+ } else {
637
+ result = {
638
+ issues: [
639
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_PENDING, NotificationHealthCheckStatus.PENDING, {
640
+ message: 'We sent a test email and are still waiting to hear whether it arrived.',
641
+ fix: 'Nothing to do — the result appears here on its own as soon as our email provider reports it.',
642
+ data: {
643
+ dispatchedAt: pendingProbe.at
644
+ }
645
+ })
646
+ ],
647
+ probe: pendingProbe
648
+ };
649
+ }
650
+ return [
651
+ 2,
652
+ result
653
+ ];
654
+ }
655
+ });
656
+ })();
657
+ }
658
+ /**
659
+ * Records a dispatch attempt that produced nothing to track it by.
660
+ *
661
+ * A probe is recorded all the same, with no correlation id and a status that is already settled. That
662
+ * matters beyond the report: the test message window is derived from the recorded probe's `at`, so an
663
+ * attempt that recorded nothing would leave both the server's throttle and the client's countdown with
664
+ * nothing to key on — the action would come back looking successful and be immediately repeatable, one
665
+ * provider call per click. An attempt is an attempt whether or not it can be followed up.
666
+ *
667
+ * The provider's own status distinguishes the two very different cases: a send it accepted but did not
668
+ * give an id for (typical of an environment with sending suppressed, where the address is probably fine
669
+ * and simply unverifiable), and a send it refused outright (a real failure, reported as one).
670
+ *
671
+ * @param sendResult - What the provider returned for the send.
672
+ * @param input - The probe target and dispatch time.
673
+ * @returns The finding describing the attempt, and the settled probe recording it.
674
+ */ function dispatchWithoutMessageIdResult(sendResult, input) {
675
+ var target = input.target, now = input.now;
676
+ var status = sendResult.status, message = sendResult.message;
677
+ var accepted = status != null && status >= 200 && status < 300;
678
+ var data = {
679
+ status: status,
680
+ message: message
681
+ };
682
+ var probeStatus = accepted ? NotificationHealthCheckStatus.UNKNOWN : NotificationHealthCheckStatus.ERROR;
683
+ return {
684
+ issues: accepted ? [
685
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_DISPATCH_FAILED, NotificationHealthCheckStatus.UNKNOWN, {
686
+ message: 'Our email provider accepted the test email but did not give us a way to track it, so we cannot confirm whether it arrives.',
687
+ fix: 'Check your inbox — and your spam or junk folder — for a test email sent just now.',
688
+ data: data
689
+ })
690
+ ] : [
691
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_DISPATCH_FAILED, NotificationHealthCheckStatus.ERROR, {
692
+ message: "Our email provider refused to send the test email".concat(failureReasonSuffix(message)),
693
+ fix: 'Contact support with this report.',
694
+ data: data
695
+ })
696
+ ],
697
+ probe: untrackableNotificationHealthCheckProbe({
698
+ at: now,
699
+ s: probeStatus,
700
+ tg: target,
701
+ d: accepted ? 'Sent, but not trackable' : message !== null && message !== void 0 ? message : 'Refused by the email provider'
702
+ })
703
+ };
704
+ }
705
+ /**
706
+ * Sends a new probe message and records it as pending.
707
+
708
+ /**
709
+ * Sends a new probe message and records it as pending.
710
+ *
711
+ * @param input - The probe target and message builder.
712
+ * @returns The probe findings and the newly dispatched probe, if one could be sent.
713
+ */ function dispatchProbe(input) {
714
+ return _async_to_generator$j(function() {
715
+ var mailgunService, probeBuilder, target, uid, now, recipient, result, request, sendResult, messageId, e;
716
+ return _ts_generator$j(this, function(_state) {
717
+ switch(_state.label){
718
+ case 0:
719
+ mailgunService = input.mailgunService, probeBuilder = input.probeBuilder, target = input.target, uid = input.uid, now = input.now;
720
+ if (!probeBuilder) {
721
+ return [
722
+ 2,
723
+ {
724
+ issues: [
725
+ notificationHealthCheckIssue(MailgunNotificationHealthCheckIssueCode.PROBE_NOT_CONFIGURED, NotificationHealthCheckStatus.SKIPPED, {
726
+ message: 'Sending a test email is not available on this system.',
727
+ data: {
728
+ target: target
729
+ }
730
+ })
731
+ ]
732
+ }
733
+ ];
734
+ }
735
+ recipient = {
736
+ email: target
737
+ };
738
+ _state.label = 1;
739
+ case 1:
740
+ _state.trys.push([
741
+ 1,
742
+ 4,
743
+ ,
744
+ 5
745
+ ]);
746
+ return [
747
+ 4,
748
+ probeBuilder({
749
+ mailgunService: mailgunService,
750
+ recipient: recipient,
751
+ uid: uid
752
+ })
753
+ ];
754
+ case 2:
755
+ request = _state.sent();
756
+ return [
757
+ 4,
758
+ mailgunService.sendTemplateEmail(request)
759
+ ];
760
+ case 3:
761
+ sendResult = _state.sent();
762
+ messageId = sendResult.id;
763
+ result = messageId ? {
764
+ issues: [
765
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_PENDING, NotificationHealthCheckStatus.PENDING, {
766
+ message: 'We just sent a test email to this address, and our email provider accepted it for delivery.',
767
+ fix: 'Nothing to do — the result appears here on its own as soon as our email provider reports it.',
768
+ data: {
769
+ dispatchedAt: now,
770
+ status: sendResult.status
771
+ }
772
+ })
773
+ ],
774
+ probe: {
775
+ id: bareMailgunMessageId(messageId),
776
+ at: now,
777
+ s: NotificationHealthCheckStatus.PENDING,
778
+ tg: target
779
+ }
780
+ } : dispatchWithoutMessageIdResult(sendResult, input);
781
+ return [
782
+ 3,
783
+ 5
784
+ ];
785
+ case 4:
786
+ e = _state.sent();
787
+ // recorded like any other attempt, so the window applies — a provider that is throwing is the last
788
+ // one that should be called again on the user's next click
789
+ result = {
790
+ issues: [
791
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.PROBE_DISPATCH_FAILED, NotificationHealthCheckStatus.ERROR, {
792
+ message: 'The test email could not be sent at all, which points to a problem with our email system rather than your address.',
793
+ fix: 'Contact support with this report.',
794
+ data: {
795
+ error: "".concat(e)
796
+ }
797
+ })
798
+ ],
799
+ probe: untrackableNotificationHealthCheckProbe({
800
+ at: now,
801
+ s: NotificationHealthCheckStatus.ERROR,
802
+ tg: target,
803
+ d: 'Could not be sent'
804
+ })
805
+ };
806
+ return [
807
+ 3,
808
+ 5
809
+ ];
810
+ case 5:
811
+ return [
812
+ 2,
813
+ result
814
+ ];
815
+ }
816
+ });
817
+ })();
818
+ }
819
+
820
+ function _array_like_to_array$b(arr, len) {
821
+ if (len == null || len > arr.length) len = arr.length;
822
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
823
+ return arr2;
824
+ }
825
+ function _array_with_holes$5(arr) {
826
+ if (Array.isArray(arr)) return arr;
827
+ }
828
+ function asyncGeneratorStep$i(gen, resolve, reject, _next, _throw, key, arg) {
829
+ try {
830
+ var info = gen[key](arg);
831
+ var value = info.value;
832
+ } catch (error) {
833
+ reject(error);
834
+ return;
835
+ }
836
+ if (info.done) {
837
+ resolve(value);
838
+ } else {
839
+ Promise.resolve(value).then(_next, _throw);
840
+ }
841
+ }
842
+ function _async_to_generator$i(fn) {
843
+ return function() {
844
+ var self = this, args = arguments;
845
+ return new Promise(function(resolve, reject) {
846
+ var gen = fn.apply(self, args);
847
+ function _next(value) {
848
+ asyncGeneratorStep$i(gen, resolve, reject, _next, _throw, "next", value);
849
+ }
850
+ function _throw(err) {
851
+ asyncGeneratorStep$i(gen, resolve, reject, _next, _throw, "throw", err);
852
+ }
853
+ _next(undefined);
854
+ });
855
+ };
856
+ }
857
+ function _iterable_to_array_limit$5(arr, i) {
858
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
859
+ if (_i == null) return;
860
+ var _arr = [];
861
+ var _n = true;
862
+ var _d = false;
863
+ var _s, _e;
864
+ try {
865
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
866
+ _arr.push(_s.value);
867
+ if (i && _arr.length === i) break;
868
+ }
869
+ } catch (err) {
870
+ _d = true;
871
+ _e = err;
872
+ } finally{
873
+ try {
874
+ if (!_n && _i["return"] != null) _i["return"]();
875
+ } finally{
876
+ if (_d) throw _e;
877
+ }
878
+ }
879
+ return _arr;
880
+ }
881
+ function _non_iterable_rest$5() {
882
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
883
+ }
884
+ function _sliced_to_array$5(arr, i) {
885
+ return _array_with_holes$5(arr) || _iterable_to_array_limit$5(arr, i) || _unsupported_iterable_to_array$b(arr, i) || _non_iterable_rest$5();
886
+ }
887
+ function _unsupported_iterable_to_array$b(o, minLen) {
888
+ if (!o) return;
889
+ if (typeof o === "string") return _array_like_to_array$b(o, minLen);
890
+ var n = Object.prototype.toString.call(o).slice(8, -1);
891
+ if (n === "Object" && o.constructor) n = o.constructor.name;
892
+ if (n === "Map" || n === "Set") return Array.from(n);
893
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$b(o, minLen);
894
+ }
895
+ function _ts_generator$i(thisArg, body) {
896
+ var f, y, t, _ = {
897
+ label: 0,
898
+ sent: function() {
899
+ if (t[0] & 1) throw t[1];
900
+ return t[1];
901
+ },
902
+ trys: [],
903
+ ops: []
904
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
905
+ return d(g, "next", {
906
+ value: verb(0)
907
+ }), d(g, "throw", {
908
+ value: verb(1)
909
+ }), d(g, "return", {
910
+ value: verb(2)
911
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
912
+ value: function() {
913
+ return this;
914
+ }
915
+ }), g;
916
+ function verb(n) {
917
+ return function(v) {
918
+ return step([
919
+ n,
920
+ v
921
+ ]);
922
+ };
923
+ }
924
+ function step(op) {
925
+ if (f) throw new TypeError("Generator is already executing.");
926
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
927
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
928
+ if (y = 0, t) op = [
929
+ op[0] & 2,
930
+ t.value
931
+ ];
932
+ switch(op[0]){
933
+ case 0:
934
+ case 1:
935
+ t = op;
936
+ break;
937
+ case 4:
938
+ _.label++;
939
+ return {
940
+ value: op[1],
941
+ done: false
942
+ };
943
+ case 5:
944
+ _.label++;
945
+ y = op[1];
946
+ op = [
947
+ 0
948
+ ];
949
+ continue;
950
+ case 7:
951
+ op = _.ops.pop();
952
+ _.trys.pop();
953
+ continue;
954
+ default:
955
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
956
+ _ = 0;
957
+ continue;
958
+ }
959
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
960
+ _.label = op[1];
961
+ break;
962
+ }
963
+ if (op[0] === 6 && _.label < t[1]) {
964
+ _.label = t[1];
965
+ t = op;
966
+ break;
967
+ }
968
+ if (t && _.label < t[2]) {
969
+ _.label = t[2];
970
+ _.ops.push(op);
971
+ break;
972
+ }
973
+ if (t[2]) _.ops.pop();
974
+ _.trys.pop();
975
+ continue;
976
+ }
977
+ op = body.call(thisArg, _);
978
+ } catch (e) {
979
+ op = [
980
+ 6,
981
+ e
982
+ ];
983
+ y = 0;
984
+ } finally{
985
+ f = t = 0;
986
+ }
987
+ if (op[0] & 5) throw op[1];
988
+ return {
989
+ value: op[0] ? op[1] : void 0,
990
+ done: true
991
+ };
992
+ }
993
+ }
994
+ var DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST = 50;
995
+ /**
996
+ * Creates a {@link NotificationEmailSendService} that sends notification emails via Mailgun.
997
+ *
998
+ * Groups messages by their send template name, batches them (up to `maxBatchSizePerRequest`),
999
+ * converts each batch to a {@link MailgunTemplateEmailRequest} using the configured template builders,
1000
+ * and dispatches them through the Mailgun API.
1001
+ *
1002
+ * @param config - Service configuration including the Mailgun service, template builders, and batch size.
1003
+ * @returns A {@link NotificationEmailSendService} that batches and sends emails through Mailgun.
1004
+ *
1005
+ * @example
1006
+ * ```ts
1007
+ * const emailService = mailgunNotificationEmailSendService({
1008
+ * mailgunService,
1009
+ * defaultSendTemplateName: 'notification',
1010
+ * messageBuilders: {
1011
+ * notification: buildNotificationTemplate
1012
+ * }
1013
+ * });
1014
+ *
1015
+ * const sendInstance = await emailService.buildSendInstanceForEmailNotificationMessages(messages);
1016
+ * const result = await sendInstance();
1017
+ * ```
1018
+ */ function mailgunNotificationEmailSendService(config) {
1019
+ var mailgunService = config.mailgunService, defaultSendTemplateName = config.defaultSendTemplateName, inputMaxBatchSizePerRequest = config.maxBatchSizePerRequest, inputMessageBuilders = config.messageBuilders;
1020
+ var lowercaseKeysMessageBuilders = mapObjectKeysToLowercase(inputMessageBuilders);
1021
+ var maxBatchSizePerRequest = inputMaxBatchSizePerRequest !== null && inputMaxBatchSizePerRequest !== void 0 ? inputMaxBatchSizePerRequest : DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST;
1022
+ var sendService = {
1023
+ buildSendInstanceForEmailNotificationMessages: function buildSendInstanceForEmailNotificationMessages(notificationMessages) {
1024
+ return _async_to_generator$i(function() {
1025
+ var templateMap, messageSendBatches, templateRequestArrays, templateRequests, sendFn;
1026
+ return _ts_generator$i(this, function(_state) {
1027
+ switch(_state.label){
1028
+ case 0:
1029
+ templateMap = multiValueMapBuilder();
1030
+ // group by templates
1031
+ notificationMessages.forEach(function(x) {
1032
+ var _ref, _ref1;
1033
+ var _x_emailContent;
1034
+ var sendTemplateName = (_ref = (_ref1 = (_x_emailContent = x.emailContent) === null || _x_emailContent === void 0 ? void 0 : _x_emailContent.sendTemplateName) !== null && _ref1 !== void 0 ? _ref1 : x.content.sendTemplateName) !== null && _ref !== void 0 ? _ref : defaultSendTemplateName;
1035
+ if (sendTemplateName == null) {
1036
+ throw new Error("mailgunNotificationEmailSendService(): A sendTemplateName for a message was not available and no default was provided. Consider configuring a default send template.");
1037
+ }
1038
+ templateMap.add(sendTemplateName, x);
1039
+ });
1040
+ // build send batches
1041
+ messageSendBatches = templateMap.entries().flatMap(function(param) {
1042
+ var _param = _sliced_to_array$5(param, 2), templateType = _param[0], messages = _param[1];
1043
+ return batch(messages, maxBatchSizePerRequest).map(function(x) {
1044
+ return [
1045
+ templateType,
1046
+ x
1047
+ ];
1048
+ });
1049
+ });
1050
+ return [
1051
+ 4,
1052
+ Promise.all(messageSendBatches.map(function(param) {
1053
+ var _param = _sliced_to_array$5(param, 2), sendTemplateName = _param[0], messages = _param[1];
1054
+ return _async_to_generator$i(function() {
1055
+ var sendTemplateNameToLowercase, builderForKey, input;
1056
+ return _ts_generator$i(this, function(_state) {
1057
+ sendTemplateNameToLowercase = sendTemplateName.toLowerCase();
1058
+ builderForKey = lowercaseKeysMessageBuilders[sendTemplateNameToLowercase];
1059
+ if (builderForKey) {
1060
+ input = {
1061
+ mailgunService: mailgunService,
1062
+ sendTemplateName: sendTemplateName,
1063
+ messages: messages
1064
+ };
1065
+ return [
1066
+ 2,
1067
+ builderForKey(input)
1068
+ ];
1069
+ }
1070
+ throw new Error('mailgunNotificationEmailSendService(): A template builder was not available for template type "'.concat(sendTemplateName, '".'));
1071
+ });
1072
+ })();
1073
+ }))
1074
+ ];
1075
+ case 1:
1076
+ templateRequestArrays = _state.sent();
1077
+ templateRequests = templateRequestArrays.flat();
1078
+ sendFn = function sendFn() {
1079
+ return _async_to_generator$i(function() {
1080
+ var success, failed, result;
1081
+ return _ts_generator$i(this, function(_state) {
1082
+ switch(_state.label){
1083
+ case 0:
1084
+ success = [];
1085
+ failed = [];
1086
+ // send the template emails
1087
+ return [
1088
+ 4,
1089
+ runAsyncTasksForValues(templateRequests, function(x) {
1090
+ var recipients = asArray(x.to).map(function(z) {
1091
+ return z.email;
1092
+ });
1093
+ return mailgunService.sendTemplateEmail(x).then(function() {
1094
+ pushArrayItemsIntoArray(success, recipients);
1095
+ }).catch(function(e) {
1096
+ pushArrayItemsIntoArray(failed, recipients);
1097
+ console.error('mailgunNotificationEmailSendService(): failed sending template emails', e);
1098
+ // suppress error
1099
+ });
1100
+ }, {
1101
+ maxParallelTasks: 3
1102
+ })
1103
+ ];
1104
+ case 1:
1105
+ _state.sent();
1106
+ result = {
1107
+ success: success,
1108
+ failed: failed,
1109
+ ignored: []
1110
+ };
1111
+ return [
1112
+ 2,
1113
+ result
1114
+ ];
1115
+ }
1116
+ });
1117
+ })();
1118
+ };
1119
+ return [
1120
+ 2,
1121
+ sendFn
1122
+ ];
1123
+ }
1124
+ });
1125
+ })();
1126
+ }
1127
+ };
1128
+ return sendService;
1129
+ }
1130
+
1131
+ /**
1132
+ * Creates an error indicating that a required notification ID was missing during creation.
1133
+ *
1134
+ * Thrown when attempting to create a {@link Notification} document without providing the mandatory ID field.
1135
+ *
1136
+ * @returns A precondition conflict error with the notification ID required error code.
1137
+ */ function createNotificationIdRequiredError() {
1138
+ return preconditionConflictError({
1139
+ message: "The required id was not present when attempting to create a Notification.",
1140
+ code: CREATE_NOTIFICATION_ID_REQUIRED_ERROR_CODE
1141
+ });
1142
+ }
1143
+ /**
1144
+ * Creates an error indicating that a notification model (box or summary) has already been initialized.
1145
+ *
1146
+ * Thrown during initialization when `throwErrorIfAlreadyInitialized` is true and the model's
1147
+ * setup flag (`s`) indicates it was previously initialized.
1148
+ *
1149
+ * @returns A precondition conflict error with the already-initialized error code.
1150
+ */ function notificationModelAlreadyInitializedError() {
1151
+ return preconditionConflictError({
1152
+ message: "This model has already been initialized.",
1153
+ code: NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE
1154
+ });
1155
+ }
1156
+ /**
1157
+ * Creates an error indicating that a {@link NotificationBox} is associated with an unregistered model type.
1158
+ *
1159
+ * Thrown when the model key's collection name does not match any registered notification model type.
1160
+ *
1161
+ * @param key - The Firestore model key that has no registered notification model type.
1162
+ * @returns A precondition conflict error with the unregistered model type error code.
1163
+ */ function notificationBoxUnregistredModelTypeInitializationError(key) {
1164
+ return preconditionConflictError({
1165
+ message: "This NotificationBox is associated with an unregistered model type.",
1166
+ code: NOTIFICATION_MODEL_ALREADY_INITIALIZED_ERROR_CODE,
1167
+ data: {
1168
+ key: key
1169
+ }
1170
+ });
1171
+ }
1172
+ /**
1173
+ * Creates an error indicating that no {@link NotificationBox} exists for the target model.
1174
+ *
1175
+ * Thrown when an operation requires a NotificationBox but none has been created for the given model key.
1176
+ *
1177
+ * @returns A precondition conflict error with the box-not-found error code.
1178
+ */ function notificationBoxDoesNotExist() {
1179
+ return preconditionConflictError({
1180
+ message: "A NotificationBox does not exist for this model.",
1181
+ code: NOTIFICATION_BOX_DOES_NOT_EXIST_ERROR_CODE
1182
+ });
1183
+ }
1184
+ /**
1185
+ * Creates an error indicating that the exclusion target is invalid.
1186
+ *
1187
+ * Thrown when the target recipient on the {@link NotificationBox} does not exist on the box
1188
+ * or does not have a UID, making it ineligible for exclusion.
1189
+ *
1190
+ * @returns A precondition conflict error with the invalid exclusion target error code.
1191
+ */ function notificationBoxExclusionTargetInvalidError() {
1192
+ return preconditionConflictError({
1193
+ message: "The target for exclusion is invalid. The target recipient on the NotificationBox must be exist on the NotificationBox and have a uid to be excluded.",
1194
+ code: NOTIFICATION_BOX_EXCLUSION_TARGET_INVALID_ERROR_CODE
1195
+ });
1196
+ }
1197
+ /**
1198
+ * Creates an error indicating that a {@link NotificationBox} already exists for this model.
1199
+ *
1200
+ * Thrown when attempting to create a duplicate NotificationBox for a model that already has one.
1201
+ *
1202
+ * @returns A precondition conflict error with the box-exists error code.
1203
+ */ function notificationBoxExistsForModelError() {
1204
+ return preconditionConflictError({
1205
+ message: "A NotificationBox already exists for this model.",
1206
+ code: NOTIFICATION_BOX_EXISTS_FOR_MODEL_ERROR_CODE
1207
+ });
1208
+ }
1209
+ /**
1210
+ * Creates an error indicating that the target recipient does not exist on the {@link NotificationBox}.
1211
+ *
1212
+ * Thrown when attempting to update a recipient that is not registered on the box
1213
+ * and `insert=true` was not passed to allow creating a new recipient entry.
1214
+ *
1215
+ * @returns A precondition conflict error with the recipient-not-found error code.
1216
+ */ function notificationBoxRecipientDoesNotExistsError() {
1217
+ return preconditionConflictError({
1218
+ message: "An existing NotificationBox recipient for the target does not exist. You must pass insert=true to create a new recipient.",
1219
+ code: NOTIFICATION_BOX_RECIPIENT_DOES_NOT_EXIST_ERROR_CODE
1220
+ });
1221
+ }
1222
+ /**
1223
+ * Creates an error indicating that a health check was run again before its throttle window passed.
1224
+ *
1225
+ * Thrown by the {@link NotificationUser} health check when the stored check is too recent.
1226
+ *
1227
+ * @param nextRunAt - The time the next health check may be run.
1228
+ * @returns A precondition conflict error with the health-check-throttled error code.
1229
+ */ function notificationUserHealthCheckThrottledError(nextRunAt) {
1230
+ return preconditionConflictError({
1231
+ message: "A notification delivery health check was run too recently. The next one can be run at ".concat(nextRunAt.toISOString(), "."),
1232
+ code: NOTIFICATION_USER_HEALTH_CHECK_THROTTLED_ERROR_CODE,
1233
+ data: {
1234
+ // serialized: the error's data travels to the client as JSON
1235
+ nextRunAt: nextRunAt.toISOString()
1236
+ }
1237
+ });
1238
+ }
1239
+ /**
1240
+ * Creates an error indicating that a test message was requested again before its throttle window passed.
1241
+ *
1242
+ * Thrown by the {@link NotificationUser} health check when a probe was dispatched too recently. Separate
1243
+ * from {@link notificationUserHealthCheckThrottledError}: running the check does not consume the test
1244
+ * message allowance.
1245
+ *
1246
+ * @param nextProbeAt - The time the next test message may be dispatched.
1247
+ * @returns A precondition conflict error with the probe-throttled error code.
1248
+ */ function notificationUserHealthCheckProbeThrottledError(nextProbeAt) {
1249
+ return preconditionConflictError({
1250
+ message: "A notification delivery test message was sent too recently. The next one can be sent at ".concat(nextProbeAt.toISOString(), "."),
1251
+ code: NOTIFICATION_USER_HEALTH_CHECK_PROBE_THROTTLED_ERROR_CODE,
1252
+ data: {
1253
+ // serialized: the error's data travels to the client as JSON
1254
+ nextProbeAt: nextProbeAt.toISOString()
1255
+ }
1256
+ });
1257
+ }
1258
+ /**
1259
+ * Creates an error indicating that pending probes were verified again before their throttle window passed.
1260
+ *
1261
+ * Thrown by the {@link NotificationUser} health check on a `verifyPendingProbesOnly` run that arrives too
1262
+ * soon after the last one. Separate from {@link notificationUserHealthCheckThrottledError}: verifying an
1263
+ * in-flight test message is a poll, so it neither answers to nor consumes the user's run allowance.
1264
+ *
1265
+ * @param nextVerifyAt - The time the next verification may be run.
1266
+ * @returns A precondition conflict error with the verify-throttled error code.
1267
+ */ function notificationUserHealthCheckVerifyThrottledError(nextVerifyAt) {
1268
+ return preconditionConflictError({
1269
+ message: "The pending notification delivery test messages were verified too recently. The next verification can be run at ".concat(nextVerifyAt.toISOString(), "."),
1270
+ code: NOTIFICATION_USER_HEALTH_CHECK_VERIFY_THROTTLED_ERROR_CODE,
1271
+ data: {
1272
+ // serialized: the error's data travels to the client as JSON
1273
+ nextVerifyAt: nextVerifyAt.toISOString()
1274
+ }
1275
+ });
1276
+ }
1277
+ /**
1278
+ * Creates an error indicating that the given UID does not correspond to an existing Firebase Auth user.
1279
+ *
1280
+ * Thrown during {@link NotificationUser} creation when the provided UID cannot be found in Firebase Auth.
1281
+ *
1282
+ * @param uid - The Firebase Auth user ID that was not found.
1283
+ * @returns A precondition conflict error with the invalid-uid error code.
1284
+ */ function notificationUserInvalidUidForCreateError(uid) {
1285
+ return preconditionConflictError({
1286
+ message: "The user with the uid '".concat(uid, "' does not exist. Cannot create a NotificationUser for them."),
1287
+ code: NOTIFICATION_USER_INVALID_UID_FOR_CREATE_ERROR_CODE,
1288
+ data: {
1289
+ uid: uid
1290
+ }
1291
+ });
1292
+ }
1293
+ /**
1294
+ * Creates an error indicating that the user has blocked themselves from being added as a recipient.
1295
+ *
1296
+ * Thrown when a {@link NotificationUser} has the `bk` (blocked-from-add) flag set on their config
1297
+ * and an operation attempts to insert them into a {@link NotificationBox}'s recipient list.
1298
+ *
1299
+ * @param uid - The Firebase Auth user ID of the blocked user.
1300
+ * @returns A precondition conflict error with the blocked-from-add error code.
1301
+ */ function notificationUserBlockedFromBeingAddedToRecipientsError(uid) {
1302
+ return preconditionConflictError({
1303
+ message: "The user with the uid '".concat(uid, "' has blocked themselves from from being added recipients."),
1304
+ code: NOTIFICATION_USER_BLOCKED_FROM_BEING_ADD_TO_RECIPIENTS_ERROR_CODE,
1305
+ data: {
1306
+ uid: uid
1307
+ }
1308
+ });
1309
+ }
1310
+ /**
1311
+ * Creates an error indicating that the user has locked their notification configuration from external updates.
1312
+ *
1313
+ * Thrown when a {@link NotificationUser} has the `lk` (locked) flag set on their config
1314
+ * and an operation attempts to modify their recipient settings on a {@link NotificationBox}.
1315
+ *
1316
+ * @param uid - The Firebase Auth user ID of the locked user.
1317
+ * @returns A precondition conflict error with the locked-config error code.
1318
+ */ function notificationUserLockedConfigFromBeingUpdatedError(uid) {
1319
+ return preconditionConflictError({
1320
+ message: "The user with the uid '".concat(uid, "' has locked their config from being updated."),
1321
+ code: NOTIFICATION_USER_LOCKED_CONFIG_FROM_BEING_UPDATED_ERROR_CODE,
1322
+ data: {
1323
+ uid: uid
1324
+ }
1325
+ });
1326
+ }
1327
+
1328
+ function _array_like_to_array$a(arr, len) {
1329
+ if (len == null || len > arr.length) len = arr.length;
1330
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1331
+ return arr2;
1332
+ }
1333
+ function _array_with_holes$4(arr) {
1334
+ if (Array.isArray(arr)) return arr;
1335
+ }
1336
+ function _array_without_holes$8(arr) {
1337
+ if (Array.isArray(arr)) return _array_like_to_array$a(arr);
1338
+ }
1339
+ function asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, key, arg) {
1340
+ try {
1341
+ var info = gen[key](arg);
1342
+ var value = info.value;
1343
+ } catch (error) {
1344
+ reject(error);
1345
+ return;
1346
+ }
1347
+ if (info.done) {
1348
+ resolve(value);
1349
+ } else {
1350
+ Promise.resolve(value).then(_next, _throw);
1351
+ }
1352
+ }
1353
+ function _async_to_generator$h(fn) {
1354
+ return function() {
1355
+ var self = this, args = arguments;
1356
+ return new Promise(function(resolve, reject) {
1357
+ var gen = fn.apply(self, args);
1358
+ function _next(value) {
1359
+ asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, "next", value);
1360
+ }
1361
+ function _throw(err) {
1362
+ asyncGeneratorStep$h(gen, resolve, reject, _next, _throw, "throw", err);
1363
+ }
1364
+ _next(undefined);
1365
+ });
1366
+ };
1367
+ }
1368
+ function _define_property$f(obj, key, value) {
1369
+ if (key in obj) {
1370
+ Object.defineProperty(obj, key, {
1371
+ value: value,
1372
+ enumerable: true,
1373
+ configurable: true,
1374
+ writable: true
1375
+ });
1376
+ } else {
1377
+ obj[key] = value;
1378
+ }
1379
+ return obj;
1380
+ }
1381
+ function _iterable_to_array$8(iter) {
1382
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1383
+ }
1384
+ function _iterable_to_array_limit$4(arr, i) {
1385
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
1386
+ if (_i == null) return;
1387
+ var _arr = [];
1388
+ var _n = true;
1389
+ var _d = false;
1390
+ var _s, _e;
1391
+ try {
1392
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
1393
+ _arr.push(_s.value);
1394
+ if (i && _arr.length === i) break;
1395
+ }
1396
+ } catch (err) {
1397
+ _d = true;
1398
+ _e = err;
1399
+ } finally{
1400
+ try {
1401
+ if (!_n && _i["return"] != null) _i["return"]();
1402
+ } finally{
1403
+ if (_d) throw _e;
1404
+ }
1405
+ }
1406
+ return _arr;
1407
+ }
1408
+ function _non_iterable_rest$4() {
1409
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1410
+ }
1411
+ function _non_iterable_spread$8() {
1412
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1413
+ }
1414
+ function _object_spread$e(target) {
1415
+ for(var i = 1; i < arguments.length; i++){
1416
+ var source = arguments[i] != null ? arguments[i] : {};
1417
+ var ownKeys = Object.keys(source);
1418
+ if (typeof Object.getOwnPropertySymbols === "function") {
1419
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
1420
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
1421
+ }));
1422
+ }
1423
+ ownKeys.forEach(function(key) {
1424
+ _define_property$f(target, key, source[key]);
1425
+ });
1426
+ }
1427
+ return target;
1428
+ }
1429
+ function ownKeys$d(object, enumerableOnly) {
1430
+ var keys = Object.keys(object);
1431
+ if (Object.getOwnPropertySymbols) {
1432
+ var symbols = Object.getOwnPropertySymbols(object);
1433
+ keys.push.apply(keys, symbols);
1434
+ }
1435
+ return keys;
1436
+ }
1437
+ function _object_spread_props$d(target, source) {
1438
+ source = source != null ? source : {};
1439
+ if (Object.getOwnPropertyDescriptors) {
1440
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
1441
+ } else {
1442
+ ownKeys$d(Object(source)).forEach(function(key) {
1443
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
1444
+ });
1445
+ }
1446
+ return target;
1447
+ }
1448
+ function _sliced_to_array$4(arr, i) {
1449
+ return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$a(arr, i) || _non_iterable_rest$4();
1450
+ }
1451
+ function _to_consumable_array$8(arr) {
1452
+ return _array_without_holes$8(arr) || _iterable_to_array$8(arr) || _unsupported_iterable_to_array$a(arr) || _non_iterable_spread$8();
1453
+ }
1454
+ function _unsupported_iterable_to_array$a(o, minLen) {
1455
+ if (!o) return;
1456
+ if (typeof o === "string") return _array_like_to_array$a(o, minLen);
1457
+ var n = Object.prototype.toString.call(o).slice(8, -1);
1458
+ if (n === "Object" && o.constructor) n = o.constructor.name;
1459
+ if (n === "Map" || n === "Set") return Array.from(n);
1460
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$a(o, minLen);
1461
+ }
1462
+ function _ts_generator$h(thisArg, body) {
1463
+ var f, y, t, _ = {
1464
+ label: 0,
1465
+ sent: function() {
1466
+ if (t[0] & 1) throw t[1];
1467
+ return t[1];
1468
+ },
1469
+ trys: [],
1470
+ ops: []
1471
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
1472
+ return d(g, "next", {
1473
+ value: verb(0)
1474
+ }), d(g, "throw", {
1475
+ value: verb(1)
1476
+ }), d(g, "return", {
1477
+ value: verb(2)
1478
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
1479
+ value: function() {
1480
+ return this;
1481
+ }
1482
+ }), g;
1483
+ function verb(n) {
1484
+ return function(v) {
1485
+ return step([
1486
+ n,
1487
+ v
1488
+ ]);
1489
+ };
1490
+ }
1491
+ function step(op) {
1492
+ if (f) throw new TypeError("Generator is already executing.");
1493
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
1494
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
1495
+ if (y = 0, t) op = [
1496
+ op[0] & 2,
1497
+ t.value
1498
+ ];
1499
+ switch(op[0]){
1500
+ case 0:
1501
+ case 1:
1502
+ t = op;
1503
+ break;
1504
+ case 4:
1505
+ _.label++;
1506
+ return {
1507
+ value: op[1],
1508
+ done: false
1509
+ };
1510
+ case 5:
1511
+ _.label++;
1512
+ y = op[1];
1513
+ op = [
1514
+ 0
1515
+ ];
1516
+ continue;
1517
+ case 7:
1518
+ op = _.ops.pop();
1519
+ _.trys.pop();
1520
+ continue;
1521
+ default:
1522
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
1523
+ _ = 0;
1524
+ continue;
1525
+ }
1526
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
1527
+ _.label = op[1];
1528
+ break;
1529
+ }
1530
+ if (op[0] === 6 && _.label < t[1]) {
1531
+ _.label = t[1];
1532
+ t = op;
1533
+ break;
1534
+ }
1535
+ if (t && _.label < t[2]) {
1536
+ _.label = t[2];
1537
+ _.ops.push(op);
1538
+ break;
1539
+ }
1540
+ if (t[2]) _.ops.pop();
1541
+ _.trys.pop();
1542
+ continue;
1543
+ }
1544
+ op = body.call(thisArg, _);
1545
+ } catch (e) {
1546
+ op = [
1547
+ 6,
1548
+ e
1549
+ ];
1550
+ y = 0;
1551
+ } finally{
1552
+ f = t = 0;
1553
+ }
1554
+ if (op[0] & 5) throw op[1];
1555
+ return {
1556
+ value: op[0] ? op[1] : void 0,
1557
+ done: true
1558
+ };
1559
+ }
1560
+ }
1561
+ /**
1562
+ * The maximum number of the user's notification boxes to inspect in a single health check.
1563
+ *
1564
+ * A user can be subscribed to an unbounded number of boxes; a health check is interactive, so it
1565
+ * samples rather than walking all of them.
1566
+ */ var DEFAULT_MAX_NOTIFICATION_BOXES_TO_INSPECT_PER_HEALTH_CHECK = 10;
1567
+ /**
1568
+ * Issue codes that describe a delivery probe rather than a configuration finding.
1569
+ *
1570
+ * A verify-only run refreshes these and carries every other finding forward unchanged.
1571
+ */ var PROBE_NOTIFICATION_HEALTH_CHECK_ISSUE_CODES = new Set([
1572
+ KnownNotificationHealthCheckIssueCode.PROBE_PENDING,
1573
+ KnownNotificationHealthCheckIssueCode.PROBE_DELIVERED,
1574
+ KnownNotificationHealthCheckIssueCode.PROBE_FAILED,
1575
+ KnownNotificationHealthCheckIssueCode.PROBE_DISPATCH_FAILED
1576
+ ]);
1577
+ /**
1578
+ * Factory for the `healthCheck` action on a {@link NotificationUser}.
1579
+ *
1580
+ * @param context - The notification server actions context.
1581
+ * @returns A transform-and-validate function that runs a delivery health check for a notification user.
1582
+ */ function notificationUserHealthCheckFactory(context) {
1583
+ var firebaseServerActionTransformFunctionFactory = context.firebaseServerActionTransformFunctionFactory, notificationBoxCollection = context.notificationBoxCollection, notificationSendService = context.notificationSendService, authService = context.authService, notificationUserHealthCheckConfig = context.notificationUserHealthCheckConfig;
1584
+ var probeThrottleMinutes = notificationUserHealthCheckConfig === null || notificationUserHealthCheckConfig === void 0 ? void 0 : notificationUserHealthCheckConfig.probeThrottleMinutes;
1585
+ var runThrottleMinutes = notificationUserHealthCheckConfig === null || notificationUserHealthCheckConfig === void 0 ? void 0 : notificationUserHealthCheckConfig.runThrottleMinutes;
1586
+ var verifyThrottleSeconds = notificationUserHealthCheckConfig === null || notificationUserHealthCheckConfig === void 0 ? void 0 : notificationUserHealthCheckConfig.verifyThrottleSeconds;
1587
+ return firebaseServerActionTransformFunctionFactory(notificationUserHealthCheckParamsType, function(params) {
1588
+ return _async_to_generator$h(function() {
1589
+ var inputMethods, inputSendProbe, inputVerifyPendingProbesOnly, inputNotificationTemplateType, inputSkipSubscriptionChecks, inputForce, inputReturnFullHealthCheck, sendProbe, verifyPendingProbesOnly, skipSubscriptionChecks, force;
1590
+ return _ts_generator$h(this, function(_state) {
1591
+ inputMethods = params.methods, inputSendProbe = params.sendProbe, inputVerifyPendingProbesOnly = params.verifyPendingProbesOnly, inputNotificationTemplateType = params.notificationTemplateType, inputSkipSubscriptionChecks = params.skipSubscriptionChecks, inputForce = params.force, inputReturnFullHealthCheck = params.returnFullHealthCheck;
1592
+ sendProbe = inputSendProbe === true;
1593
+ verifyPendingProbesOnly = inputVerifyPendingProbesOnly === true;
1594
+ skipSubscriptionChecks = inputSkipSubscriptionChecks === true || verifyPendingProbesOnly;
1595
+ // Privileged, and unverifiable from here: the action cannot see who is calling, so the API layer is
1596
+ // responsible for clearing this unless the caller is an admin. See NotificationUserHealthCheckParams.
1597
+ force = inputForce === true;
1598
+ return [
1599
+ 2,
1600
+ function(notificationUserDocument) {
1601
+ return _async_to_generator$h(function() {
1602
+ var _ref, _ref1, _ref2, _ref3, now, notificationUser, uid, previousHealthCheck, nextProbeAt, nextVerifyAt, nextRunAt, notificationTemplateType, authDetails, methodContexts, requestedMethods, methodContextsToCheck, subscriptions, _tmp, disabledMethodsByBox, accountIssues, probesDispatched, probesResolved, methodResults, freshResultsByMethod, mergedMethodResults, healthCheck, probedMethods, returnFullHealthCheck, returnedHealthCheck;
1603
+ return _ts_generator$h(this, function(_state) {
1604
+ switch(_state.label){
1605
+ case 0:
1606
+ now = new Date();
1607
+ return [
1608
+ 4,
1609
+ assertSnapshotData(notificationUserDocument)
1610
+ ];
1611
+ case 1:
1612
+ notificationUser = _state.sent();
1613
+ uid = notificationUser.uid, previousHealthCheck = notificationUser.hc;
1614
+ // Runs, test messages and verifications are throttled independently, because they cost different
1615
+ // things: a run reads provider state, a probe delivers a real message, and a verification only asks
1616
+ // a provider what became of a message already sent. None of them may consume another's allowance —
1617
+ // in particular, polling an in-flight test message must not push the user's next run out, which is
1618
+ // why a verify-only run answers to its own window and advances `vat` rather than `at` below.
1619
+ //
1620
+ // The probe window is scoped to the methods being probed, since each method has its own test
1621
+ // message action — a test email must not hold the test text message off.
1622
+ //
1623
+ // `force` skips every window, so an admin diagnosing someone else's delivery does not have to wait
1624
+ // out that user's own throttle.
1625
+ if (!force) {
1626
+ if (sendProbe) {
1627
+ nextProbeAt = notificationUserHealthCheckNextProbeAt({
1628
+ healthCheck: previousHealthCheck,
1629
+ methods: inputMethods,
1630
+ throttleMinutes: probeThrottleMinutes
1631
+ });
1632
+ if (nextProbeAt != null && isAfter(nextProbeAt, now)) {
1633
+ throw notificationUserHealthCheckProbeThrottledError(nextProbeAt);
1634
+ }
1635
+ } else if (verifyPendingProbesOnly) {
1636
+ nextVerifyAt = notificationUserHealthCheckNextVerifyAt({
1637
+ healthCheck: previousHealthCheck,
1638
+ throttleSeconds: verifyThrottleSeconds
1639
+ });
1640
+ if (nextVerifyAt != null && isAfter(nextVerifyAt, now)) {
1641
+ throw notificationUserHealthCheckVerifyThrottledError(nextVerifyAt);
1642
+ }
1643
+ } else {
1644
+ nextRunAt = notificationUserHealthCheckNextRunAt({
1645
+ healthCheck: previousHealthCheck,
1646
+ throttleMinutes: runThrottleMinutes
1647
+ });
1648
+ if (nextRunAt != null && isAfter(nextRunAt, now)) {
1649
+ throw notificationUserHealthCheckThrottledError(nextRunAt);
1650
+ }
1651
+ }
1652
+ }
1653
+ notificationTemplateType = inputNotificationTemplateType || DEFAULT_NOTIFICATION_TEMPLATE_TYPE;
1654
+ return [
1655
+ 4,
1656
+ authService.userContext(uid).loadDetails().catch(function() {
1657
+ return undefined;
1658
+ })
1659
+ ];
1660
+ case 2:
1661
+ authDetails = _state.sent();
1662
+ methodContexts = buildNotificationDeliveryMethodContexts({
1663
+ notificationUser: notificationUser,
1664
+ notificationSendService: notificationSendService,
1665
+ authEmail: authDetails === null || authDetails === void 0 ? void 0 : authDetails.email,
1666
+ authPhone: authDetails === null || authDetails === void 0 ? void 0 : authDetails.phoneNumber,
1667
+ uid: uid
1668
+ });
1669
+ requestedMethods = (inputMethods === null || inputMethods === void 0 ? void 0 : inputMethods.length) ? new Set(inputMethods) : undefined;
1670
+ methodContextsToCheck = methodContexts.filter(function(x) {
1671
+ return requestedMethods ? requestedMethods.has(x.method) : true;
1672
+ });
1673
+ if (!skipSubscriptionChecks) return [
1674
+ 3,
1675
+ 3
1676
+ ];
1677
+ _tmp = undefined;
1678
+ return [
1679
+ 3,
1680
+ 5
1681
+ ];
1682
+ case 3:
1683
+ return [
1684
+ 4,
1685
+ inspectNotificationUserSubscriptions({
1686
+ notificationUser: notificationUser,
1687
+ notificationBoxCollection: notificationBoxCollection,
1688
+ notificationTemplateType: notificationTemplateType
1689
+ })
1690
+ ];
1691
+ case 4:
1692
+ _tmp = _state.sent();
1693
+ _state.label = 5;
1694
+ case 5:
1695
+ subscriptions = _tmp;
1696
+ disabledMethodsByBox = (_ref = subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.issuesByMethod) !== null && _ref !== void 0 ? _ref : new Map();
1697
+ accountIssues = verifyPendingProbesOnly ? _to_consumable_array$8((_ref1 = previousHealthCheck === null || previousHealthCheck === void 0 ? void 0 : previousHealthCheck.is) !== null && _ref1 !== void 0 ? _ref1 : []) : //
1698
+ _to_consumable_array$8(notificationUserAccountIssues({
1699
+ notificationUser: notificationUser,
1700
+ accountDisabled: (authDetails === null || authDetails === void 0 ? void 0 : authDetails.disabled) === true,
1701
+ accountExists: authDetails != null
1702
+ })).concat(_to_consumable_array$8((_ref2 = subscriptions === null || subscriptions === void 0 ? void 0 : subscriptions.sharedIssues) !== null && _ref2 !== void 0 ? _ref2 : []));
1703
+ // MARK: per-method
1704
+ probesDispatched = 0;
1705
+ probesResolved = 0;
1706
+ return [
1707
+ 4,
1708
+ Promise.all(methodContextsToCheck.map(function(methodContext) {
1709
+ return _async_to_generator$h(function() {
1710
+ var _ref, _disabledMethodsByBox_get, method, target, label, sendServiceConfigured, healthCheckService, previousMethodResult, previousProbe, pendingProbe, providerHasSomethingToReport, willConsultProvider, issues, probe, // on a verify-only run only the probe findings are refreshed
1711
+ _issues, response, e, methodResult;
1712
+ return _ts_generator$h(this, function(_state) {
1713
+ switch(_state.label){
1714
+ case 0:
1715
+ method = methodContext.method, target = methodContext.target, label = methodContext.label, sendServiceConfigured = methodContext.sendServiceConfigured, healthCheckService = methodContext.healthCheckService;
1716
+ previousMethodResult = previousHealthCheck === null || previousHealthCheck === void 0 ? void 0 : previousHealthCheck.m.find(function(x) {
1717
+ return x.me === method;
1718
+ });
1719
+ previousProbe = previousMethodResult === null || previousMethodResult === void 0 ? void 0 : previousMethodResult.pr;
1720
+ pendingProbe = isPendingNotificationHealthCheckProbe(previousProbe) ? previousProbe : undefined;
1721
+ // A verify-only run is a poll of an in-flight test message, so it consults a provider only
1722
+ // where there is actually a probe to ask about. With nothing pending it costs no provider
1723
+ // calls at all, which is what makes it cheap enough to be polled on a short window.
1724
+ providerHasSomethingToReport = !verifyPendingProbesOnly || pendingProbe != null;
1725
+ // whether the provider will be consulted, and so whether fresh probe findings are coming
1726
+ willConsultProvider = healthCheckService != null && target != null && providerHasSomethingToReport;
1727
+ // A verify-only run carries the previous findings forward. The stale probe findings are only
1728
+ // dropped when the provider is actually going to replace them — otherwise the method would
1729
+ // lose its probe explanation while still reporting the probe itself.
1730
+ issues = verifyPendingProbesOnly ? ((_ref = previousMethodResult === null || previousMethodResult === void 0 ? void 0 : previousMethodResult.is) !== null && _ref !== void 0 ? _ref : []).filter(function(x) {
1731
+ return !(willConsultProvider && isProbeIssueCode(x.c));
1732
+ }) : _to_consumable_array$8(notificationDeliveryMethodConfigIssues({
1733
+ methodContext: methodContext,
1734
+ notificationUser: notificationUser,
1735
+ notificationTemplateType: notificationTemplateType
1736
+ })).concat(_to_consumable_array$8((_disabledMethodsByBox_get = disabledMethodsByBox.get(method)) !== null && _disabledMethodsByBox_get !== void 0 ? _disabledMethodsByBox_get : []));
1737
+ // keep any previously resolved probe visible unless the provider supplies a newer one
1738
+ probe = previousProbe;
1739
+ if (!(healthCheckService && target != null && providerHasSomethingToReport)) return [
1740
+ 3,
1741
+ 5
1742
+ ];
1743
+ _state.label = 1;
1744
+ case 1:
1745
+ _state.trys.push([
1746
+ 1,
1747
+ 3,
1748
+ ,
1749
+ 4
1750
+ ]);
1751
+ return [
1752
+ 4,
1753
+ healthCheckService.runHealthCheck({
1754
+ method: method,
1755
+ target: target,
1756
+ uid: uid,
1757
+ sendProbe: sendProbe,
1758
+ pendingProbe: pendingProbe,
1759
+ notificationTemplateType: notificationTemplateType,
1760
+ now: now
1761
+ })
1762
+ ];
1763
+ case 2:
1764
+ response = _state.sent();
1765
+ (_issues = issues).push.apply(_issues, _to_consumable_array$8(verifyPendingProbesOnly ? response.issues.filter(function(x) {
1766
+ return isProbeIssueCode(x.c);
1767
+ }) : response.issues));
1768
+ if (response.probe) {
1769
+ probe = response.probe;
1770
+ // A probe the provider gave no correlation id for cannot be told apart from the last one
1771
+ // by id, so the dispatch time is what settles whether this is a new attempt.
1772
+ if (response.probe.id !== (previousProbe === null || previousProbe === void 0 ? void 0 : previousProbe.id) || response.probe.at.getTime() !== (previousProbe === null || previousProbe === void 0 ? void 0 : previousProbe.at.getTime())) {
1773
+ probesDispatched += 1;
1774
+ } else if (pendingProbe && !isPendingNotificationHealthCheckProbe(response.probe)) {
1775
+ probesResolved += 1;
1776
+ }
1777
+ }
1778
+ return [
1779
+ 3,
1780
+ 4
1781
+ ];
1782
+ case 3:
1783
+ e = _state.sent();
1784
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SEND_SERVICE_HEALTH_CHECK_UNAVAILABLE, NotificationHealthCheckStatus.UNKNOWN, {
1785
+ message: "The ".concat(label.toLowerCase(), " provider could not be reached, so its delivery status is unknown."),
1786
+ fix: 'Try again in a few minutes. If this keeps happening, the delivery provider may be having an outage.',
1787
+ data: {
1788
+ error: "".concat(e)
1789
+ }
1790
+ }));
1791
+ return [
1792
+ 3,
1793
+ 4
1794
+ ];
1795
+ case 4:
1796
+ return [
1797
+ 3,
1798
+ 6
1799
+ ];
1800
+ case 5:
1801
+ if (sendServiceConfigured && !healthCheckService && !verifyPendingProbesOnly && target != null) {
1802
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SEND_SERVICE_HEALTH_CHECK_UNAVAILABLE, NotificationHealthCheckStatus.SKIPPED, {
1803
+ message: "".concat(label, " delivery could not be verified with the provider, so only your settings were checked."),
1804
+ data: {
1805
+ method: method
1806
+ }
1807
+ }));
1808
+ }
1809
+ _state.label = 6;
1810
+ case 6:
1811
+ methodResult = {
1812
+ me: method,
1813
+ s: rollupNotificationDeliveryHealthCheckResultStatus({
1814
+ is: issues,
1815
+ pr: probe
1816
+ }),
1817
+ tg: target == null ? undefined : String(target),
1818
+ is: issues,
1819
+ pr: probe,
1820
+ // whether probing is supported is only knowable here, so it is reported for a client that
1821
+ // offers a per-method test message action. A supporting provider with nothing to deliver to
1822
+ // still cannot be probed.
1823
+ pb: (healthCheckService === null || healthCheckService === void 0 ? void 0 : healthCheckService.supportsProbe) === true && target != null ? true : undefined
1824
+ };
1825
+ return [
1826
+ 2,
1827
+ methodResult
1828
+ ];
1829
+ }
1830
+ });
1831
+ })();
1832
+ }))
1833
+ ];
1834
+ case 6:
1835
+ methodResults = _state.sent();
1836
+ // A run can be scoped to a subset of methods (a probe poll usually is). Carry forward the
1837
+ // previous result for every method that was not re-checked so the stored health check stays a
1838
+ // complete picture rather than shrinking to whatever was last asked about.
1839
+ freshResultsByMethod = new Map(methodResults.map(function(x) {
1840
+ return [
1841
+ x.me,
1842
+ x
1843
+ ];
1844
+ }));
1845
+ mergedMethodResults = filterMaybeArrayValues(methodContexts.map(function(x) {
1846
+ var _freshResultsByMethod_get;
1847
+ return (_freshResultsByMethod_get = freshResultsByMethod.get(x.method)) !== null && _freshResultsByMethod_get !== void 0 ? _freshResultsByMethod_get : previousHealthCheck === null || previousHealthCheck === void 0 ? void 0 : previousHealthCheck.m.find(function(y) {
1848
+ return y.me === x.method;
1849
+ });
1850
+ }));
1851
+ healthCheck = {
1852
+ // A verify-only run refreshes the probe findings of the check that is already stored rather than
1853
+ // producing a new diagnosis, so it leaves `at` — and with it the user's run window — where it
1854
+ // was. Only `vat` moves, which is what a poll paces itself against.
1855
+ at: verifyPendingProbesOnly ? (_ref3 = previousHealthCheck === null || previousHealthCheck === void 0 ? void 0 : previousHealthCheck.at) !== null && _ref3 !== void 0 ? _ref3 : now : now,
1856
+ vat: now,
1857
+ s: rollupNotificationHealthCheckResultStatus({
1858
+ is: accountIssues,
1859
+ m: mergedMethodResults
1860
+ }),
1861
+ t: notificationTemplateType,
1862
+ is: accountIssues,
1863
+ m: mergedMethodResults
1864
+ };
1865
+ return [
1866
+ 4,
1867
+ notificationUserDocument.update({
1868
+ hc: healthCheck
1869
+ })
1870
+ ];
1871
+ case 7:
1872
+ _state.sent();
1873
+ // The complete check is what gets STORED; what comes BACK can be narrower. A probe run is about one
1874
+ // method's test message, and a client renders the report from the document anyway, so returning the
1875
+ // whole account's diagnosis is payload the caller did not ask for.
1876
+ //
1877
+ // The same predicate as the `pb` flag, so what is returned matches what the client was told it could
1878
+ // test. An empty set means nothing probe-capable was in scope, and then there is no narrower answer
1879
+ // to give than the diagnosis explaining why nothing was sent.
1880
+ probedMethods = new Set(sendProbe ? methodContextsToCheck.filter(function(x) {
1881
+ var _x_healthCheckService;
1882
+ return ((_x_healthCheckService = x.healthCheckService) === null || _x_healthCheckService === void 0 ? void 0 : _x_healthCheckService.supportsProbe) === true && x.target != null;
1883
+ }).map(function(x) {
1884
+ return x.method;
1885
+ }) : []);
1886
+ returnFullHealthCheck = inputReturnFullHealthCheck === true || !sendProbe || probedMethods.size === 0;
1887
+ returnedHealthCheck = returnFullHealthCheck ? healthCheck : narrowNotificationHealthCheckToMethods(healthCheck, probedMethods);
1888
+ return [
1889
+ 2,
1890
+ {
1891
+ healthCheck: returnedHealthCheck,
1892
+ probesDispatched: probesDispatched,
1893
+ probesResolved: probesResolved
1894
+ }
1895
+ ];
1896
+ }
1897
+ });
1898
+ })();
1899
+ }
1900
+ ];
1901
+ });
1902
+ })();
1903
+ });
390
1904
  }
391
1905
  /**
392
- * Creates an error indicating that a {@link NotificationBox} already exists for this model.
393
- *
394
- * Thrown when attempting to create a duplicate NotificationBox for a model that already has one.
395
- *
396
- * @returns A precondition conflict error with the box-exists error code.
397
- */ function notificationBoxExistsForModelError() {
398
- return preconditionConflictError({
399
- message: "A NotificationBox already exists for this model.",
400
- code: NOTIFICATION_BOX_EXISTS_FOR_MODEL_ERROR_CODE
1906
+ * Narrows a health check down to a subset of its delivery methods.
1907
+ *
1908
+ * Used for the result of a test message call, which is about the method it just sent through rather than
1909
+ * the whole account. `s` is re-rolled over the methods that are kept so the returned check is
1910
+ * self-consistent, and the account-wide findings are dropped — they are not what the call was about. Only
1911
+ * ever applied to the RETURNED value; the stored check keeps every method and finding.
1912
+ *
1913
+ * @param healthCheck - The complete check.
1914
+ * @param methods - The delivery methods to keep.
1915
+ * @returns The check, covering only those methods.
1916
+ */ function narrowNotificationHealthCheckToMethods(healthCheck, methods) {
1917
+ var m = healthCheck.m.filter(function(x) {
1918
+ return methods.has(x.me);
1919
+ });
1920
+ return _object_spread_props$d(_object_spread$e({}, healthCheck), {
1921
+ is: [],
1922
+ m: m,
1923
+ s: rollupNotificationHealthCheckResultStatus({
1924
+ is: [],
1925
+ m: m
1926
+ })
401
1927
  });
402
1928
  }
403
1929
  /**
404
- * Creates an error indicating that the target recipient does not exist on the {@link NotificationBox}.
405
- *
406
- * Thrown when attempting to update a recipient that is not registered on the box
407
- * and `insert=true` was not passed to allow creating a new recipient entry.
408
- *
409
- * @returns A precondition conflict error with the recipient-not-found error code.
410
- */ function notificationBoxRecipientDoesNotExistsError() {
411
- return preconditionConflictError({
412
- message: "An existing NotificationBox recipient for the target does not exist. You must pass insert=true to create a new recipient.",
413
- code: NOTIFICATION_BOX_RECIPIENT_DOES_NOT_EXIST_ERROR_CODE
414
- });
1930
+ * Builds the per-method view of what is configured and where each method would deliver to.
1931
+ *
1932
+ * Target resolution mirrors the send pipeline: a recipient's explicit override on their global or
1933
+ * default config wins, otherwise the value on their Firebase Auth record is used.
1934
+ *
1935
+ * @param input - The user, the configured send service, and their auth contact details.
1936
+ * @returns One context per delivery method, in report order.
1937
+ */ function buildNotificationDeliveryMethodContexts(input) {
1938
+ var _ref, _gc_e, _ref1, _gc_t;
1939
+ var notificationUser = input.notificationUser, notificationSendService = input.notificationSendService, authEmail = input.authEmail, authPhone = input.authPhone, uid = input.uid;
1940
+ var gc = notificationUser.gc, dc = notificationUser.dc;
1941
+ var emailSendService = notificationSendService.emailSendService, textSendService = notificationSendService.textSendService, notificationSummarySendService = notificationSendService.notificationSummarySendService, notificationSummaryIdForUidFunction = notificationSendService.notificationSummaryIdForUidFunction;
1942
+ var emailContext = {
1943
+ method: NotificationDeliveryMethod.EMAIL,
1944
+ label: 'Email',
1945
+ sendServiceConfigured: emailSendService != null,
1946
+ healthCheckService: emailSendService === null || emailSendService === void 0 ? void 0 : emailSendService.healthCheckService,
1947
+ target: (_ref = (_gc_e = gc.e) !== null && _gc_e !== void 0 ? _gc_e : dc.e) !== null && _ref !== void 0 ? _ref : authEmail,
1948
+ readTemplateConfigFlag: function readTemplateConfigFlag(x) {
1949
+ return x.se;
1950
+ },
1951
+ requiresExplicitOptIn: false
1952
+ };
1953
+ var textContext = {
1954
+ method: NotificationDeliveryMethod.TEXT,
1955
+ label: 'Text message',
1956
+ sendServiceConfigured: textSendService != null,
1957
+ healthCheckService: textSendService === null || textSendService === void 0 ? void 0 : textSendService.healthCheckService,
1958
+ target: (_ref1 = (_gc_t = gc.t) !== null && _gc_t !== void 0 ? _gc_t : dc.t) !== null && _ref1 !== void 0 ? _ref1 : authPhone,
1959
+ readTemplateConfigFlag: function readTemplateConfigFlag(x) {
1960
+ return x.st;
1961
+ },
1962
+ // the send pipeline only texts recipients who have explicitly opted in
1963
+ requiresExplicitOptIn: true
1964
+ };
1965
+ var summaryContext = {
1966
+ method: NotificationDeliveryMethod.NOTIFICATION_SUMMARY,
1967
+ label: 'In-app notification',
1968
+ sendServiceConfigured: notificationSummarySendService != null,
1969
+ healthCheckService: notificationSummarySendService === null || notificationSummarySendService === void 0 ? void 0 : notificationSummarySendService.healthCheckService,
1970
+ target: notificationSummaryIdForUidFunction === null || notificationSummaryIdForUidFunction === void 0 ? void 0 : notificationSummaryIdForUidFunction(uid),
1971
+ readTemplateConfigFlag: function readTemplateConfigFlag(x) {
1972
+ return x.sn;
1973
+ },
1974
+ requiresExplicitOptIn: false
1975
+ };
1976
+ var pushContext = {
1977
+ method: NotificationDeliveryMethod.PUSH,
1978
+ label: 'Push notification',
1979
+ // push delivery is not part of the send service yet
1980
+ sendServiceConfigured: false,
1981
+ readTemplateConfigFlag: function readTemplateConfigFlag(x) {
1982
+ return x.sp;
1983
+ },
1984
+ requiresExplicitOptIn: true
1985
+ };
1986
+ return [
1987
+ emailContext,
1988
+ textContext,
1989
+ summaryContext,
1990
+ pushContext
1991
+ ];
415
1992
  }
416
1993
  /**
417
- * Creates an error indicating that the given UID does not correspond to an existing Firebase Auth user.
418
- *
419
- * Thrown during {@link NotificationUser} creation when the provided UID cannot be found in Firebase Auth.
420
- *
421
- * @param uid - The Firebase Auth user ID that was not found.
422
- * @returns A precondition conflict error with the invalid-uid error code.
423
- */ function notificationUserInvalidUidForCreateError(uid) {
424
- return preconditionConflictError({
425
- message: "The user with the uid '".concat(uid, "' does not exist. Cannot create a NotificationUser for them."),
426
- code: NOTIFICATION_USER_INVALID_UID_FOR_CREATE_ERROR_CODE,
427
- data: {
428
- uid: uid
1994
+ * Evaluates the checks that suppress every delivery method at once, rather than any single one.
1995
+ *
1996
+ * Reported at the top level of the health check so a single account-wide problem does not appear once
1997
+ * per delivery method.
1998
+ *
1999
+ * @param input - The user and the state of their auth record.
2000
+ * @returns The account-wide findings.
2001
+ */ function notificationUserAccountIssues(input) {
2002
+ var notificationUser = input.notificationUser, accountDisabled = input.accountDisabled, accountExists = input.accountExists;
2003
+ var gc = notificationUser.gc, dc = notificationUser.dc, b = notificationUser.b, x = notificationUser.x;
2004
+ var issues = [];
2005
+ if (!accountExists) {
2006
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NO_DELIVERY_TARGET, NotificationHealthCheckStatus.ERROR, {
2007
+ message: 'Your sign-in account could not be read, so no contact details could be resolved.',
2008
+ fix: 'Contact support with this report.'
2009
+ }));
2010
+ return issues; // nothing else can be determined without the auth record
2011
+ }
2012
+ if (accountDisabled) {
2013
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.RECIPIENT_DISABLED, NotificationHealthCheckStatus.ERROR, {
2014
+ message: 'Your account is disabled, so no notifications are being sent to you.',
2015
+ fix: 'Contact support to have your account re-enabled.'
2016
+ }));
2017
+ }
2018
+ // The global config is applied as a final override at send time, so it is the decisive one.
2019
+ var flagScopes = [
2020
+ [
2021
+ 'global',
2022
+ gc.f
2023
+ ],
2024
+ [
2025
+ 'default',
2026
+ dc.f
2027
+ ]
2028
+ ];
2029
+ flagScopes.forEach(function(param) {
2030
+ var _param = _sliced_to_array$4(param, 2), scope = _param[0], flag = _param[1];
2031
+ if (flag === NotificationBoxRecipientFlag.OPT_OUT) {
2032
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.RECIPIENT_OPTED_OUT, NotificationHealthCheckStatus.ERROR, {
2033
+ message: 'You have opted out of notifications, so none are being sent to you.',
2034
+ fix: 'Turn notifications back on in your notification settings.',
2035
+ data: {
2036
+ scope: scope
2037
+ }
2038
+ }));
2039
+ } else if (flag === NotificationBoxRecipientFlag.DISABLED) {
2040
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.RECIPIENT_DISABLED, NotificationHealthCheckStatus.ERROR, {
2041
+ message: 'Notifications have been turned off for your account, so none are being sent to you.',
2042
+ fix: 'Contact support to have notifications re-enabled.',
2043
+ data: {
2044
+ scope: scope
2045
+ }
2046
+ }));
429
2047
  }
430
2048
  });
2049
+ if (!b.length) {
2050
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NO_NOTIFICATION_BOXES, NotificationHealthCheckStatus.WARNING, {
2051
+ message: 'You are not subscribed to notifications for anything yet, so there is nothing to notify you about.',
2052
+ fix: 'This usually resolves itself once you are added to a group or record that sends notifications.'
2053
+ }));
2054
+ }
2055
+ if (x.length) {
2056
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NOTIFICATION_BOX_EXCLUSIONS, NotificationHealthCheckStatus.WARNING, {
2057
+ message: "Notifications from ".concat(x.length, " subscription").concat(x.length === 1 ? '' : 's', " are being suppressed for your account."),
2058
+ fix: 'Contact support if you expect notifications from one of these.',
2059
+ data: {
2060
+ exclusions: takeFront(x, 10)
2061
+ }
2062
+ }));
2063
+ }
2064
+ return issues;
431
2065
  }
432
2066
  /**
433
- * Creates an error indicating that the user has blocked themselves from being added as a recipient.
434
- *
435
- * Thrown when a {@link NotificationUser} has the `bk` (blocked-from-add) flag set on their config
436
- * and an operation attempts to insert them into a {@link NotificationBox}'s recipient list.
437
- *
438
- * @param uid - The Firebase Auth user ID of the blocked user.
439
- * @returns A precondition conflict error with the blocked-from-add error code.
440
- */ function notificationUserBlockedFromBeingAddedToRecipientsError(uid) {
441
- return preconditionConflictError({
442
- message: "The user with the uid '".concat(uid, "' has blocked themselves from from being added recipients."),
443
- code: NOTIFICATION_USER_BLOCKED_FROM_BEING_ADD_TO_RECIPIENTS_ERROR_CODE,
444
- data: {
445
- uid: uid
446
- }
447
- });
2067
+ * Evaluates the user's own configuration for a single delivery method.
2068
+ *
2069
+ * These mirror the gates in {@link expandNotificationRecipients}, so each finding corresponds to a real
2070
+ * reason the send pipeline would drop a message.
2071
+ *
2072
+ * @param input - The delivery method context, the user, and the template type being evaluated.
2073
+ * @returns The findings for the method.
2074
+ */ function notificationDeliveryMethodConfigIssues(input) {
2075
+ var _gc_c, _dc_c;
2076
+ var methodContext = input.methodContext, notificationUser = input.notificationUser, notificationTemplateType = input.notificationTemplateType;
2077
+ var method = methodContext.method, label = methodContext.label, sendServiceConfigured = methodContext.sendServiceConfigured, target = methodContext.target, readTemplateConfigFlag = methodContext.readTemplateConfigFlag, requiresExplicitOptIn = methodContext.requiresExplicitOptIn;
2078
+ var gc = notificationUser.gc, dc = notificationUser.dc;
2079
+ var issues = [];
2080
+ if (!sendServiceConfigured) {
2081
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SEND_SERVICE_NOT_CONFIGURED, NotificationHealthCheckStatus.SKIPPED, {
2082
+ message: "".concat(label, " notifications are not enabled on this system."),
2083
+ data: {
2084
+ method: method
2085
+ }
2086
+ }));
2087
+ return issues; // nothing else about this method is meaningful
2088
+ }
2089
+ if (target == null) {
2090
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NO_DELIVERY_TARGET, NotificationHealthCheckStatus.ERROR, {
2091
+ message: "There is no ".concat(label.toLowerCase(), " destination on your account, so nothing can be delivered."),
2092
+ fix: method === NotificationDeliveryMethod.EMAIL ? 'Add an email address to your account.' : 'Add a phone number to your notification settings.',
2093
+ data: {
2094
+ method: method
2095
+ }
2096
+ }));
2097
+ return issues; // every remaining check is about a destination that does not exist
2098
+ }
2099
+ // per-template channel flags on the global and default configs
2100
+ var globalTemplateFlag = readEffectiveTemplateConfigFlag((_gc_c = gc.c) === null || _gc_c === void 0 ? void 0 : _gc_c[notificationTemplateType], readTemplateConfigFlag);
2101
+ var defaultTemplateFlag = readEffectiveTemplateConfigFlag((_dc_c = dc.c) === null || _dc_c === void 0 ? void 0 : _dc_c[notificationTemplateType], readTemplateConfigFlag);
2102
+ if (globalTemplateFlag === false) {
2103
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.METHOD_DISABLED_GLOBALLY, NotificationHealthCheckStatus.ERROR, {
2104
+ message: "".concat(label, " is switched off for you across every notification, which overrides all other settings."),
2105
+ fix: "Turn ".concat(label.toLowerCase(), " back on in your notification settings."),
2106
+ data: {
2107
+ method: method,
2108
+ notificationTemplateType: notificationTemplateType,
2109
+ scope: 'global'
2110
+ }
2111
+ }));
2112
+ } else if (defaultTemplateFlag === false) {
2113
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.METHOD_DISABLED_FOR_TEMPLATE, NotificationHealthCheckStatus.WARNING, {
2114
+ message: "".concat(label, " is switched off in your default settings for this kind of notification."),
2115
+ fix: "Turn ".concat(label.toLowerCase(), " back on for this notification type."),
2116
+ data: {
2117
+ method: method,
2118
+ notificationTemplateType: notificationTemplateType,
2119
+ scope: 'default'
2120
+ }
2121
+ }));
2122
+ } else if (requiresExplicitOptIn && globalTemplateFlag !== true && defaultTemplateFlag !== true) {
2123
+ issues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.METHOD_DISABLED_FOR_TEMPLATE, NotificationHealthCheckStatus.WARNING, {
2124
+ message: "".concat(label, " is only sent to people who have turned it on, and you have not turned it on."),
2125
+ fix: "Turn ".concat(label.toLowerCase(), " on in your notification settings."),
2126
+ data: {
2127
+ method: method,
2128
+ notificationTemplateType: notificationTemplateType,
2129
+ requiresExplicitOptIn: true
2130
+ }
2131
+ }));
2132
+ }
2133
+ return issues;
448
2134
  }
449
2135
  /**
450
- * Creates an error indicating that the user has locked their notification configuration from external updates.
2136
+ * Resolves a single channel flag out of a template config, applying the `sd` send-default fallback.
451
2137
  *
452
- * Thrown when a {@link NotificationUser} has the `lk` (locked) flag set on their config
453
- * and an operation attempts to modify their recipient settings on a {@link NotificationBox}.
2138
+ * @param config - The template config to read, if the user has one for this template type.
2139
+ * @param readFlag - Selects the delivery method's flag out of an effective config.
2140
+ * @returns The flag's effective value, or undefined when the config leaves it unset.
2141
+ */ function readEffectiveTemplateConfigFlag(config, readFlag) {
2142
+ return config ? readFlag(effectiveNotificationBoxRecipientTemplateConfig(config)) : undefined;
2143
+ }
2144
+ /**
2145
+ * Inspects the user's notification box subscriptions.
2146
+ *
2147
+ * This catches the failure modes that live between the user's own settings and the boxes that actually
2148
+ * drive delivery: a config that never synced across, a box that was never initialized, or a recipient
2149
+ * entry that was disabled or excluded on the box side.
2150
+ *
2151
+ * @param input - The user, the notification box collection, and the template type being evaluated.
2152
+ * @returns The account-wide findings plus any findings scoped to a single delivery method.
2153
+ */ function inspectNotificationUserSubscriptions(input) {
2154
+ return _async_to_generator$h(function() {
2155
+ var notificationUser, notificationBoxCollection, notificationTemplateType, uid, bc, ns, sharedIssues, issuesByMethod, activeConfigs, configsToInspect, documentAccessor, boxPairs, uninitializedBoxIds, invalidBoxIds, unsyncedBoxIds, disabledBoxIdsByMethod;
2156
+ return _ts_generator$h(this, function(_state) {
2157
+ switch(_state.label){
2158
+ case 0:
2159
+ notificationUser = input.notificationUser, notificationBoxCollection = input.notificationBoxCollection, notificationTemplateType = input.notificationTemplateType;
2160
+ uid = notificationUser.uid, bc = notificationUser.bc, ns = notificationUser.ns;
2161
+ sharedIssues = [];
2162
+ issuesByMethod = new Map();
2163
+ if (ns) {
2164
+ sharedIssues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NEEDS_CONFIG_SYNC, NotificationHealthCheckStatus.WARNING, {
2165
+ message: 'Your notification settings have not finished saving everywhere yet.',
2166
+ fix: 'This usually clears itself within a few minutes. Re-run this check to confirm.'
2167
+ }));
2168
+ }
2169
+ activeConfigs = bc.filter(function(x) {
2170
+ return !x.rm;
2171
+ });
2172
+ configsToInspect = takeFront(activeConfigs, DEFAULT_MAX_NOTIFICATION_BOXES_TO_INSPECT_PER_HEALTH_CHECK);
2173
+ if (!configsToInspect.length) {
2174
+ return [
2175
+ 2,
2176
+ {
2177
+ sharedIssues: sharedIssues,
2178
+ issuesByMethod: issuesByMethod
2179
+ }
2180
+ ];
2181
+ }
2182
+ documentAccessor = notificationBoxCollection.documentAccessor();
2183
+ return [
2184
+ 4,
2185
+ Promise.all(configsToInspect.map(function(config) {
2186
+ return _async_to_generator$h(function() {
2187
+ var box;
2188
+ return _ts_generator$h(this, function(_state) {
2189
+ switch(_state.label){
2190
+ case 0:
2191
+ return [
2192
+ 4,
2193
+ documentAccessor.loadDocumentForId(config.nb).snapshotData().catch(function() {
2194
+ return undefined;
2195
+ })
2196
+ ];
2197
+ case 1:
2198
+ box = _state.sent();
2199
+ return [
2200
+ 2,
2201
+ {
2202
+ config: config,
2203
+ box: box
2204
+ }
2205
+ ];
2206
+ }
2207
+ });
2208
+ })();
2209
+ }))
2210
+ ];
2211
+ case 1:
2212
+ boxPairs = _state.sent();
2213
+ uninitializedBoxIds = [];
2214
+ invalidBoxIds = [];
2215
+ unsyncedBoxIds = [];
2216
+ disabledBoxIdsByMethod = new Map();
2217
+ boxPairs.forEach(function(param) {
2218
+ var config = param.config, box = param.box;
2219
+ if (!box) {
2220
+ return; // a box that has never been created simply has nothing to send
2221
+ }
2222
+ if (box.fi) {
2223
+ invalidBoxIds.push(config.nb);
2224
+ } else if (box.s) {
2225
+ uninitializedBoxIds.push(config.nb);
2226
+ }
2227
+ var boxRecipient = box.r.find(function(x) {
2228
+ return x.uid === uid;
2229
+ });
2230
+ if (!boxRecipient || config.ns) {
2231
+ unsyncedBoxIds.push(config.nb);
2232
+ }
2233
+ collectDisabledMethodsForBoxRecipient({
2234
+ boxRecipient: boxRecipient,
2235
+ config: config,
2236
+ notificationTemplateType: notificationTemplateType
2237
+ }).forEach(function(method) {
2238
+ var _disabledBoxIdsByMethod_get;
2239
+ var existing = (_disabledBoxIdsByMethod_get = disabledBoxIdsByMethod.get(method)) !== null && _disabledBoxIdsByMethod_get !== void 0 ? _disabledBoxIdsByMethod_get : [];
2240
+ existing.push(config.nb);
2241
+ disabledBoxIdsByMethod.set(method, existing);
2242
+ });
2243
+ });
2244
+ if (invalidBoxIds.length) {
2245
+ sharedIssues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SUBSCRIPTION_BROKEN, NotificationHealthCheckStatus.ERROR, {
2246
+ message: "".concat(invalidBoxIds.length, " of your subscriptions are broken and will not send notifications."),
2247
+ fix: 'Contact support with this report so the subscription can be repaired.',
2248
+ data: {
2249
+ notificationBoxIds: invalidBoxIds
2250
+ }
2251
+ }));
2252
+ }
2253
+ if (uninitializedBoxIds.length) {
2254
+ sharedIssues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.SUBSCRIPTION_NOT_READY, NotificationHealthCheckStatus.WARNING, {
2255
+ message: "".concat(uninitializedBoxIds.length, " of your subscriptions are still being set up, so their notifications are delayed."),
2256
+ fix: 'This usually clears itself within a few minutes.',
2257
+ data: {
2258
+ notificationBoxIds: uninitializedBoxIds
2259
+ }
2260
+ }));
2261
+ }
2262
+ if (unsyncedBoxIds.length) {
2263
+ sharedIssues.push(notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.NEEDS_CONFIG_SYNC, NotificationHealthCheckStatus.WARNING, {
2264
+ message: "Your settings for ".concat(unsyncedBoxIds.length, " subscription").concat(unsyncedBoxIds.length === 1 ? '' : 's', " have not been applied yet."),
2265
+ fix: 'Re-run this check in a few minutes. If it persists, contact support with this report.',
2266
+ data: {
2267
+ notificationBoxIds: unsyncedBoxIds
2268
+ }
2269
+ }));
2270
+ }
2271
+ disabledBoxIdsByMethod.forEach(function(notificationBoxIds, method) {
2272
+ issuesByMethod.set(method, [
2273
+ notificationHealthCheckIssue(KnownNotificationHealthCheckIssueCode.METHOD_DISABLED_FOR_BOX, NotificationHealthCheckStatus.WARNING, {
2274
+ message: "This delivery method is switched off for ".concat(notificationBoxIds.length, " of your subscriptions."),
2275
+ fix: 'Check the per-subscription settings in your notification settings.',
2276
+ data: {
2277
+ method: method,
2278
+ notificationBoxIds: notificationBoxIds
2279
+ }
2280
+ })
2281
+ ]);
2282
+ });
2283
+ return [
2284
+ 2,
2285
+ {
2286
+ sharedIssues: sharedIssues,
2287
+ issuesByMethod: issuesByMethod
2288
+ }
2289
+ ];
2290
+ }
2291
+ });
2292
+ })();
2293
+ }
2294
+ /**
2295
+ * Determines which delivery methods are switched off for a user within a single notification box.
2296
+ *
2297
+ * Reads the box's own recipient entry when present, since that is what the send pipeline consults,
2298
+ * and falls back to the user's mirrored config otherwise.
2299
+ *
2300
+ * @param input - The box's recipient entry, the user's mirrored config, and the template type.
2301
+ * @returns The delivery methods explicitly switched off for the user in this box.
2302
+ */ function collectDisabledMethodsForBoxRecipient(input) {
2303
+ var _effectiveRecipient_c;
2304
+ var boxRecipient = input.boxRecipient, config = input.config, notificationTemplateType = input.notificationTemplateType;
2305
+ var effectiveRecipient = boxRecipient !== null && boxRecipient !== void 0 ? boxRecipient : config;
2306
+ // a flagged or excluded recipient receives nothing at all from this box, which is reported
2307
+ // separately rather than as a per-method finding
2308
+ if (effectiveRecipient.f || effectiveRecipient.x) {
2309
+ return [];
2310
+ }
2311
+ var templateConfig = (_effectiveRecipient_c = effectiveRecipient.c) === null || _effectiveRecipient_c === void 0 ? void 0 : _effectiveRecipient_c[notificationTemplateType];
2312
+ if (!templateConfig) {
2313
+ return [];
2314
+ }
2315
+ var effective = effectiveNotificationBoxRecipientTemplateConfig(templateConfig);
2316
+ return filterMaybeArrayValues([
2317
+ //
2318
+ effective.se === false ? NotificationDeliveryMethod.EMAIL : undefined,
2319
+ effective.st === false ? NotificationDeliveryMethod.TEXT : undefined,
2320
+ effective.sp === false ? NotificationDeliveryMethod.PUSH : undefined,
2321
+ effective.sn === false ? NotificationDeliveryMethod.NOTIFICATION_SUMMARY : undefined
2322
+ ]);
2323
+ }
2324
+ /**
2325
+ * True if the issue code describes a delivery probe rather than a configuration finding.
454
2326
  *
455
- * @param uid - The Firebase Auth user ID of the locked user.
456
- * @returns A precondition conflict error with the locked-config error code.
457
- */ function notificationUserLockedConfigFromBeingUpdatedError(uid) {
458
- return preconditionConflictError({
459
- message: "The user with the uid '".concat(uid, "' has locked their config from being updated."),
460
- code: NOTIFICATION_USER_LOCKED_CONFIG_FROM_BEING_UPDATED_ERROR_CODE,
461
- data: {
462
- uid: uid
463
- }
464
- });
2327
+ * @param code - The issue code to test.
2328
+ * @returns True if the code is one of the probe lifecycle codes.
2329
+ */ function isProbeIssueCode(code) {
2330
+ return PROBE_NOTIFICATION_HEALTH_CHECK_ISSUE_CODES.has(code);
465
2331
  }
466
2332
 
467
2333
  function _array_like_to_array$9(arr, len) {
@@ -1507,6 +3373,7 @@ function _ts_generator$f(thisArg, body) {
1507
3373
  updateNotificationUser: updateNotificationUserFactory(context),
1508
3374
  resyncNotificationUser: resyncNotificationUserFactory(context),
1509
3375
  resyncAllNotificationUsers: resyncAllNotificationUsersFactory(context),
3376
+ notificationUserHealthCheck: notificationUserHealthCheckFactory(context),
1510
3377
  createNotificationSummary: createNotificationSummaryFactory(context),
1511
3378
  updateNotificationSummary: updateNotificationSummaryFactory(context),
1512
3379
  createNotificationBox: createNotificationBoxFactory(context),
@@ -14544,4 +16411,4 @@ function _ts_generator(thisArg, body) {
14544
16411
  };
14545
16412
  }
14546
16413
 
14547
- export { AbstractAppNotificationModule, BASE_NOTIFICATION_SERVER_ACTION_CONTEXT_TOKEN, BASE_STORAGE_FILE_SERVER_ACTION_CONTEXT_TOKEN, DEFAULT_COMPRESS_IMAGE_FORMAT, DEFAULT_COMPRESS_IMAGE_INITIAL_QUALITY, DEFAULT_COMPRESS_IMAGE_MAX_DIMENSION, DEFAULT_COMPRESS_IMAGE_MIN_QUALITY, DEFAULT_COMPRESS_IMAGE_QUALITY_STEP, DEFAULT_COMPRESS_PDF_IMAGE_MAX_DIMENSION, DEFAULT_COMPRESS_PDF_IMAGE_QUALITY, DEFAULT_COMPRESS_PDF_IMAGE_SIZE_THRESHOLD_BYTES, DEFAULT_DOWNLOAD_MULTIPLE_STORAGE_FILES_MAX_PARALLEL_TASKS, DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST, DEFAULT_READ_MULTIPLE_STORAGE_FILES_METADATA_MAX_PARALLEL_TASKS, GlobalNotificationModule, KNOWN_BUT_UNCONFIGURED_NOTIFICATION_TEMPLATE_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, KNOWN_BUT_UNCONFIGURED_NOTIFICATION_TEMPLATE_TYPE_HOURS_DELAY, MAKE_TEMPLATE_FOR_NOTIFICATION_RELATED_MODEL_INITIALIZATION_FUNCTION_DELETE_RESPONSE, MAKE_TEMPLATE_FOR_STORAGEFILE_RELATED_MODEL_INITIALIZATION_FUNCTION_DELETE_RESPONSE, MutableNotificationExpediteService, NOTIFICATION_BOX_NOT_INITIALIZED_DELAY_MINUTES, NOTIFICATION_INIT_SERVER_ACTIONS_CONTEXT_CONFIG_TOKEN, NOTIFICATION_MAX_SEND_ATTEMPTS, NOTIFICATION_SERVER_ACTION_CONTEXT_TOKEN, NOTIFICATION_TASK_MINIMUM_SET_AT_THROTTLE_TIME_MINUTES, NOTIFICATION_TASK_TYPE_FAILURE_DELAY_HOURS, NOTIFICATION_TASK_TYPE_FAILURE_DELAY_MS, NOTIFICATION_TASK_TYPE_MAX_SEND_ATTEMPTS, NOTIFICATION_TEMPLATE_SERVICE_CONFIGS_ARRAY_TOKEN, NOTIFICATION_TEMPLATE_SERVICE_DEFAULTS_OVERRIDE_TOKEN, NotificationExpediteService, NotificationInitServerActions, NotificationSendService, NotificationServerActions, NotificationTaskService, NotificationTaskSubTaskMissingRequiredDataTermination, NotificationTemplateService, SEND_QUEUE_NOTIFICATIONS_TASK_EXCESS_THRESHOLD, STORAGE_FILE_INIT_SERVER_ACTIONS_CONTEXT_CONFIG_TOKEN, STORAGE_FILE_SERVER_ACTION_CONTEXT_TOKEN, STORAGE_FILE_SIGNED_UPLOAD_POLICIES_TOKEN, StorageFileInitServerActions, StorageFileInitializeFromUploadService, StorageFileServerActions, UNKNOWN_NOTIFICATION_TASK_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, UNKNOWN_NOTIFICATION_TASK_TYPE_HOURS_DELAY, UNKNOWN_NOTIFICATION_TEMPLATE_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, UNKNOWN_NOTIFICATION_TEMPLATE_TYPE_HOURS_DELAY, _initializeStorageFileFromUploadFileFactory, _processStorageFileInTransactionFactory, _syncStorageFileWithGroupsInTransactionFactory, allStorageFileGroupStorageFileProcessingPurposeSubtaskProcessors, appNotificationModuleMetadata, appStorageFileModuleMetadata, cleanupOldNotificationLoggedEventDaysFactory, cleanupSentNotificationsFactory, compressImageBufferToTargetSize, compressPdfImagesToTargetSize, createNotificationBoxFactory, createNotificationBoxInTransactionFactory, createNotificationIdRequiredError, createNotificationSummaryFactory, createNotificationUserFactory, createOrRunUniqueNotificationDocument, createStorageFileFactory, createStorageFileGroupFactory, createStorageFileGroupInTransactionFactory, createStorageFileGroupInputError, createStorageFileSignedUploadUrlFactory, deleteAllQueuedStorageFilesFactory, deleteStorageFileFactory, downloadMultipleStorageFilesFactory, downloadStorageFileFactory, expandNotificationRecipients, exportMutableNotificationExpediteService, firestoreNotificationSummarySendService, ignoreSendNotificationTextSendService, initializeAllApplicableNotificationBoxesFactory, initializeAllApplicableNotificationSummariesFactory, initializeAllApplicableStorageFileGroupsFactory, initializeAllStorageFilesFromUploadsFactory, initializeNotificationBoxFactory, initializeNotificationBoxInTransactionFactory, initializeNotificationModelInTransaction, initializeNotificationSummaryFactory, initializeNotificationSummaryInTransactionFactory, initializeStorageFileFromUploadFactory, initializeStorageFileGroupFactory, initializeStorageFileGroupInTransactionFactory, initializeStorageFileModelInTransaction, mailgunNotificationEmailSendService, makeNewNotificationSummaryTemplate, markStorageFileForDeleteTemplate, notificationBoxDoesNotExist, notificationBoxExclusionTargetInvalidError, notificationBoxExistsForModelError, notificationBoxRecipientDoesNotExistsError, notificationBoxUnregistredModelTypeInitializationError, notificationExpediteServiceInstance, notificationInitServerActions, notificationInitServerActionsFactory, notificationModelAlreadyInitializedError, notificationServerActions, notificationServerActionsContextFactory, notificationServerActionsFactory, notificationTaskService, notificationTaskSubTaskMissingRequiredDataTermination, notificationTaskSubtaskNotificationTaskHandlerFactory, notificationTemplateServiceInstance, notificationUserBlockedFromBeingAddedToRecipientsError, notificationUserInvalidUidForCreateError, notificationUserLockedConfigFromBeingUpdatedError, processAllQueuedStorageFilesFactory, processStorageFileFactory, provideMutableNotificationExpediteService, queryAndFlagStorageFilesForDelete, readMultipleStorageFilesMetadataFactory, readStorageFileMetadataFactory, regenerateAllFlaggedStorageFileGroupsContentFactory, regenerateStorageFileGroupContentFactory, resyncAllNotificationUsersFactory, resyncNotificationUserFactory, sendNotificationFactory, sendQueuedNotificationsFactory, storageFileAlreadyProcessedError, storageFileCannotBeDeletedYetError, storageFileCreateSignedUploadUrlToolDetailsFactory, storageFileGroupQueuedForInitializationError, storageFileGroupZipStorageFileProcessingPurposeSubtaskProcessor, storageFileInitServerActions, storageFileInitServerActionsFactory, storageFileInitializeFromUploadService, storageFileInitializeFromUploadServiceInitializerResultPermanentFailure, storageFileModelAlreadyInitializedError, storageFileNotFlaggedForDeletionError, storageFileNotFlaggedForGroupsSyncError, storageFileProcessingNotAllowedForInvalidStateError, storageFileProcessingNotAvailableForTypeError, storageFileProcessingNotQueuedForProcessingError, storageFileProcessingNotificationTaskHandler, storageFileProcessingNotificationTaskHandlerDefaultCleanup, storageFileServerActions, storageFileServerActionsContextFactory, storageFileServerActionsFactory, syncAllFlaggedStorageFilesWithGroupsFactory, syncStorageFileWithGroupsFactory, updateNotificationBoxFactory, updateNotificationBoxRecipientExclusionInTransactionFactory, updateNotificationBoxRecipientFactory, updateNotificationBoxRecipientInTransactionFactory, updateNotificationSummaryFactory, updateNotificationUserFactory, updateNotificationUserNotificationBoxRecipientConfig, updateStorageFileFactory, updateStorageFileGroupFactory, uploadedFileDoesNotExistError, uploadedFileInitializationDiscardedError, uploadedFileInitializationFailedError, uploadedFileIsNotAllowedToBeInitializedError };
16414
+ export { AbstractAppNotificationModule, BASE_NOTIFICATION_SERVER_ACTION_CONTEXT_TOKEN, BASE_STORAGE_FILE_SERVER_ACTION_CONTEXT_TOKEN, DEFAULT_COMPRESS_IMAGE_FORMAT, DEFAULT_COMPRESS_IMAGE_INITIAL_QUALITY, DEFAULT_COMPRESS_IMAGE_MAX_DIMENSION, DEFAULT_COMPRESS_IMAGE_MIN_QUALITY, DEFAULT_COMPRESS_IMAGE_QUALITY_STEP, DEFAULT_COMPRESS_PDF_IMAGE_MAX_DIMENSION, DEFAULT_COMPRESS_PDF_IMAGE_QUALITY, DEFAULT_COMPRESS_PDF_IMAGE_SIZE_THRESHOLD_BYTES, DEFAULT_DOWNLOAD_MULTIPLE_STORAGE_FILES_MAX_PARALLEL_TASKS, DEFAULT_MAILGUN_HEALTH_CHECK_PROBE_TIMEOUT_MINUTES, DEFAULT_MAILGUN_NOTIFICATION_EMAIL_SEND_SERVICE_MAX_BATCH_SIZE_PER_REQUEST, DEFAULT_MAX_NOTIFICATION_BOXES_TO_INSPECT_PER_HEALTH_CHECK, DEFAULT_READ_MULTIPLE_STORAGE_FILES_METADATA_MAX_PARALLEL_TASKS, GlobalNotificationModule, KNOWN_BUT_UNCONFIGURED_NOTIFICATION_TEMPLATE_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, KNOWN_BUT_UNCONFIGURED_NOTIFICATION_TEMPLATE_TYPE_HOURS_DELAY, MAKE_TEMPLATE_FOR_NOTIFICATION_RELATED_MODEL_INITIALIZATION_FUNCTION_DELETE_RESPONSE, MAKE_TEMPLATE_FOR_STORAGEFILE_RELATED_MODEL_INITIALIZATION_FUNCTION_DELETE_RESPONSE, MutableNotificationExpediteService, NOTIFICATION_BOX_NOT_INITIALIZED_DELAY_MINUTES, NOTIFICATION_INIT_SERVER_ACTIONS_CONTEXT_CONFIG_TOKEN, NOTIFICATION_MAX_SEND_ATTEMPTS, NOTIFICATION_SERVER_ACTION_CONTEXT_TOKEN, NOTIFICATION_TASK_MINIMUM_SET_AT_THROTTLE_TIME_MINUTES, NOTIFICATION_TASK_TYPE_FAILURE_DELAY_HOURS, NOTIFICATION_TASK_TYPE_FAILURE_DELAY_MS, NOTIFICATION_TASK_TYPE_MAX_SEND_ATTEMPTS, NOTIFICATION_TEMPLATE_SERVICE_CONFIGS_ARRAY_TOKEN, NOTIFICATION_TEMPLATE_SERVICE_DEFAULTS_OVERRIDE_TOKEN, NotificationExpediteService, NotificationInitServerActions, NotificationSendService, NotificationServerActions, NotificationTaskService, NotificationTaskSubTaskMissingRequiredDataTermination, NotificationTemplateService, SEND_QUEUE_NOTIFICATIONS_TASK_EXCESS_THRESHOLD, STORAGE_FILE_INIT_SERVER_ACTIONS_CONTEXT_CONFIG_TOKEN, STORAGE_FILE_SERVER_ACTION_CONTEXT_TOKEN, STORAGE_FILE_SIGNED_UPLOAD_POLICIES_TOKEN, StorageFileInitServerActions, StorageFileInitializeFromUploadService, StorageFileServerActions, UNKNOWN_NOTIFICATION_TASK_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, UNKNOWN_NOTIFICATION_TASK_TYPE_HOURS_DELAY, UNKNOWN_NOTIFICATION_TEMPLATE_TYPE_DELETE_AFTER_RETRY_ATTEMPTS, UNKNOWN_NOTIFICATION_TEMPLATE_TYPE_HOURS_DELAY, _initializeStorageFileFromUploadFileFactory, _processStorageFileInTransactionFactory, _syncStorageFileWithGroupsInTransactionFactory, allStorageFileGroupStorageFileProcessingPurposeSubtaskProcessors, appNotificationModuleMetadata, appStorageFileModuleMetadata, cleanupOldNotificationLoggedEventDaysFactory, cleanupSentNotificationsFactory, compressImageBufferToTargetSize, compressPdfImagesToTargetSize, createNotificationBoxFactory, createNotificationBoxInTransactionFactory, createNotificationIdRequiredError, createNotificationSummaryFactory, createNotificationUserFactory, createOrRunUniqueNotificationDocument, createStorageFileFactory, createStorageFileGroupFactory, createStorageFileGroupInTransactionFactory, createStorageFileGroupInputError, createStorageFileSignedUploadUrlFactory, deleteAllQueuedStorageFilesFactory, deleteStorageFileFactory, downloadMultipleStorageFilesFactory, downloadStorageFileFactory, expandNotificationRecipients, exportMutableNotificationExpediteService, firestoreNotificationSummarySendService, ignoreSendNotificationTextSendService, initializeAllApplicableNotificationBoxesFactory, initializeAllApplicableNotificationSummariesFactory, initializeAllApplicableStorageFileGroupsFactory, initializeAllStorageFilesFromUploadsFactory, initializeNotificationBoxFactory, initializeNotificationBoxInTransactionFactory, initializeNotificationModelInTransaction, initializeNotificationSummaryFactory, initializeNotificationSummaryInTransactionFactory, initializeStorageFileFromUploadFactory, initializeStorageFileGroupFactory, initializeStorageFileGroupInTransactionFactory, initializeStorageFileModelInTransaction, mailgunNotificationEmailSendService, mailgunNotificationEmailSendServiceHealthCheckService, makeNewNotificationSummaryTemplate, markStorageFileForDeleteTemplate, notificationBoxDoesNotExist, notificationBoxExclusionTargetInvalidError, notificationBoxExistsForModelError, notificationBoxRecipientDoesNotExistsError, notificationBoxUnregistredModelTypeInitializationError, notificationExpediteServiceInstance, notificationInitServerActions, notificationInitServerActionsFactory, notificationModelAlreadyInitializedError, notificationServerActions, notificationServerActionsContextFactory, notificationServerActionsFactory, notificationTaskService, notificationTaskSubTaskMissingRequiredDataTermination, notificationTaskSubtaskNotificationTaskHandlerFactory, notificationTemplateServiceInstance, notificationUserBlockedFromBeingAddedToRecipientsError, notificationUserHealthCheckFactory, notificationUserHealthCheckProbeThrottledError, notificationUserHealthCheckThrottledError, notificationUserHealthCheckVerifyThrottledError, notificationUserInvalidUidForCreateError, notificationUserLockedConfigFromBeingUpdatedError, processAllQueuedStorageFilesFactory, processStorageFileFactory, provideMutableNotificationExpediteService, queryAndFlagStorageFilesForDelete, readMultipleStorageFilesMetadataFactory, readStorageFileMetadataFactory, regenerateAllFlaggedStorageFileGroupsContentFactory, regenerateStorageFileGroupContentFactory, resyncAllNotificationUsersFactory, resyncNotificationUserFactory, sendNotificationFactory, sendQueuedNotificationsFactory, storageFileAlreadyProcessedError, storageFileCannotBeDeletedYetError, storageFileCreateSignedUploadUrlToolDetailsFactory, storageFileGroupQueuedForInitializationError, storageFileGroupZipStorageFileProcessingPurposeSubtaskProcessor, storageFileInitServerActions, storageFileInitServerActionsFactory, storageFileInitializeFromUploadService, storageFileInitializeFromUploadServiceInitializerResultPermanentFailure, storageFileModelAlreadyInitializedError, storageFileNotFlaggedForDeletionError, storageFileNotFlaggedForGroupsSyncError, storageFileProcessingNotAllowedForInvalidStateError, storageFileProcessingNotAvailableForTypeError, storageFileProcessingNotQueuedForProcessingError, storageFileProcessingNotificationTaskHandler, storageFileProcessingNotificationTaskHandlerDefaultCleanup, storageFileServerActions, storageFileServerActionsContextFactory, storageFileServerActionsFactory, syncAllFlaggedStorageFilesWithGroupsFactory, syncStorageFileWithGroupsFactory, updateNotificationBoxFactory, updateNotificationBoxRecipientExclusionInTransactionFactory, updateNotificationBoxRecipientFactory, updateNotificationBoxRecipientInTransactionFactory, updateNotificationSummaryFactory, updateNotificationUserFactory, updateNotificationUserNotificationBoxRecipientConfig, updateStorageFileFactory, updateStorageFileGroupFactory, uploadedFileDoesNotExistError, uploadedFileInitializationDiscardedError, uploadedFileInitializationFailedError, uploadedFileIsNotAllowedToBeInitializedError };