@pattern-stack/codegen 0.6.4 → 0.6.6

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 (93) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -0
  3. package/dist/runtime/subsystems/auth/auth-oauth-state.schema.d.ts +81 -0
  4. package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js +12 -0
  5. package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -0
  6. package/dist/runtime/subsystems/auth/auth.module.d.ts +39 -24
  7. package/dist/runtime/subsystems/auth/auth.module.js +247 -14
  8. package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
  9. package/dist/runtime/subsystems/auth/auth.tokens.d.ts +15 -2
  10. package/dist/runtime/subsystems/auth/auth.tokens.js +9 -1
  11. package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
  12. package/dist/runtime/subsystems/auth/backends/encryption-key/env.d.ts +1 -1
  13. package/dist/runtime/subsystems/auth/backends/encryption-key/env.js +1 -1
  14. package/dist/runtime/subsystems/auth/backends/encryption-key/env.js.map +1 -1
  15. package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.d.ts +23 -0
  16. package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js +68 -0
  17. package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -0
  18. package/dist/runtime/subsystems/auth/backends/state-store.memory-backend.d.ts +21 -0
  19. package/dist/runtime/subsystems/auth/backends/state-store.memory-backend.js +51 -0
  20. package/dist/runtime/subsystems/auth/backends/state-store.memory-backend.js.map +1 -0
  21. package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +31 -0
  22. package/dist/runtime/subsystems/auth/controllers/auth.controller.js +137 -0
  23. package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -0
  24. package/dist/runtime/subsystems/auth/index.d.ts +13 -4
  25. package/dist/runtime/subsystems/auth/index.js +254 -15
  26. package/dist/runtime/subsystems/auth/index.js.map +1 -1
  27. package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +1 -1
  28. package/dist/runtime/subsystems/auth/protocols/integration-store.d.ts +37 -2
  29. package/dist/runtime/subsystems/auth/protocols/oauth-state-store.d.ts +33 -7
  30. package/dist/runtime/subsystems/auth/protocols/oauth-state-store.js +12 -0
  31. package/dist/runtime/subsystems/auth/protocols/oauth-state-store.js.map +1 -1
  32. package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +54 -0
  33. package/dist/runtime/subsystems/auth/protocols/provider-strategy.js +1 -0
  34. package/dist/runtime/subsystems/auth/protocols/provider-strategy.js.map +1 -0
  35. package/dist/runtime/subsystems/auth/protocols/user-context.d.ts +24 -0
  36. package/dist/runtime/subsystems/auth/protocols/user-context.js +1 -0
  37. package/dist/runtime/subsystems/auth/protocols/user-context.js.map +1 -0
  38. package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +2 -2
  39. package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
  40. package/dist/runtime/subsystems/auth/runtime/session-expired.error.d.ts +2 -2
  41. package/dist/runtime/subsystems/auth/runtime/session-expired.error.js.map +1 -1
  42. package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
  43. package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
  44. package/dist/runtime/subsystems/index.d.ts +9 -4
  45. package/dist/runtime/subsystems/index.js +248 -15
  46. package/dist/runtime/subsystems/index.js.map +1 -1
  47. package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +1 -1
  48. package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +1 -1
  49. package/dist/runtime/subsystems/sync/index.js.map +1 -1
  50. package/dist/runtime/subsystems/sync/sync-change-source.protocol.d.ts +1 -1
  51. package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +1 -1
  52. package/dist/runtime/subsystems/sync/sync-loopback.protocol.d.ts +3 -4
  53. package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +1 -1
  54. package/dist/runtime/subsystems/sync/sync.module.js.map +1 -1
  55. package/dist/src/cli/index.js +574 -142
  56. package/dist/src/cli/index.js.map +1 -1
  57. package/dist/src/index.js.map +1 -1
  58. package/package.json +1 -1
  59. package/runtime/subsystems/auth/auth-oauth-state.schema.ts +30 -0
  60. package/runtime/subsystems/auth/auth.module.ts +89 -32
  61. package/runtime/subsystems/auth/auth.tokens.ts +14 -1
  62. package/runtime/subsystems/auth/backends/encryption-key/env.ts +3 -3
  63. package/runtime/subsystems/auth/backends/state-store.drizzle-backend.ts +83 -0
  64. package/runtime/subsystems/auth/backends/state-store.memory-backend.ts +76 -0
  65. package/runtime/subsystems/auth/controllers/auth.controller.ts +155 -0
  66. package/runtime/subsystems/auth/index.ts +43 -4
  67. package/runtime/subsystems/auth/protocols/auth-strategy.ts +1 -1
  68. package/runtime/subsystems/auth/protocols/integration-store.ts +38 -1
  69. package/runtime/subsystems/auth/protocols/oauth-state-store.ts +38 -6
  70. package/runtime/subsystems/auth/protocols/provider-strategy.ts +48 -0
  71. package/runtime/subsystems/auth/protocols/user-context.ts +22 -0
  72. package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +2 -2
  73. package/runtime/subsystems/auth/runtime/session-expired.error.ts +2 -2
  74. package/runtime/subsystems/auth/runtime/with-auth-retry.ts +1 -1
  75. package/runtime/subsystems/index.ts +17 -2
  76. package/runtime/subsystems/sync/deep-equal.differ.ts +1 -1
  77. package/runtime/subsystems/sync/execute-sync.use-case.ts +1 -1
  78. package/runtime/subsystems/sync/sync-change-source.protocol.ts +1 -1
  79. package/runtime/subsystems/sync/sync-cursor-store.memory-backend.ts +1 -1
  80. package/runtime/subsystems/sync/sync-loopback.protocol.ts +3 -4
  81. package/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.ts +1 -1
  82. package/templates/subsystem/auth/app-module-hook.ejs.t +21 -0
  83. package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +35 -0
  84. package/templates/subsystem/auth/env-config.ejs.t +20 -0
  85. package/templates/subsystem/auth/prompt.js +46 -0
  86. package/templates/subsystem/auth-config/codegen-config-auth-block.ejs.t +20 -0
  87. package/templates/subsystem/auth-config/prompt.js +20 -0
  88. package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +16 -0
  89. package/templates/subsystem/auth-integrations/prompt.js +23 -0
  90. package/dist/runtime/subsystems/auth/backends/oauth-state-store/in-memory.d.ts +0 -24
  91. package/dist/runtime/subsystems/auth/backends/oauth-state-store/in-memory.js +0 -24
  92. package/dist/runtime/subsystems/auth/backends/oauth-state-store/in-memory.js.map +0 -1
  93. package/runtime/subsystems/auth/backends/oauth-state-store/in-memory.ts +0 -42
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/auth/protocols/oauth-state-store.ts"],"sourcesContent":["/**\n * Auth subsystem — `IOAuthStateStore` port.\n *\n * CSRF protection for the OAuth2 authorize-code callback. Generic across\n * providers. The store mints opaque state tokens at /connect time and\n * single-use consumes them at /callback time, returning the original\n * record (userId + optional post-callback redirect path).\n *\n * Concrete backends live under `../backends/`:\n * - `state-store.memory-backend.ts` — in-process Map (tests/dev).\n * - `state-store.drizzle-backend.ts` — Postgres (prod).\n *\n * Semantics:\n * - `generate(record)` → returns an opaque state token; record is stored\n * under that token until consumed or until TTL expires.\n * - `consume(state)` → atomically deletes the entry and returns the\n * record. Throws on missing, expired, or replayed state. Never returns\n * null — a missing/expired state is a CSRF signal.\n */\nexport interface OAuthStateRecord {\n userId: string;\n /** Optional post-callback redirect path (relative URL). */\n redirect?: string;\n}\n\nexport interface IOAuthStateStore {\n /** Mint an opaque state token bound to `record`. Single-use. */\n generate(record: OAuthStateRecord): Promise<string>;\n /**\n * Atomically consume `state`, returning the bound record. Throws on\n * missing / expired / replayed state.\n */\n consume(state: string): Promise<OAuthStateRecord>;\n}\n\n/**\n * Thrown by `IOAuthStateStore.consume` when the state token is unknown,\n * expired, or has already been consumed (replay attempt).\n */\nexport class OAuthStateError extends Error {\n constructor(\n message: string,\n public readonly reason: 'missing' | 'expired',\n ) {\n super(message);\n this.name = 'OAuthStateError';\n }\n}\n"],"mappings":";AAuCO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YACE,SACgB,QAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EAJkB;AAKpB;","names":[]}
@@ -0,0 +1,54 @@
1
+ import { OAuth2RefreshStrategy } from '../runtime/oauth2-refresh.strategy.js';
2
+ import './auth-strategy.js';
3
+ import './integration-store.js';
4
+
5
+ /**
6
+ * Auth subsystem — `IProviderStrategy` contract.
7
+ *
8
+ * Extension of `OAuth2RefreshStrategy` (which already covers the refresh
9
+ * path) that adds the two methods needed by the connect/callback dance:
10
+ *
11
+ * - `buildAuthorizeUrl({ state, redirectUri })` → consent-page URL.
12
+ * - `exchangeCodeForTokens({ code, redirectUri })` → tokens after consent.
13
+ *
14
+ * Concrete per-provider strategies (HubSpot, SFDC, Google, Gong, Fathom, …)
15
+ * stay consumer-side per ADR-031 ("every app has different combinations").
16
+ * They typically subclass `OAuth2RefreshStrategy` for the refresh path and
17
+ * implement these two methods structurally — that satisfies
18
+ * `IProviderStrategy` because TS lets interfaces extend classes by type.
19
+ *
20
+ * AuthController never imports a concrete strategy — it injects the
21
+ * `STRATEGY_REGISTRY` (a `ReadonlyMap<provider-slug, IProviderStrategy>`)
22
+ * and dispatches by slug.
23
+ *
24
+ * **Naming convention:** interfaces that describe behavioral ports use the
25
+ * `I` prefix (`IProviderStrategy`, `IIntegrationReader`, `IUserContext`,
26
+ * `IOAuthStateStore`, `IEncryptionKey`). Plain data types / DTOs (e.g.
27
+ * `ExchangedTokens`, `DecryptedIntegration`, `IntegrationGrantInput`) do
28
+ * not. Abstract template-method classes (e.g. `OAuth2RefreshStrategy`) also
29
+ * do not — the `I` is for interfaces only.
30
+ */
31
+
32
+ interface ExchangedTokens {
33
+ accessToken: string;
34
+ refreshToken?: string;
35
+ expiresAt?: Date;
36
+ scope?: string[];
37
+ externalAccountId?: string;
38
+ /** Provider-specific bag (SFDC `instance_url`, Google `sub`, …). */
39
+ providerMetadata?: Record<string, unknown>;
40
+ }
41
+ interface IProviderStrategy extends OAuth2RefreshStrategy {
42
+ buildAuthorizeUrl(args: {
43
+ state: string;
44
+ redirectUri: string;
45
+ }): string;
46
+ exchangeCodeForTokens(args: {
47
+ code: string;
48
+ redirectUri: string;
49
+ }): Promise<ExchangedTokens>;
50
+ }
51
+ /** The DI value type behind the `STRATEGY_REGISTRY` token. */
52
+ type ProviderStrategyRegistry = ReadonlyMap<string, IProviderStrategy>;
53
+
54
+ export type { ExchangedTokens, IProviderStrategy, ProviderStrategyRegistry };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=provider-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Auth subsystem — `IUserContext` port.
3
+ *
4
+ * Resolves "who is the current user" from a request. The shape is
5
+ * universal; the implementation is always app-specific:
6
+ *
7
+ * - WorkOS session: `req.session.user.id`
8
+ * - JWT bearer: `decode(req.headers.authorization).sub`
9
+ * - Test fixture: hardcoded UUID
10
+ *
11
+ * The auth subsystem cannot ship a default — every app does auth differently —
12
+ * but the port is universal, so the contract ships here. Consumers bind a
13
+ * concrete implementation under the `AUTH_USER_CONTEXT` token in their app
14
+ * module.
15
+ *
16
+ * `req` is typed as `unknown` deliberately: this protocol must not pull a
17
+ * dependency on `express` / `fastify` / NestJS request types. The concrete
18
+ * adapter narrows it (e.g. via a `Request` import).
19
+ */
20
+ interface IUserContext {
21
+ getCurrentUserId(req: unknown): Promise<string>;
22
+ }
23
+
24
+ export type { IUserContext };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=user-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -5,8 +5,8 @@ import { IIntegrationReader, IIntegrationTokenWriter, DecryptedIntegration } fro
5
5
  * Abstract base class for OAuth2 refresh-token strategies.
6
6
  *
7
7
  * Template-method pattern: `resolve()` is concrete; four small hooks inject
8
- * provider specifics. Validated across two providers in dealbrain-v2
9
- * (SalesforceAuthStrategy, HubSpotAuthStrategy) before extraction here — see
8
+ * provider specifics. Validated across two providers (Salesforce, HubSpot)
9
+ * in the extraction-source app before being extracted here — see
10
10
  * `docs/gate-1-auth-extraction-findings.md` for the "build first, extract
11
11
  * later" evidence.
12
12
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/integration-broken.error.ts","../../../../../runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts"],"sourcesContent":["/**\n * Thrown when an OAuth2 provider returns `400 invalid_grant`/`invalid_token`\n * on refresh — the refresh token itself is dead (user revoked, org\n * deactivated, token expired beyond the provider's rotation window). The\n * integration should be marked broken so background sync stops picking it\n * up; the user re-initiates OAuth.\n *\n * Shared across every OAuth2 strategy.\n */\nexport class IntegrationBrokenError extends Error {\n constructor(\n readonly integrationId: string,\n readonly errorCode: string,\n readonly errorDescription: string,\n ) {\n super(\n `Integration ${integrationId} broken: ${errorCode} - ${errorDescription}`,\n );\n this.name = 'IntegrationBrokenError';\n }\n}\n","/**\n * Abstract base class for OAuth2 refresh-token strategies.\n *\n * Template-method pattern: `resolve()` is concrete; four small hooks inject\n * provider specifics. Validated across two providers in dealbrain-v2\n * (SalesforceAuthStrategy, HubSpotAuthStrategy) before extraction here — see\n * `docs/gate-1-auth-extraction-findings.md` for the \"build first, extract\n * later\" evidence.\n *\n * Subclass contract:\n * - `provider` — slug matched against `integrations.provider`\n * - `defaultExpiresInSec` — fallback when refresh response omits `expires_in`\n * - `tokenEndpoint()` — URL to POST the refresh grant\n * - `refreshBodyExtras()` — provider-specific body params\n * - `parseRefreshResponse()` — raw JSON → ParsedRefreshResponse\n * - `buildCredentials()` — stored or freshly-refreshed access token +\n * integration + optional raw refresh response\n * → provider credentials\n *\n * Base handles: expiry check w/ 5-min safety window, `forceRefresh` escape\n * hatch, POST form-urlencoded body, OAuth2 error mapping to\n * `IntegrationBrokenError`, refresh-token rotation persistence, fetch +\n * clock injection for tests.\n */\nimport type {\n AuthCredentials,\n AuthResolveOptions,\n IAuthStrategy,\n} from '../protocols/auth-strategy';\nimport type {\n DecryptedIntegration,\n IIntegrationReader,\n IIntegrationTokenWriter,\n} from '../protocols/integration-store';\nimport { IntegrationBrokenError } from './integration-broken.error';\n\nexport type FetchLike = (\n input: string | URL | Request,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Safety window before expiry that triggers a refresh. */\nconst REFRESH_SAFETY_MS = 5 * 60 * 1000;\n\nexport interface OAuth2RefreshStrategyOptions {\n integrationReader: IIntegrationReader;\n tokenWriter: IIntegrationTokenWriter;\n /** Injectable fetch for tests. Defaults to the global `fetch`. */\n fetch?: FetchLike;\n /** Injectable clock for tests. Defaults to `Date.now`. */\n now?: () => number;\n}\n\nexport interface ParsedRefreshResponse {\n accessToken: string;\n /**\n * New refresh token if the provider rotated it (HubSpot: always, Salesforce:\n * sometimes). Omit when the provider reused the old refresh token.\n */\n refreshToken?: string;\n /** Seconds from now. If omitted, subclass `defaultExpiresInSec` applies. */\n expiresInSec?: number;\n}\n\nexport abstract class OAuth2RefreshStrategy implements IAuthStrategy {\n protected abstract readonly provider: string;\n protected abstract readonly defaultExpiresInSec: number;\n\n protected readonly integrationReader: IIntegrationReader;\n protected readonly tokenWriter: IIntegrationTokenWriter;\n protected readonly fetchImpl: FetchLike;\n protected readonly now: () => number;\n\n constructor(opts: OAuth2RefreshStrategyOptions) {\n this.integrationReader = opts.integrationReader;\n this.tokenWriter = opts.tokenWriter;\n this.fetchImpl = opts.fetch ?? fetch;\n this.now = opts.now ?? Date.now;\n }\n\n async resolve(\n integrationId: string,\n opts: AuthResolveOptions = {},\n ): Promise<AuthCredentials> {\n const integration =\n await this.integrationReader.findByIdDecrypted(integrationId);\n if (!integration) {\n throw new Error(`Integration ${integrationId} not found`);\n }\n if (integration.provider !== this.provider) {\n throw new Error(\n `${this.constructor.name} called for non-${this.provider} integration ${integrationId} (provider=${integration.provider})`,\n );\n }\n\n const needsRefresh =\n opts.forceRefresh ||\n this.isExpiring(integration.expiresAt) ||\n !integration.accessToken;\n\n if (!needsRefresh) {\n return this.buildCredentials(integration.accessToken, integration);\n }\n\n if (!integration.refreshToken) {\n throw new IntegrationBrokenError(\n integrationId,\n 'no_refresh_token',\n 'Integration has no refresh token; user must reconnect',\n );\n }\n\n const { parsed, raw } = await this.executeRefresh(\n integrationId,\n integration.refreshToken,\n );\n const newExpiresAt = new Date(\n this.now() + (parsed.expiresInSec ?? this.defaultExpiresInSec) * 1000,\n );\n await this.tokenWriter.persistRefresh({\n integrationId,\n accessToken: parsed.accessToken,\n refreshToken: parsed.refreshToken ?? undefined,\n expiresAt: newExpiresAt,\n });\n\n return this.buildCredentials(parsed.accessToken, integration, raw);\n }\n\n protected abstract tokenEndpoint(): string;\n protected abstract refreshBodyExtras(): Record<string, string>;\n protected abstract parseRefreshResponse(raw: unknown): ParsedRefreshResponse;\n protected abstract buildCredentials(\n accessToken: string,\n integration: DecryptedIntegration,\n refreshRaw?: unknown,\n ): AuthCredentials;\n\n private async executeRefresh(\n integrationId: string,\n refreshToken: string,\n ): Promise<{ parsed: ParsedRefreshResponse; raw: unknown }> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n ...this.refreshBodyExtras(),\n });\n const response = await this.fetchImpl(this.tokenEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n if (!response.ok) {\n const err = (await safeJson(response)) as Partial<{\n error: string;\n error_description: string;\n message: string;\n }>;\n if (\n response.status === 400 &&\n (err.error === 'invalid_grant' || err.error === 'invalid_token')\n ) {\n throw new IntegrationBrokenError(\n integrationId,\n err.error ?? 'invalid_grant',\n err.error_description ?? err.message ?? 'refresh token rejected',\n );\n }\n throw new Error(\n `${this.provider} token refresh failed: ${response.status} ${err.error ?? ''} ${err.error_description ?? err.message ?? ''}`.trim(),\n );\n }\n const raw = await response.json();\n return { parsed: this.parseRefreshResponse(raw), raw };\n }\n\n private isExpiring(expiresAt: Date | null): boolean {\n if (!expiresAt) return true;\n return expiresAt.getTime() - this.now() < REFRESH_SAFETY_MS;\n }\n}\n\nasync function safeJson(response: Response): Promise<unknown> {\n try {\n return await response.clone().json();\n } catch {\n return {};\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/integration-broken.error.ts","../../../../../runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts"],"sourcesContent":["/**\n * Thrown when an OAuth2 provider returns `400 invalid_grant`/`invalid_token`\n * on refresh — the refresh token itself is dead (user revoked, org\n * deactivated, token expired beyond the provider's rotation window). The\n * integration should be marked broken so background sync stops picking it\n * up; the user re-initiates OAuth.\n *\n * Shared across every OAuth2 strategy.\n */\nexport class IntegrationBrokenError extends Error {\n constructor(\n readonly integrationId: string,\n readonly errorCode: string,\n readonly errorDescription: string,\n ) {\n super(\n `Integration ${integrationId} broken: ${errorCode} - ${errorDescription}`,\n );\n this.name = 'IntegrationBrokenError';\n }\n}\n","/**\n * Abstract base class for OAuth2 refresh-token strategies.\n *\n * Template-method pattern: `resolve()` is concrete; four small hooks inject\n * provider specifics. Validated across two providers (Salesforce, HubSpot)\n * in the extraction-source app before being extracted here — see\n * `docs/gate-1-auth-extraction-findings.md` for the \"build first, extract\n * later\" evidence.\n *\n * Subclass contract:\n * - `provider` — slug matched against `integrations.provider`\n * - `defaultExpiresInSec` — fallback when refresh response omits `expires_in`\n * - `tokenEndpoint()` — URL to POST the refresh grant\n * - `refreshBodyExtras()` — provider-specific body params\n * - `parseRefreshResponse()` — raw JSON → ParsedRefreshResponse\n * - `buildCredentials()` — stored or freshly-refreshed access token +\n * integration + optional raw refresh response\n * → provider credentials\n *\n * Base handles: expiry check w/ 5-min safety window, `forceRefresh` escape\n * hatch, POST form-urlencoded body, OAuth2 error mapping to\n * `IntegrationBrokenError`, refresh-token rotation persistence, fetch +\n * clock injection for tests.\n */\nimport type {\n AuthCredentials,\n AuthResolveOptions,\n IAuthStrategy,\n} from '../protocols/auth-strategy';\nimport type {\n DecryptedIntegration,\n IIntegrationReader,\n IIntegrationTokenWriter,\n} from '../protocols/integration-store';\nimport { IntegrationBrokenError } from './integration-broken.error';\n\nexport type FetchLike = (\n input: string | URL | Request,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Safety window before expiry that triggers a refresh. */\nconst REFRESH_SAFETY_MS = 5 * 60 * 1000;\n\nexport interface OAuth2RefreshStrategyOptions {\n integrationReader: IIntegrationReader;\n tokenWriter: IIntegrationTokenWriter;\n /** Injectable fetch for tests. Defaults to the global `fetch`. */\n fetch?: FetchLike;\n /** Injectable clock for tests. Defaults to `Date.now`. */\n now?: () => number;\n}\n\nexport interface ParsedRefreshResponse {\n accessToken: string;\n /**\n * New refresh token if the provider rotated it (HubSpot: always, Salesforce:\n * sometimes). Omit when the provider reused the old refresh token.\n */\n refreshToken?: string;\n /** Seconds from now. If omitted, subclass `defaultExpiresInSec` applies. */\n expiresInSec?: number;\n}\n\nexport abstract class OAuth2RefreshStrategy implements IAuthStrategy {\n protected abstract readonly provider: string;\n protected abstract readonly defaultExpiresInSec: number;\n\n protected readonly integrationReader: IIntegrationReader;\n protected readonly tokenWriter: IIntegrationTokenWriter;\n protected readonly fetchImpl: FetchLike;\n protected readonly now: () => number;\n\n constructor(opts: OAuth2RefreshStrategyOptions) {\n this.integrationReader = opts.integrationReader;\n this.tokenWriter = opts.tokenWriter;\n this.fetchImpl = opts.fetch ?? fetch;\n this.now = opts.now ?? Date.now;\n }\n\n async resolve(\n integrationId: string,\n opts: AuthResolveOptions = {},\n ): Promise<AuthCredentials> {\n const integration =\n await this.integrationReader.findByIdDecrypted(integrationId);\n if (!integration) {\n throw new Error(`Integration ${integrationId} not found`);\n }\n if (integration.provider !== this.provider) {\n throw new Error(\n `${this.constructor.name} called for non-${this.provider} integration ${integrationId} (provider=${integration.provider})`,\n );\n }\n\n const needsRefresh =\n opts.forceRefresh ||\n this.isExpiring(integration.expiresAt) ||\n !integration.accessToken;\n\n if (!needsRefresh) {\n return this.buildCredentials(integration.accessToken, integration);\n }\n\n if (!integration.refreshToken) {\n throw new IntegrationBrokenError(\n integrationId,\n 'no_refresh_token',\n 'Integration has no refresh token; user must reconnect',\n );\n }\n\n const { parsed, raw } = await this.executeRefresh(\n integrationId,\n integration.refreshToken,\n );\n const newExpiresAt = new Date(\n this.now() + (parsed.expiresInSec ?? this.defaultExpiresInSec) * 1000,\n );\n await this.tokenWriter.persistRefresh({\n integrationId,\n accessToken: parsed.accessToken,\n refreshToken: parsed.refreshToken ?? undefined,\n expiresAt: newExpiresAt,\n });\n\n return this.buildCredentials(parsed.accessToken, integration, raw);\n }\n\n protected abstract tokenEndpoint(): string;\n protected abstract refreshBodyExtras(): Record<string, string>;\n protected abstract parseRefreshResponse(raw: unknown): ParsedRefreshResponse;\n protected abstract buildCredentials(\n accessToken: string,\n integration: DecryptedIntegration,\n refreshRaw?: unknown,\n ): AuthCredentials;\n\n private async executeRefresh(\n integrationId: string,\n refreshToken: string,\n ): Promise<{ parsed: ParsedRefreshResponse; raw: unknown }> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n ...this.refreshBodyExtras(),\n });\n const response = await this.fetchImpl(this.tokenEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n if (!response.ok) {\n const err = (await safeJson(response)) as Partial<{\n error: string;\n error_description: string;\n message: string;\n }>;\n if (\n response.status === 400 &&\n (err.error === 'invalid_grant' || err.error === 'invalid_token')\n ) {\n throw new IntegrationBrokenError(\n integrationId,\n err.error ?? 'invalid_grant',\n err.error_description ?? err.message ?? 'refresh token rejected',\n );\n }\n throw new Error(\n `${this.provider} token refresh failed: ${response.status} ${err.error ?? ''} ${err.error_description ?? err.message ?? ''}`.trim(),\n );\n }\n const raw = await response.json();\n return { parsed: this.parseRefreshResponse(raw), raw };\n }\n\n private isExpiring(expiresAt: Date | null): boolean {\n if (!expiresAt) return true;\n return expiresAt.getTime() - this.now() < REFRESH_SAFETY_MS;\n }\n}\n\nasync function safeJson(response: Response): Promise<unknown> {\n try {\n return await response.clone().json();\n } catch {\n return {};\n }\n}\n"],"mappings":"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","names":[]}
@@ -8,8 +8,8 @@
8
8
  * the `isSessionExpiredError` predicate to decide whether to force-refresh
9
9
  * and retry once.
10
10
  *
11
- * This discriminator replaces the SFDC-only `instanceof` check from
12
- * dealbrain-v2's original `withAuthRetry`. See
11
+ * This discriminator replaces the SFDC-only `instanceof` check from the
12
+ * extraction-source app's original `withAuthRetry`. See
13
13
  * `docs/gate-1-auth-extraction-findings.md` (recommendation 4).
14
14
  */
15
15
  declare class SessionExpiredError extends Error {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/session-expired.error.ts"],"sourcesContent":["/**\n * Provider-agnostic marker for \"the access token was rejected; a forced\n * refresh may recover.\"\n *\n * Concrete provider error classes (e.g. SalesforceSessionExpiredError,\n * HubSpotUnauthorizedError) either extend `SessionExpiredError` directly or\n * set `isSessionExpired === true` on their instances. `withAuthRetry` uses\n * the `isSessionExpiredError` predicate to decide whether to force-refresh\n * and retry once.\n *\n * This discriminator replaces the SFDC-only `instanceof` check from\n * dealbrain-v2's original `withAuthRetry`. See\n * `docs/gate-1-auth-extraction-findings.md` (recommendation 4).\n */\nexport class SessionExpiredError extends Error {\n /** Duck-type marker — works across package boundaries where `instanceof` fails. */\n readonly isSessionExpired = true as const;\n\n constructor(message = 'Access token rejected by provider') {\n super(message);\n this.name = 'SessionExpiredError';\n }\n}\n\n/**\n * Predicate used by `withAuthRetry` by default.\n *\n * Matches any error that either `instanceof SessionExpiredError` or carries\n * the `isSessionExpired === true` marker property. Provider adapters that\n * want their existing error classes to participate can simply add the\n * marker property without touching the class hierarchy.\n */\nexport function isSessionExpiredError(err: unknown): boolean {\n if (err instanceof SessionExpiredError) return true;\n if (err !== null && typeof err === 'object' && 'isSessionExpired' in err) {\n return (err as { isSessionExpired?: unknown }).isSessionExpired === true;\n }\n return false;\n}\n"],"mappings":";AAcO,IAAM,sBAAN,cAAkC,MAAM;AAAA;AAAA,EAEpC,mBAAmB;AAAA,EAE5B,YAAY,UAAU,qCAAqC;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUO,SAAS,sBAAsB,KAAuB;AAC3D,MAAI,eAAe,oBAAqB,QAAO;AAC/C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,sBAAsB,KAAK;AACxE,WAAQ,IAAuC,qBAAqB;AAAA,EACtE;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/session-expired.error.ts"],"sourcesContent":["/**\n * Provider-agnostic marker for \"the access token was rejected; a forced\n * refresh may recover.\"\n *\n * Concrete provider error classes (e.g. SalesforceSessionExpiredError,\n * HubSpotUnauthorizedError) either extend `SessionExpiredError` directly or\n * set `isSessionExpired === true` on their instances. `withAuthRetry` uses\n * the `isSessionExpiredError` predicate to decide whether to force-refresh\n * and retry once.\n *\n * This discriminator replaces the SFDC-only `instanceof` check from the\n * extraction-source app's original `withAuthRetry`. See\n * `docs/gate-1-auth-extraction-findings.md` (recommendation 4).\n */\nexport class SessionExpiredError extends Error {\n /** Duck-type marker — works across package boundaries where `instanceof` fails. */\n readonly isSessionExpired = true as const;\n\n constructor(message = 'Access token rejected by provider') {\n super(message);\n this.name = 'SessionExpiredError';\n }\n}\n\n/**\n * Predicate used by `withAuthRetry` by default.\n *\n * Matches any error that either `instanceof SessionExpiredError` or carries\n * the `isSessionExpired === true` marker property. Provider adapters that\n * want their existing error classes to participate can simply add the\n * marker property without touching the class hierarchy.\n */\nexport function isSessionExpiredError(err: unknown): boolean {\n if (err instanceof SessionExpiredError) return true;\n if (err !== null && typeof err === 'object' && 'isSessionExpired' in err) {\n return (err as { isSessionExpired?: unknown }).isSessionExpired === true;\n }\n return false;\n}\n"],"mappings":";AAcO,IAAM,sBAAN,cAAkC,MAAM;AAAA;AAAA,EAEpC,mBAAmB;AAAA,EAE5B,YAAY,UAAU,qCAAqC;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUO,SAAS,sBAAsB,KAAuB;AAC3D,MAAI,eAAe,oBAAqB,QAAO;AAC/C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,sBAAsB,KAAK;AACxE,WAAQ,IAAuC,qBAAqB;AAAA,EACtE;AACA,SAAO;AACT;","names":[]}
@@ -8,7 +8,7 @@ import { IAuthStrategy, AuthCredentials } from '../protocols/auth-strategy.js';
8
8
  * on the refreshed token propagates rather than looping, so transient
9
9
  * adapter bugs can't hang the caller.
10
10
  *
11
- * Generalisation over dealbrain's original SFDC-specific version: the
11
+ * Generalisation over the extraction source's SFDC-specific original: the
12
12
  * session-expired classifier is injected. Providers mark their session-
13
13
  * expired errors (via `instanceof` of a marker class, or by setting a known
14
14
  * property) and pass a classifier matching that shape.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/session-expired.error.ts","../../../../../runtime/subsystems/auth/runtime/with-auth-retry.ts"],"sourcesContent":["/**\n * Provider-agnostic marker for \"the access token was rejected; a forced\n * refresh may recover.\"\n *\n * Concrete provider error classes (e.g. SalesforceSessionExpiredError,\n * HubSpotUnauthorizedError) either extend `SessionExpiredError` directly or\n * set `isSessionExpired === true` on their instances. `withAuthRetry` uses\n * the `isSessionExpiredError` predicate to decide whether to force-refresh\n * and retry once.\n *\n * This discriminator replaces the SFDC-only `instanceof` check from\n * dealbrain-v2's original `withAuthRetry`. See\n * `docs/gate-1-auth-extraction-findings.md` (recommendation 4).\n */\nexport class SessionExpiredError extends Error {\n /** Duck-type marker — works across package boundaries where `instanceof` fails. */\n readonly isSessionExpired = true as const;\n\n constructor(message = 'Access token rejected by provider') {\n super(message);\n this.name = 'SessionExpiredError';\n }\n}\n\n/**\n * Predicate used by `withAuthRetry` by default.\n *\n * Matches any error that either `instanceof SessionExpiredError` or carries\n * the `isSessionExpired === true` marker property. Provider adapters that\n * want their existing error classes to participate can simply add the\n * marker property without touching the class hierarchy.\n */\nexport function isSessionExpiredError(err: unknown): boolean {\n if (err instanceof SessionExpiredError) return true;\n if (err !== null && typeof err === 'object' && 'isSessionExpired' in err) {\n return (err as { isSessionExpired?: unknown }).isSessionExpired === true;\n }\n return false;\n}\n","/**\n * Run `op` with auth-aware retry-once on session-expired errors.\n *\n * Pattern: resolve creds → run op → if `isSessionExpired(e)` → resolve with\n * `forceRefresh: true` → retry → propagate. A second session-expired error\n * on the refreshed token propagates rather than looping, so transient\n * adapter bugs can't hang the caller.\n *\n * Generalisation over dealbrain's original SFDC-specific version: the\n * session-expired classifier is injected. Providers mark their session-\n * expired errors (via `instanceof` of a marker class, or by setting a known\n * property) and pass a classifier matching that shape.\n *\n * Default classifier recognises the marker interface `SessionExpiredError`\n * shipped in `session-expired.error.ts` — concrete provider errors that\n * extend it (or set `isSessionExpired === true`) get retried without any\n * further wiring.\n */\nimport type {\n AuthCredentials,\n IAuthStrategy,\n} from '../protocols/auth-strategy';\nimport { isSessionExpiredError } from './session-expired.error';\n\nexport interface WithAuthRetryOptions {\n /**\n * Classifier that decides whether a thrown error is a session-expired\n * signal worth retrying once with a fresh token. Defaults to the marker-\n * interface check in `session-expired.error.ts`.\n */\n isSessionExpired?: (err: unknown) => boolean;\n}\n\nexport async function withAuthRetry<T>(\n authStrategy: IAuthStrategy,\n integrationId: string,\n op: (credentials: AuthCredentials) => Promise<T>,\n options: WithAuthRetryOptions = {},\n): Promise<T> {\n const classify = options.isSessionExpired ?? isSessionExpiredError;\n\n let creds = await authStrategy.resolve(integrationId);\n try {\n return await op(creds);\n } catch (e) {\n if (!classify(e)) throw e;\n creds = await authStrategy.resolve(integrationId, { forceRefresh: true });\n return op(creds);\n }\n}\n"],"mappings":";AAcO,IAAM,sBAAN,cAAkC,MAAM;AAAA;AAAA,EAEpC,mBAAmB;AAAA,EAE5B,YAAY,UAAU,qCAAqC;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUO,SAAS,sBAAsB,KAAuB;AAC3D,MAAI,eAAe,oBAAqB,QAAO;AAC/C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,sBAAsB,KAAK;AACxE,WAAQ,IAAuC,qBAAqB;AAAA,EACtE;AACA,SAAO;AACT;;;ACLA,eAAsB,cACpB,cACA,eACA,IACA,UAAgC,CAAC,GACrB;AACZ,QAAM,WAAW,QAAQ,oBAAoB;AAE7C,MAAI,QAAQ,MAAM,aAAa,QAAQ,aAAa;AACpD,MAAI;AACF,WAAO,MAAM,GAAG,KAAK;AAAA,EACvB,SAAS,GAAG;AACV,QAAI,CAAC,SAAS,CAAC,EAAG,OAAM;AACxB,YAAQ,MAAM,aAAa,QAAQ,eAAe,EAAE,cAAc,KAAK,CAAC;AACxE,WAAO,GAAG,KAAK;AAAA,EACjB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../runtime/subsystems/auth/runtime/session-expired.error.ts","../../../../../runtime/subsystems/auth/runtime/with-auth-retry.ts"],"sourcesContent":["/**\n * Provider-agnostic marker for \"the access token was rejected; a forced\n * refresh may recover.\"\n *\n * Concrete provider error classes (e.g. SalesforceSessionExpiredError,\n * HubSpotUnauthorizedError) either extend `SessionExpiredError` directly or\n * set `isSessionExpired === true` on their instances. `withAuthRetry` uses\n * the `isSessionExpiredError` predicate to decide whether to force-refresh\n * and retry once.\n *\n * This discriminator replaces the SFDC-only `instanceof` check from the\n * extraction-source app's original `withAuthRetry`. See\n * `docs/gate-1-auth-extraction-findings.md` (recommendation 4).\n */\nexport class SessionExpiredError extends Error {\n /** Duck-type marker — works across package boundaries where `instanceof` fails. */\n readonly isSessionExpired = true as const;\n\n constructor(message = 'Access token rejected by provider') {\n super(message);\n this.name = 'SessionExpiredError';\n }\n}\n\n/**\n * Predicate used by `withAuthRetry` by default.\n *\n * Matches any error that either `instanceof SessionExpiredError` or carries\n * the `isSessionExpired === true` marker property. Provider adapters that\n * want their existing error classes to participate can simply add the\n * marker property without touching the class hierarchy.\n */\nexport function isSessionExpiredError(err: unknown): boolean {\n if (err instanceof SessionExpiredError) return true;\n if (err !== null && typeof err === 'object' && 'isSessionExpired' in err) {\n return (err as { isSessionExpired?: unknown }).isSessionExpired === true;\n }\n return false;\n}\n","/**\n * Run `op` with auth-aware retry-once on session-expired errors.\n *\n * Pattern: resolve creds → run op → if `isSessionExpired(e)` → resolve with\n * `forceRefresh: true` → retry → propagate. A second session-expired error\n * on the refreshed token propagates rather than looping, so transient\n * adapter bugs can't hang the caller.\n *\n * Generalisation over the extraction source's SFDC-specific original: the\n * session-expired classifier is injected. Providers mark their session-\n * expired errors (via `instanceof` of a marker class, or by setting a known\n * property) and pass a classifier matching that shape.\n *\n * Default classifier recognises the marker interface `SessionExpiredError`\n * shipped in `session-expired.error.ts` — concrete provider errors that\n * extend it (or set `isSessionExpired === true`) get retried without any\n * further wiring.\n */\nimport type {\n AuthCredentials,\n IAuthStrategy,\n} from '../protocols/auth-strategy';\nimport { isSessionExpiredError } from './session-expired.error';\n\nexport interface WithAuthRetryOptions {\n /**\n * Classifier that decides whether a thrown error is a session-expired\n * signal worth retrying once with a fresh token. Defaults to the marker-\n * interface check in `session-expired.error.ts`.\n */\n isSessionExpired?: (err: unknown) => boolean;\n}\n\nexport async function withAuthRetry<T>(\n authStrategy: IAuthStrategy,\n integrationId: string,\n op: (credentials: AuthCredentials) => Promise<T>,\n options: WithAuthRetryOptions = {},\n): Promise<T> {\n const classify = options.isSessionExpired ?? isSessionExpiredError;\n\n let creds = await authStrategy.resolve(integrationId);\n try {\n return await op(creds);\n } catch (e) {\n if (!classify(e)) throw e;\n creds = await authStrategy.resolve(integrationId, { forceRefresh: true });\n return op(creds);\n }\n}\n"],"mappings":";AAcO,IAAM,sBAAN,cAAkC,MAAM;AAAA;AAAA,EAEpC,mBAAmB;AAAA,EAE5B,YAAY,UAAU,qCAAqC;AACzD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAUO,SAAS,sBAAsB,KAAuB;AAC3D,MAAI,eAAe,oBAAqB,QAAO;AAC/C,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,sBAAsB,KAAK;AACxE,WAAQ,IAAuC,qBAAqB;AAAA,EACtE;AACA,SAAO;AACT;;;ACLA,eAAsB,cACpB,cACA,eACA,IACA,UAAgC,CAAC,GACrB;AACZ,QAAM,WAAW,QAAQ,oBAAoB;AAE7C,MAAI,QAAQ,MAAM,aAAa,QAAQ,aAAa;AACpD,MAAI;AACF,WAAO,MAAM,GAAG,KAAK;AAAA,EACvB,SAAS,GAAG;AACV,QAAI,CAAC,SAAS,CAAC,EAAG,OAAM;AACxB,YAAQ,MAAM,aAAa,QAAQ,eAAe,EAAE,cAAc,KAAK,CAAC;AACxE,WAAO,GAAG,KAAK;AAAA,EACjB;AACF;","names":[]}
@@ -21,15 +21,20 @@ export { ObservabilityModule, ObservabilityModuleOptions } from './observability
21
21
  export { ObservabilityError } from './observability/observability-errors.js';
22
22
  export { AuthCredentials, AuthResolveOptions, IAuthStrategy } from './auth/protocols/auth-strategy.js';
23
23
  export { IEncryptionKey } from './auth/protocols/encryption-key.js';
24
- export { IOAuthStateStore, OAuthStateEntry } from './auth/protocols/oauth-state-store.js';
25
- export { DecryptedIntegration, IIntegrationReader, IIntegrationTokenWriter, IntegrationTokenUpdate } from './auth/protocols/integration-store.js';
26
- export { AUTH_INTEGRATION_READER, AUTH_INTEGRATION_TOKEN_WRITER, ENCRYPTION_KEY, OAUTH_STATE_STORE } from './auth/auth.tokens.js';
24
+ export { IOAuthStateStore, OAuthStateError, OAuthStateRecord } from './auth/protocols/oauth-state-store.js';
25
+ export { DecryptedIntegration, IIntegrationGrantSink, IIntegrationReader, IIntegrationTokenWriter, IntegrationGrantInput, IntegrationTokenUpdate } from './auth/protocols/integration-store.js';
26
+ export { IUserContext } from './auth/protocols/user-context.js';
27
+ export { ExchangedTokens, IProviderStrategy, ProviderStrategyRegistry } from './auth/protocols/provider-strategy.js';
28
+ export { AUTH_INTEGRATION_GRANT_SINK, AUTH_INTEGRATION_READER, AUTH_INTEGRATION_TOKEN_WRITER, AUTH_OPTIONS, AUTH_USER_CONTEXT, ENCRYPTION_KEY, OAUTH_STATE_STORE, STRATEGY_REGISTRY } from './auth/auth.tokens.js';
27
29
  export { OAuth2RefreshStrategy, ParsedRefreshResponse } from './auth/runtime/oauth2-refresh.strategy.js';
28
30
  export { withAuthRetry } from './auth/runtime/with-auth-retry.js';
29
31
  export { IntegrationBrokenError } from './auth/runtime/integration-broken.error.js';
30
32
  export { SessionExpiredError, isSessionExpiredError } from './auth/runtime/session-expired.error.js';
33
+ export { AuthOAuthState, authOAuthState } from './auth/auth-oauth-state.schema.js';
31
34
  export { EnvEncryptionKey } from './auth/backends/encryption-key/env.js';
32
- export { InMemoryOAuthStateStore } from './auth/backends/oauth-state-store/in-memory.js';
35
+ export { MemoryOAuthStateStore } from './auth/backends/state-store.memory-backend.js';
36
+ export { DrizzleOAuthStateStore } from './auth/backends/state-store.drizzle-backend.js';
37
+ export { AuthController } from './auth/controllers/auth.controller.js';
33
38
  export { AuthModule } from './auth/auth.module.js';
34
39
  export { CursorSnapshot } from './sync/sync-cursor-store.protocol.js';
35
40
  export { StatusHistogram } from './bridge/bridge.protocol.js';
@@ -3971,11 +3971,25 @@ var ObservabilityError = class extends Error {
3971
3971
  cause;
3972
3972
  };
3973
3973
 
3974
+ // runtime/subsystems/auth/protocols/oauth-state-store.ts
3975
+ var OAuthStateError = class extends Error {
3976
+ constructor(message, reason) {
3977
+ super(message);
3978
+ this.reason = reason;
3979
+ this.name = "OAuthStateError";
3980
+ }
3981
+ reason;
3982
+ };
3983
+
3974
3984
  // runtime/subsystems/auth/auth.tokens.ts
3975
3985
  var ENCRYPTION_KEY = /* @__PURE__ */ Symbol("ENCRYPTION_KEY");
3976
3986
  var OAUTH_STATE_STORE = /* @__PURE__ */ Symbol("OAUTH_STATE_STORE");
3977
3987
  var AUTH_INTEGRATION_READER = /* @__PURE__ */ Symbol("AUTH_INTEGRATION_READER");
3978
3988
  var AUTH_INTEGRATION_TOKEN_WRITER = /* @__PURE__ */ Symbol("AUTH_INTEGRATION_TOKEN_WRITER");
3989
+ var AUTH_INTEGRATION_GRANT_SINK = /* @__PURE__ */ Symbol("AUTH_INTEGRATION_GRANT_SINK");
3990
+ var AUTH_USER_CONTEXT = /* @__PURE__ */ Symbol("AUTH_USER_CONTEXT");
3991
+ var STRATEGY_REGISTRY = /* @__PURE__ */ Symbol("STRATEGY_REGISTRY");
3992
+ var AUTH_OPTIONS = /* @__PURE__ */ Symbol("AUTH_OPTIONS");
3979
3993
 
3980
3994
  // runtime/subsystems/auth/runtime/integration-broken.error.ts
3981
3995
  var IntegrationBrokenError = class extends Error {
@@ -4112,6 +4126,15 @@ async function withAuthRetry(authStrategy, integrationId, op, options = {}) {
4112
4126
  }
4113
4127
  }
4114
4128
 
4129
+ // runtime/subsystems/auth/auth-oauth-state.schema.ts
4130
+ import { pgTable as pgTable4, text as text4, timestamp as timestamp4 } from "drizzle-orm/pg-core";
4131
+ var authOAuthState = pgTable4("auth_oauth_state", {
4132
+ state: text4("state").primaryKey(),
4133
+ userId: text4("user_id").notNull(),
4134
+ redirect: text4("redirect"),
4135
+ expiresAt: timestamp4("expires_at", { withTimezone: true }).notNull()
4136
+ });
4137
+
4115
4138
  // runtime/subsystems/auth/backends/encryption-key/env.ts
4116
4139
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
4117
4140
  var ALGO = "aes-256-gcm";
@@ -4122,7 +4145,7 @@ var EnvEncryptionKey = class {
4122
4145
  key;
4123
4146
  constructor(opts = {}) {
4124
4147
  const env = opts.env ?? process.env;
4125
- const envVar = opts.envVar ?? "TOKEN_ENCRYPTION_KEY";
4148
+ const envVar = opts.envVar ?? "INTEGRATION_TOKEN_ENCRYPTION_KEY";
4126
4149
  const raw = env[envVar];
4127
4150
  if (!raw) {
4128
4151
  throw new Error(
@@ -4162,26 +4185,198 @@ var EnvEncryptionKey = class {
4162
4185
  }
4163
4186
  };
4164
4187
 
4165
- // runtime/subsystems/auth/backends/oauth-state-store/in-memory.ts
4166
- var InMemoryOAuthStateStore = class {
4188
+ // runtime/subsystems/auth/backends/state-store.memory-backend.ts
4189
+ import { randomBytes as randomBytes2 } from "crypto";
4190
+ var MemoryOAuthStateStore = class {
4167
4191
  store = /* @__PURE__ */ new Map();
4168
4192
  ttlMs;
4169
4193
  now;
4194
+ generateToken;
4170
4195
  constructor(opts = {}) {
4171
4196
  this.ttlMs = opts.ttlMs ?? 10 * 60 * 1e3;
4172
4197
  this.now = opts.now ?? (() => Date.now());
4198
+ this.generateToken = opts.generateToken ?? (() => randomBytes2(32).toString("base64url"));
4173
4199
  }
4174
- async put(state, entry) {
4175
- this.store.set(state, { entry, expiresAt: this.now() + this.ttlMs });
4200
+ async generate(record) {
4201
+ const state = this.generateToken();
4202
+ this.store.set(state, {
4203
+ record: { ...record },
4204
+ expiresAt: this.now() + this.ttlMs
4205
+ });
4206
+ return state;
4176
4207
  }
4177
4208
  async consume(state) {
4178
4209
  const slot = this.store.get(state);
4179
- if (!slot) return null;
4210
+ if (!slot) {
4211
+ throw new OAuthStateError(
4212
+ `OAuth state token unknown or already consumed`,
4213
+ "missing"
4214
+ );
4215
+ }
4180
4216
  this.store.delete(state);
4181
- if (slot.expiresAt <= this.now()) return null;
4182
- return slot.entry;
4217
+ if (slot.expiresAt <= this.now()) {
4218
+ throw new OAuthStateError(`OAuth state token expired`, "expired");
4219
+ }
4220
+ return slot.record;
4221
+ }
4222
+ };
4223
+
4224
+ // runtime/subsystems/auth/backends/state-store.drizzle-backend.ts
4225
+ import { randomBytes as randomBytes3 } from "crypto";
4226
+ import { eq as eq7 } from "drizzle-orm";
4227
+ var DrizzleOAuthStateStore = class {
4228
+ constructor(db, opts = {}) {
4229
+ this.db = db;
4230
+ this.ttlMs = opts.ttlMs ?? 10 * 60 * 1e3;
4231
+ this.now = opts.now ?? (() => Date.now());
4232
+ this.generateToken = opts.generateToken ?? (() => randomBytes3(32).toString("base64url"));
4233
+ }
4234
+ db;
4235
+ ttlMs;
4236
+ now;
4237
+ generateToken;
4238
+ async generate(record) {
4239
+ const state = this.generateToken();
4240
+ const expiresAt = new Date(this.now() + this.ttlMs);
4241
+ await this.db.insert(authOAuthState).values({
4242
+ state,
4243
+ userId: record.userId,
4244
+ redirect: record.redirect ?? null,
4245
+ expiresAt
4246
+ });
4247
+ return state;
4248
+ }
4249
+ async consume(state) {
4250
+ const rows = await this.db.delete(authOAuthState).where(eq7(authOAuthState.state, state)).returning();
4251
+ const row = rows[0];
4252
+ if (!row) {
4253
+ throw new OAuthStateError(
4254
+ `OAuth state token unknown or already consumed`,
4255
+ "missing"
4256
+ );
4257
+ }
4258
+ if (row.expiresAt.getTime() <= this.now()) {
4259
+ throw new OAuthStateError(`OAuth state token expired`, "expired");
4260
+ }
4261
+ return {
4262
+ userId: row.userId,
4263
+ redirect: row.redirect ?? void 0
4264
+ };
4265
+ }
4266
+ };
4267
+
4268
+ // runtime/subsystems/auth/controllers/auth.controller.ts
4269
+ import {
4270
+ Controller,
4271
+ Get,
4272
+ Inject as Inject16,
4273
+ Param,
4274
+ Query,
4275
+ Req,
4276
+ Res,
4277
+ HttpException,
4278
+ HttpStatus
4279
+ } from "@nestjs/common";
4280
+ var AuthController = class {
4281
+ constructor(registry, userContext, stateStore, grantSink, options) {
4282
+ this.registry = registry;
4283
+ this.userContext = userContext;
4284
+ this.stateStore = stateStore;
4285
+ this.grantSink = grantSink;
4286
+ this.options = options;
4287
+ }
4288
+ registry;
4289
+ userContext;
4290
+ stateStore;
4291
+ grantSink;
4292
+ options;
4293
+ async connect(slug, redirect, req, res) {
4294
+ const strategy = this.requireStrategy(slug);
4295
+ const userId = await this.userContext.getCurrentUserId(req);
4296
+ const state = await this.stateStore.generate({ userId, redirect });
4297
+ const url = strategy.buildAuthorizeUrl({
4298
+ state,
4299
+ redirectUri: this.redirectUriFor(slug)
4300
+ });
4301
+ return res.redirect(HttpStatus.FOUND, url);
4302
+ }
4303
+ async callback(slug, code, state, res) {
4304
+ const strategy = this.requireStrategy(slug);
4305
+ if (!code) {
4306
+ throw new HttpException(
4307
+ `Missing 'code' query param`,
4308
+ HttpStatus.BAD_REQUEST
4309
+ );
4310
+ }
4311
+ if (!state) {
4312
+ throw new HttpException(
4313
+ `Missing 'state' query param`,
4314
+ HttpStatus.BAD_REQUEST
4315
+ );
4316
+ }
4317
+ const { userId, redirect } = await this.stateStore.consume(state);
4318
+ const tokens = await strategy.exchangeCodeForTokens({
4319
+ code,
4320
+ redirectUri: this.redirectUriFor(slug)
4321
+ });
4322
+ await this.grantSink.createOrUpdateFromOAuthGrant({
4323
+ userId,
4324
+ provider: slug,
4325
+ accessToken: tokens.accessToken,
4326
+ refreshToken: tokens.refreshToken,
4327
+ expiresAt: tokens.expiresAt,
4328
+ scope: tokens.scope,
4329
+ externalAccountId: tokens.externalAccountId,
4330
+ providerMetadata: tokens.providerMetadata
4331
+ });
4332
+ return res.redirect(
4333
+ HttpStatus.FOUND,
4334
+ redirect ?? `/settings/integrations?connected=${encodeURIComponent(slug)}`
4335
+ );
4336
+ }
4337
+ requireStrategy(slug) {
4338
+ const strategy = this.registry.get(slug);
4339
+ if (!strategy) {
4340
+ throw new HttpException(
4341
+ `Unknown provider '${slug}'`,
4342
+ HttpStatus.NOT_FOUND
4343
+ );
4344
+ }
4345
+ return strategy;
4346
+ }
4347
+ redirectUriFor(slug) {
4348
+ const base = this.options.redirectUriBase;
4349
+ if (!base) {
4350
+ throw new Error(
4351
+ `AuthModule.forRoot: redirectUriBase is required when AuthController is enabled`
4352
+ );
4353
+ }
4354
+ const trimmed = base.replace(/\/+$/, "");
4355
+ return `${trimmed}/auth/${encodeURIComponent(slug)}/callback`;
4183
4356
  }
4184
4357
  };
4358
+ __decorateClass([
4359
+ Get(":provider/connect"),
4360
+ __decorateParam(0, Param("provider")),
4361
+ __decorateParam(1, Query("redirect")),
4362
+ __decorateParam(2, Req()),
4363
+ __decorateParam(3, Res())
4364
+ ], AuthController.prototype, "connect", 1);
4365
+ __decorateClass([
4366
+ Get(":provider/callback"),
4367
+ __decorateParam(0, Param("provider")),
4368
+ __decorateParam(1, Query("code")),
4369
+ __decorateParam(2, Query("state")),
4370
+ __decorateParam(3, Res())
4371
+ ], AuthController.prototype, "callback", 1);
4372
+ AuthController = __decorateClass([
4373
+ Controller("auth"),
4374
+ __decorateParam(0, Inject16(STRATEGY_REGISTRY)),
4375
+ __decorateParam(1, Inject16(AUTH_USER_CONTEXT)),
4376
+ __decorateParam(2, Inject16(OAUTH_STATE_STORE)),
4377
+ __decorateParam(3, Inject16(AUTH_INTEGRATION_GRANT_SINK)),
4378
+ __decorateParam(4, Inject16(AUTH_OPTIONS))
4379
+ ], AuthController);
4185
4380
 
4186
4381
  // runtime/subsystems/auth/auth.module.ts
4187
4382
  import { Module as Module7 } from "@nestjs/common";
@@ -4192,24 +4387,54 @@ function resolveEncryptionKeyProvider(choice) {
4192
4387
  return { provide: ENCRYPTION_KEY, ...choice };
4193
4388
  }
4194
4389
  function resolveOAuthStateStoreProvider(choice) {
4195
- if (choice === "in-memory") {
4196
- return { provide: OAUTH_STATE_STORE, useClass: InMemoryOAuthStateStore };
4390
+ if (choice === "memory") {
4391
+ return { provide: OAUTH_STATE_STORE, useClass: MemoryOAuthStateStore };
4392
+ }
4393
+ if (choice === "drizzle") {
4394
+ return {
4395
+ provide: OAUTH_STATE_STORE,
4396
+ useFactory: (db) => {
4397
+ if (!db) {
4398
+ throw new Error(
4399
+ "AuthModule.forRoot: oauthStateStore: 'drizzle' selected but DRIZZLE provider is not available. Ensure DatabaseModule (or another provider exposing DRIZZLE) is imported before AuthModule.forRoot."
4400
+ );
4401
+ }
4402
+ return new DrizzleOAuthStateStore(db);
4403
+ },
4404
+ inject: [{ token: DRIZZLE, optional: true }]
4405
+ };
4197
4406
  }
4198
4407
  return { provide: OAUTH_STATE_STORE, ...choice };
4199
4408
  }
4200
4409
  var AuthModule = class {
4201
4410
  static forRoot(options = {}) {
4411
+ const resolved = {
4412
+ encryptionKey: options.encryptionKey ?? "env",
4413
+ oauthStateStore: options.oauthStateStore ?? "memory",
4414
+ enableController: options.enableController ?? false,
4415
+ redirectUriBase: options.redirectUriBase
4416
+ };
4417
+ if (resolved.enableController && !resolved.redirectUriBase) {
4418
+ throw new Error(
4419
+ "AuthModule.forRoot: redirectUriBase is required when enableController: true"
4420
+ );
4421
+ }
4202
4422
  const encryptionKeyProvider = resolveEncryptionKeyProvider(
4203
- options.encryptionKey ?? "env"
4423
+ resolved.encryptionKey
4204
4424
  );
4205
4425
  const oauthStateStoreProvider = resolveOAuthStateStoreProvider(
4206
- options.oauthStateStore ?? "in-memory"
4426
+ resolved.oauthStateStore
4207
4427
  );
4428
+ const optionsProvider = {
4429
+ provide: AUTH_OPTIONS,
4430
+ useValue: resolved
4431
+ };
4208
4432
  return {
4209
4433
  module: AuthModule,
4210
4434
  global: true,
4211
- providers: [encryptionKeyProvider, oauthStateStoreProvider],
4212
- exports: [ENCRYPTION_KEY, OAUTH_STATE_STORE]
4435
+ providers: [encryptionKeyProvider, oauthStateStoreProvider, optionsProvider],
4436
+ controllers: resolved.enableController ? [AuthController] : [],
4437
+ exports: [ENCRYPTION_KEY, OAUTH_STATE_STORE, AUTH_OPTIONS]
4213
4438
  };
4214
4439
  }
4215
4440
  };
@@ -4217,32 +4442,40 @@ AuthModule = __decorateClass([
4217
4442
  Module7({})
4218
4443
  ], AuthModule);
4219
4444
  export {
4445
+ AUTH_INTEGRATION_GRANT_SINK,
4220
4446
  AUTH_INTEGRATION_READER,
4221
4447
  AUTH_INTEGRATION_TOKEN_WRITER,
4448
+ AUTH_OPTIONS,
4449
+ AUTH_USER_CONTEXT,
4450
+ AuthController,
4222
4451
  AuthModule,
4223
4452
  CACHE,
4224
4453
  CacheModule,
4225
4454
  DrizzleCacheService,
4226
4455
  DrizzleEventBus,
4456
+ DrizzleOAuthStateStore,
4227
4457
  ENCRYPTION_KEY,
4228
4458
  EVENT_BUS,
4229
4459
  EnvEncryptionKey,
4230
4460
  EventsModule,
4231
- InMemoryOAuthStateStore,
4232
4461
  IntegrationBrokenError,
4233
4462
  LocalStorageBackend,
4234
4463
  MemoryCacheService,
4235
4464
  MemoryEventBus,
4465
+ MemoryOAuthStateStore,
4236
4466
  MemoryStorageBackend,
4237
4467
  OAUTH_STATE_STORE,
4238
4468
  OAuth2RefreshStrategy,
4469
+ OAuthStateError,
4239
4470
  OBSERVABILITY,
4240
4471
  OBSERVABILITY_MODULE_OPTIONS,
4241
4472
  ObservabilityError,
4242
4473
  ObservabilityModule,
4243
4474
  STORAGE,
4475
+ STRATEGY_REGISTRY,
4244
4476
  SessionExpiredError,
4245
4477
  StorageModule,
4478
+ authOAuthState,
4246
4479
  collisionModeEnum,
4247
4480
  isSessionExpiredError,
4248
4481
  jobRunStatusEnum,