@pattern-stack/codegen 0.6.5 → 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.
- package/CHANGELOG.md +11 -1
- package/dist/runtime/subsystems/auth/auth.module.js +1 -1
- package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.d.ts +1 -1
- package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
- package/dist/runtime/subsystems/auth/backends/encryption-key/env.d.ts +1 -1
- package/dist/runtime/subsystems/auth/backends/encryption-key/env.js +1 -1
- package/dist/runtime/subsystems/auth/backends/encryption-key/env.js.map +1 -1
- package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
- package/dist/runtime/subsystems/auth/index.d.ts +1 -1
- package/dist/runtime/subsystems/auth/index.js +1 -1
- package/dist/runtime/subsystems/auth/index.js.map +1 -1
- package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +1 -1
- package/dist/runtime/subsystems/auth/protocols/integration-store.d.ts +2 -2
- package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +13 -6
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +2 -2
- package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/session-expired.error.d.ts +2 -2
- package/dist/runtime/subsystems/auth/runtime/session-expired.error.js.map +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
- package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
- package/dist/runtime/subsystems/index.d.ts +1 -1
- package/dist/runtime/subsystems/index.js +1 -1
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +1 -1
- package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +1 -1
- package/dist/runtime/subsystems/sync/index.js.map +1 -1
- package/dist/runtime/subsystems/sync/sync-change-source.protocol.d.ts +1 -1
- package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/sync/sync-loopback.protocol.d.ts +3 -4
- package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +1 -1
- package/dist/runtime/subsystems/sync/sync.module.js.map +1 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/subsystems/auth/auth.tokens.ts +1 -1
- package/runtime/subsystems/auth/backends/encryption-key/env.ts +3 -3
- package/runtime/subsystems/auth/controllers/auth.controller.ts +3 -3
- package/runtime/subsystems/auth/index.ts +2 -2
- package/runtime/subsystems/auth/protocols/auth-strategy.ts +1 -1
- package/runtime/subsystems/auth/protocols/integration-store.ts +2 -2
- package/runtime/subsystems/auth/protocols/provider-strategy.ts +12 -5
- package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +2 -2
- package/runtime/subsystems/auth/runtime/session-expired.error.ts +2 -2
- package/runtime/subsystems/auth/runtime/with-auth-retry.ts +1 -1
- package/runtime/subsystems/index.ts +1 -1
- package/runtime/subsystems/sync/deep-equal.differ.ts +1 -1
- package/runtime/subsystems/sync/execute-sync.use-case.ts +1 -1
- package/runtime/subsystems/sync/sync-change-source.protocol.ts +1 -1
- package/runtime/subsystems/sync/sync-cursor-store.memory-backend.ts +1 -1
- package/runtime/subsystems/sync/sync-loopback.protocol.ts +3 -4
- package/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.ts +1 -1
- package/templates/subsystem/auth/app-module-hook.ejs.t +1 -1
- package/templates/subsystem/auth/env-config.ejs.t +5 -5
- package/templates/subsystem/auth/prompt.js +3 -3
- package/templates/subsystem/auth-config/codegen-config-auth-block.ejs.t +1 -1
|
@@ -3,7 +3,7 @@ import './auth-strategy.js';
|
|
|
3
3
|
import './integration-store.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Auth subsystem — `
|
|
6
|
+
* Auth subsystem — `IProviderStrategy` contract.
|
|
7
7
|
*
|
|
8
8
|
* Extension of `OAuth2RefreshStrategy` (which already covers the refresh
|
|
9
9
|
* path) that adds the two methods needed by the connect/callback dance:
|
|
@@ -15,11 +15,18 @@ import './integration-store.js';
|
|
|
15
15
|
* stay consumer-side per ADR-031 ("every app has different combinations").
|
|
16
16
|
* They typically subclass `OAuth2RefreshStrategy` for the refresh path and
|
|
17
17
|
* implement these two methods structurally — that satisfies
|
|
18
|
-
* `
|
|
18
|
+
* `IProviderStrategy` because TS lets interfaces extend classes by type.
|
|
19
19
|
*
|
|
20
20
|
* AuthController never imports a concrete strategy — it injects the
|
|
21
|
-
* `STRATEGY_REGISTRY` (a `ReadonlyMap<provider-slug,
|
|
21
|
+
* `STRATEGY_REGISTRY` (a `ReadonlyMap<provider-slug, IProviderStrategy>`)
|
|
22
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.
|
|
23
30
|
*/
|
|
24
31
|
|
|
25
32
|
interface ExchangedTokens {
|
|
@@ -31,7 +38,7 @@ interface ExchangedTokens {
|
|
|
31
38
|
/** Provider-specific bag (SFDC `instance_url`, Google `sub`, …). */
|
|
32
39
|
providerMetadata?: Record<string, unknown>;
|
|
33
40
|
}
|
|
34
|
-
interface
|
|
41
|
+
interface IProviderStrategy extends OAuth2RefreshStrategy {
|
|
35
42
|
buildAuthorizeUrl(args: {
|
|
36
43
|
state: string;
|
|
37
44
|
redirectUri: string;
|
|
@@ -42,6 +49,6 @@ interface ProviderStrategy extends OAuth2RefreshStrategy {
|
|
|
42
49
|
}): Promise<ExchangedTokens>;
|
|
43
50
|
}
|
|
44
51
|
/** The DI value type behind the `STRATEGY_REGISTRY` token. */
|
|
45
|
-
type ProviderStrategyRegistry = ReadonlyMap<string,
|
|
52
|
+
type ProviderStrategyRegistry = ReadonlyMap<string, IProviderStrategy>;
|
|
46
53
|
|
|
47
|
-
export type { ExchangedTokens,
|
|
54
|
+
export type { ExchangedTokens, IProviderStrategy, ProviderStrategyRegistry };
|
|
@@ -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
|
|
9
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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 *
|
|
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
|
|
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 *
|
|
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":[]}
|
|
@@ -24,7 +24,7 @@ export { IEncryptionKey } from './auth/protocols/encryption-key.js';
|
|
|
24
24
|
export { IOAuthStateStore, OAuthStateError, OAuthStateRecord } from './auth/protocols/oauth-state-store.js';
|
|
25
25
|
export { DecryptedIntegration, IIntegrationGrantSink, IIntegrationReader, IIntegrationTokenWriter, IntegrationGrantInput, IntegrationTokenUpdate } from './auth/protocols/integration-store.js';
|
|
26
26
|
export { IUserContext } from './auth/protocols/user-context.js';
|
|
27
|
-
export { ExchangedTokens,
|
|
27
|
+
export { ExchangedTokens, IProviderStrategy, ProviderStrategyRegistry } from './auth/protocols/provider-strategy.js';
|
|
28
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';
|
|
29
29
|
export { OAuth2RefreshStrategy, ParsedRefreshResponse } from './auth/runtime/oauth2-refresh.strategy.js';
|
|
30
30
|
export { withAuthRetry } from './auth/runtime/with-auth-retry.js';
|
|
@@ -4145,7 +4145,7 @@ var EnvEncryptionKey = class {
|
|
|
4145
4145
|
key;
|
|
4146
4146
|
constructor(opts = {}) {
|
|
4147
4147
|
const env = opts.env ?? process.env;
|
|
4148
|
-
const envVar = opts.envVar ?? "
|
|
4148
|
+
const envVar = opts.envVar ?? "INTEGRATION_TOKEN_ENCRYPTION_KEY";
|
|
4149
4149
|
const raw = env[envVar];
|
|
4150
4150
|
if (!raw) {
|
|
4151
4151
|
throw new Error(
|