@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
@@ -0,0 +1,172 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ import { type FirebaseAuthUserId, type FirestoreContextReference, type UserExternalConnectionDocument, type UserExternalConnectionEntryStatus, type UserExternalConnectionErrorCode, type UserExternalConnectionFirestoreCollections, type UserExternalConnectionProviderType } from '@dereekb/firebase';
3
+ import { type UserExternalConnectionCredentials, type UserExternalConnectionServerFirestoreCollections } from './userexternalconnection.private';
4
+ /**
5
+ * Context required by {@link userExternalConnectionServerActions}.
6
+ *
7
+ * Carries BOTH halves of the pair. Nothing else in the workspace should hold the private collection.
8
+ */
9
+ export interface UserExternalConnectionServerActionsContext extends FirestoreContextReference, UserExternalConnectionFirestoreCollections, UserExternalConnectionServerFirestoreCollections {
10
+ }
11
+ /**
12
+ * Reference to a {@link UserExternalConnectionServerActions} instance.
13
+ */
14
+ export interface UserExternalConnectionServerActionsRef {
15
+ readonly userExternalConnectionActions: UserExternalConnectionServerActions;
16
+ }
17
+ /**
18
+ * Parameters for connecting a user to a provider.
19
+ *
20
+ * NOTE what is absent: there is no parameter for `status`, `scopes`, `externalAccountId`,
21
+ * `expiresAt`, `connectedAt`, `updatedAt`, or the connected-provider array. Every one of those is
22
+ * derived from `credentials`, so a caller has no way to submit a summary that contradicts the
23
+ * credentials it summarizes. `label` rides on the credentials because it is a fact about the grant.
24
+ */
25
+ export interface UserExternalConnectionConnectParams {
26
+ readonly uid: FirebaseAuthUserId;
27
+ readonly providerType: UserExternalConnectionProviderType;
28
+ readonly credentials: UserExternalConnectionCredentials;
29
+ /**
30
+ * Optional instant to apply the change at. Defaults to now.
31
+ */
32
+ readonly now?: Maybe<Date>;
33
+ }
34
+ /**
35
+ * Parameters for replacing a provider's credentials after a token refresh.
36
+ */
37
+ export type UserExternalConnectionRefreshCredentialsParams = UserExternalConnectionConnectParams;
38
+ /**
39
+ * Parameters for marking a provider's connection as errored.
40
+ *
41
+ * The stored credentials are retained so the connection can be repaired without a full reconnect.
42
+ */
43
+ export interface UserExternalConnectionMarkErrorParams {
44
+ readonly uid: FirebaseAuthUserId;
45
+ readonly providerType: UserExternalConnectionProviderType;
46
+ readonly error?: Maybe<UserExternalConnectionErrorCode>;
47
+ readonly now?: Maybe<Date>;
48
+ }
49
+ /**
50
+ * Parameters for disconnecting a user from a provider.
51
+ */
52
+ export interface UserExternalConnectionDisconnectParams {
53
+ readonly uid: FirebaseAuthUserId;
54
+ readonly providerType: UserExternalConnectionProviderType;
55
+ /**
56
+ * Whether to retain a `disconnected` history entry on the public document rather than removing the
57
+ * provider's key. The credentials are removed either way. Defaults to false.
58
+ */
59
+ readonly retainEntry?: Maybe<boolean>;
60
+ readonly now?: Maybe<Date>;
61
+ }
62
+ /**
63
+ * Parameters for creating a user's connection document.
64
+ */
65
+ export interface UserExternalConnectionCreateParams {
66
+ readonly uid: FirebaseAuthUserId;
67
+ /**
68
+ * Optional instant to stamp the new document with. Defaults to now.
69
+ */
70
+ readonly now?: Maybe<Date>;
71
+ }
72
+ /**
73
+ * Parameters for deleting a user's entire connection pair.
74
+ */
75
+ export interface UserExternalConnectionDeleteAllParams {
76
+ readonly uid: FirebaseAuthUserId;
77
+ }
78
+ /**
79
+ * Server-only actions for the UserExternalConnection document pair.
80
+ *
81
+ * This is the ENTIRE write surface, and ONLY the write surface. The two collections are never exposed
82
+ * for independent mutation, so there is no way for a caller to write one document without the other —
83
+ * and therefore no sync, reconciliation, or drift-detection process to maintain.
84
+ *
85
+ * Reading is `UserExternalConnectionAccessor` (raw) or `UserExternalConnectionReader` (the one a
86
+ * consumer wants). A read used to live here too, which meant every path that only needed to look at a
87
+ * user's credentials had to hold the write surface to do it.
88
+ */
89
+ export declare abstract class UserExternalConnectionServerActions {
90
+ abstract createUserExternalConnection(params: UserExternalConnectionCreateParams): Promise<UserExternalConnectionDocument>;
91
+ abstract connectUserExternalConnection(params: UserExternalConnectionConnectParams): Promise<UserExternalConnectionDocument>;
92
+ abstract refreshUserExternalConnectionCredentials(params: UserExternalConnectionRefreshCredentialsParams): Promise<UserExternalConnectionDocument>;
93
+ abstract markUserExternalConnectionError(params: UserExternalConnectionMarkErrorParams): Promise<UserExternalConnectionDocument>;
94
+ abstract disconnectUserExternalConnection(params: UserExternalConnectionDisconnectParams): Promise<UserExternalConnectionDocument>;
95
+ abstract deleteAllUserExternalConnectionsForUser(params: UserExternalConnectionDeleteAllParams): Promise<void>;
96
+ }
97
+ /**
98
+ * The single write a per-user token cache needs: persisting credentials the provider just issued.
99
+ *
100
+ * Narrower than {@link UserExternalConnectionServerActions} on purpose. A token cache has no business
101
+ * connecting, disconnecting, or deleting anything, and the resolved document is of no use to it — hence
102
+ * the unconstrained result, which also means a caller can satisfy this without producing a document it
103
+ * would only throw away.
104
+ */
105
+ export interface UserExternalConnectionCredentialsWriter {
106
+ refreshUserExternalConnectionCredentials(params: UserExternalConnectionRefreshCredentialsParams): Promise<unknown>;
107
+ }
108
+ /**
109
+ * The two writes a reader performs: persisting a refresh, and recording that a provider rejected the
110
+ * credentials.
111
+ *
112
+ * Also narrower than {@link UserExternalConnectionServerActions} on purpose — a read surface has no
113
+ * business creating or deleting a connection — and for the same reason unconstrained in its results.
114
+ */
115
+ export interface UserExternalConnectionCredentialsAndFailureWriter extends UserExternalConnectionCredentialsWriter {
116
+ markUserExternalConnectionError(params: UserExternalConnectionMarkErrorParams): Promise<unknown>;
117
+ }
118
+ /**
119
+ * Creates a {@link UserExternalConnectionServerActions} bound to the given context.
120
+ *
121
+ * @param context - The context carrying both halves of the connection pair.
122
+ * @returns A concrete UserExternalConnectionServerActions implementation.
123
+ */
124
+ export declare function userExternalConnectionServerActions(context: UserExternalConnectionServerActionsContext): UserExternalConnectionServerActions;
125
+ /**
126
+ * Creates a function that creates a user's connection document.
127
+ *
128
+ * Only the public half is written: the private half exists to hold credentials, and the paired write
129
+ * creates it on the first connect. Creation runs in a transaction so two concurrent calls cannot both
130
+ * see an absent document and both write one.
131
+ *
132
+ * @param context - The context carrying both halves of the pair.
133
+ * @returns A function that creates the document for a uid, throwing if it already exists.
134
+ */
135
+ export declare function createUserExternalConnectionFactory(context: UserExternalConnectionServerActionsContext): (params: UserExternalConnectionCreateParams) => Promise<UserExternalConnectionDocument>;
136
+ /**
137
+ * Parameters for the paired write.
138
+ */
139
+ export interface WriteUserExternalConnectionPairParams {
140
+ readonly uid: FirebaseAuthUserId;
141
+ readonly providerType: UserExternalConnectionProviderType;
142
+ /**
143
+ * The outcome the operation produced. Drives BOTH sides of the pair.
144
+ */
145
+ readonly outcome: UserExternalConnectionEntryStatus;
146
+ /**
147
+ * The credentials the outcome produced. Required for a `connected` outcome; ignored for a
148
+ * `disconnected` one (which always removes the stored credentials).
149
+ */
150
+ readonly credentials?: Maybe<UserExternalConnectionCredentials>;
151
+ readonly error?: Maybe<UserExternalConnectionErrorCode>;
152
+ readonly retainEntry?: Maybe<boolean>;
153
+ readonly now?: Maybe<Date>;
154
+ }
155
+ /**
156
+ * Creates the single function through which every mutation of the connection pair flows.
157
+ *
158
+ * Both documents are loaded from the same transaction, all reads happen before any write (a
159
+ * Firestore transaction requirement), and both are written with the COMPLETE next value derived from
160
+ * one input. A failure at any point leaves neither document changed.
161
+ *
162
+ * @param context - The context carrying both halves of the pair.
163
+ * @returns A function that applies one provider's outcome to both documents atomically.
164
+ */
165
+ export declare function writeUserExternalConnectionPairInTransactionFactory(context: UserExternalConnectionServerActionsContext): (params: WriteUserExternalConnectionPairParams) => Promise<UserExternalConnectionDocument>;
166
+ /**
167
+ * Creates a function that deletes a user's entire connection pair in one transaction.
168
+ *
169
+ * @param context - The context carrying both halves of the pair.
170
+ * @returns A function that removes both documents for a uid.
171
+ */
172
+ export declare function deleteAllUserExternalConnectionsForUserFactory(context: UserExternalConnectionServerActionsContext): (params: UserExternalConnectionDeleteAllParams) => Promise<void>;
@@ -0,0 +1,44 @@
1
+ import { type FirebaseAuthUserId, type UserExternalConnectionProviderType } from '@dereekb/firebase';
2
+ export declare const USER_EXTERNAL_CONNECTION_PROVIDER_NOT_CONNECTED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_PROVIDER_NOT_CONNECTED";
3
+ export declare const USER_EXTERNAL_CONNECTION_PROVIDER_NOT_ALLOWED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_PROVIDER_NOT_ALLOWED";
4
+ export declare const USER_EXTERNAL_CONNECTION_ALREADY_EXISTS_ERROR_CODE = "USER_EXTERNAL_CONNECTION_ALREADY_EXISTS";
5
+ export declare const USER_EXTERNAL_CONNECTION_CREDENTIALS_EXPIRED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_CREDENTIALS_EXPIRED";
6
+ /**
7
+ * Creates an error indicating the user already has a connection document.
8
+ *
9
+ * There is exactly one per user, so creating a second is a caller mistake rather than a conflict to
10
+ * resolve. Clients that create on page load are expected to skip the call when the document is
11
+ * already loaded, and to treat this code as success when two of them race.
12
+ *
13
+ * @param uid - The user whose document already exists.
14
+ * @returns A precondition-conflict HttpsError.
15
+ */
16
+ export declare function userExternalConnectionAlreadyExistsError(uid: FirebaseAuthUserId): import("firebase-functions/https").HttpsError;
17
+ /**
18
+ * Creates an error indicating the user has no connection to the requested provider.
19
+ *
20
+ * @param providerType - The provider that was requested.
21
+ * @returns A precondition-conflict HttpsError.
22
+ */
23
+ export declare function userExternalConnectionProviderNotConnectedError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
24
+ /**
25
+ * Creates an error indicating the user's credentials for a provider have expired and could not be
26
+ * renewed.
27
+ *
28
+ * Distinct from {@link userExternalConnectionProviderNotConnectedError}: the user IS connected and the
29
+ * credentials are still stored, they just cannot be used right now. Either no refresher was configured,
30
+ * the provider has no refresh path, or the refresh itself failed. The remedy for the last of those is a
31
+ * reconnect; for the first two it is a configuration change, so the code is deliberately the same and
32
+ * the distinction is left to the server logs.
33
+ *
34
+ * @param providerType - The provider whose credentials expired.
35
+ * @returns A precondition-conflict HttpsError.
36
+ */
37
+ export declare function userExternalConnectionCredentialsExpiredError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
38
+ /**
39
+ * Creates an error indicating the requested provider is not one this app allows connecting to.
40
+ *
41
+ * @param providerType - The provider that was requested.
42
+ * @returns A precondition-conflict HttpsError.
43
+ */
44
+ export declare function userExternalConnectionProviderNotAllowedError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
@@ -0,0 +1,129 @@
1
+ import { type InjectionToken, type ModuleMetadata, type Provider } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { type Maybe, type Milliseconds } from '@dereekb/util';
4
+ import { type FirestoreContext, type FirestoreContextReference, type UserExternalConnectionFirestoreCollections } from '@dereekb/firebase';
5
+ import { FirebaseServerEnvService } from '@dereekb/firebase-server';
6
+ import { type AES256GCMEncryptionSecret } from '@dereekb/nestjs';
7
+ import { type UserExternalConnectionPrivateConverterConfig, UserExternalConnectionServerFirestoreCollections } from './userexternalconnection.private';
8
+ import { type UserExternalConnectionServerActionsContext } from './userexternalconnection.action.server';
9
+ /**
10
+ * Environment variable name for the external-connection credentials encryption secret
11
+ * (hex-encoded AES-256 key).
12
+ *
13
+ * IMPORTANT: there is NO key rotation. `firestoreEncryptedField` resolves and validates the key once
14
+ * at converter construction and closes over it, so changing this value makes every existing
15
+ * `uecp` document permanently undecryptable. Treat it as write-once per environment.
16
+ */
17
+ export declare const USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET_ENV_KEY = "USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET";
18
+ /**
19
+ * Deterministic secret used when running in a testing environment and no real secret is configured,
20
+ * so specs never need a live credential.
21
+ *
22
+ * Deliberately distinct from the OIDC JWKS testing secret so a leaked emulator blob is attributable.
23
+ */
24
+ export declare const TESTING_USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET: AES256GCMEncryptionSecret;
25
+ /**
26
+ * Configuration for the UserExternalConnection server module.
27
+ */
28
+ export declare abstract class UserExternalConnectionModuleConfig {
29
+ abstract readonly userExternalConnectionPrivateConverterConfig: UserExternalConnectionPrivateConverterConfig;
30
+ }
31
+ /**
32
+ * Builds the {@link UserExternalConnectionModuleConfig} from the environment.
33
+ *
34
+ * @param configService - The Nest config service used to read the encryption secret.
35
+ * @param envService - Used to detect a testing environment for the secret fallback.
36
+ * @returns The module configuration.
37
+ * @throws {Error} When the configured secret is invalid outside a testing environment.
38
+ */
39
+ export declare function userExternalConnectionModuleConfigFactory(configService: ConfigService, envService: FirebaseServerEnvService): UserExternalConnectionModuleConfig;
40
+ /**
41
+ * Creates the {@link UserExternalConnectionServerFirestoreCollections}.
42
+ *
43
+ * This is a bespoke provider rather than a `provideAppFirestoreCollections()` entry because that
44
+ * helper hard-codes a `(context: FirestoreContext) => T` factory and cannot carry the encryption
45
+ * secret this collection needs.
46
+ *
47
+ * @param firestoreContext - The Firestore context.
48
+ * @param config - The module configuration carrying the encryption secret.
49
+ * @returns The server-only collections.
50
+ */
51
+ export declare function userExternalConnectionServerFirestoreCollectionsFactory(firestoreContext: FirestoreContext, config: UserExternalConnectionModuleConfig): UserExternalConnectionServerFirestoreCollections;
52
+ /**
53
+ * Assembles the {@link UserExternalConnectionServerActionsContext} from the app-supplied public
54
+ * collections and the module-owned private collection.
55
+ *
56
+ * @param appCollections - The app's collections, carrying the public UserExternalConnection collection.
57
+ * @param serverCollections - The module-owned private collection.
58
+ * @returns The assembled server actions context.
59
+ */
60
+ export declare function userExternalConnectionServerActionsContextFactory(appCollections: UserExternalConnectionFirestoreCollections & FirestoreContextReference, serverCollections: UserExternalConnectionServerFirestoreCollections): UserExternalConnectionServerActionsContext;
61
+ /**
62
+ * NestJS injection token for the assembled {@link UserExternalConnectionServerActionsContext}.
63
+ */
64
+ export declare const USER_EXTERNAL_CONNECTION_SERVER_ACTIONS_CONTEXT_TOKEN: InjectionToken;
65
+ export interface ProvideAppUserExternalConnectionModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
66
+ /**
67
+ * Module that exports the app's collections token. When provided, it is automatically included in
68
+ * the generated `imports` array.
69
+ */
70
+ readonly dependencyModule?: Maybe<Required<ModuleMetadata>['imports']['0']>;
71
+ /**
72
+ * Token that resolves the app's collections — anything implementing both
73
+ * `UserExternalConnectionFirestoreCollections` and `FirestoreContextReference`.
74
+ *
75
+ * Taking this as a token is what keeps the package from ever naming an app's collections class.
76
+ */
77
+ readonly appCollectionsToken: InjectionToken;
78
+ }
79
+ /**
80
+ * Convenience function used to generate ModuleMetadata for an app's UserExternalConnectionModule.
81
+ *
82
+ * By default this module exports:
83
+ * - UserExternalConnectionServerActions
84
+ * - UserExternalConnectionAccessor
85
+ * - UserExternalConnectionServerFirestoreCollections
86
+ * - UserExternalConnectionModuleConfig
87
+ * - UserExternalConnectionStateCoder
88
+ *
89
+ * NOTE what is absent: `UserExternalConnectionReader`. The reader can be configured with a refresher,
90
+ * and the only refresher an app normally wants is backed by the OAuth provider registry — which this
91
+ * module cannot see without importing the provider modules that import it. Provide the reader with
92
+ * `userExternalConnectionReaderProvider()` from wherever the registry is declared.
93
+ *
94
+ * @param config - The module configuration.
95
+ * @returns The assembled {@link ModuleMetadata}.
96
+ */
97
+ export declare function appUserExternalConnectionModuleMetadata(config: ProvideAppUserExternalConnectionModuleMetadataConfig): ModuleMetadata;
98
+ /**
99
+ * Configuration for {@link userExternalConnectionReaderProvider}.
100
+ */
101
+ export interface UserExternalConnectionReaderProviderConfig {
102
+ /**
103
+ * Whether to build the reader's refresher from the {@link UserExternalConnectionOAuthProviderRegistry}.
104
+ * Defaults to true.
105
+ *
106
+ * Set false for an app that registers no OAuth provider services — the registry token would not
107
+ * resolve, and a reader with no refresher is still useful for reading.
108
+ */
109
+ readonly refreshWithOAuthProviderRegistry?: Maybe<boolean>;
110
+ /**
111
+ * Overrides how long before its stated expiration a credential is treated as expired.
112
+ */
113
+ readonly expirationBuffer?: Maybe<Milliseconds>;
114
+ }
115
+ /**
116
+ * Creates the NestJS provider for the {@link UserExternalConnectionReader}.
117
+ *
118
+ * Declared by the app rather than by the UserExternalConnection module, for the same reason
119
+ * {@link userExternalConnectionOAuthProviderRegistryProvider} is: the reader refreshes through the
120
+ * registry, and the registry can only be assembled somewhere that is able to import the provider
121
+ * modules — each of which imports the UserExternalConnection module. Put this beside the registry
122
+ * provider.
123
+ *
124
+ * @param config - Optional configuration. By default the reader refreshes through the registry.
125
+ * @returns The NestJS provider.
126
+ *
127
+ * @__NO_SIDE_EFFECTS__
128
+ */
129
+ export declare function userExternalConnectionReaderProvider(config?: Maybe<UserExternalConnectionReaderProviderConfig>): Provider;
@@ -0,0 +1,192 @@
1
+ import { type ISO8601DateString, type Maybe } from '@dereekb/util';
2
+ import { AbstractFirestoreDocument, type CollectionReference, type FirebaseAuthUserId, type FirestoreCollection, type FirestoreContext, type UserExternalConnectionCapability, type UserExternalConnectionExternalAccountId, type UserExternalConnectionGrantSummary, type UserExternalConnectionProviderType, type UserRelated, type UserRelatedById } from '@dereekb/firebase';
3
+ import { type AES256GCMEncryptionSecretSource } from '@dereekb/nestjs';
4
+ /**
5
+ * Provides access to the {@link UserExternalConnectionPrivate} collection.
6
+ *
7
+ * Provided ONLY by the UserExternalConnectionModule. This is deliberately never declared on an
8
+ * app's shared `FirestoreCollections` class: `provideAppFirestoreCollections()` hard-codes a
9
+ * `(context: FirestoreContext) => T` factory, which cannot carry the encryption secret this
10
+ * collection's converter needs — so the private collection structurally cannot leak into the
11
+ * client-shared collection set.
12
+ */
13
+ export declare abstract class UserExternalConnectionServerFirestoreCollections {
14
+ abstract readonly userExternalConnectionPrivateCollection: UserExternalConnectionPrivateFirestoreCollection;
15
+ }
16
+ /**
17
+ * Firestore model identity for {@link UserExternalConnectionPrivate} documents.
18
+ *
19
+ * This is a server-side only model. It has no provisions for client-side access, and deliberately
20
+ * has NO `firestore.rules` match block — the root catch-all `allow read, write: if false;` is what
21
+ * denies every client, and a block here would only create an opportunity to grant access by accident.
22
+ */
23
+ export declare const userExternalConnectionPrivateIdentity: import("@dereekb/firebase").RootFirestoreModelIdentity<"userExternalConnectionPrivate", "uecp">;
24
+ /**
25
+ * Credentials granted by a third-party provider for a single user.
26
+ *
27
+ * NOTE: every timestamp here is an {@link ISO8601DateString}, NOT a `Date`. The whole credentials
28
+ * map is JSON round-tripped through `encryptValue`/`decryptValue`, so a `Date` would silently come
29
+ * back as a string.
30
+ */
31
+ export interface UserExternalConnectionCredentials {
32
+ readonly accessToken: string;
33
+ readonly refreshToken?: Maybe<string>;
34
+ readonly tokenType?: Maybe<string>;
35
+ readonly issuedAt: ISO8601DateString;
36
+ readonly expiresAt?: Maybe<ISO8601DateString>;
37
+ readonly scopes?: Maybe<UserExternalConnectionCapability[]>;
38
+ readonly externalAccountId?: Maybe<UserExternalConnectionExternalAccountId>;
39
+ readonly label?: Maybe<string>;
40
+ /**
41
+ * Any additional provider-specific values that must be retained to use the credentials.
42
+ */
43
+ readonly extra?: Maybe<Record<string, Maybe<string | number | boolean>>>;
44
+ }
45
+ /**
46
+ * Map of provider type to the credentials stored for that provider.
47
+ */
48
+ export type UserExternalConnectionCredentialsMap = Record<UserExternalConnectionProviderType, UserExternalConnectionCredentials>;
49
+ /**
50
+ * The server-only half of a user's third-party OAuth connection state.
51
+ *
52
+ * Shares its document id (the user's uid) with the client-readable `UserExternalConnection` in
53
+ * `@dereekb/firebase`, and is only ever written in the same transaction as it. See
54
+ * `writeUserExternalConnectionPairInTransactionFactory`.
55
+ */
56
+ export interface UserExternalConnectionPrivate extends UserRelated, UserRelatedById {
57
+ /**
58
+ * Per-provider credentials. Stored as ONE encrypted string covering the whole map.
59
+ *
60
+ * A single encrypted field rather than a map of encrypted values: there is no server-side need to
61
+ * query inside credentials, and one field keeps the whole blob atomic with the paired write.
62
+ */
63
+ cr: UserExternalConnectionCredentialsMap;
64
+ /**
65
+ * Date this document was last updated at.
66
+ */
67
+ uat: Date;
68
+ }
69
+ export declare class UserExternalConnectionPrivateDocument extends AbstractFirestoreDocument<UserExternalConnectionPrivate, UserExternalConnectionPrivateDocument, typeof userExternalConnectionPrivateIdentity> {
70
+ get modelIdentity(): import("@dereekb/firebase").RootFirestoreModelIdentity<"userExternalConnectionPrivate", "uecp">;
71
+ }
72
+ /**
73
+ * Configuration for creating a {@link UserExternalConnectionPrivate} snapshot converter.
74
+ */
75
+ export interface UserExternalConnectionPrivateConverterConfig {
76
+ /**
77
+ * Encryption secret source for the credentials field.
78
+ */
79
+ readonly encryptionSecret: AES256GCMEncryptionSecretSource;
80
+ }
81
+ /**
82
+ * Creates a snapshot converter for {@link UserExternalConnectionPrivate} documents.
83
+ *
84
+ * This is a factory rather than a module-level const because `firestoreEncryptedField` resolves and
85
+ * validates the encryption key eagerly at construction — the secret must be known at runtime.
86
+ *
87
+ * @param config - Encryption configuration for the credentials field.
88
+ * @returns Snapshot converter functions for UserExternalConnectionPrivate documents.
89
+ */
90
+ export declare function userExternalConnectionPrivateConverter(config: UserExternalConnectionPrivateConverterConfig): import("@dereekb/firebase").SnapshotConverterFunctions<UserExternalConnectionPrivate, Partial<import("@dereekb/util").ReplaceType<UserExternalConnectionPrivate, import("@dereekb/util").MaybeMap<object>, any>>>;
91
+ /**
92
+ * Copies the document id into `uid` on write, so the stored uid can never drift from the document id.
93
+ */
94
+ export declare const userExternalConnectionPrivateAccessorFactory: import("@dereekb/firebase").InterceptAccessorFactoryFunction<UserExternalConnectionPrivate, import("@dereekb/firebase").DocumentData>;
95
+ /**
96
+ * Returns the Firestore {@link CollectionReference} for {@link UserExternalConnectionPrivate} documents.
97
+ *
98
+ * @param context - The Firestore context to create the collection reference from.
99
+ * @returns The typed collection reference.
100
+ */
101
+ export declare function userExternalConnectionPrivateCollectionReference(context: FirestoreContext): CollectionReference<UserExternalConnectionPrivate>;
102
+ export type UserExternalConnectionPrivateFirestoreCollection = FirestoreCollection<UserExternalConnectionPrivate, UserExternalConnectionPrivateDocument>;
103
+ /**
104
+ * Configuration for creating a {@link UserExternalConnectionPrivateFirestoreCollection}.
105
+ */
106
+ export interface UserExternalConnectionPrivateFirestoreCollectionConfig extends UserExternalConnectionPrivateConverterConfig {
107
+ readonly firestoreContext: FirestoreContext;
108
+ }
109
+ /**
110
+ * Creates a {@link UserExternalConnectionPrivateFirestoreCollection} with encrypted credentials support.
111
+ *
112
+ * @param config - Configuration including the Firestore context and encryption settings.
113
+ * @returns The configured collection.
114
+ */
115
+ export declare function userExternalConnectionPrivateFirestoreCollection(config: UserExternalConnectionPrivateFirestoreCollectionConfig): UserExternalConnectionPrivateFirestoreCollection;
116
+ /**
117
+ * Projects the client-readable facts out of a set of credentials.
118
+ *
119
+ * This is the ONLY bridge between the private and public halves of the pair. Because the summary
120
+ * entry is derived from this projection, the public document cannot describe scopes, an account, or
121
+ * an expiration that the credentials do not actually carry.
122
+ *
123
+ * @param credentials - The credentials to project.
124
+ * @returns The grant summary the public entry is derived from.
125
+ */
126
+ export declare function userExternalConnectionGrantSummaryFromCredentials(credentials: UserExternalConnectionCredentials): UserExternalConnectionGrantSummary;
127
+ /**
128
+ * Input for {@link mergeRefreshedUserExternalConnectionCredentials}.
129
+ */
130
+ export interface MergeRefreshedUserExternalConnectionCredentialsInput {
131
+ /**
132
+ * The credentials that were stored before the refresh.
133
+ */
134
+ readonly previous: UserExternalConnectionCredentials;
135
+ /**
136
+ * The credentials a refresh produced.
137
+ */
138
+ readonly refreshed: UserExternalConnectionCredentials;
139
+ }
140
+ /**
141
+ * Merges refreshed credentials over the stored ones, retaining anything the refresh response omitted.
142
+ *
143
+ * Required because the paired write replaces a provider's credentials WHOLESALE (see
144
+ * {@link applyUserExternalConnectionCredentials}) — so persisting a refresh response verbatim erases
145
+ * every field the provider did not resend, while leaving the entry `connected`. Two of those are
146
+ * load-bearing:
147
+ *
148
+ * - `refreshToken`, which Zoho (and any provider that issues one only on first consent) omits on
149
+ * refresh. Losing it yields a connection that can never be refreshed again and does not look broken.
150
+ * - `extra`, which carries the values needed to USE the credentials — Zoho's `apiDomain` and the
151
+ * `accountsServer` datacenter a later refresh must be sent to. Dropping `accountsServer` breaks the
152
+ * refresh AFTER this one, which is considerably harder to attribute.
153
+ *
154
+ * `extra` is merged key-by-key rather than replaced, so a refresh that resends only `apiDomain` still
155
+ * updates it without discarding the rest. Keys the refresh left UNDEFINED are dropped before merging,
156
+ * because an object literal built with an absent optional field carries the key with an undefined value
157
+ * and a plain spread would use it to erase the stored one. A refresh that means to CLEAR a key can
158
+ * still say so with an explicit null.
159
+ *
160
+ * The generalization of `AbstractUserExternalConnectionOAuthService.credentialsRetainingStoredRefreshToken`,
161
+ * which does the same thing for the authorization-code exchange.
162
+ *
163
+ * @param input - The stored credentials and the refreshed ones.
164
+ * @returns The credentials to persist.
165
+ */
166
+ export declare function mergeRefreshedUserExternalConnectionCredentials(input: MergeRefreshedUserExternalConnectionCredentialsInput): UserExternalConnectionCredentials;
167
+ /**
168
+ * Input for {@link applyUserExternalConnectionCredentials}.
169
+ */
170
+ export interface ApplyUserExternalConnectionCredentialsInput {
171
+ /**
172
+ * The currently stored private document, when one exists.
173
+ */
174
+ readonly current?: Maybe<UserExternalConnectionPrivate>;
175
+ readonly uid: FirebaseAuthUserId;
176
+ readonly providerType: UserExternalConnectionProviderType;
177
+ /**
178
+ * The next credentials for this provider, or null to remove the provider's key entirely.
179
+ */
180
+ readonly credentials: Maybe<UserExternalConnectionCredentials>;
181
+ readonly now: Date;
182
+ }
183
+ /**
184
+ * Applies a single provider's credentials and returns the COMPLETE next private document.
185
+ *
186
+ * The counterpart of `applyUserExternalConnectionEntry()` — both are always called with the same
187
+ * provider and instant inside one transaction.
188
+ *
189
+ * @param input - The current document plus the credentials to apply.
190
+ * @returns The next UserExternalConnectionPrivate value to write.
191
+ */
192
+ export declare function applyUserExternalConnectionCredentials(input: ApplyUserExternalConnectionCredentialsInput): UserExternalConnectionPrivate;