@dereekb/firebase-server 13.31.0 → 13.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/calcom/index.cjs.default.js +1 -0
  2. package/calcom/index.cjs.js +1451 -0
  3. package/calcom/index.cjs.mjs +2 -0
  4. package/calcom/index.d.ts +1 -0
  5. package/calcom/index.esm.js +1437 -0
  6. package/calcom/package.json +29 -0
  7. package/calcom/src/index.d.ts +1 -0
  8. package/calcom/src/lib/calcom.oauth.connection.cache.d.ts +49 -0
  9. package/calcom/src/lib/calcom.oauth.connection.config.d.ts +75 -0
  10. package/calcom/src/lib/calcom.oauth.connection.context.d.ts +84 -0
  11. package/calcom/src/lib/calcom.oauth.connection.controller.d.ts +18 -0
  12. package/calcom/src/lib/calcom.oauth.connection.module.d.ts +55 -0
  13. package/calcom/src/lib/calcom.oauth.connection.service.d.ts +33 -0
  14. package/calcom/src/lib/index.d.ts +6 -0
  15. package/discord/index.cjs.default.js +1 -0
  16. package/discord/index.cjs.js +761 -0
  17. package/discord/index.cjs.mjs +2 -0
  18. package/discord/index.d.ts +1 -0
  19. package/discord/index.esm.js +753 -0
  20. package/discord/package.json +29 -0
  21. package/discord/src/index.d.ts +1 -0
  22. package/discord/src/lib/discord.oauth.connection.config.d.ts +69 -0
  23. package/discord/src/lib/discord.oauth.connection.controller.d.ts +18 -0
  24. package/discord/src/lib/discord.oauth.connection.module.d.ts +39 -0
  25. package/discord/src/lib/discord.oauth.connection.service.d.ts +43 -0
  26. package/discord/src/lib/index.d.ts +4 -0
  27. package/index.cjs.js +14 -3
  28. package/index.esm.js +14 -3
  29. package/mailgun/package.json +9 -9
  30. package/mcp/package.json +11 -11
  31. package/model/index.cjs.js +11077 -6072
  32. package/model/index.esm.js +11020 -6081
  33. package/model/package.json +10 -9
  34. package/model/src/lib/index.d.ts +1 -0
  35. package/model/src/lib/mailgun/index.d.ts +1 -0
  36. package/model/src/lib/mailgun/notification.healthcheck.mailgun.d.ts +115 -0
  37. package/model/src/lib/notification/index.d.ts +2 -0
  38. package/model/src/lib/notification/notification.action.server.d.ts +42 -3
  39. package/model/src/lib/notification/notification.error.d.ts +31 -0
  40. package/model/src/lib/notification/notification.healthcheck.d.ts +39 -0
  41. package/model/src/lib/notification/notification.healthcheck.service.d.ts +118 -0
  42. package/model/src/lib/notification/notification.send.service.d.ts +22 -0
  43. package/model/src/lib/userexternalconnection/index.d.ts +8 -0
  44. package/model/src/lib/userexternalconnection/oauth/index.d.ts +7 -0
  45. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.config.d.ts +110 -0
  46. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.controller.d.ts +68 -0
  47. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.error.d.ts +29 -0
  48. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.refresh.d.ts +24 -0
  49. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.registry.d.ts +54 -0
  50. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.service.d.ts +216 -0
  51. package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.state.d.ts +114 -0
  52. package/model/src/lib/userexternalconnection/userexternalconnection.accessor.service.d.ts +111 -0
  53. package/model/src/lib/userexternalconnection/userexternalconnection.action.server.d.ts +172 -0
  54. package/model/src/lib/userexternalconnection/userexternalconnection.error.d.ts +44 -0
  55. package/model/src/lib/userexternalconnection/userexternalconnection.module.d.ts +129 -0
  56. package/model/src/lib/userexternalconnection/userexternalconnection.private.d.ts +192 -0
  57. package/model/src/lib/userexternalconnection/userexternalconnection.reader.service.d.ts +157 -0
  58. package/model/src/lib/userexternalconnection/userexternalconnection.refresh.service.d.ts +46 -0
  59. package/oidc/index.cjs.js +104 -78
  60. package/oidc/index.esm.js +104 -78
  61. package/oidc/package.json +10 -10
  62. package/package.json +24 -10
  63. package/src/lib/env/env.config.d.ts +15 -0
  64. package/src/lib/env/env.service.d.ts +7 -0
  65. package/src/lib/nest/env/env.service.d.ts +1 -0
  66. package/test/index.cjs.js +14 -2
  67. package/test/index.esm.js +15 -3
  68. package/test/package.json +11 -11
  69. package/test/src/lib/firebase/firebase.admin.auth.d.ts +1 -1
  70. package/twilio/package.json +8 -8
  71. package/zoho/README.md +8 -0
  72. package/zoho/index.cjs.js +1223 -33
  73. package/zoho/index.esm.js +1212 -36
  74. package/zoho/package.json +12 -9
  75. package/zoho/src/lib/index.d.ts +5 -0
  76. package/zoho/src/lib/zoho.oauth.connection.cache.d.ts +45 -0
  77. package/zoho/src/lib/zoho.oauth.connection.config.d.ts +80 -0
  78. package/zoho/src/lib/zoho.oauth.connection.controller.d.ts +18 -0
  79. package/zoho/src/lib/zoho.oauth.connection.module.d.ts +43 -0
  80. package/zoho/src/lib/zoho.oauth.connection.service.d.ts +107 -0
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.31.0",
3
+ "version": "13.33.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.31.0",
6
- "@dereekb/date": "13.31.0",
7
- "@dereekb/firebase": "13.31.0",
8
- "@dereekb/firebase-server": "13.31.0",
9
- "@dereekb/model": "13.31.0",
10
- "@dereekb/nestjs": "13.31.0",
11
- "@dereekb/rxjs": "13.31.0",
12
- "@dereekb/util": "13.31.0",
5
+ "@dereekb/analytics": "13.33.0",
6
+ "@dereekb/date": "13.33.0",
7
+ "@dereekb/firebase": "13.33.0",
8
+ "@dereekb/firebase-server": "13.33.0",
9
+ "@dereekb/model": "13.33.0",
10
+ "@dereekb/nestjs": "13.33.0",
11
+ "@dereekb/rxjs": "13.33.0",
12
+ "@dereekb/util": "13.33.0",
13
13
  "@nestjs/common": "^11.1.19",
14
14
  "@nestjs/config": "^4.0.4",
15
15
  "archiver": "^7.0.1",
16
16
  "date-fns": "^4.1.0",
17
+ "express": "^5.2.1",
17
18
  "firebase-functions": "^7.2.5",
18
19
  "make-error": "^1.3.6",
19
20
  "@cantoo/pdf-lib": "^2.6.5",
@@ -1,3 +1,4 @@
1
1
  export * from './mailgun';
2
2
  export * from './notification';
3
3
  export * from './storagefile';
4
+ export * from './userexternalconnection';
@@ -1 +1,2 @@
1
+ export * from './notification.healthcheck.mailgun';
1
2
  export * from './notification.send.service.mailgun';
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @module notification.healthcheck.mailgun
3
+ *
4
+ * Mailgun-backed diagnostics for the email delivery method.
5
+ *
6
+ * This is where "the user says they aren't getting emails" turns into an actual answer. In practice the
7
+ * cause is almost always one of:
8
+ *
9
+ * - the address is on the domain's **suppression list** (a past bounce or spam complaint), after which
10
+ * Mailgun silently drops every subsequent message to it
11
+ * - recent messages to the address were **rejected or failed**, with a reason Mailgun recorded
12
+ * - nothing was ever sent to the address at all, which points upstream at configuration
13
+ * - the sending **domain** itself is not active
14
+ *
15
+ * All four are readable from Mailgun without sending anything. When that is not conclusive, an opt-in
16
+ * probe sends a real message and resolves its outcome from the Events API. Resolution is asynchronous —
17
+ * Mailgun records the outcome seconds after the send — so the probe is recorded as pending and settled
18
+ * by a later verification, which the client polls for automatically. Nothing here ever asks the user to
19
+ * come back and check for themselves.
20
+ */
21
+ import { type Maybe, type Minutes, type PromiseOrValue } from '@dereekb/util';
22
+ import { type FirebaseAuthUserId } from '@dereekb/firebase';
23
+ import { type MailgunRecipient, type MailgunService, type MailgunTemplateEmailRequest } from '@dereekb/nestjs/mailgun';
24
+ import { type NotificationEmailSendServiceHealthCheckService } from '../notification/notification.healthcheck.service';
25
+ /**
26
+ * How long a dispatched probe may stay unresolved before it is reported as failed.
27
+ *
28
+ * Mailgun normally records a `delivered` or `failed` event within seconds; a probe with no event after
29
+ * this long is not going to arrive.
30
+ */
31
+ export declare const DEFAULT_MAILGUN_HEALTH_CHECK_PROBE_TIMEOUT_MINUTES = 15;
32
+ /**
33
+ * Input for building the test email dispatched as a delivery probe.
34
+ */
35
+ export interface MailgunNotificationHealthCheckProbeBuilderInput {
36
+ /**
37
+ * The Mailgun service, for reading the configured sender and client url.
38
+ */
39
+ readonly mailgunService: MailgunService;
40
+ /**
41
+ * The recipient to send the probe to.
42
+ */
43
+ readonly recipient: MailgunRecipient;
44
+ /**
45
+ * The user the health check is running for.
46
+ */
47
+ readonly uid: FirebaseAuthUserId;
48
+ }
49
+ /**
50
+ * Builds the test email dispatched as a delivery probe.
51
+ *
52
+ * The message should be recognizable to whoever receives it — it lands in a real inbox, unannounced,
53
+ * because someone asked the system to check whether their email works.
54
+ */
55
+ export type MailgunNotificationHealthCheckProbeBuilder = (input: MailgunNotificationHealthCheckProbeBuilderInput) => PromiseOrValue<MailgunTemplateEmailRequest>;
56
+ /**
57
+ * Configuration for {@link mailgunNotificationEmailSendServiceHealthCheckService}.
58
+ */
59
+ export interface MailgunNotificationEmailSendServiceHealthCheckServiceConfig {
60
+ /**
61
+ * The Mailgun service to diagnose against.
62
+ */
63
+ readonly mailgunService: MailgunService;
64
+ /**
65
+ * Builds the probe message. Probing is unavailable when this is not provided.
66
+ */
67
+ readonly probeBuilder?: Maybe<MailgunNotificationHealthCheckProbeBuilder>;
68
+ /**
69
+ * The maximum number of recent events to inspect per address.
70
+ */
71
+ readonly recentEventsLimit?: Maybe<number>;
72
+ /**
73
+ * How far back to look for recent events, in days.
74
+ */
75
+ readonly recentEventsWindowDays?: Maybe<number>;
76
+ /**
77
+ * Whether to run Mailgun address validation as part of the check.
78
+ *
79
+ * Off by default, because validation consumes a separate Mailgun quota.
80
+ */
81
+ readonly validateAddress?: Maybe<boolean>;
82
+ /**
83
+ * How long a dispatched probe may stay unresolved before it is reported as failed.
84
+ *
85
+ * Defaults to {@link DEFAULT_MAILGUN_HEALTH_CHECK_PROBE_TIMEOUT_MINUTES}.
86
+ */
87
+ readonly probeTimeoutMinutes?: Maybe<Minutes>;
88
+ }
89
+ /**
90
+ * Creates a {@link NotificationEmailSendServiceHealthCheckService} backed by the Mailgun API.
91
+ *
92
+ * Attach the result to a {@link NotificationEmailSendService} as its `healthCheckService` and the
93
+ * notification health check will pick it up automatically.
94
+ *
95
+ * @param config - The Mailgun service plus optional probe builder and inspection limits.
96
+ * @returns A health check service for the email delivery method.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const emailSendService = mailgunNotificationEmailSendService({ mailgunService, messageBuilders });
101
+ *
102
+ * const sendServiceWithHealthCheck: NotificationEmailSendService = {
103
+ * ...emailSendService,
104
+ * healthCheckService: mailgunNotificationEmailSendServiceHealthCheckService({
105
+ * mailgunService,
106
+ * probeBuilder: ({ recipient }) => ({
107
+ * to: recipient,
108
+ * template: 'notificationtemplate',
109
+ * subject: 'Email delivery test'
110
+ * })
111
+ * })
112
+ * };
113
+ * ```
114
+ */
115
+ export declare function mailgunNotificationEmailSendServiceHealthCheckService(config: MailgunNotificationEmailSendServiceHealthCheckServiceConfig): NotificationEmailSendServiceHealthCheckService;
@@ -3,6 +3,8 @@ export * from './notification.action.server.init';
3
3
  export * from './notification.config.service';
4
4
  export * from './notification.config';
5
5
  export * from './notification.create.run';
6
+ export * from './notification.healthcheck';
7
+ export * from './notification.healthcheck.service';
6
8
  export * from './notification.module';
7
9
  export * from './notification.send.service.notificationsummary';
8
10
  export * from './notification.send.service.text';
@@ -1,7 +1,7 @@
1
- import { type AsyncNotificationSummaryCreateAction, type AsyncNotificationUserCreateAction, type AsyncNotificationUserUpdateAction, type CleanupSentNotificationsParams, type CleanupOldNotificationLoggedEventDaysParams, type CleanupOldNotificationLoggedEventDaysResult, type CreateNotificationBoxParams, type CreateNotificationSummaryParams, type CreateNotificationUserParams, type NotificationSummaryDocument, type NotificationUser, type NotificationUserDocument, type SendNotificationParams, type SendQueuedNotificationsParams, type UpdateNotificationBoxParams, type UpdateNotificationBoxRecipientParams, type UpdateNotificationUserParams, type AsyncNotificationBoxCreateAction, type AsyncNotificationBoxUpdateAction, type CleanupSentNotificationsResult, type FirestoreContextReference, type NotificationBox, type NotificationBoxDocument, type NotificationDocument, type NotificationFirestoreCollections, type SendNotificationResult, type SendQueuedNotificationsResult, type Transaction, type ResyncAllNotificationUserParams, type ResyncAllNotificationUsersResult, type ResyncNotificationUserResult, type ResyncNotificationUserParams, type AppNotificationTemplateTypeInfoRecordServiceRef, type AsyncNotificationSummaryUpdateAction, type UpdateNotificationSummaryParams, type NotificationBoxDocumentReferencePair } from '@dereekb/firebase';
1
+ import { type AsyncNotificationSummaryCreateAction, type AsyncNotificationUserCreateAction, type AsyncNotificationUserUpdateAction, type CleanupSentNotificationsParams, type CleanupOldNotificationLoggedEventDaysParams, type CleanupOldNotificationLoggedEventDaysResult, type CreateNotificationBoxParams, type CreateNotificationSummaryParams, type CreateNotificationUserParams, type NotificationSummaryDocument, type NotificationUser, type NotificationUserDocument, type SendNotificationParams, type SendQueuedNotificationsParams, type UpdateNotificationBoxParams, type UpdateNotificationBoxRecipientParams, type NotificationUserHealthCheckParams, type NotificationUserHealthCheckResult, type UpdateNotificationUserParams, type AsyncNotificationBoxCreateAction, type AsyncNotificationBoxUpdateAction, type CleanupSentNotificationsResult, type FirestoreContextReference, type NotificationBox, type NotificationBoxDocument, type NotificationDocument, type NotificationFirestoreCollections, type SendNotificationResult, type SendQueuedNotificationsResult, type Transaction, type ResyncAllNotificationUserParams, type ResyncAllNotificationUsersResult, type ResyncNotificationUserResult, type ResyncNotificationUserParams, type AppNotificationTemplateTypeInfoRecordServiceRef, type AsyncNotificationSummaryUpdateAction, type UpdateNotificationSummaryParams, type NotificationBoxDocumentReferencePair } from '@dereekb/firebase';
2
2
  import { type FirebaseServerActionsContext, type FirebaseServerAuthServiceRef } from '@dereekb/firebase-server';
3
3
  import { type TransformAndValidateFunctionResult } from '@dereekb/model';
4
- import { type Maybe } from '@dereekb/util';
4
+ import { type Maybe, type Minutes, type Seconds } from '@dereekb/util';
5
5
  import { type InjectionToken } from '@nestjs/common';
6
6
  import { type NotificationTemplateServiceRef } from './notification.config.service';
7
7
  import { type NotificationSendServiceRef } from './notification.send.service';
@@ -24,11 +24,49 @@ export declare const NOTIFICATION_SERVER_ACTION_CONTEXT_TOKEN: InjectionToken;
24
24
  */
25
25
  export interface BaseNotificationServerActionsContext extends FirebaseServerActionsContext, NotificationFirestoreCollections, FirebaseServerAuthServiceRef, FirestoreContextReference {
26
26
  }
27
+ /**
28
+ * App-level tuning for the NotificationUser delivery health check.
29
+ *
30
+ * Both windows are enforced by the server and counted down by the client, so an app that overrides one
31
+ * must give the SAME value to its client (see the dbx-firebase health check config). Declaring the value
32
+ * once in a package both sides import is the way to keep them from drifting — otherwise the UI will
33
+ * offer a check or a test message that the server then rejects.
34
+ */
35
+ export interface NotificationUserHealthCheckServerConfig {
36
+ /**
37
+ * How long a user must wait between test messages on a single delivery method.
38
+ *
39
+ * Defaults to {@link DEFAULT_NOTIFICATION_USER_HEALTH_CHECK_PROBE_THROTTLE_MINUTES}.
40
+ */
41
+ readonly probeThrottleMinutes?: Maybe<Minutes>;
42
+ /**
43
+ * How long a user must wait between health check runs.
44
+ *
45
+ * Defaults to {@link DEFAULT_NOTIFICATION_USER_HEALTH_CHECK_THROTTLE_MINUTES}.
46
+ */
47
+ readonly runThrottleMinutes?: Maybe<Minutes>;
48
+ /**
49
+ * How long a caller must wait between verifications of a check's pending probes.
50
+ *
51
+ * Defaults to {@link DEFAULT_NOTIFICATION_USER_HEALTH_CHECK_VERIFY_THROTTLE_SECONDS}. In seconds
52
+ * because a verification exists to be polled while the user watches for their test message to land.
53
+ */
54
+ readonly verifyThrottleSeconds?: Maybe<Seconds>;
55
+ }
56
+ /**
57
+ * Reference to a {@link NotificationUserHealthCheckServerConfig}.
58
+ */
59
+ export interface NotificationUserHealthCheckServerConfigRef {
60
+ /**
61
+ * Tuning for the delivery health check. Every field falls back to the library default when absent.
62
+ */
63
+ readonly notificationUserHealthCheckConfig?: Maybe<NotificationUserHealthCheckServerConfig>;
64
+ }
27
65
  /**
28
66
  * Full context for notification server actions, extending the base with template resolution,
29
67
  * send channel orchestration, task dispatch, and expedite services.
30
68
  */
31
- export interface NotificationServerActionsContext extends BaseNotificationServerActionsContext, AppNotificationTemplateTypeInfoRecordServiceRef, NotificationTemplateServiceRef, NotificationSendServiceRef, NotificationTaskServiceRef {
69
+ export interface NotificationServerActionsContext extends BaseNotificationServerActionsContext, AppNotificationTemplateTypeInfoRecordServiceRef, NotificationTemplateServiceRef, NotificationSendServiceRef, NotificationTaskServiceRef, NotificationUserHealthCheckServerConfigRef {
32
70
  }
33
71
  /**
34
72
  * Abstract service class defining all server-side notification CRUD and delivery actions.
@@ -50,6 +88,7 @@ export declare abstract class NotificationServerActions {
50
88
  abstract updateNotificationUser(params: UpdateNotificationUserParams): AsyncNotificationUserUpdateAction<UpdateNotificationUserParams>;
51
89
  abstract resyncNotificationUser(params: ResyncNotificationUserParams): Promise<TransformAndValidateFunctionResult<ResyncNotificationUserParams, (notificationUserDocument: NotificationUserDocument) => Promise<ResyncNotificationUserResult>>>;
52
90
  abstract resyncAllNotificationUsers(params?: ResyncAllNotificationUserParams): Promise<ResyncAllNotificationUsersResult>;
91
+ abstract notificationUserHealthCheck(params: NotificationUserHealthCheckParams): Promise<TransformAndValidateFunctionResult<NotificationUserHealthCheckParams, (notificationUserDocument: NotificationUserDocument) => Promise<NotificationUserHealthCheckResult>>>;
53
92
  abstract createNotificationSummary(params: CreateNotificationSummaryParams): AsyncNotificationSummaryCreateAction<CreateNotificationSummaryParams>;
54
93
  abstract updateNotificationSummary(params: UpdateNotificationSummaryParams): AsyncNotificationSummaryUpdateAction<UpdateNotificationSummaryParams>;
55
94
  abstract createNotificationBox(params: CreateNotificationBoxParams): AsyncNotificationBoxCreateAction<CreateNotificationBoxParams>;
@@ -59,6 +59,37 @@ export declare function notificationBoxExistsForModelError(): import("firebase-f
59
59
  * @returns A precondition conflict error with the recipient-not-found error code.
60
60
  */
61
61
  export declare function notificationBoxRecipientDoesNotExistsError(): import("firebase-functions/https").HttpsError;
62
+ /**
63
+ * Creates an error indicating that a health check was run again before its throttle window passed.
64
+ *
65
+ * Thrown by the {@link NotificationUser} health check when the stored check is too recent.
66
+ *
67
+ * @param nextRunAt - The time the next health check may be run.
68
+ * @returns A precondition conflict error with the health-check-throttled error code.
69
+ */
70
+ export declare function notificationUserHealthCheckThrottledError(nextRunAt: Date): import("firebase-functions/https").HttpsError;
71
+ /**
72
+ * Creates an error indicating that a test message was requested again before its throttle window passed.
73
+ *
74
+ * Thrown by the {@link NotificationUser} health check when a probe was dispatched too recently. Separate
75
+ * from {@link notificationUserHealthCheckThrottledError}: running the check does not consume the test
76
+ * message allowance.
77
+ *
78
+ * @param nextProbeAt - The time the next test message may be dispatched.
79
+ * @returns A precondition conflict error with the probe-throttled error code.
80
+ */
81
+ export declare function notificationUserHealthCheckProbeThrottledError(nextProbeAt: Date): import("firebase-functions/https").HttpsError;
82
+ /**
83
+ * Creates an error indicating that pending probes were verified again before their throttle window passed.
84
+ *
85
+ * Thrown by the {@link NotificationUser} health check on a `verifyPendingProbesOnly` run that arrives too
86
+ * soon after the last one. Separate from {@link notificationUserHealthCheckThrottledError}: verifying an
87
+ * in-flight test message is a poll, so it neither answers to nor consumes the user's run allowance.
88
+ *
89
+ * @param nextVerifyAt - The time the next verification may be run.
90
+ * @returns A precondition conflict error with the verify-throttled error code.
91
+ */
92
+ export declare function notificationUserHealthCheckVerifyThrottledError(nextVerifyAt: Date): import("firebase-functions/https").HttpsError;
62
93
  /**
63
94
  * Creates an error indicating that the given UID does not correspond to an existing Firebase Auth user.
64
95
  *
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @module notification.healthcheck
3
+ *
4
+ * The `healthCheck` server action: a self-serve diagnosis of why a given user is or is not receiving
5
+ * notifications on each delivery method.
6
+ *
7
+ * The check runs in three passes:
8
+ *
9
+ * 1. **Configuration** — mirrors the gates {@link expandNotificationRecipients} applies at send time,
10
+ * so a finding here corresponds to a real reason a message would be dropped.
11
+ * 2. **Subscriptions** — inspects the user's {@link NotificationBox} documents to confirm they exist,
12
+ * are initialized, and carry a recipient entry matching the user's own configuration.
13
+ * 3. **Provider** — delegates to each delivery method's optional
14
+ * {@link NotificationSendServiceHealthCheckService} for provider-specific diagnostics and for
15
+ * dispatching/resolving a delivery probe.
16
+ *
17
+ * The result is persisted to the user's `hc` field, because delivery confirmation is asynchronous: a
18
+ * probe dispatched by one run is resolved by a later one. That later run is normally a
19
+ * `verifyPendingProbesOnly` VERIFICATION rather than anything the user does — a cheap poll that consults
20
+ * a provider only where a probe is actually in flight, answers to its own short window, and advances
21
+ * `vat` instead of `at` so that watching a test message never consumes the user's run allowance. The
22
+ * client reads the stored check live, so a resolved probe simply appears.
23
+ */
24
+ import { type NotificationUserDocument, type NotificationUserHealthCheckParams, type NotificationUserHealthCheckResult } from '@dereekb/firebase';
25
+ import { type NotificationServerActionsContext } from './notification.action.server';
26
+ /**
27
+ * The maximum number of the user's notification boxes to inspect in a single health check.
28
+ *
29
+ * A user can be subscribed to an unbounded number of boxes; a health check is interactive, so it
30
+ * samples rather than walking all of them.
31
+ */
32
+ export declare const DEFAULT_MAX_NOTIFICATION_BOXES_TO_INSPECT_PER_HEALTH_CHECK = 10;
33
+ /**
34
+ * Factory for the `healthCheck` action on a {@link NotificationUser}.
35
+ *
36
+ * @param context - The notification server actions context.
37
+ * @returns A transform-and-validate function that runs a delivery health check for a notification user.
38
+ */
39
+ export declare function notificationUserHealthCheckFactory(context: NotificationServerActionsContext): import("@dereekb/model").TransformAndValidateFunctionResultFunction<NotificationUserHealthCheckParams, (notificationUserDocument: NotificationUserDocument) => Promise<NotificationUserHealthCheckResult>, object, unknown>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @module notification.healthcheck.service
3
+ *
4
+ * The extension point that lets each delivery method contribute its own diagnostics to a
5
+ * notification delivery health check.
6
+ *
7
+ * The shared health check action can only inspect what it knows about: the user's configuration,
8
+ * their notification boxes, and the send queue. Everything beyond that is provider-specific — whether
9
+ * an address is on an email provider's suppression list, whether a carrier rejected an SMS, whether a
10
+ * test message actually arrived. A send service opts into contributing that knowledge by exposing a
11
+ * {@link NotificationSendServiceHealthCheckService}.
12
+ */
13
+ import { type EmailAddress, type E164PhoneNumber, type Maybe } from '@dereekb/util';
14
+ import { type FirebaseAuthUserId, type NotificationDeliveryMethod, type NotificationHealthCheckIssue, type NotificationHealthCheckProbe, type NotificationSummaryId, type NotificationTemplateType } from '@dereekb/firebase';
15
+ /**
16
+ * Input to a {@link NotificationSendServiceHealthCheckService}.
17
+ *
18
+ * @template T - The delivery target type for the method (email address, phone number, summary id).
19
+ */
20
+ export interface NotificationSendServiceHealthCheckRequest<T = unknown> {
21
+ /**
22
+ * The delivery method being diagnosed.
23
+ */
24
+ readonly method: NotificationDeliveryMethod;
25
+ /**
26
+ * The resolved delivery target to diagnose.
27
+ */
28
+ readonly target: T;
29
+ /**
30
+ * The user the check is being run for.
31
+ */
32
+ readonly uid: FirebaseAuthUserId;
33
+ /**
34
+ * Whether this run is permitted to dispatch a real probe message to the target.
35
+ *
36
+ * Always respect this — a probe sends real mail or SMS to a real person, and the caller has decided
37
+ * whether that is acceptable right now.
38
+ */
39
+ readonly sendProbe: boolean;
40
+ /**
41
+ * A probe dispatched by an earlier run that has not reached a final status yet.
42
+ *
43
+ * When present, the service should try to resolve it and return it (updated) as the response's probe.
44
+ */
45
+ readonly pendingProbe?: Maybe<NotificationHealthCheckProbe>;
46
+ /**
47
+ * The notification template type the check is being evaluated against, if the caller specified one.
48
+ */
49
+ readonly notificationTemplateType?: Maybe<NotificationTemplateType>;
50
+ /**
51
+ * The time the health check started. Use this rather than reading the clock so every finding in a
52
+ * single check shares one reference time.
53
+ */
54
+ readonly now: Date;
55
+ }
56
+ /**
57
+ * Output of a {@link NotificationSendServiceHealthCheckService}.
58
+ */
59
+ export interface NotificationSendServiceHealthCheckResponse {
60
+ /**
61
+ * Provider-level findings. May be empty when the provider had nothing to report.
62
+ */
63
+ readonly issues: NotificationHealthCheckIssue[];
64
+ /**
65
+ * The probe for this delivery method — a newly dispatched one, a resolved pending one, or a pending
66
+ * one that still has no outcome.
67
+ */
68
+ readonly probe?: Maybe<NotificationHealthCheckProbe>;
69
+ }
70
+ /**
71
+ * Contributes provider-specific diagnostics for one delivery method.
72
+ *
73
+ * Implementations should be failure-tolerant: if a provider API is unreachable, report that as an
74
+ * {@link NotificationHealthCheckStatus.UNKNOWN} issue rather than throwing, so the rest of the health
75
+ * check still reaches the user.
76
+ *
77
+ * @template T - The delivery target type for the method.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * const healthCheckService: NotificationEmailSendServiceHealthCheckService = {
82
+ * supportsProbe: true,
83
+ * async runHealthCheck({ target, sendProbe }) {
84
+ * const issues = await inspectSuppressionLists(target);
85
+ * const probe = sendProbe ? await dispatchProbe(target) : undefined;
86
+ * return { issues, probe };
87
+ * }
88
+ * };
89
+ * ```
90
+ */
91
+ export interface NotificationSendServiceHealthCheckService<T = unknown> {
92
+ /**
93
+ * Whether this service can dispatch a probe message.
94
+ *
95
+ * Reported to callers so a UI can hide the "send a test message" affordance for methods that do not
96
+ * support it. Defaults to false.
97
+ */
98
+ readonly supportsProbe?: Maybe<boolean>;
99
+ /**
100
+ * Runs the provider's diagnostics for a single delivery target.
101
+ *
102
+ * @param request - The target to diagnose and whether probing is permitted.
103
+ * @returns The provider's findings and probe state.
104
+ */
105
+ runHealthCheck(request: NotificationSendServiceHealthCheckRequest<T>): Promise<NotificationSendServiceHealthCheckResponse>;
106
+ }
107
+ /**
108
+ * A {@link NotificationSendServiceHealthCheckService} for the email delivery method.
109
+ */
110
+ export type NotificationEmailSendServiceHealthCheckService = NotificationSendServiceHealthCheckService<EmailAddress>;
111
+ /**
112
+ * A {@link NotificationSendServiceHealthCheckService} for the text/SMS delivery method.
113
+ */
114
+ export type NotificationTextSendServiceHealthCheckService = NotificationSendServiceHealthCheckService<E164PhoneNumber>;
115
+ /**
116
+ * A {@link NotificationSendServiceHealthCheckService} for the in-app notification summary delivery method.
117
+ */
118
+ export type NotificationSummarySendServiceHealthCheckService = NotificationSendServiceHealthCheckService<NotificationSummaryId>;
@@ -1,5 +1,6 @@
1
1
  import { type NotificationSummaryIdForUidFunction, type NotificationMessage, type NotificationSendEmailMessagesResult, type NotificationSendNotificationSummaryMessagesResult, type NotificationSendTextMessagesResult } from '@dereekb/firebase';
2
2
  import { type NotificationSendMessagesInstance } from './notification.send';
3
+ import { type NotificationEmailSendServiceHealthCheckService, type NotificationSummarySendServiceHealthCheckService, type NotificationTextSendServiceHealthCheckService } from './notification.healthcheck.service';
3
4
  import { type Maybe } from '@dereekb/util';
4
5
  /**
5
6
  * Provides a reference to a NotificationSendService instance.
@@ -46,6 +47,13 @@ export interface NotificationEmailSendService {
46
47
  * Can throw an error if the messages cannot be sent or generated properly due to a configuration error.
47
48
  */
48
49
  buildSendInstanceForEmailNotificationMessages(notificationMessages: NotificationMessage[]): Promise<NotificationSendMessagesInstance<NotificationSendEmailMessagesResult>>;
50
+ /**
51
+ * Optional. Contributes provider-specific diagnostics for the email channel to a notification
52
+ * delivery health check.
53
+ *
54
+ * When absent, a health check reports only what it can determine from configuration and send history.
55
+ */
56
+ readonly healthCheckService?: Maybe<NotificationEmailSendServiceHealthCheckService>;
49
57
  }
50
58
  /**
51
59
  * Service dedicated to sending notification text messages.
@@ -57,6 +65,13 @@ export interface NotificationTextSendService {
57
65
  * Can throw an error if the messages cannot be sent or generated properly due to a configuration error.
58
66
  */
59
67
  buildSendInstanceForTextNotificationMessages(notificationMessages: NotificationMessage[]): Promise<NotificationSendMessagesInstance<NotificationSendTextMessagesResult>>;
68
+ /**
69
+ * Optional. Contributes provider-specific diagnostics for the text/SMS channel to a notification
70
+ * delivery health check.
71
+ *
72
+ * When absent, a health check reports only what it can determine from configuration and send history.
73
+ */
74
+ readonly healthCheckService?: Maybe<NotificationTextSendServiceHealthCheckService>;
60
75
  }
61
76
  /**
62
77
  * Service dedicated to sending/updating NotificationSummary values in the system for the input messages.
@@ -68,4 +83,11 @@ export interface NotificationSummarySendService {
68
83
  * Can throw an error if the messages cannot be sent or generated properly due to a configuration error.
69
84
  */
70
85
  buildSendInstanceForNotificationSummaryMessages(notificationMessages: NotificationMessage[]): Promise<NotificationSendMessagesInstance<NotificationSendNotificationSummaryMessagesResult>>;
86
+ /**
87
+ * Optional. Contributes diagnostics for the in-app notification summary channel to a notification
88
+ * delivery health check.
89
+ *
90
+ * When absent, a health check reports only what it can determine from configuration and send history.
91
+ */
92
+ readonly healthCheckService?: Maybe<NotificationSummarySendServiceHealthCheckService>;
71
93
  }
@@ -0,0 +1,8 @@
1
+ export * from './oauth';
2
+ export * from './userexternalconnection.private';
3
+ export * from './userexternalconnection.accessor.service';
4
+ export * from './userexternalconnection.action.server';
5
+ export * from './userexternalconnection.error';
6
+ export * from './userexternalconnection.module';
7
+ export * from './userexternalconnection.reader.service';
8
+ export * from './userexternalconnection.refresh.service';
@@ -0,0 +1,7 @@
1
+ export * from './userexternalconnection.oauth.config';
2
+ export * from './userexternalconnection.oauth.controller';
3
+ export * from './userexternalconnection.oauth.error';
4
+ export * from './userexternalconnection.oauth.refresh';
5
+ export * from './userexternalconnection.oauth.registry';
6
+ export * from './userexternalconnection.oauth.service';
7
+ export * from './userexternalconnection.oauth.state';
@@ -0,0 +1,110 @@
1
+ import { type UserExternalConnectionProviderType } from '@dereekb/firebase';
2
+ import { type FirebaseServerEnvService } from '@dereekb/firebase-server';
3
+ import { type Maybe, type WebsiteUrl } from '@dereekb/util';
4
+ /**
5
+ * Controller path a provider's external-connection OAuth endpoints are mounted at.
6
+ *
7
+ * Matches the Angular registry's `DEFAULT_EXTERNAL_CONNECTION_AUTHORIZE_PATH_FACTORY`, which builds
8
+ * `/oauth/<providerType>/authorize`.
9
+ *
10
+ * @param providerType - The provider to build a controller path for.
11
+ * @returns The controller path, without a leading slash.
12
+ *
13
+ * @__NO_SIDE_EFFECTS__
14
+ */
15
+ export declare function userExternalConnectionOAuthControllerPath(providerType: UserExternalConnectionProviderType): string;
16
+ /**
17
+ * Routes to exclude from an app's global API route prefix so a provider's callback controller stays
18
+ * mounted at `/oauth/<providerType>/*`.
19
+ *
20
+ * Spread the result into the `exclude` list of the app's `globalApiRoutePrefix` config, alongside
21
+ * `FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE`. Without it an `/api` prefix moves the
22
+ * routes to `/api/oauth/<providerType>/*`, which no longer matches the redirect URI registered with
23
+ * the provider — and providers require a byte-identical match, so this is not something they
24
+ * tolerate.
25
+ *
26
+ * @param providerType - The provider whose routes should be excluded.
27
+ * @returns The route patterns to exclude.
28
+ *
29
+ * @__NO_SIDE_EFFECTS__
30
+ */
31
+ export declare function userExternalConnectionOAuthRoutesForGlobalRouteExclude(providerType: UserExternalConnectionProviderType): string[];
32
+ export interface UserExternalConnectionOAuthRedirectUriInput {
33
+ /**
34
+ * The origin the OAuth endpoints are reachable at, e.g. `https://example.com`.
35
+ */
36
+ readonly origin: WebsiteUrl;
37
+ readonly providerType: UserExternalConnectionProviderType;
38
+ }
39
+ /**
40
+ * Builds the redirect URI to register with a provider.
41
+ *
42
+ * Derived from {@link userExternalConnectionOAuthControllerPath}, the same expression the controller
43
+ * mounts on, so the registered URI cannot drift from the route that serves it.
44
+ *
45
+ * @param input - The origin and the provider.
46
+ * @returns The redirect URI.
47
+ *
48
+ * @__NO_SIDE_EFFECTS__
49
+ */
50
+ export declare function userExternalConnectionOAuthRedirectUri(input: UserExternalConnectionOAuthRedirectUriInput): WebsiteUrl;
51
+ export interface UserExternalConnectionOAuthApiConfig {
52
+ /**
53
+ * The provider this flow connects.
54
+ */
55
+ readonly providerType: UserExternalConnectionProviderType;
56
+ /**
57
+ * The redirect URI registered on the provider's OAuth client.
58
+ *
59
+ * Must match the registered value byte-for-byte, including the port, since it is sent on both the
60
+ * authorize redirect and the token exchange.
61
+ */
62
+ readonly redirectUri: WebsiteUrl;
63
+ /**
64
+ * Where the user is sent after a connection succeeds.
65
+ */
66
+ readonly successUrl: WebsiteUrl;
67
+ /**
68
+ * Where the user is sent after a connection fails. Defaults to the `successUrl`.
69
+ */
70
+ readonly failureUrl?: Maybe<WebsiteUrl>;
71
+ }
72
+ /**
73
+ * Configuration for an {@link AbstractUserExternalConnectionOAuthService}.
74
+ *
75
+ * Provided privately by each provider's module, so two registered providers never collide on this
76
+ * token.
77
+ */
78
+ export declare abstract class UserExternalConnectionOAuthServiceConfig {
79
+ readonly userExternalConnectionOAuth: UserExternalConnectionOAuthApiConfig;
80
+ static assertValidConfig(config: UserExternalConnectionOAuthServiceConfig): void;
81
+ }
82
+ export interface UserExternalConnectionOAuthServiceConfigFactoryConfig {
83
+ /**
84
+ * Used to resolve the OAuth origin and the app URL. No part of this configuration is read from the
85
+ * environment as a value — only the origins the app is already configured with.
86
+ */
87
+ readonly envService: FirebaseServerEnvService;
88
+ readonly providerType: UserExternalConnectionProviderType;
89
+ /**
90
+ * Path on the app URL the user is returned to after a successful connect, e.g. `/app/settings`.
91
+ */
92
+ readonly successPath: string;
93
+ /**
94
+ * Path on the app URL the user is returned to after a failed connect. Defaults to `successPath`.
95
+ */
96
+ readonly failurePath?: Maybe<string>;
97
+ }
98
+ /**
99
+ * Builds a provider's OAuth configuration from the app's configured origins plus code-declared
100
+ * return paths.
101
+ *
102
+ * The redirect URI is derived rather than configured: it is the app's OAuth origin joined to the
103
+ * same controller path the callback mounts on. Registering a provider therefore requires no
104
+ * deployment configuration of its own.
105
+ *
106
+ * @param config - The env service, the provider, and the return paths.
107
+ * @returns The validated service configuration.
108
+ * @throws {Error} When no app URL is configured, or the derived URIs are inconsistent.
109
+ */
110
+ export declare function userExternalConnectionOAuthServiceConfigFactory(config: UserExternalConnectionOAuthServiceConfigFactoryConfig): UserExternalConnectionOAuthServiceConfig;