@mastra/factory 0.10.0-alpha.14 → 0.10.0-alpha.3

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 (123) hide show
  1. package/CHANGELOG.md +0 -180
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +0 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +0 -8
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +6 -23
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.d.ts +0 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -2
  12. package/dist/integrations/base.d.ts +1 -3
  13. package/dist/integrations/base.d.ts.map +1 -1
  14. package/dist/integrations/github/integration.d.ts +0 -14
  15. package/dist/integrations/github/integration.d.ts.map +1 -1
  16. package/dist/integrations/github/integration.js +1 -48
  17. package/dist/integrations/github/integration.js.map +1 -1
  18. package/dist/integrations/github/provenance.d.ts +2 -7
  19. package/dist/integrations/github/provenance.d.ts.map +1 -1
  20. package/dist/integrations/github/provenance.js +2 -16
  21. package/dist/integrations/github/provenance.js.map +1 -1
  22. package/dist/integrations/github/routes.d.ts +1 -8
  23. package/dist/integrations/github/routes.d.ts.map +1 -1
  24. package/dist/integrations/github/routes.js +7 -228
  25. package/dist/integrations/github/routes.js.map +1 -1
  26. package/dist/integrations/github/session-subscriptions.d.ts +0 -9
  27. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  28. package/dist/integrations/github/session-subscriptions.js +1 -39
  29. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  30. package/dist/integrations/linear/routes.d.ts.map +1 -1
  31. package/dist/integrations/linear/routes.js +0 -50
  32. package/dist/integrations/linear/routes.js.map +1 -1
  33. package/dist/integrations/platform/github/integration.d.ts +1 -3
  34. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  35. package/dist/integrations/platform/github/integration.js +0 -33
  36. package/dist/integrations/platform/github/integration.js.map +1 -1
  37. package/dist/routes/intake.d.ts +0 -5
  38. package/dist/routes/intake.d.ts.map +1 -1
  39. package/dist/routes/intake.js +19 -70
  40. package/dist/routes/intake.js.map +1 -1
  41. package/dist/routes/surface.d.ts +3 -4
  42. package/dist/routes/surface.d.ts.map +1 -1
  43. package/dist/routes/surface.js +12 -10
  44. package/dist/routes/surface.js.map +1 -1
  45. package/dist/routes/work-items.d.ts.map +1 -1
  46. package/dist/routes/work-items.js +1 -1
  47. package/dist/routes/work-items.js.map +1 -1
  48. package/dist/rules/defaults.js +5 -5
  49. package/dist/rules/defaults.js.map +1 -1
  50. package/dist/rules/dispatcher.d.ts +0 -9
  51. package/dist/rules/dispatcher.d.ts.map +1 -1
  52. package/dist/rules/dispatcher.js +17 -69
  53. package/dist/rules/dispatcher.js.map +1 -1
  54. package/dist/rules/processor.d.ts.map +1 -1
  55. package/dist/rules/processor.js +11 -28
  56. package/dist/rules/processor.js.map +1 -1
  57. package/dist/rules/tools.d.ts.map +1 -1
  58. package/dist/rules/tools.js +5 -9
  59. package/dist/rules/tools.js.map +1 -1
  60. package/dist/rules/transition-service.d.ts +2 -5
  61. package/dist/rules/transition-service.d.ts.map +1 -1
  62. package/dist/rules/transition-service.js +2 -15
  63. package/dist/rules/transition-service.js.map +1 -1
  64. package/dist/rules/types.d.ts +1 -4
  65. package/dist/rules/types.d.ts.map +1 -1
  66. package/dist/rules/types.js +1 -17
  67. package/dist/rules/types.js.map +1 -1
  68. package/dist/session/factory-session.d.ts +3 -9
  69. package/dist/session/factory-session.d.ts.map +1 -1
  70. package/dist/session/factory-session.js +4 -5
  71. package/dist/session/factory-session.js.map +1 -1
  72. package/dist/session/first-exec-capture.d.ts +10 -16
  73. package/dist/session/first-exec-capture.d.ts.map +1 -1
  74. package/dist/session/first-exec-capture.js +14 -74
  75. package/dist/session/first-exec-capture.js.map +1 -1
  76. package/dist/session/first-message-capture.d.ts +10 -14
  77. package/dist/session/first-message-capture.d.ts.map +1 -1
  78. package/dist/session/first-message-capture.js +11 -17
  79. package/dist/session/first-message-capture.js.map +1 -1
  80. package/dist/storage/domains/credentials/base.d.ts +1 -3
  81. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  82. package/dist/storage/domains/credentials/base.js +15 -33
  83. package/dist/storage/domains/credentials/base.js.map +1 -1
  84. package/dist/storage/domains/custom-providers/base.d.ts +1 -3
  85. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  86. package/dist/storage/domains/custom-providers/base.js +25 -45
  87. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  88. package/dist/storage/domains/integrations/base.d.ts +1 -3
  89. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  90. package/dist/storage/domains/integrations/base.js +12 -28
  91. package/dist/storage/domains/integrations/base.js.map +1 -1
  92. package/dist/storage/domains/source-control/base.d.ts +0 -5
  93. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  94. package/dist/storage/domains/source-control/base.js +0 -6
  95. package/dist/storage/domains/source-control/base.js.map +1 -1
  96. package/dist/storage/domains/source-control/inmemory.d.ts +0 -4
  97. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  98. package/dist/storage/domains/source-control/inmemory.js +0 -7
  99. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  100. package/dist/storage/domains/work-items/base.d.ts +2 -21
  101. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  102. package/dist/storage/domains/work-items/base.js +9 -65
  103. package/dist/storage/domains/work-items/base.js.map +1 -1
  104. package/factory-skills/factory-rereview/SKILL.md +0 -2
  105. package/factory-skills/factory-review/SKILL.md +0 -2
  106. package/factory-skills/factory-triage/SKILL.md +17 -7
  107. package/package.json +9 -9
  108. package/dist/secret-encryption.d.ts +0 -25
  109. package/dist/secret-encryption.d.ts.map +0 -1
  110. package/dist/secret-encryption.js +0 -96
  111. package/dist/secret-encryption.js.map +0 -1
  112. package/dist/session/session-title.d.ts +0 -7
  113. package/dist/session/session-title.d.ts.map +0 -1
  114. package/dist/session/session-title.js +0 -14
  115. package/dist/session/session-title.js.map +0 -1
  116. package/dist/session/thread-title-mirror.d.ts +0 -35
  117. package/dist/session/thread-title-mirror.d.ts.map +0 -1
  118. package/dist/session/thread-title-mirror.js +0 -51
  119. package/dist/session/thread-title-mirror.js.map +0 -1
  120. package/dist/work-item-branch.d.ts +0 -23
  121. package/dist/work-item-branch.d.ts.map +0 -1
  122. package/dist/work-item-branch.js +0 -39
  123. package/dist/work-item-branch.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#migrateCredential","#db","#decryptCredential"],"sources":["../../../../src/storage/domains/credentials/base.ts"],"sourcesContent":["/**\n * Model provider credentials domain — tenant-scoped OAuth tokens and API keys\n * for model providers (Anthropic/Claude Max, OpenAI Codex, GitHub Copilot,\n * xAI, plus plain API-key providers).\n *\n * Tenancy is two-level:\n * - **user-scoped** rows (`userId` present): personal plan OAuth tokens and\n * personal API keys. OAuth plan tokens are ONLY ever user-scoped — they are\n * personal subscriptions, and sharing one would bill/rate-limit a single\n * account for the whole org.\n * - **org-scoped** rows (`userId` absent): shared API keys set by an org\n * admin, inherited by every member.\n *\n * Resolution order at model-call time is user > org (the caller layers server\n * env vars underneath as a final fallback).\n *\n * Secrets posture: credentials are stored server-side only and never returned\n * to the client (same as `linear_connections.access_token`). Encryption at\n * rest is out of scope, matching the existing posture.\n *\n * The domain also owns `oauth_login_sessions`: pending web login flows\n * (paste-code PKCE verifiers, device-code poll state) persisted server-side so\n * any replica can complete or poll a flow started on another.\n */\n\nimport type { AuthCredential, OAuthCredential } from '@mastra/code-sdk/auth/types';\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\n/** Owning tenant of a credential row. `userId` absent = org-scoped row. */\nexport interface CredentialTenant {\n orgId: string;\n userId?: string;\n}\n\n/** Which tenancy level a credential row lives at. */\nexport type CredentialScope = 'user' | 'org';\n\n/** One stored credential, annotated with its scope. */\nexport interface CredentialRecord {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n updatedAt: Date;\n}\n\n/** Result of per-caller resolution: the winning credential and its scope. */\nexport interface ResolvedCredential {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n}\n\n/** Kind of pending web login flow a session row tracks. */\nexport type LoginSessionKind = 'paste-code' | 'device-code';\n\n/** Scope the completed credential should be written to. */\nexport type LoginCredentialScope = 'user' | 'org';\n\n/** One pending OAuth login flow, persisted so any replica can continue it. */\nexport interface LoginSessionRow {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n /** Where the completed credential lands; absent rows default to `'user'`. */\n credentialScope?: LoginCredentialScope;\n /** Serialized flow state (PKCE verifier, device-code pending state, ...). */\n pending: Record<string, unknown>;\n expiresAt: Date;\n /** Earliest time the next upstream poll is allowed (device-code flows). */\n nextPollAt: Date | null;\n createdAt: Date;\n}\n\nexport interface CreateLoginSessionInput {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n credentialScope?: LoginCredentialScope;\n pending: Record<string, unknown>;\n expiresAt: Date;\n nextPollAt?: Date | null;\n}\n\n/** Mirror of `AuthStorage.getApiKey()`'s expiry check. */\nexport function isOAuthCredentialExpired(credential: OAuthCredential, now = Date.now()): boolean {\n return now >= credential.expires;\n}\n\nexport const MODEL_CREDENTIALS_SCHEMA: CollectionSchema = {\n name: 'model_provider_credentials',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text', nullable: true },\n provider: { type: 'text' },\n type: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [\n // Scope is encoded in `user_id` nullability (NULL = org-scoped shared\n // row), enforced by two partial unique indexes.\n {\n name: 'model_provider_credentials_user_unique',\n columns: ['org_id', 'user_id', 'provider'],\n whereNotNull: 'user_id',\n },\n { name: 'model_provider_credentials_org_unique', columns: ['org_id', 'provider'], whereNull: 'user_id' },\n ],\n};\n\nexport const OAUTH_LOGIN_SESSIONS_SCHEMA: CollectionSchema = {\n name: 'oauth_login_sessions',\n columns: {\n session_id: { type: 'text', primaryKey: true },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n provider: { type: 'text' },\n kind: { type: 'text' },\n credential_scope: { type: 'text', nullable: true },\n pending: { type: 'json' },\n expires_at: { type: 'timestamp' },\n next_poll_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n },\n};\n\n/** Column shape of one `model_provider_credentials` row as returned by ops. */\ninterface CredentialDbRow extends Record<string, unknown> {\n id: string;\n provider: string;\n user_id: string | null;\n data: unknown;\n updated_at: Date;\n}\n\n/** Column shape of one `oauth_login_sessions` row as returned by ops. */\ninterface LoginSessionDbRow extends Record<string, unknown> {\n session_id: string;\n org_id: string;\n user_id: string;\n provider: string;\n kind: LoginSessionKind;\n credential_scope: LoginCredentialScope | null;\n pending: Record<string, unknown>;\n expires_at: Date;\n next_poll_at: Date | null;\n created_at: Date;\n}\n\nfunction toSessionRow(db: LoginSessionDbRow): LoginSessionRow {\n return {\n sessionId: db.session_id,\n orgId: db.org_id,\n userId: db.user_id,\n provider: db.provider,\n kind: db.kind,\n ...(db.credential_scope ? { credentialScope: db.credential_scope } : {}),\n pending: db.pending,\n expiresAt: db.expires_at,\n nextPollAt: db.next_poll_at,\n createdAt: db.created_at,\n };\n}\n\n/** Filter selecting exactly the tenant's row for a provider (`null` = org row). */\nfunction tenantWhere(tenant: CredentialTenant, provider: string): CollectionWhere {\n return { org_id: tenant.orgId, provider, user_id: tenant.userId ?? null };\n}\n\n/**\n * Model-credentials storage, written once against the generic\n * `FactoryStorageOps` surface. `refreshOAuth()` and `claimLoginSession()`\n * ride `updateAtomic` so concurrent replicas serialize instead of\n * invalidating each other's rotating tokens / double-claiming a flow.\n */\nexport class ModelCredentialsStorage extends FactoryStorageDomain {\n constructor(private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption()) {\n super('model-credentials');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);\n const rows = await this.ops.findMany<CredentialDbRow>('model_provider_credentials', {});\n await Promise.all(rows.map(row => this.#migrateCredential(row.id)));\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('oauth_login_sessions', {});\n await this.ops.deleteMany('model_provider_credentials', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #decryptCredential(value: unknown): Promise<AuthCredential> {\n return (await this.encryption.decrypt<AuthCredential>(value)).value;\n }\n\n async #migrateCredential(id: string): Promise<void> {\n await this.#db.updateAtomic<CredentialDbRow>('model_provider_credentials', { id }, async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (!decrypted.needsReencryption) return null;\n return { data: await this.encryption.encrypt(decrypted.value) };\n });\n }\n\n /** Read the tenant's credential for a provider at exactly that scope. */\n async getCredential(tenant: CredentialTenant, provider: string): Promise<AuthCredential | undefined> {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', tenantWhere(tenant, provider));\n return row ? this.#decryptCredential(row.data) : undefined;\n }\n\n /** Upsert the tenant's credential (`created_at` is preserved on update). */\n async setCredential(tenant: CredentialTenant, provider: string, credential: AuthCredential): Promise<void> {\n const where = tenantWhere(tenant, provider);\n const encrypted = await this.encryption.encrypt(credential);\n const update = async () =>\n this.#db.updateMany('model_provider_credentials', where, {\n type: credential.type,\n data: encrypted,\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne<CredentialDbRow>('model_provider_credentials', {\n org_id: tenant.orgId,\n user_id: tenant.userId ?? null,\n provider,\n type: credential.type,\n data: encrypted,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n }\n\n /** Delete the tenant's credential at exactly that scope. True when a row was removed. */\n async removeCredential(tenant: CredentialTenant, provider: string): Promise<boolean> {\n const deleted = await this.#db.deleteMany('model_provider_credentials', tenantWhere(tenant, provider));\n return deleted > 0;\n }\n\n /** All credentials visible to a user: their own rows plus the org's shared rows. */\n async listCredentials(orgId: string, userId: string): Promise<CredentialRecord[]> {\n // SQL `IN` never matches NULL, so the org rows need their own query.\n const [userRows, orgRows] = await Promise.all([\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: userId }),\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: null }),\n ]);\n return Promise.all([\n ...userRows.map(async row => ({\n provider: row.provider,\n scope: 'user' as const,\n credential: await this.#decryptCredential(row.data),\n updatedAt: row.updated_at,\n })),\n ...orgRows.map(async row => ({\n provider: row.provider,\n scope: 'org' as const,\n credential: await this.#decryptCredential(row.data),\n updatedAt: row.updated_at,\n })),\n ]);\n }\n\n /**\n * Resolve the credential a caller should use for a provider. By default the\n * user's own row wins over the org's shared row; `precedence: 'org'`\n * (automated factory runs) checks the org's shared row first.\n */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n precedence: CredentialScope = 'user',\n ): Promise<ResolvedCredential | undefined> {\n const readUser = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: userId,\n });\n return row\n ? { provider: row.provider, scope: 'user', credential: await this.#decryptCredential(row.data) }\n : undefined;\n };\n const readOrg = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: null,\n });\n return row\n ? { provider: row.provider, scope: 'org', credential: await this.#decryptCredential(row.data) }\n : undefined;\n };\n return precedence === 'org' ? ((await readOrg()) ?? (await readUser())) : ((await readUser()) ?? (await readOrg()));\n }\n\n /**\n * Refresh the tenant's OAuth credential under the backend's atomic\n * read-modify-write so concurrent replicas don't invalidate each other's\n * rotating refresh tokens. The expiry is re-checked under the lock —\n * another replica may have refreshed already, in which case `refreshFn` is\n * skipped and the fresh credential is returned. Returns `undefined` when no\n * OAuth row exists for the tenant.\n */\n async refreshOAuth(\n tenant: CredentialTenant,\n provider: string,\n refreshFn: (current: OAuthCredential) => Promise<OAuthCredential>,\n ): Promise<OAuthCredential | undefined> {\n let result: OAuthCredential | undefined;\n await this.#db.updateAtomic<CredentialDbRow>(\n 'model_provider_credentials',\n tenantWhere(tenant, provider),\n async row => {\n const decrypted = await this.encryption.decrypt<AuthCredential>(row.data);\n if (decrypted.value.type !== 'oauth') return null;\n const current = decrypted.value;\n // Re-check under the lock: another replica may have refreshed while we waited.\n if (!isOAuthCredentialExpired(current)) {\n result = current;\n return decrypted.needsReencryption ? { data: await this.encryption.encrypt(current) } : null;\n }\n const next = await refreshFn(current);\n result = next;\n return { data: await this.encryption.encrypt(next), updated_at: new Date() };\n },\n );\n return result;\n }\n\n /** Persist a pending login flow started by a web route. */\n async createLoginSession(input: CreateLoginSessionInput): Promise<LoginSessionRow> {\n const inserted = await this.#db.insertOne<LoginSessionDbRow>('oauth_login_sessions', {\n session_id: input.sessionId,\n org_id: input.orgId,\n user_id: input.userId,\n provider: input.provider,\n kind: input.kind,\n credential_scope: input.credentialScope ?? null,\n pending: input.pending,\n expires_at: input.expiresAt,\n next_poll_at: input.nextPollAt ?? null,\n created_at: new Date(),\n });\n return toSessionRow(inserted);\n }\n\n /**\n * Read a pending login flow. An expired session is deleted on read (TTL\n * cleanup) and reported as absent.\n */\n async getLoginSession(sessionId: string): Promise<LoginSessionRow | undefined> {\n const row = await this.#db.findOne<LoginSessionDbRow>('oauth_login_sessions', { session_id: sessionId });\n if (!row) return undefined;\n if (row.expires_at.getTime() <= Date.now()) {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n return undefined;\n }\n return toSessionRow(row);\n }\n\n /**\n * Atomically claim a due login session for one upstream completion/poll attempt.\n * Returns undefined when the session is missing, expired, owned by another\n * tenant/provider, or already claimed by a concurrent request.\n */\n async claimLoginSession(\n sessionId: string,\n owner: Pick<LoginSessionRow, 'orgId' | 'userId' | 'provider' | 'kind'>,\n ): Promise<LoginSessionRow | undefined> {\n const now = Date.now();\n let claimed = false;\n const updated = await this.#db.updateAtomic<LoginSessionDbRow>(\n 'oauth_login_sessions',\n { session_id: sessionId },\n row => {\n if (\n row.org_id !== owner.orgId ||\n row.user_id !== owner.userId ||\n row.provider !== owner.provider ||\n row.kind !== owner.kind\n ) {\n return null;\n }\n if (row.expires_at.getTime() <= now) return null;\n if (row.next_poll_at && row.next_poll_at.getTime() > now) return null;\n claimed = true;\n // Park the next poll at expiry so a concurrent claim loses until the\n // claimer either touches the session forward or deletes it.\n return { next_poll_at: row.expires_at };\n },\n );\n return claimed && updated ? toSessionRow(updated) : undefined;\n }\n\n /** Update a pending flow's serialized state and/or next allowed poll time. */\n async touchLoginSession(\n sessionId: string,\n updates: { pending?: Record<string, unknown>; nextPollAt?: Date | null },\n ): Promise<void> {\n const set: Record<string, unknown> = {};\n if (updates.pending !== undefined) set.pending = updates.pending;\n if (updates.nextPollAt !== undefined) set.next_poll_at = updates.nextPollAt;\n if (Object.keys(set).length === 0) return;\n await this.#db.updateMany('oauth_login_sessions', { session_id: sessionId }, set);\n }\n\n /** Remove a pending login flow (completed, cancelled, or failed). */\n async deleteLoginSession(sessionId: string): Promise<void> {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n }\n}\n"],"mappings":";;;;AA2FA,SAAgB,yBAAyB,YAA6B,MAAM,KAAK,IAAI,GAAY;CAC/F,OAAO,OAAO,WAAW;AAC3B;AAEA,MAAa,2BAA6C;CACxD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS;GAAE,MAAM;GAAQ,UAAU;EAAK;EACxC,UAAU,EAAE,MAAM,OAAO;EACzB,MAAM,EAAE,MAAM,OAAO;EACrB,MAAM,EAAE,MAAM,OAAO;EACrB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAGb;EACE,MAAM;EACN,SAAS;GAAC;GAAU;GAAW;EAAU;EACzC,cAAc;CAChB,GACA;EAAE,MAAM;EAAyC,SAAS,CAAC,UAAU,UAAU;EAAG,WAAW;CAAU,CACzG;AACF;AAEA,MAAa,8BAAgD;CAC3D,MAAM;CACN,SAAS;EACP,YAAY;GAAE,MAAM;GAAQ,YAAY;EAAK;EAC7C,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS,EAAE,MAAM,OAAO;EACxB,UAAU,EAAE,MAAM,OAAO;EACzB,MAAM,EAAE,MAAM,OAAO;EACrB,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,SAAS,EAAE,MAAM,OAAO;EACxB,YAAY,EAAE,MAAM,YAAY;EAChC,cAAc;GAAE,MAAM;GAAa,UAAU;EAAK;EAClD,YAAY,EAAE,MAAM,YAAY;CAClC;AACF;AAyBA,SAAS,aAAa,IAAwC;CAC5D,OAAO;EACL,WAAW,GAAG;EACd,OAAO,GAAG;EACV,QAAQ,GAAG;EACX,UAAU,GAAG;EACb,MAAM,GAAG;EACT,GAAI,GAAG,mBAAmB,EAAE,iBAAiB,GAAG,iBAAiB,IAAI,CAAC;EACtE,SAAS,GAAG;EACZ,WAAW,GAAG;EACd,YAAY,GAAG;EACf,WAAW,GAAG;CAChB;AACF;;AAGA,SAAS,YAAY,QAA0B,UAAmC;CAChF,OAAO;EAAE,QAAQ,OAAO;EAAO;EAAU,SAAS,OAAO,UAAU;CAAK;AAC1E;;;;;;;AAQA,IAAa,0BAAb,cAA6C,qBAAqB;CACnC;CAA7B,YAAY,aAAuD,uCAAuC,GAAG;EAC3G,MAAM,mBAAmB;EADE,KAAA,aAAA;CAE7B;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,0BAA0B,2BAA2B,CAAC;EACpF,MAAM,OAAO,MAAM,KAAK,IAAI,SAA0B,8BAA8B,CAAC,CAAC;EACtF,MAAM,QAAQ,IAAI,KAAK,KAAI,QAAO,KAAKA,mBAAmB,IAAI,EAAE,CAAC,CAAC;CACpE;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,wBAAwB,CAAC,CAAC;EACpD,MAAM,KAAK,IAAI,WAAW,8BAA8B,CAAC,CAAC;CAC5D;CAEA,IAAIC,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAMC,mBAAmB,OAAyC;EAChE,QAAQ,MAAM,KAAK,WAAW,QAAwB,KAAK,EAAA,CAAG;CAChE;CAEA,MAAMF,mBAAmB,IAA2B;EAClD,MAAM,KAAKC,IAAI,aAA8B,8BAA8B,EAAE,GAAG,GAAG,OAAM,QAAO;GAC9F,MAAM,YAAY,MAAM,KAAK,WAAW,QAAwB,IAAI,IAAI;GACxE,IAAI,CAAC,UAAU,mBAAmB,OAAO;GACzC,OAAO,EAAE,MAAM,MAAM,KAAK,WAAW,QAAQ,UAAU,KAAK,EAAE;EAChE,CAAC;CACH;;CAGA,MAAM,cAAc,QAA0B,UAAuD;EACnG,MAAM,MAAM,MAAM,KAAKA,IAAI,QAAyB,8BAA8B,YAAY,QAAQ,QAAQ,CAAC;EAC/G,OAAO,MAAM,KAAKC,mBAAmB,IAAI,IAAI,IAAI,KAAA;CACnD;;CAGA,MAAM,cAAc,QAA0B,UAAkB,YAA2C;EACzG,MAAM,QAAQ,YAAY,QAAQ,QAAQ;EAC1C,MAAM,YAAY,MAAM,KAAK,WAAW,QAAQ,UAAU;EAC1D,MAAM,SAAS,YACb,KAAKD,IAAI,WAAW,8BAA8B,OAAO;GACvD,MAAM,WAAW;GACjB,MAAM;GACN,4BAAY,IAAI,KAAK;EACvB,CAAC;EAEH,IAAI;EACJ,KAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;GAC5C,IAAK,MAAM,OAAO,IAAK,GAAG;GAE1B,MAAM,sBAAM,IAAI,KAAK;GACrB,IAAI;IACF,MAAM,KAAKA,IAAI,UAA2B,8BAA8B;KACtE,QAAQ,OAAO;KACf,SAAS,OAAO,UAAU;KAC1B;KACA,MAAM,WAAW;KACjB,MAAM;KACN,YAAY;KACZ,YAAY;IACd,CAAC;IACD;GACF,SAAS,OAAO;IACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;IACpD,YAAY;GACd;EACF;EACA,MAAM;CACR;;CAGA,MAAM,iBAAiB,QAA0B,UAAoC;EAEnF,OAAO,MADe,KAAKA,IAAI,WAAW,8BAA8B,YAAY,QAAQ,QAAQ,CAAC,IACpF;CACnB;;CAGA,MAAM,gBAAgB,OAAe,QAA6C;EAEhF,MAAM,CAAC,UAAU,WAAW,MAAM,QAAQ,IAAI,CAC5C,KAAKA,IAAI,SAA0B,8BAA8B;GAAE,QAAQ;GAAO,SAAS;EAAO,CAAC,GACnG,KAAKA,IAAI,SAA0B,8BAA8B;GAAE,QAAQ;GAAO,SAAS;EAAK,CAAC,CACnG,CAAC;EACD,OAAO,QAAQ,IAAI,CACjB,GAAG,SAAS,IAAI,OAAM,SAAQ;GAC5B,UAAU,IAAI;GACd,OAAO;GACP,YAAY,MAAM,KAAKC,mBAAmB,IAAI,IAAI;GAClD,WAAW,IAAI;EACjB,EAAE,GACF,GAAG,QAAQ,IAAI,OAAM,SAAQ;GAC3B,UAAU,IAAI;GACd,OAAO;GACP,YAAY,MAAM,KAAKA,mBAAmB,IAAI,IAAI;GAClD,WAAW,IAAI;EACjB,EAAE,CACJ,CAAC;CACH;;;;;;CAOA,MAAM,kBACJ,OACA,QACA,UACA,aAA8B,QACW;EACzC,MAAM,WAAW,YAAqD;GACpE,MAAM,MAAM,MAAM,KAAKD,IAAI,QAAyB,8BAA8B;IAChF,QAAQ;IACR;IACA,SAAS;GACX,CAAC;GACD,OAAO,MACH;IAAE,UAAU,IAAI;IAAU,OAAO;IAAQ,YAAY,MAAM,KAAKC,mBAAmB,IAAI,IAAI;GAAE,IAC7F,KAAA;EACN;EACA,MAAM,UAAU,YAAqD;GACnE,MAAM,MAAM,MAAM,KAAKD,IAAI,QAAyB,8BAA8B;IAChF,QAAQ;IACR;IACA,SAAS;GACX,CAAC;GACD,OAAO,MACH;IAAE,UAAU,IAAI;IAAU,OAAO;IAAO,YAAY,MAAM,KAAKC,mBAAmB,IAAI,IAAI;GAAE,IAC5F,KAAA;EACN;EACA,OAAO,eAAe,QAAU,MAAM,QAAQ,KAAO,MAAM,SAAS,IAAQ,MAAM,SAAS,KAAO,MAAM,QAAQ;CAClH;;;;;;;;;CAUA,MAAM,aACJ,QACA,UACA,WACsC;EACtC,IAAI;EACJ,MAAM,KAAKD,IAAI,aACb,8BACA,YAAY,QAAQ,QAAQ,GAC5B,OAAM,QAAO;GACX,MAAM,YAAY,MAAM,KAAK,WAAW,QAAwB,IAAI,IAAI;GACxE,IAAI,UAAU,MAAM,SAAS,SAAS,OAAO;GAC7C,MAAM,UAAU,UAAU;GAE1B,IAAI,CAAC,yBAAyB,OAAO,GAAG;IACtC,SAAS;IACT,OAAO,UAAU,oBAAoB,EAAE,MAAM,MAAM,KAAK,WAAW,QAAQ,OAAO,EAAE,IAAI;GAC1F;GACA,MAAM,OAAO,MAAM,UAAU,OAAO;GACpC,SAAS;GACT,OAAO;IAAE,MAAM,MAAM,KAAK,WAAW,QAAQ,IAAI;IAAG,4BAAY,IAAI,KAAK;GAAE;EAC7E,CACF;EACA,OAAO;CACT;;CAGA,MAAM,mBAAmB,OAA0D;EAajF,OAAO,aAAa,MAZG,KAAKA,IAAI,UAA6B,wBAAwB;GACnF,YAAY,MAAM;GAClB,QAAQ,MAAM;GACd,SAAS,MAAM;GACf,UAAU,MAAM;GAChB,MAAM,MAAM;GACZ,kBAAkB,MAAM,mBAAmB;GAC3C,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,cAAc,MAAM,cAAc;GAClC,4BAAY,IAAI,KAAK;EACvB,CAAC,CAC2B;CAC9B;;;;;CAMA,MAAM,gBAAgB,WAAyD;EAC7E,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA2B,wBAAwB,EAAE,YAAY,UAAU,CAAC;EACvG,IAAI,CAAC,KAAK,OAAO,KAAA;EACjB,IAAI,IAAI,WAAW,QAAQ,KAAK,KAAK,IAAI,GAAG;GAC1C,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,CAAC;GAC3E;EACF;EACA,OAAO,aAAa,GAAG;CACzB;;;;;;CAOA,MAAM,kBACJ,WACA,OACsC;EACtC,MAAM,MAAM,KAAK,IAAI;EACrB,IAAI,UAAU;EACd,MAAM,UAAU,MAAM,KAAKA,IAAI,aAC7B,wBACA,EAAE,YAAY,UAAU,IACxB,QAAO;GACL,IACE,IAAI,WAAW,MAAM,SACrB,IAAI,YAAY,MAAM,UACtB,IAAI,aAAa,MAAM,YACvB,IAAI,SAAS,MAAM,MAEnB,OAAO;GAET,IAAI,IAAI,WAAW,QAAQ,KAAK,KAAK,OAAO;GAC5C,IAAI,IAAI,gBAAgB,IAAI,aAAa,QAAQ,IAAI,KAAK,OAAO;GACjE,UAAU;GAGV,OAAO,EAAE,cAAc,IAAI,WAAW;EACxC,CACF;EACA,OAAO,WAAW,UAAU,aAAa,OAAO,IAAI,KAAA;CACtD;;CAGA,MAAM,kBACJ,WACA,SACe;EACf,MAAM,MAA+B,CAAC;EACtC,IAAI,QAAQ,YAAY,KAAA,GAAW,IAAI,UAAU,QAAQ;EACzD,IAAI,QAAQ,eAAe,KAAA,GAAW,IAAI,eAAe,QAAQ;EACjE,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW,GAAG;EACnC,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,GAAG,GAAG;CAClF;;CAGA,MAAM,mBAAmB,WAAkC;EACzD,MAAM,KAAKA,IAAI,WAAW,wBAAwB,EAAE,YAAY,UAAU,CAAC;CAC7E;AACF"}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/credentials/base.ts"],"sourcesContent":["/**\n * Model provider credentials domain — tenant-scoped OAuth tokens and API keys\n * for model providers (Anthropic/Claude Max, OpenAI Codex, GitHub Copilot,\n * xAI, plus plain API-key providers).\n *\n * Tenancy is two-level:\n * - **user-scoped** rows (`userId` present): personal plan OAuth tokens and\n * personal API keys. OAuth plan tokens are ONLY ever user-scoped — they are\n * personal subscriptions, and sharing one would bill/rate-limit a single\n * account for the whole org.\n * - **org-scoped** rows (`userId` absent): shared API keys set by an org\n * admin, inherited by every member.\n *\n * Resolution order at model-call time is user > org (the caller layers server\n * env vars underneath as a final fallback).\n *\n * Secrets posture: credentials are stored server-side only and never returned\n * to the client (same as `linear_connections.access_token`). Encryption at\n * rest is out of scope, matching the existing posture.\n *\n * The domain also owns `oauth_login_sessions`: pending web login flows\n * (paste-code PKCE verifiers, device-code poll state) persisted server-side so\n * any replica can complete or poll a flow started on another.\n */\n\nimport type { AuthCredential, OAuthCredential } from '@mastra/code-sdk/auth/types';\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\n\n/** Owning tenant of a credential row. `userId` absent = org-scoped row. */\nexport interface CredentialTenant {\n orgId: string;\n userId?: string;\n}\n\n/** Which tenancy level a credential row lives at. */\nexport type CredentialScope = 'user' | 'org';\n\n/** One stored credential, annotated with its scope. */\nexport interface CredentialRecord {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n updatedAt: Date;\n}\n\n/** Result of per-caller resolution: the winning credential and its scope. */\nexport interface ResolvedCredential {\n provider: string;\n scope: CredentialScope;\n credential: AuthCredential;\n}\n\n/** Kind of pending web login flow a session row tracks. */\nexport type LoginSessionKind = 'paste-code' | 'device-code';\n\n/** Scope the completed credential should be written to. */\nexport type LoginCredentialScope = 'user' | 'org';\n\n/** One pending OAuth login flow, persisted so any replica can continue it. */\nexport interface LoginSessionRow {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n /** Where the completed credential lands; absent rows default to `'user'`. */\n credentialScope?: LoginCredentialScope;\n /** Serialized flow state (PKCE verifier, device-code pending state, ...). */\n pending: Record<string, unknown>;\n expiresAt: Date;\n /** Earliest time the next upstream poll is allowed (device-code flows). */\n nextPollAt: Date | null;\n createdAt: Date;\n}\n\nexport interface CreateLoginSessionInput {\n sessionId: string;\n orgId: string;\n userId: string;\n provider: string;\n kind: LoginSessionKind;\n credentialScope?: LoginCredentialScope;\n pending: Record<string, unknown>;\n expiresAt: Date;\n nextPollAt?: Date | null;\n}\n\n/** Mirror of `AuthStorage.getApiKey()`'s expiry check. */\nexport function isOAuthCredentialExpired(credential: OAuthCredential, now = Date.now()): boolean {\n return now >= credential.expires;\n}\n\nexport const MODEL_CREDENTIALS_SCHEMA: CollectionSchema = {\n name: 'model_provider_credentials',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text', nullable: true },\n provider: { type: 'text' },\n type: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [\n // Scope is encoded in `user_id` nullability (NULL = org-scoped shared\n // row), enforced by two partial unique indexes.\n {\n name: 'model_provider_credentials_user_unique',\n columns: ['org_id', 'user_id', 'provider'],\n whereNotNull: 'user_id',\n },\n { name: 'model_provider_credentials_org_unique', columns: ['org_id', 'provider'], whereNull: 'user_id' },\n ],\n};\n\nexport const OAUTH_LOGIN_SESSIONS_SCHEMA: CollectionSchema = {\n name: 'oauth_login_sessions',\n columns: {\n session_id: { type: 'text', primaryKey: true },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n provider: { type: 'text' },\n kind: { type: 'text' },\n credential_scope: { type: 'text', nullable: true },\n pending: { type: 'json' },\n expires_at: { type: 'timestamp' },\n next_poll_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n },\n};\n\n/** Column shape of one `model_provider_credentials` row as returned by ops. */\ninterface CredentialDbRow extends Record<string, unknown> {\n id: string;\n provider: string;\n user_id: string | null;\n data: AuthCredential;\n updated_at: Date;\n}\n\n/** Column shape of one `oauth_login_sessions` row as returned by ops. */\ninterface LoginSessionDbRow extends Record<string, unknown> {\n session_id: string;\n org_id: string;\n user_id: string;\n provider: string;\n kind: LoginSessionKind;\n credential_scope: LoginCredentialScope | null;\n pending: Record<string, unknown>;\n expires_at: Date;\n next_poll_at: Date | null;\n created_at: Date;\n}\n\nfunction toSessionRow(db: LoginSessionDbRow): LoginSessionRow {\n return {\n sessionId: db.session_id,\n orgId: db.org_id,\n userId: db.user_id,\n provider: db.provider,\n kind: db.kind,\n ...(db.credential_scope ? { credentialScope: db.credential_scope } : {}),\n pending: db.pending,\n expiresAt: db.expires_at,\n nextPollAt: db.next_poll_at,\n createdAt: db.created_at,\n };\n}\n\n/** Filter selecting exactly the tenant's row for a provider (`null` = org row). */\nfunction tenantWhere(tenant: CredentialTenant, provider: string): CollectionWhere {\n return { org_id: tenant.orgId, provider, user_id: tenant.userId ?? null };\n}\n\n/**\n * Model-credentials storage, written once against the generic\n * `FactoryStorageOps` surface. `refreshOAuth()` and `claimLoginSession()`\n * ride `updateAtomic` so concurrent replicas serialize instead of\n * invalidating each other's rotating tokens / double-claiming a flow.\n */\nexport class ModelCredentialsStorage extends FactoryStorageDomain {\n constructor() {\n super('model-credentials');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([MODEL_CREDENTIALS_SCHEMA, OAUTH_LOGIN_SESSIONS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('oauth_login_sessions', {});\n await this.ops.deleteMany('model_provider_credentials', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Read the tenant's credential for a provider at exactly that scope. */\n async getCredential(tenant: CredentialTenant, provider: string): Promise<AuthCredential | undefined> {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', tenantWhere(tenant, provider));\n return row?.data;\n }\n\n /** Upsert the tenant's credential (`created_at` is preserved on update). */\n async setCredential(tenant: CredentialTenant, provider: string, credential: AuthCredential): Promise<void> {\n const where = tenantWhere(tenant, provider);\n const update = async () =>\n this.#db.updateMany('model_provider_credentials', where, {\n type: credential.type,\n data: credential,\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne<CredentialDbRow>('model_provider_credentials', {\n org_id: tenant.orgId,\n user_id: tenant.userId ?? null,\n provider,\n type: credential.type,\n data: credential,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n }\n\n /** Delete the tenant's credential at exactly that scope. True when a row was removed. */\n async removeCredential(tenant: CredentialTenant, provider: string): Promise<boolean> {\n const deleted = await this.#db.deleteMany('model_provider_credentials', tenantWhere(tenant, provider));\n return deleted > 0;\n }\n\n /** All credentials visible to a user: their own rows plus the org's shared rows. */\n async listCredentials(orgId: string, userId: string): Promise<CredentialRecord[]> {\n // SQL `IN` never matches NULL, so the org rows need their own query.\n const [userRows, orgRows] = await Promise.all([\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: userId }),\n this.#db.findMany<CredentialDbRow>('model_provider_credentials', { org_id: orgId, user_id: null }),\n ]);\n return [\n ...userRows.map(row => ({\n provider: row.provider,\n scope: 'user' as const,\n credential: row.data,\n updatedAt: row.updated_at,\n })),\n ...orgRows.map(row => ({\n provider: row.provider,\n scope: 'org' as const,\n credential: row.data,\n updatedAt: row.updated_at,\n })),\n ];\n }\n\n /**\n * Resolve the credential a caller should use for a provider. By default the\n * user's own row wins over the org's shared row; `precedence: 'org'`\n * (automated factory runs) checks the org's shared row first.\n */\n async resolveCredential(\n orgId: string,\n userId: string,\n provider: string,\n precedence: CredentialScope = 'user',\n ): Promise<ResolvedCredential | undefined> {\n const readUser = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: userId,\n });\n return row ? { provider: row.provider, scope: 'user', credential: row.data } : undefined;\n };\n const readOrg = async (): Promise<ResolvedCredential | undefined> => {\n const row = await this.#db.findOne<CredentialDbRow>('model_provider_credentials', {\n org_id: orgId,\n provider,\n user_id: null,\n });\n return row ? { provider: row.provider, scope: 'org', credential: row.data } : undefined;\n };\n return precedence === 'org' ? ((await readOrg()) ?? (await readUser())) : ((await readUser()) ?? (await readOrg()));\n }\n\n /**\n * Refresh the tenant's OAuth credential under the backend's atomic\n * read-modify-write so concurrent replicas don't invalidate each other's\n * rotating refresh tokens. The expiry is re-checked under the lock —\n * another replica may have refreshed already, in which case `refreshFn` is\n * skipped and the fresh credential is returned. Returns `undefined` when no\n * OAuth row exists for the tenant.\n */\n async refreshOAuth(\n tenant: CredentialTenant,\n provider: string,\n refreshFn: (current: OAuthCredential) => Promise<OAuthCredential>,\n ): Promise<OAuthCredential | undefined> {\n let result: OAuthCredential | undefined;\n await this.#db.updateAtomic<CredentialDbRow>(\n 'model_provider_credentials',\n tenantWhere(tenant, provider),\n async row => {\n if (row.data.type !== 'oauth') return null;\n const current = row.data;\n // Re-check under the lock: another replica may have refreshed while we waited.\n if (!isOAuthCredentialExpired(current)) {\n result = current;\n return null;\n }\n const next = await refreshFn(current);\n result = next;\n return { data: next, updated_at: new Date() };\n },\n );\n return result;\n }\n\n /** Persist a pending login flow started by a web route. */\n async createLoginSession(input: CreateLoginSessionInput): Promise<LoginSessionRow> {\n const inserted = await this.#db.insertOne<LoginSessionDbRow>('oauth_login_sessions', {\n session_id: input.sessionId,\n org_id: input.orgId,\n user_id: input.userId,\n provider: input.provider,\n kind: input.kind,\n credential_scope: input.credentialScope ?? null,\n pending: input.pending,\n expires_at: input.expiresAt,\n next_poll_at: input.nextPollAt ?? null,\n created_at: new Date(),\n });\n return toSessionRow(inserted);\n }\n\n /**\n * Read a pending login flow. An expired session is deleted on read (TTL\n * cleanup) and reported as absent.\n */\n async getLoginSession(sessionId: string): Promise<LoginSessionRow | undefined> {\n const row = await this.#db.findOne<LoginSessionDbRow>('oauth_login_sessions', { session_id: sessionId });\n if (!row) return undefined;\n if (row.expires_at.getTime() <= Date.now()) {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n return undefined;\n }\n return toSessionRow(row);\n }\n\n /**\n * Atomically claim a due login session for one upstream completion/poll attempt.\n * Returns undefined when the session is missing, expired, owned by another\n * tenant/provider, or already claimed by a concurrent request.\n */\n async claimLoginSession(\n sessionId: string,\n owner: Pick<LoginSessionRow, 'orgId' | 'userId' | 'provider' | 'kind'>,\n ): Promise<LoginSessionRow | undefined> {\n const now = Date.now();\n let claimed = false;\n const updated = await this.#db.updateAtomic<LoginSessionDbRow>(\n 'oauth_login_sessions',\n { session_id: sessionId },\n row => {\n if (\n row.org_id !== owner.orgId ||\n row.user_id !== owner.userId ||\n row.provider !== owner.provider ||\n row.kind !== owner.kind\n ) {\n return null;\n }\n if (row.expires_at.getTime() <= now) return null;\n if (row.next_poll_at && row.next_poll_at.getTime() > now) return null;\n claimed = true;\n // Park the next poll at expiry so a concurrent claim loses until the\n // claimer either touches the session forward or deletes it.\n return { next_poll_at: row.expires_at };\n },\n );\n return claimed && updated ? toSessionRow(updated) : undefined;\n }\n\n /** Update a pending flow's serialized state and/or next allowed poll time. */\n async touchLoginSession(\n sessionId: string,\n updates: { pending?: Record<string, unknown>; nextPollAt?: Date | null },\n ): Promise<void> {\n const set: Record<string, unknown> = {};\n if (updates.pending !== undefined) set.pending = updates.pending;\n if (updates.nextPollAt !== undefined) set.next_poll_at = updates.nextPollAt;\n if (Object.keys(set).length === 0) return;\n await this.#db.updateMany('oauth_login_sessions', { session_id: sessionId }, set);\n }\n\n /** Remove a pending login flow (completed, cancelled, or failed). */\n async deleteLoginSession(sessionId: string): Promise<void> {\n await this.#db.deleteMany('oauth_login_sessions', { session_id: sessionId });\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  import { FactoryStorageDomain } from '@mastra/core/storage';
2
2
  import type { CollectionSchema } from '@mastra/core/storage';
3
- import type { FactorySecretEncryption } from '../../../secret-encryption.js';
4
3
  /**
5
4
  * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the
6
5
  * local `settings.json` `customProviders` entries: org-scoped rows keyed by the
@@ -30,8 +29,7 @@ export interface UpsertCustomProviderInput {
30
29
  export declare const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema;
31
30
  export declare class CustomProvidersStorage extends FactoryStorageDomain {
32
31
  #private;
33
- private readonly encryption;
34
- constructor(encryption?: FactorySecretEncryption);
32
+ constructor();
35
33
  init(): Promise<void>;
36
34
  dangerouslyClearAll(): Promise<void>;
37
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/custom-providers/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAerC,CAAC;AAeF,qBAAa,sBAAuB,SAAQ,oBAAoB;;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,GAAE,uBAAkE;IAIrG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC1C;;;;;;;OAOG;IACG,MAAM,CAAC,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,yBAAyB,CAAC;QACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAqG3B,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IASnE,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAG7F"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/custom-providers/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAerC,CAAC;AA8BF,qBAAa,sBAAuB,SAAQ,oBAAoB;;;IAKxD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ1C;;;;;;;OAOG;IACG,MAAM,CAAC,EACX,KAAK,EACL,MAAM,EACN,KAAK,EACL,kBAAkB,GACnB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,yBAAyB,CAAC;QACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkG3B,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IASnE,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAG7F"}
@@ -1,4 +1,3 @@
1
- import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
2
1
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
3
2
  //#region src/storage/domains/custom-providers/base.ts
4
3
  const CUSTOM_PROVIDERS_SCHEMA = {
@@ -23,16 +22,26 @@ const CUSTOM_PROVIDERS_SCHEMA = {
23
22
  columns: ["org_id", "provider_id"]
24
23
  }]
25
24
  };
25
+ function toRecord(row) {
26
+ return {
27
+ id: row.id,
28
+ orgId: row.org_id,
29
+ createdBy: row.created_by,
30
+ providerId: row.provider_id,
31
+ name: row.name,
32
+ url: row.url,
33
+ apiKey: row.api_key,
34
+ models: Array.isArray(row.models) ? row.models : [],
35
+ createdAt: row.created_at,
36
+ updatedAt: row.updated_at
37
+ };
38
+ }
26
39
  var CustomProvidersStorage = class extends FactoryStorageDomain {
27
- encryption;
28
- constructor(encryption = createPlaintextFactorySecretEncryption()) {
40
+ constructor() {
29
41
  super("custom-providers");
30
- this.encryption = encryption;
31
42
  }
32
43
  async init() {
33
44
  await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);
34
- const rows = await this.ops.findMany("custom_providers", {});
35
- await Promise.all(rows.filter((row) => row.api_key !== null).map((row) => this.#migrateApiKey(row.id)));
36
45
  }
37
46
  async dangerouslyClearAll() {
38
47
  await this.ops.deleteMany("custom_providers", {});
@@ -40,31 +49,6 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
40
49
  get #db() {
41
50
  return this.ops;
42
51
  }
43
- async #toRecord(row) {
44
- const apiKey = row.api_key === null ? null : (await this.encryption.decrypt(row.api_key)).value;
45
- return {
46
- id: row.id,
47
- orgId: row.org_id,
48
- createdBy: row.created_by,
49
- providerId: row.provider_id,
50
- name: row.name,
51
- url: row.url,
52
- apiKey,
53
- models: Array.isArray(row.models) ? row.models : [],
54
- createdAt: row.created_at,
55
- updatedAt: row.updated_at
56
- };
57
- }
58
- async #encryptApiKey(apiKey) {
59
- return apiKey === void 0 ? null : this.encryption.encrypt(apiKey);
60
- }
61
- async #migrateApiKey(id) {
62
- await this.#db.updateAtomic("custom_providers", { id }, async (row) => {
63
- if (row.api_key === null) return null;
64
- const decrypted = await this.encryption.decrypt(row.api_key);
65
- return decrypted.needsReencryption ? { api_key: await this.encryption.encrypt(decrypted.value) } : null;
66
- });
67
- }
68
52
  /**
69
53
  * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
70
54
  * the settings.json upsert semantics (no key retention: an absent `apiKey`
@@ -75,7 +59,6 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
75
59
  */
76
60
  async upsert({ orgId, userId, input, previousProviderId }) {
77
61
  const now = /* @__PURE__ */ new Date();
78
- const encryptedApiKey = await this.#encryptApiKey(input.apiKey);
79
62
  const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
80
63
  if (renameFrom) {
81
64
  if (!await this.#db.findOne("custom_providers", {
@@ -89,18 +72,17 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
89
72
  provider_id: input.providerId,
90
73
  name: input.name,
91
74
  url: input.url,
92
- api_key: encryptedApiKey,
75
+ api_key: input.apiKey ?? null,
93
76
  models: input.models,
94
77
  updated_at: now
95
78
  }));
96
- if (renamed) return this.#toRecord(renamed);
79
+ if (renamed) return toRecord(renamed);
97
80
  }
98
81
  }
99
82
  const record = await this.#write({
100
83
  orgId,
101
84
  userId,
102
85
  input,
103
- encryptedApiKey,
104
86
  now
105
87
  });
106
88
  if (renameFrom) await this.#db.deleteMany("custom_providers", {
@@ -115,42 +97,40 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
115
97
  * the single row) instead of one failing on the unique index, and a row
116
98
  * deleted mid-flight is recreated rather than reported as a stale success.
117
99
  */
118
- async #write({ orgId, userId, input, encryptedApiKey, now }) {
100
+ async #write({ orgId, userId, input, now }) {
119
101
  const updateExisting = () => this.#db.updateAtomic("custom_providers", {
120
102
  org_id: orgId,
121
103
  provider_id: input.providerId
122
104
  }, () => ({
123
105
  name: input.name,
124
106
  url: input.url,
125
- api_key: encryptedApiKey,
107
+ api_key: input.apiKey ?? null,
126
108
  models: input.models,
127
109
  updated_at: now
128
110
  }));
129
111
  const updated = await updateExisting();
130
- if (updated) return this.#toRecord(updated);
112
+ if (updated) return toRecord(updated);
131
113
  try {
132
- const row = await this.#db.insertOne("custom_providers", {
114
+ return toRecord(await this.#db.insertOne("custom_providers", {
133
115
  org_id: orgId,
134
116
  created_by: userId,
135
117
  provider_id: input.providerId,
136
118
  name: input.name,
137
119
  url: input.url,
138
- api_key: encryptedApiKey,
120
+ api_key: input.apiKey ?? null,
139
121
  models: input.models,
140
122
  created_at: now,
141
123
  updated_at: now
142
- });
143
- return this.#toRecord(row);
124
+ }));
144
125
  } catch (error) {
145
126
  if (!(error instanceof UniqueViolationError)) throw error;
146
127
  const row = await updateExisting();
147
128
  if (!row) throw error;
148
- return this.#toRecord(row);
129
+ return toRecord(row);
149
130
  }
150
131
  }
151
132
  async list({ orgId }) {
152
- const rows = await this.#db.findMany("custom_providers", { org_id: orgId }, { orderBy: [["name", "asc"]] });
153
- return Promise.all(rows.map((row) => this.#toRecord(row)));
133
+ return (await this.#db.findMany("custom_providers", { org_id: orgId }, { orderBy: [["name", "asc"]] })).map(toRecord);
154
134
  }
155
135
  async delete({ orgId, providerId }) {
156
136
  return await this.#db.deleteMany("custom_providers", {
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#migrateApiKey","#db","#toRecord","#encryptApiKey","#write"],"sources":["../../../../src/storage/domains/custom-providers/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\n/**\n * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the\n * local `settings.json` `customProviders` entries: org-scoped rows keyed by the\n * derived provider id (name slug), API key stored alongside so a deployed\n * server never reads the shared settings file.\n */\nexport interface CustomProviderRecord {\n id: string;\n orgId: string;\n createdBy: string;\n /** Stable slug derived from the name (`getCustomProviderId`). */\n providerId: string;\n name: string;\n url: string;\n apiKey: string | null;\n models: string[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertCustomProviderInput {\n providerId: string;\n name: string;\n url: string;\n apiKey?: string;\n models: string[];\n}\n\nexport const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema = {\n name: 'custom_providers',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n provider_id: { type: 'text' },\n name: { type: 'text' },\n url: { type: 'text' },\n api_key: { type: 'text', nullable: true },\n models: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'custom_providers_org_provider_key', columns: ['org_id', 'provider_id'] }],\n};\n\ninterface CustomProviderDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n provider_id: string;\n name: string;\n url: string;\n api_key: string | null;\n models: string[];\n created_at: Date;\n updated_at: Date;\n}\n\nexport class CustomProvidersStorage extends FactoryStorageDomain {\n constructor(private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption()) {\n super('custom-providers');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);\n const rows = await this.ops.findMany<CustomProviderDbRow>('custom_providers', {});\n await Promise.all(rows.filter(row => row.api_key !== null).map(row => this.#migrateApiKey(row.id)));\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('custom_providers', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #toRecord(row: CustomProviderDbRow): Promise<CustomProviderRecord> {\n const apiKey = row.api_key === null ? null : (await this.encryption.decrypt<string>(row.api_key)).value;\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n providerId: row.provider_id,\n name: row.name,\n url: row.url,\n apiKey,\n models: Array.isArray(row.models) ? row.models : [],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n }\n\n async #encryptApiKey(apiKey: string | undefined): Promise<string | null> {\n return apiKey === undefined ? null : this.encryption.encrypt(apiKey);\n }\n\n async #migrateApiKey(id: string): Promise<void> {\n await this.#db.updateAtomic<CustomProviderDbRow>('custom_providers', { id }, async row => {\n if (row.api_key === null) return null;\n const decrypted = await this.encryption.decrypt<string>(row.api_key);\n return decrypted.needsReencryption ? { api_key: await this.encryption.encrypt(decrypted.value) } : null;\n });\n }\n\n /**\n * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors\n * the settings.json upsert semantics (no key retention: an absent `apiKey`\n * clears the stored key). `previousProviderId` renames the provider: the\n * common path is a single atomic in-place update of the old row; renaming\n * onto an id that already exists overwrites the target first and removes the\n * old row last, so no interleaving or failure can lose the provider.\n */\n async upsert({\n orgId,\n userId,\n input,\n previousProviderId,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n previousProviderId?: string;\n }): Promise<CustomProviderRecord> {\n const now = new Date();\n const encryptedApiKey = await this.#encryptApiKey(input.apiKey);\n const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : undefined;\n\n if (renameFrom) {\n const target = await this.#db.findOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n provider_id: input.providerId,\n });\n if (!target) {\n // In-place rename: one atomic write with no delete/insert gap, so a\n // failure can never drop the provider (and `created_at`/`created_by`\n // are preserved). If a concurrent create grabs the new id between the\n // lookup and this update, the unique index rejects the update and the\n // old row stays intact — the request fails cleanly and a retry\n // converges on the rename-onto-existing path below.\n const renamed = await this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: renameFrom },\n () => ({\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n updated_at: now,\n }),\n );\n if (renamed) return this.#toRecord(renamed);\n // Old row already gone — fall through to a plain create of the new id.\n }\n // Rename onto an existing id: overwrite the target below, then delete\n // the old row last — a failure in the gap leaves a redundant duplicate\n // (recoverable by re-submitting or deleting) instead of losing data.\n }\n\n const record = await this.#write({ orgId, userId, input, encryptedApiKey, now });\n if (renameFrom) {\n await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: renameFrom });\n }\n return record;\n }\n\n /**\n * Update-first, then insert-and-catch-unique-violation (see `queue_health`):\n * concurrent creates of the same provider both succeed (last write wins on\n * the single row) instead of one failing on the unique index, and a row\n * deleted mid-flight is recreated rather than reported as a stale success.\n */\n async #write({\n orgId,\n userId,\n input,\n encryptedApiKey,\n now,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n encryptedApiKey: string | null;\n now: Date;\n }): Promise<CustomProviderRecord> {\n const updateExisting = () =>\n this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: input.providerId },\n () => ({\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n updated_at: now,\n }),\n );\n\n const updated = await updateExisting();\n if (updated) return this.#toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n created_by: userId,\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: encryptedApiKey,\n models: input.models,\n created_at: now,\n updated_at: now,\n });\n return this.#toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — apply this write to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return this.#toRecord(row);\n }\n }\n\n async list({ orgId }: { orgId: string }): Promise<CustomProviderRecord[]> {\n const rows = await this.#db.findMany<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId },\n { orderBy: [['name', 'asc']] },\n );\n return Promise.all(rows.map(row => this.#toRecord(row)));\n }\n\n async delete({ orgId, providerId }: { orgId: string; providerId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: providerId })) > 0;\n }\n}\n"],"mappings":";;;AAiCA,MAAa,0BAA4C;CACvD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,OAAO;EAC3B,aAAa,EAAE,MAAM,OAAO;EAC5B,MAAM,EAAE,MAAM,OAAO;EACrB,KAAK,EAAE,MAAM,OAAO;EACpB,SAAS;GAAE,MAAM;GAAQ,UAAU;EAAK;EACxC,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAAC;EAAE,MAAM;EAAqC,SAAS,CAAC,UAAU,aAAa;CAAE,CAAC;AACnG;AAeA,IAAa,yBAAb,cAA4C,qBAAqB;CAClC;CAA7B,YAAY,aAAuD,uCAAuC,GAAG;EAC3G,MAAM,kBAAkB;EADG,KAAA,aAAA;CAE7B;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,uBAAuB,CAAC;EACtD,MAAM,OAAO,MAAM,KAAK,IAAI,SAA8B,oBAAoB,CAAC,CAAC;EAChF,MAAM,QAAQ,IAAI,KAAK,QAAO,QAAO,IAAI,YAAY,IAAI,CAAC,CAAC,KAAI,QAAO,KAAKA,eAAe,IAAI,EAAE,CAAC,CAAC;CACpG;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,oBAAoB,CAAC,CAAC;CAClD;CAEA,IAAIC,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAMC,UAAU,KAAyD;EACvE,MAAM,SAAS,IAAI,YAAY,OAAO,QAAQ,MAAM,KAAK,WAAW,QAAgB,IAAI,OAAO,EAAA,CAAG;EAClG,OAAO;GACL,IAAI,IAAI;GACR,OAAO,IAAI;GACX,WAAW,IAAI;GACf,YAAY,IAAI;GAChB,MAAM,IAAI;GACV,KAAK,IAAI;GACT;GACA,QAAQ,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,SAAS,CAAC;GAClD,WAAW,IAAI;GACf,WAAW,IAAI;EACjB;CACF;CAEA,MAAMC,eAAe,QAAoD;EACvE,OAAO,WAAW,KAAA,IAAY,OAAO,KAAK,WAAW,QAAQ,MAAM;CACrE;CAEA,MAAMH,eAAe,IAA2B;EAC9C,MAAM,KAAKC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,GAAG,OAAM,QAAO;GACxF,IAAI,IAAI,YAAY,MAAM,OAAO;GACjC,MAAM,YAAY,MAAM,KAAK,WAAW,QAAgB,IAAI,OAAO;GACnE,OAAO,UAAU,oBAAoB,EAAE,SAAS,MAAM,KAAK,WAAW,QAAQ,UAAU,KAAK,EAAE,IAAI;EACrG,CAAC;CACH;;;;;;;;;CAUA,MAAM,OAAO,EACX,OACA,QACA,OACA,sBAMgC;EAChC,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,kBAAkB,MAAM,KAAKE,eAAe,MAAM,MAAM;EAC9D,MAAM,aAAa,sBAAsB,uBAAuB,MAAM,aAAa,qBAAqB,KAAA;EAExG,IAAI,YAKE;OAAA,CAAC,MAJgB,KAAKF,IAAI,QAA6B,oBAAoB;IAC7E,QAAQ;IACR,aAAa,MAAM;GACrB,CAAC,GACY;IAOX,MAAM,UAAU,MAAM,KAAKA,IAAI,aAC7B,oBACA;KAAE,QAAQ;KAAO,aAAa;IAAW,UAClC;KACL,aAAa,MAAM;KACnB,MAAM,MAAM;KACZ,KAAK,MAAM;KACX,SAAS;KACT,QAAQ,MAAM;KACd,YAAY;IACd,EACF;IACA,IAAI,SAAS,OAAO,KAAKC,UAAU,OAAO;GAE5C;;EAMF,MAAM,SAAS,MAAM,KAAKE,OAAO;GAAE;GAAO;GAAQ;GAAO;GAAiB;EAAI,CAAC;EAC/E,IAAI,YACF,MAAM,KAAKH,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO,aAAa;EAAW,CAAC;EAE1F,OAAO;CACT;;;;;;;CAQA,MAAMG,OAAO,EACX,OACA,QACA,OACA,iBACA,OAOgC;EAChC,MAAM,uBACJ,KAAKH,IAAI,aACP,oBACA;GAAE,QAAQ;GAAO,aAAa,MAAM;EAAW,UACxC;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,SAAS;GACT,QAAQ,MAAM;GACd,YAAY;EACd,EACF;EAEF,MAAM,UAAU,MAAM,eAAe;EACrC,IAAI,SAAS,OAAO,KAAKC,UAAU,OAAO;EAE1C,IAAI;GACF,MAAM,MAAM,MAAM,KAAKD,IAAI,UAA+B,oBAAoB;IAC5E,QAAQ;IACR,YAAY;IACZ,aAAa,MAAM;IACnB,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,SAAS;IACT,QAAQ,MAAM;IACd,YAAY;IACZ,YAAY;GACd,CAAC;GACD,OAAO,KAAKC,UAAU,GAAG;EAC3B,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GAEpD,MAAM,MAAM,MAAM,eAAe;GACjC,IAAI,CAAC,KAAK,MAAM;GAChB,OAAO,KAAKA,UAAU,GAAG;EAC3B;CACF;CAEA,MAAM,KAAK,EAAE,SAA6D;EACxE,MAAM,OAAO,MAAM,KAAKD,IAAI,SAC1B,oBACA,EAAE,QAAQ,MAAM,GAChB,EAAE,SAAS,CAAC,CAAC,QAAQ,KAAK,CAAC,EAAE,CAC/B;EACA,OAAO,QAAQ,IAAI,KAAK,KAAI,QAAO,KAAKC,UAAU,GAAG,CAAC,CAAC;CACzD;CAEA,MAAM,OAAO,EAAE,OAAO,cAAuE;EAC3F,OAAQ,MAAM,KAAKD,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO,aAAa;EAAW,CAAC,IAAK;CACvG;AACF"}
1
+ {"version":3,"file":"base.js","names":["#db","#write"],"sources":["../../../../src/storage/domains/custom-providers/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\n/**\n * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the\n * local `settings.json` `customProviders` entries: org-scoped rows keyed by the\n * derived provider id (name slug), API key stored alongside so a deployed\n * server never reads the shared settings file.\n */\nexport interface CustomProviderRecord {\n id: string;\n orgId: string;\n createdBy: string;\n /** Stable slug derived from the name (`getCustomProviderId`). */\n providerId: string;\n name: string;\n url: string;\n apiKey: string | null;\n models: string[];\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertCustomProviderInput {\n providerId: string;\n name: string;\n url: string;\n apiKey?: string;\n models: string[];\n}\n\nexport const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema = {\n name: 'custom_providers',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n provider_id: { type: 'text' },\n name: { type: 'text' },\n url: { type: 'text' },\n api_key: { type: 'text', nullable: true },\n models: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'custom_providers_org_provider_key', columns: ['org_id', 'provider_id'] }],\n};\n\ninterface CustomProviderDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n provider_id: string;\n name: string;\n url: string;\n api_key: string | null;\n models: string[];\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toRecord(row: CustomProviderDbRow): CustomProviderRecord {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n providerId: row.provider_id,\n name: row.name,\n url: row.url,\n apiKey: row.api_key,\n models: Array.isArray(row.models) ? row.models : [],\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class CustomProvidersStorage extends FactoryStorageDomain {\n constructor() {\n super('custom-providers');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('custom_providers', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /**\n * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors\n * the settings.json upsert semantics (no key retention: an absent `apiKey`\n * clears the stored key). `previousProviderId` renames the provider: the\n * common path is a single atomic in-place update of the old row; renaming\n * onto an id that already exists overwrites the target first and removes the\n * old row last, so no interleaving or failure can lose the provider.\n */\n async upsert({\n orgId,\n userId,\n input,\n previousProviderId,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n previousProviderId?: string;\n }): Promise<CustomProviderRecord> {\n const now = new Date();\n const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : undefined;\n\n if (renameFrom) {\n const target = await this.#db.findOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n provider_id: input.providerId,\n });\n if (!target) {\n // In-place rename: one atomic write with no delete/insert gap, so a\n // failure can never drop the provider (and `created_at`/`created_by`\n // are preserved). If a concurrent create grabs the new id between the\n // lookup and this update, the unique index rejects the update and the\n // old row stays intact — the request fails cleanly and a retry\n // converges on the rename-onto-existing path below.\n const renamed = await this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: renameFrom },\n () => ({\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n updated_at: now,\n }),\n );\n if (renamed) return toRecord(renamed);\n // Old row already gone — fall through to a plain create of the new id.\n }\n // Rename onto an existing id: overwrite the target below, then delete\n // the old row last — a failure in the gap leaves a redundant duplicate\n // (recoverable by re-submitting or deleting) instead of losing data.\n }\n\n const record = await this.#write({ orgId, userId, input, now });\n if (renameFrom) {\n await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: renameFrom });\n }\n return record;\n }\n\n /**\n * Update-first, then insert-and-catch-unique-violation (see `queue_health`):\n * concurrent creates of the same provider both succeed (last write wins on\n * the single row) instead of one failing on the unique index, and a row\n * deleted mid-flight is recreated rather than reported as a stale success.\n */\n async #write({\n orgId,\n userId,\n input,\n now,\n }: {\n orgId: string;\n userId: string;\n input: UpsertCustomProviderInput;\n now: Date;\n }): Promise<CustomProviderRecord> {\n const updateExisting = () =>\n this.#db.updateAtomic<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId, provider_id: input.providerId },\n () => ({\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n updated_at: now,\n }),\n );\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n const row = await this.#db.insertOne<CustomProviderDbRow>('custom_providers', {\n org_id: orgId,\n created_by: userId,\n provider_id: input.providerId,\n name: input.name,\n url: input.url,\n api_key: input.apiKey ?? null,\n models: input.models,\n created_at: now,\n updated_at: now,\n });\n return toRecord(row);\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — apply this write to the winning row.\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n\n async list({ orgId }: { orgId: string }): Promise<CustomProviderRecord[]> {\n const rows = await this.#db.findMany<CustomProviderDbRow>(\n 'custom_providers',\n { org_id: orgId },\n { orderBy: [['name', 'asc']] },\n );\n return rows.map(toRecord);\n }\n\n async delete({ orgId, providerId }: { orgId: string; providerId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('custom_providers', { org_id: orgId, provider_id: providerId })) > 0;\n }\n}\n"],"mappings":"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"}
@@ -20,7 +20,6 @@
20
20
  */
21
21
  import { FactoryStorageDomain } from '@mastra/core/storage';
22
22
  import type { CollectionSchema } from '@mastra/core/storage';
23
- import type { FactorySecretEncryption } from '../../../secret-encryption.js';
24
23
  export declare const INTEGRATION_CONNECTIONS_SCHEMA: CollectionSchema;
25
24
  export declare const INTEGRATION_SUBSCRIPTIONS_SCHEMA: CollectionSchema;
26
25
  export declare const INTEGRATION_SETTINGS_SCHEMA: CollectionSchema;
@@ -118,8 +117,7 @@ export interface IntegrationStorageHandle<TConnection = Record<string, unknown>,
118
117
  */
119
118
  export declare class IntegrationStorage extends FactoryStorageDomain {
120
119
  #private;
121
- private readonly encryption;
122
- constructor(encryption?: FactorySecretEncryption);
120
+ constructor();
123
121
  init(): Promise<void>;
124
122
  dangerouslyClearAll(): Promise<void>;
125
123
  /** A typed handle pre-scoped to `integrationId`. */
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/integrations/base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/integrations/base.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
2
1
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
3
2
  //#region src/storage/domains/integrations/base.ts
4
3
  /**
@@ -104,10 +103,8 @@ const INTEGRATION_SETTINGS_SCHEMA = {
104
103
  * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.
105
104
  */
106
105
  var IntegrationStorage = class extends FactoryStorageDomain {
107
- encryption;
108
- constructor(encryption = createPlaintextFactorySecretEncryption()) {
106
+ constructor() {
109
107
  super("integrations");
110
- this.encryption = encryption;
111
108
  }
112
109
  async init() {
113
110
  await this.ensureCollections([
@@ -115,8 +112,6 @@ var IntegrationStorage = class extends FactoryStorageDomain {
115
112
  INTEGRATION_SUBSCRIPTIONS_SCHEMA,
116
113
  INTEGRATION_SETTINGS_SCHEMA
117
114
  ]);
118
- const [connections, settings] = await Promise.all([this.ops.findMany("integration_connections", {}), this.ops.findMany("integration_settings", {})]);
119
- await Promise.all([...connections.map((row) => this.#migrateValue("integration_connections", row.id, "data")), ...settings.map((row) => this.#migrateValue("integration_settings", row.id, "config"))]);
120
115
  }
121
116
  async dangerouslyClearAll() {
122
117
  await this.ops.deleteMany("integration_subscriptions", {});
@@ -126,22 +121,16 @@ var IntegrationStorage = class extends FactoryStorageDomain {
126
121
  get #db() {
127
122
  return this.ops;
128
123
  }
129
- async #migrateValue(collection, id, column) {
130
- await this.#db.updateAtomic(collection, { id }, async (row) => {
131
- const decrypted = await this.encryption.decrypt(row[column]);
132
- return decrypted.needsReencryption ? { [column]: await this.encryption.encrypt(decrypted.value) } : null;
133
- });
134
- }
135
124
  /** A typed handle pre-scoped to `integrationId`. */
136
125
  forIntegration(integrationId) {
137
126
  if (!integrationId) throw new Error("[IntegrationStorage] integrationId is required.");
138
127
  const db = () => this.#db;
139
128
  const scoped = { integration_id: integrationId };
140
- const mapConnection = async (row) => ({
129
+ const mapConnection = (row) => ({
141
130
  id: row.id,
142
131
  orgId: row.org_id,
143
132
  userId: row.user_id,
144
- data: (await this.encryption.decrypt(row.data)).value,
133
+ data: row.data,
145
134
  metadata: row.metadata ?? {},
146
135
  createdAt: row.created_at,
147
136
  updatedAt: row.updated_at
@@ -174,10 +163,9 @@ var IntegrationStorage = class extends FactoryStorageDomain {
174
163
  ...scoped,
175
164
  org_id: orgId
176
165
  };
177
- const encrypted = await this.encryption.encrypt(input.data);
178
166
  const update = async () => db().updateMany("integration_connections", where, {
179
167
  user_id: input.userId ?? null,
180
- data: encrypted,
168
+ data: input.data,
181
169
  metadata: input.metadata ?? {},
182
170
  updated_at: /* @__PURE__ */ new Date()
183
171
  });
@@ -189,7 +177,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
189
177
  await db().insertOne("integration_connections", {
190
178
  ...where,
191
179
  user_id: input.userId ?? null,
192
- data: encrypted,
180
+ data: input.data,
193
181
  metadata: input.metadata ?? {},
194
182
  created_at: now,
195
183
  updated_at: now
@@ -206,13 +194,10 @@ var IntegrationStorage = class extends FactoryStorageDomain {
206
194
  const row = await db().updateAtomic("integration_connections", {
207
195
  ...scoped,
208
196
  org_id: orgId
209
- }, async (current) => {
210
- const data = (await this.encryption.decrypt(current.data)).value;
211
- return {
212
- data: await this.encryption.encrypt(fn(data)),
213
- updated_at: /* @__PURE__ */ new Date()
214
- };
215
- });
197
+ }, (current) => ({
198
+ data: fn(current.data),
199
+ updated_at: /* @__PURE__ */ new Date()
200
+ }));
216
201
  return row ? mapConnection(row) : null;
217
202
  },
218
203
  delete: async (orgId) => {
@@ -291,7 +276,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
291
276
  org_id: orgId,
292
277
  user_id: userId
293
278
  });
294
- return row ? (await this.encryption.decrypt(row.config)).value : null;
279
+ return row ? structuredClone(row.config) : null;
295
280
  },
296
281
  save: async (orgId, userId, config) => {
297
282
  const where = {
@@ -299,9 +284,8 @@ var IntegrationStorage = class extends FactoryStorageDomain {
299
284
  org_id: orgId,
300
285
  user_id: userId
301
286
  };
302
- const encrypted = await this.encryption.encrypt(config);
303
287
  const update = async () => db().updateMany("integration_settings", where, {
304
- config: encrypted,
288
+ config,
305
289
  updated_at: /* @__PURE__ */ new Date()
306
290
  });
307
291
  let lastError;
@@ -311,7 +295,7 @@ var IntegrationStorage = class extends FactoryStorageDomain {
311
295
  try {
312
296
  await db().insertOne("integration_settings", {
313
297
  ...where,
314
- config: encrypted,
298
+ config,
315
299
  created_at: now,
316
300
  updated_at: now
317
301
  });
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#migrateValue","#db"],"sources":["../../../../src/storage/domains/integrations/base.ts"],"sourcesContent":["/**\n * Generic integration storage — the default persistence surface for\n * `FactoryIntegration`s.\n *\n * Most integrations persist the same three shapes: an org-owned OAuth\n * **connection**, **subscriptions** binding external objects to\n * sessions/threads, and per-`(org, user)` **settings**. This built-in domain\n * stores all three in shared collections keyed by `integration_id`, so an\n * integration author writes zero schema, zero DDL, and zero queries — and\n * org scoping is enforced by construction through the pre-scoped handle\n * returned by {@link IntegrationStorage.forIntegration}.\n *\n * Source-control integrations additionally use the shared, provider-scoped\n * source-control domain for installations, projects, worktrees, and sandboxes.\n *\n * Payloads (`data` / `config`) are JSON documents typed per-integration via\n * the handle's generics. JSON round-trips exactly what JSON can represent:\n * store timestamps inside payloads as epoch millis or ISO strings, not\n * `Date`s.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\nimport { createPlaintextFactorySecretEncryption } from '../../../secret-encryption.js';\nimport type { FactorySecretEncryption } from '../../../secret-encryption.js';\n\nexport const INTEGRATION_CONNECTIONS_SCHEMA: CollectionSchema = {\n name: 'integration_connections',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n /** Who connected it (audit only — never scopes reads). */\n user_id: { type: 'text', nullable: true },\n data: { type: 'json' },\n metadata: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'integration_connections_integration_org_unique', columns: ['integration_id', 'org_id'] }],\n};\n\nexport const INTEGRATION_SUBSCRIPTIONS_SCHEMA: CollectionSchema = {\n name: 'integration_subscriptions',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n /** Canonical external-object key the integration derives (e.g. `pr:{installation}:{repo}:{number}`). */\n target_key: { type: 'text' },\n session_id: { type: 'text', nullable: true },\n resource_id: { type: 'text', nullable: true },\n thread_id: { type: 'text', nullable: true },\n session_scope: { type: 'text', nullable: true },\n status: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [\n { name: 'integration_subscriptions_target_idx', columns: ['integration_id', 'target_key'] },\n { name: 'integration_subscriptions_org_idx', columns: ['integration_id', 'org_id'] },\n ],\n};\n\nexport const INTEGRATION_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'integration_settings',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [\n { name: 'integration_settings_integration_org_user_unique', columns: ['integration_id', 'org_id', 'user_id'] },\n ],\n};\n\n/** An integration's org-owned connection (typically the OAuth grant). */\nexport interface IntegrationConnection<TData> {\n id: string;\n orgId: string;\n /** Who connected it (audit only). */\n userId: string | null;\n data: TData;\n metadata: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** A subscription binding an external object to a session/thread. */\nexport interface IntegrationSubscription<TData> {\n id: string;\n orgId: string;\n targetKey: string;\n sessionId: string | null;\n resourceId: string | null;\n threadId: string | null;\n sessionScope: string | null;\n status: string;\n data: TData;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport type CreateIntegrationSubscriptionInput<TData> = {\n orgId: string;\n targetKey: string;\n sessionId?: string | null;\n resourceId?: string | null;\n threadId?: string | null;\n sessionScope?: string | null;\n /** Defaults to `'active'`. */\n status?: string;\n} & ({} extends TData ? { data?: TData } : { data: TData });\n\n/**\n * Typed query surface pre-scoped to one `integration_id`. Every read and\n * write is automatically filtered/stamped with the integration id, so an\n * integration cannot reach another integration's rows.\n */\nexport interface IntegrationStorageHandle<\n TConnection = Record<string, unknown>,\n TSettings = Record<string, unknown>,\n TSubscription = Record<string, unknown>,\n> {\n readonly integrationId: string;\n connections: {\n /** The org's connection, or `null` when not connected. */\n get(orgId: string): Promise<IntegrationConnection<TConnection> | null>;\n /** Insert or replace the org's connection (one per org; `created_at` preserved on update). */\n upsert(\n orgId: string,\n input: { userId?: string | null; data: TConnection; metadata?: Record<string, unknown> },\n ): Promise<void>;\n /**\n * Atomic read-modify-write of the connection's `data` payload (token\n * rotation etc.). Returns the updated connection, or `null` when the org\n * has no connection.\n */\n update(orgId: string, fn: (data: TConnection) => TConnection): Promise<IntegrationConnection<TConnection> | null>;\n /** Remove the org's connection. Returns whether a row was deleted. */\n delete(orgId: string): Promise<boolean>;\n };\n subscriptions: {\n create(input: CreateIntegrationSubscriptionInput<TSubscription>): Promise<IntegrationSubscription<TSubscription>>;\n /** All subscriptions for an external object (webhook fan-out), optionally filtered by status. */\n listByTarget(targetKey: string, opts?: { status?: string }): Promise<IntegrationSubscription<TSubscription>[]>;\n listBySession(sessionId: string): Promise<IntegrationSubscription<TSubscription>[]>;\n listByThread(resourceId: string, threadId: string): Promise<IntegrationSubscription<TSubscription>[]>;\n updateStatus(id: string, status: string): Promise<void>;\n delete(id: string): Promise<boolean>;\n /** Targeted cleanup, always org-scoped. Returns the number of rows deleted. */\n deleteWhere(where: { orgId: string; targetKey?: string; sessionId?: string }): Promise<number>;\n };\n settings: {\n /** The `(org, user)` config, or `null` when never saved. */\n get(orgId: string, userId: string): Promise<TSettings | null>;\n /** Upsert the `(org, user)` config (`created_at` preserved on update). */\n save(orgId: string, userId: string, config: TSettings): Promise<void>;\n };\n}\n\ninterface ConnectionRow extends Record<string, unknown> {\n id: string;\n integration_id: string;\n org_id: string;\n user_id: string | null;\n data: unknown;\n metadata: Record<string, unknown>;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface SubscriptionRow extends Record<string, unknown> {\n id: string;\n integration_id: string;\n org_id: string;\n target_key: string;\n session_id: string | null;\n resource_id: string | null;\n thread_id: string | null;\n session_scope: string | null;\n status: string;\n data: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\n/**\n * The built-in generic integration domain, written once against the\n * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.\n */\nexport class IntegrationStorage extends FactoryStorageDomain {\n constructor(\n private readonly encryption: FactorySecretEncryption = createPlaintextFactorySecretEncryption(),\n ) {\n super('integrations');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([\n INTEGRATION_CONNECTIONS_SCHEMA,\n INTEGRATION_SUBSCRIPTIONS_SCHEMA,\n INTEGRATION_SETTINGS_SCHEMA,\n ]);\n const [connections, settings] = await Promise.all([\n this.ops.findMany<ConnectionRow>('integration_connections', {}),\n this.ops.findMany<{ id: string; config: unknown }>('integration_settings', {}),\n ]);\n await Promise.all([\n ...connections.map(row => this.#migrateValue('integration_connections', row.id, 'data')),\n ...settings.map(row => this.#migrateValue('integration_settings', row.id, 'config')),\n ]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('integration_subscriptions', {});\n await this.ops.deleteMany('integration_settings', {});\n await this.ops.deleteMany('integration_connections', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async #migrateValue(collection: string, id: string, column: 'data' | 'config'): Promise<void> {\n await this.#db.updateAtomic<Record<string, unknown>>(collection, { id }, async row => {\n const decrypted = await this.encryption.decrypt(row[column]);\n return decrypted.needsReencryption ? { [column]: await this.encryption.encrypt(decrypted.value) } : null;\n });\n }\n\n /** A typed handle pre-scoped to `integrationId`. */\n forIntegration<\n TConnection = Record<string, unknown>,\n TSettings = Record<string, unknown>,\n TSubscription = Record<string, unknown>,\n >(integrationId: string): IntegrationStorageHandle<TConnection, TSettings, TSubscription> {\n if (!integrationId) throw new Error('[IntegrationStorage] integrationId is required.');\n const db = () => this.#db;\n const scoped = { integration_id: integrationId };\n\n const mapConnection = async (row: ConnectionRow): Promise<IntegrationConnection<TConnection>> => ({\n id: row.id,\n orgId: row.org_id,\n userId: row.user_id,\n data: (await this.encryption.decrypt<TConnection>(row.data)).value,\n metadata: row.metadata ?? {},\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n });\n\n const mapSubscription = (row: SubscriptionRow): IntegrationSubscription<TSubscription> => ({\n id: row.id,\n orgId: row.org_id,\n targetKey: row.target_key,\n sessionId: row.session_id,\n resourceId: row.resource_id,\n threadId: row.thread_id,\n sessionScope: row.session_scope,\n status: row.status,\n data: row.data as TSubscription,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n });\n\n return {\n integrationId,\n connections: {\n get: async orgId => {\n const row = await db().findOne<ConnectionRow>('integration_connections', { ...scoped, org_id: orgId });\n return row ? mapConnection(row) : null;\n },\n upsert: async (orgId, input) => {\n const where = { ...scoped, org_id: orgId };\n const encrypted = await this.encryption.encrypt(input.data);\n const update = async () =>\n db().updateMany('integration_connections', where, {\n user_id: input.userId ?? null,\n data: encrypted,\n metadata: input.metadata ?? {},\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await db().insertOne<ConnectionRow>('integration_connections', {\n ...where,\n user_id: input.userId ?? null,\n data: encrypted,\n metadata: input.metadata ?? {},\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n },\n update: async (orgId, fn) => {\n const row = await db().updateAtomic<ConnectionRow>(\n 'integration_connections',\n { ...scoped, org_id: orgId },\n async current => {\n const data = (await this.encryption.decrypt<TConnection>(current.data)).value;\n return { data: await this.encryption.encrypt(fn(data)), updated_at: new Date() };\n },\n );\n return row ? mapConnection(row) : null;\n },\n delete: async orgId => {\n const deleted = await db().deleteMany('integration_connections', { ...scoped, org_id: orgId });\n return deleted > 0;\n },\n },\n subscriptions: {\n create: async input => {\n const now = new Date();\n const row = await db().insertOne<SubscriptionRow>('integration_subscriptions', {\n ...scoped,\n org_id: input.orgId,\n target_key: input.targetKey,\n session_id: input.sessionId ?? null,\n resource_id: input.resourceId ?? null,\n thread_id: input.threadId ?? null,\n session_scope: input.sessionScope ?? null,\n status: input.status ?? 'active',\n data: input.data ?? {},\n created_at: now,\n updated_at: now,\n });\n return mapSubscription(row);\n },\n listByTarget: async (targetKey, opts) => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, target_key: targetKey, ...(opts?.status !== undefined ? { status: opts.status } : {}) },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n listBySession: async sessionId => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, session_id: sessionId },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n listByThread: async (resourceId, threadId) => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, resource_id: resourceId, thread_id: threadId },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n updateStatus: async (id, status) => {\n await db().updateMany('integration_subscriptions', { ...scoped, id }, { status, updated_at: new Date() });\n },\n delete: async id => {\n const deleted = await db().deleteMany('integration_subscriptions', { ...scoped, id });\n return deleted > 0;\n },\n deleteWhere: async where => {\n return db().deleteMany('integration_subscriptions', {\n ...scoped,\n org_id: where.orgId,\n ...(where.targetKey !== undefined ? { target_key: where.targetKey } : {}),\n ...(where.sessionId !== undefined ? { session_id: where.sessionId } : {}),\n });\n },\n },\n settings: {\n get: async (orgId, userId) => {\n const row = await db().findOne<{ config: unknown }>('integration_settings', {\n ...scoped,\n org_id: orgId,\n user_id: userId,\n });\n return row ? (await this.encryption.decrypt<TSettings>(row.config)).value : null;\n },\n save: async (orgId, userId, config) => {\n const where = { ...scoped, org_id: orgId, user_id: userId };\n const encrypted = await this.encryption.encrypt(config);\n const update = async () =>\n db().updateMany('integration_settings', where, {\n config: encrypted,\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await db().insertOne('integration_settings', {\n ...where,\n config: encrypted,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n },\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/integrations/base.ts"],"sourcesContent":["/**\n * Generic integration storage — the default persistence surface for\n * `FactoryIntegration`s.\n *\n * Most integrations persist the same three shapes: an org-owned OAuth\n * **connection**, **subscriptions** binding external objects to\n * sessions/threads, and per-`(org, user)` **settings**. This built-in domain\n * stores all three in shared collections keyed by `integration_id`, so an\n * integration author writes zero schema, zero DDL, and zero queries — and\n * org scoping is enforced by construction through the pre-scoped handle\n * returned by {@link IntegrationStorage.forIntegration}.\n *\n * Source-control integrations additionally use the shared, provider-scoped\n * source-control domain for installations, projects, worktrees, and sandboxes.\n *\n * Payloads (`data` / `config`) are JSON documents typed per-integration via\n * the handle's generics. JSON round-trips exactly what JSON can represent:\n * store timestamps inside payloads as epoch millis or ISO strings, not\n * `Date`s.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport const INTEGRATION_CONNECTIONS_SCHEMA: CollectionSchema = {\n name: 'integration_connections',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n /** Who connected it (audit only — never scopes reads). */\n user_id: { type: 'text', nullable: true },\n data: { type: 'json' },\n metadata: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'integration_connections_integration_org_unique', columns: ['integration_id', 'org_id'] }],\n};\n\nexport const INTEGRATION_SUBSCRIPTIONS_SCHEMA: CollectionSchema = {\n name: 'integration_subscriptions',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n /** Canonical external-object key the integration derives (e.g. `pr:{installation}:{repo}:{number}`). */\n target_key: { type: 'text' },\n session_id: { type: 'text', nullable: true },\n resource_id: { type: 'text', nullable: true },\n thread_id: { type: 'text', nullable: true },\n session_scope: { type: 'text', nullable: true },\n status: { type: 'text' },\n data: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [\n { name: 'integration_subscriptions_target_idx', columns: ['integration_id', 'target_key'] },\n { name: 'integration_subscriptions_org_idx', columns: ['integration_id', 'org_id'] },\n ],\n};\n\nexport const INTEGRATION_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'integration_settings',\n columns: {\n id: { type: 'uuid-pk' },\n integration_id: { type: 'text' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [\n { name: 'integration_settings_integration_org_user_unique', columns: ['integration_id', 'org_id', 'user_id'] },\n ],\n};\n\n/** An integration's org-owned connection (typically the OAuth grant). */\nexport interface IntegrationConnection<TData> {\n id: string;\n orgId: string;\n /** Who connected it (audit only). */\n userId: string | null;\n data: TData;\n metadata: Record<string, unknown>;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** A subscription binding an external object to a session/thread. */\nexport interface IntegrationSubscription<TData> {\n id: string;\n orgId: string;\n targetKey: string;\n sessionId: string | null;\n resourceId: string | null;\n threadId: string | null;\n sessionScope: string | null;\n status: string;\n data: TData;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport type CreateIntegrationSubscriptionInput<TData> = {\n orgId: string;\n targetKey: string;\n sessionId?: string | null;\n resourceId?: string | null;\n threadId?: string | null;\n sessionScope?: string | null;\n /** Defaults to `'active'`. */\n status?: string;\n} & ({} extends TData ? { data?: TData } : { data: TData });\n\n/**\n * Typed query surface pre-scoped to one `integration_id`. Every read and\n * write is automatically filtered/stamped with the integration id, so an\n * integration cannot reach another integration's rows.\n */\nexport interface IntegrationStorageHandle<\n TConnection = Record<string, unknown>,\n TSettings = Record<string, unknown>,\n TSubscription = Record<string, unknown>,\n> {\n readonly integrationId: string;\n connections: {\n /** The org's connection, or `null` when not connected. */\n get(orgId: string): Promise<IntegrationConnection<TConnection> | null>;\n /** Insert or replace the org's connection (one per org; `created_at` preserved on update). */\n upsert(\n orgId: string,\n input: { userId?: string | null; data: TConnection; metadata?: Record<string, unknown> },\n ): Promise<void>;\n /**\n * Atomic read-modify-write of the connection's `data` payload (token\n * rotation etc.). Returns the updated connection, or `null` when the org\n * has no connection.\n */\n update(orgId: string, fn: (data: TConnection) => TConnection): Promise<IntegrationConnection<TConnection> | null>;\n /** Remove the org's connection. Returns whether a row was deleted. */\n delete(orgId: string): Promise<boolean>;\n };\n subscriptions: {\n create(input: CreateIntegrationSubscriptionInput<TSubscription>): Promise<IntegrationSubscription<TSubscription>>;\n /** All subscriptions for an external object (webhook fan-out), optionally filtered by status. */\n listByTarget(targetKey: string, opts?: { status?: string }): Promise<IntegrationSubscription<TSubscription>[]>;\n listBySession(sessionId: string): Promise<IntegrationSubscription<TSubscription>[]>;\n listByThread(resourceId: string, threadId: string): Promise<IntegrationSubscription<TSubscription>[]>;\n updateStatus(id: string, status: string): Promise<void>;\n delete(id: string): Promise<boolean>;\n /** Targeted cleanup, always org-scoped. Returns the number of rows deleted. */\n deleteWhere(where: { orgId: string; targetKey?: string; sessionId?: string }): Promise<number>;\n };\n settings: {\n /** The `(org, user)` config, or `null` when never saved. */\n get(orgId: string, userId: string): Promise<TSettings | null>;\n /** Upsert the `(org, user)` config (`created_at` preserved on update). */\n save(orgId: string, userId: string, config: TSettings): Promise<void>;\n };\n}\n\ninterface ConnectionRow extends Record<string, unknown> {\n id: string;\n integration_id: string;\n org_id: string;\n user_id: string | null;\n data: unknown;\n metadata: Record<string, unknown>;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface SubscriptionRow extends Record<string, unknown> {\n id: string;\n integration_id: string;\n org_id: string;\n target_key: string;\n session_id: string | null;\n resource_id: string | null;\n thread_id: string | null;\n session_scope: string | null;\n status: string;\n data: unknown;\n created_at: Date;\n updated_at: Date;\n}\n\n/**\n * The built-in generic integration domain, written once against the\n * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.\n */\nexport class IntegrationStorage extends FactoryStorageDomain {\n constructor() {\n super('integrations');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([\n INTEGRATION_CONNECTIONS_SCHEMA,\n INTEGRATION_SUBSCRIPTIONS_SCHEMA,\n INTEGRATION_SETTINGS_SCHEMA,\n ]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('integration_subscriptions', {});\n await this.ops.deleteMany('integration_settings', {});\n await this.ops.deleteMany('integration_connections', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** A typed handle pre-scoped to `integrationId`. */\n forIntegration<\n TConnection = Record<string, unknown>,\n TSettings = Record<string, unknown>,\n TSubscription = Record<string, unknown>,\n >(integrationId: string): IntegrationStorageHandle<TConnection, TSettings, TSubscription> {\n if (!integrationId) throw new Error('[IntegrationStorage] integrationId is required.');\n const db = () => this.#db;\n const scoped = { integration_id: integrationId };\n\n const mapConnection = (row: ConnectionRow): IntegrationConnection<TConnection> => ({\n id: row.id,\n orgId: row.org_id,\n userId: row.user_id,\n data: row.data as TConnection,\n metadata: row.metadata ?? {},\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n });\n\n const mapSubscription = (row: SubscriptionRow): IntegrationSubscription<TSubscription> => ({\n id: row.id,\n orgId: row.org_id,\n targetKey: row.target_key,\n sessionId: row.session_id,\n resourceId: row.resource_id,\n threadId: row.thread_id,\n sessionScope: row.session_scope,\n status: row.status,\n data: row.data as TSubscription,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n });\n\n return {\n integrationId,\n connections: {\n get: async orgId => {\n const row = await db().findOne<ConnectionRow>('integration_connections', { ...scoped, org_id: orgId });\n return row ? mapConnection(row) : null;\n },\n upsert: async (orgId, input) => {\n const where = { ...scoped, org_id: orgId };\n const update = async () =>\n db().updateMany('integration_connections', where, {\n user_id: input.userId ?? null,\n data: input.data,\n metadata: input.metadata ?? {},\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await db().insertOne<ConnectionRow>('integration_connections', {\n ...where,\n user_id: input.userId ?? null,\n data: input.data,\n metadata: input.metadata ?? {},\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n },\n update: async (orgId, fn) => {\n const row = await db().updateAtomic<ConnectionRow>(\n 'integration_connections',\n { ...scoped, org_id: orgId },\n current => ({ data: fn(current.data as TConnection), updated_at: new Date() }),\n );\n return row ? mapConnection(row) : null;\n },\n delete: async orgId => {\n const deleted = await db().deleteMany('integration_connections', { ...scoped, org_id: orgId });\n return deleted > 0;\n },\n },\n subscriptions: {\n create: async input => {\n const now = new Date();\n const row = await db().insertOne<SubscriptionRow>('integration_subscriptions', {\n ...scoped,\n org_id: input.orgId,\n target_key: input.targetKey,\n session_id: input.sessionId ?? null,\n resource_id: input.resourceId ?? null,\n thread_id: input.threadId ?? null,\n session_scope: input.sessionScope ?? null,\n status: input.status ?? 'active',\n data: input.data ?? {},\n created_at: now,\n updated_at: now,\n });\n return mapSubscription(row);\n },\n listByTarget: async (targetKey, opts) => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, target_key: targetKey, ...(opts?.status !== undefined ? { status: opts.status } : {}) },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n listBySession: async sessionId => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, session_id: sessionId },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n listByThread: async (resourceId, threadId) => {\n const rows = await db().findMany<SubscriptionRow>(\n 'integration_subscriptions',\n { ...scoped, resource_id: resourceId, thread_id: threadId },\n { orderBy: [['created_at', 'asc']] },\n );\n return rows.map(mapSubscription);\n },\n updateStatus: async (id, status) => {\n await db().updateMany('integration_subscriptions', { ...scoped, id }, { status, updated_at: new Date() });\n },\n delete: async id => {\n const deleted = await db().deleteMany('integration_subscriptions', { ...scoped, id });\n return deleted > 0;\n },\n deleteWhere: async where => {\n return db().deleteMany('integration_subscriptions', {\n ...scoped,\n org_id: where.orgId,\n ...(where.targetKey !== undefined ? { target_key: where.targetKey } : {}),\n ...(where.sessionId !== undefined ? { session_id: where.sessionId } : {}),\n });\n },\n },\n settings: {\n get: async (orgId, userId) => {\n const row = await db().findOne<{ config: TSettings }>('integration_settings', {\n ...scoped,\n org_id: orgId,\n user_id: userId,\n });\n return row ? structuredClone(row.config) : null;\n },\n save: async (orgId, userId, config) => {\n const where = { ...scoped, org_id: orgId, user_id: userId };\n const update = async () =>\n db().updateMany('integration_settings', where, {\n config,\n updated_at: new Date(),\n });\n\n let lastError: unknown;\n for (let attempt = 0; attempt < 2; attempt++) {\n if ((await update()) > 0) return;\n\n const now = new Date();\n try {\n await db().insertOne('integration_settings', {\n ...where,\n config,\n created_at: now,\n updated_at: now,\n });\n return;\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n lastError = error;\n }\n }\n throw lastError;\n },\n },\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,iCAAmD;CAC9D,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,gBAAgB,EAAE,MAAM,OAAO;EAC/B,QAAQ,EAAE,MAAM,OAAO;;EAEvB,SAAS;GAAE,MAAM;GAAQ,UAAU;EAAK;EACxC,MAAM,EAAE,MAAM,OAAO;EACrB,UAAU,EAAE,MAAM,OAAO;EACzB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAAC;EAAE,MAAM;EAAkD,SAAS,CAAC,kBAAkB,QAAQ;CAAE,CAAC;AACnH;AAEA,MAAa,mCAAqD;CAChE,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,gBAAgB,EAAE,MAAM,OAAO;EAC/B,QAAQ,EAAE,MAAM,OAAO;;EAEvB,YAAY,EAAE,MAAM,OAAO;EAC3B,YAAY;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC3C,aAAa;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC5C,WAAW;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC1C,eAAe;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC9C,QAAQ,EAAE,MAAM,OAAO;EACvB,MAAM,EAAE,MAAM,OAAO;EACrB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,SAAS,CACP;EAAE,MAAM;EAAwC,SAAS,CAAC,kBAAkB,YAAY;CAAE,GAC1F;EAAE,MAAM;EAAqC,SAAS,CAAC,kBAAkB,QAAQ;CAAE,CACrF;AACF;AAEA,MAAa,8BAAgD;CAC3D,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,gBAAgB,EAAE,MAAM,OAAO;EAC/B,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS,EAAE,MAAM,OAAO;EACxB,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CACb;EAAE,MAAM;EAAoD,SAAS;GAAC;GAAkB;GAAU;EAAS;CAAE,CAC/G;AACF;;;;;AAqHA,IAAa,qBAAb,cAAwC,qBAAqB;CAC3D,cAAc;EACZ,MAAM,cAAc;CACtB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB;GAC3B;GACA;GACA;EACF,CAAC;CACH;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,6BAA6B,CAAC,CAAC;EACzD,MAAM,KAAK,IAAI,WAAW,wBAAwB,CAAC,CAAC;EACpD,MAAM,KAAK,IAAI,WAAW,2BAA2B,CAAC,CAAC;CACzD;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;;CAGA,eAIE,eAAwF;EACxF,IAAI,CAAC,eAAe,MAAM,IAAI,MAAM,iDAAiD;EACrF,MAAM,WAAW,KAAKA;EACtB,MAAM,SAAS,EAAE,gBAAgB,cAAc;EAE/C,MAAM,iBAAiB,SAA4D;GACjF,IAAI,IAAI;GACR,OAAO,IAAI;GACX,QAAQ,IAAI;GACZ,MAAM,IAAI;GACV,UAAU,IAAI,YAAY,CAAC;GAC3B,WAAW,IAAI;GACf,WAAW,IAAI;EACjB;EAEA,MAAM,mBAAmB,SAAkE;GACzF,IAAI,IAAI;GACR,OAAO,IAAI;GACX,WAAW,IAAI;GACf,WAAW,IAAI;GACf,YAAY,IAAI;GAChB,UAAU,IAAI;GACd,cAAc,IAAI;GAClB,QAAQ,IAAI;GACZ,MAAM,IAAI;GACV,WAAW,IAAI;GACf,WAAW,IAAI;EACjB;EAEA,OAAO;GACL;GACA,aAAa;IACX,KAAK,OAAM,UAAS;KAClB,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,QAAuB,2BAA2B;MAAE,GAAG;MAAQ,QAAQ;KAAM,CAAC;KACrG,OAAO,MAAM,cAAc,GAAG,IAAI;IACpC;IACA,QAAQ,OAAO,OAAO,UAAU;KAC9B,MAAM,QAAQ;MAAE,GAAG;MAAQ,QAAQ;KAAM;KACzC,MAAM,SAAS,YACb,GAAG,CAAC,CAAC,WAAW,2BAA2B,OAAO;MAChD,SAAS,MAAM,UAAU;MACzB,MAAM,MAAM;MACZ,UAAU,MAAM,YAAY,CAAC;MAC7B,4BAAY,IAAI,KAAK;KACvB,CAAC;KAEH,IAAI;KACJ,KAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;MAC5C,IAAK,MAAM,OAAO,IAAK,GAAG;MAE1B,MAAM,sBAAM,IAAI,KAAK;MACrB,IAAI;OACF,MAAM,GAAG,CAAC,CAAC,UAAyB,2BAA2B;QAC7D,GAAG;QACH,SAAS,MAAM,UAAU;QACzB,MAAM,MAAM;QACZ,UAAU,MAAM,YAAY,CAAC;QAC7B,YAAY;QACZ,YAAY;OACd,CAAC;OACD;MACF,SAAS,OAAO;OACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;OACpD,YAAY;MACd;KACF;KACA,MAAM;IACR;IACA,QAAQ,OAAO,OAAO,OAAO;KAC3B,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,aACrB,2BACA;MAAE,GAAG;MAAQ,QAAQ;KAAM,IAC3B,aAAY;MAAE,MAAM,GAAG,QAAQ,IAAmB;MAAG,4BAAY,IAAI,KAAK;KAAE,EAC9E;KACA,OAAO,MAAM,cAAc,GAAG,IAAI;IACpC;IACA,QAAQ,OAAM,UAAS;KAErB,OAAO,MADe,GAAG,CAAC,CAAC,WAAW,2BAA2B;MAAE,GAAG;MAAQ,QAAQ;KAAM,CAAC,IAC5E;IACnB;GACF;GACA,eAAe;IACb,QAAQ,OAAM,UAAS;KACrB,MAAM,sBAAM,IAAI,KAAK;KACrB,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,UAA2B,6BAA6B;MAC7E,GAAG;MACH,QAAQ,MAAM;MACd,YAAY,MAAM;MAClB,YAAY,MAAM,aAAa;MAC/B,aAAa,MAAM,cAAc;MACjC,WAAW,MAAM,YAAY;MAC7B,eAAe,MAAM,gBAAgB;MACrC,QAAQ,MAAM,UAAU;MACxB,MAAM,MAAM,QAAQ,CAAC;MACrB,YAAY;MACZ,YAAY;KACd,CAAC;KACD,OAAO,gBAAgB,GAAG;IAC5B;IACA,cAAc,OAAO,WAAW,SAAS;KAMvC,QAAO,MALY,GAAG,CAAC,CAAC,SACtB,6BACA;MAAE,GAAG;MAAQ,YAAY;MAAW,GAAI,MAAM,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;KAAG,GACnG,EAAE,SAAS,CAAC,CAAC,cAAc,KAAK,CAAC,EAAE,CACrC,EAAA,CACY,IAAI,eAAe;IACjC;IACA,eAAe,OAAM,cAAa;KAMhC,QAAO,MALY,GAAG,CAAC,CAAC,SACtB,6BACA;MAAE,GAAG;MAAQ,YAAY;KAAU,GACnC,EAAE,SAAS,CAAC,CAAC,cAAc,KAAK,CAAC,EAAE,CACrC,EAAA,CACY,IAAI,eAAe;IACjC;IACA,cAAc,OAAO,YAAY,aAAa;KAM5C,QAAO,MALY,GAAG,CAAC,CAAC,SACtB,6BACA;MAAE,GAAG;MAAQ,aAAa;MAAY,WAAW;KAAS,GAC1D,EAAE,SAAS,CAAC,CAAC,cAAc,KAAK,CAAC,EAAE,CACrC,EAAA,CACY,IAAI,eAAe;IACjC;IACA,cAAc,OAAO,IAAI,WAAW;KAClC,MAAM,GAAG,CAAC,CAAC,WAAW,6BAA6B;MAAE,GAAG;MAAQ;KAAG,GAAG;MAAE;MAAQ,4BAAY,IAAI,KAAK;KAAE,CAAC;IAC1G;IACA,QAAQ,OAAM,OAAM;KAElB,OAAO,MADe,GAAG,CAAC,CAAC,WAAW,6BAA6B;MAAE,GAAG;MAAQ;KAAG,CAAC,IACnE;IACnB;IACA,aAAa,OAAM,UAAS;KAC1B,OAAO,GAAG,CAAC,CAAC,WAAW,6BAA6B;MAClD,GAAG;MACH,QAAQ,MAAM;MACd,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,YAAY,MAAM,UAAU,IAAI,CAAC;MACvE,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,YAAY,MAAM,UAAU,IAAI,CAAC;KACzE,CAAC;IACH;GACF;GACA,UAAU;IACR,KAAK,OAAO,OAAO,WAAW;KAC5B,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,QAA+B,wBAAwB;MAC5E,GAAG;MACH,QAAQ;MACR,SAAS;KACX,CAAC;KACD,OAAO,MAAM,gBAAgB,IAAI,MAAM,IAAI;IAC7C;IACA,MAAM,OAAO,OAAO,QAAQ,WAAW;KACrC,MAAM,QAAQ;MAAE,GAAG;MAAQ,QAAQ;MAAO,SAAS;KAAO;KAC1D,MAAM,SAAS,YACb,GAAG,CAAC,CAAC,WAAW,wBAAwB,OAAO;MAC7C;MACA,4BAAY,IAAI,KAAK;KACvB,CAAC;KAEH,IAAI;KACJ,KAAK,IAAI,UAAU,GAAG,UAAU,GAAG,WAAW;MAC5C,IAAK,MAAM,OAAO,IAAK,GAAG;MAE1B,MAAM,sBAAM,IAAI,KAAK;MACrB,IAAI;OACF,MAAM,GAAG,CAAC,CAAC,UAAU,wBAAwB;QAC3C,GAAG;QACH;QACA,YAAY;QACZ,YAAY;OACd,CAAC;OACD;MACF,SAAS,OAAO;OACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;OACpD,YAAY;MACd;KACF;KACA,MAAM;IACR;GACF;EACF;CACF;AACF"}
@@ -393,11 +393,6 @@ export interface SourceControlStorageHandle {
393
393
  projectRepositoryId: string;
394
394
  }): Promise<SourceControlSession[]>;
395
395
  getBySessionId(sessionId: string): Promise<SourceControlSession | null>;
396
- /** Overwrite the session's display title. Keyed by the controller-facing `sessionId`. */
397
- rename(args: {
398
- sessionId: string;
399
- title: string;
400
- }): Promise<void>;
401
396
  getForBranch(args: {
402
397
  projectRepositoryId: string;
403
398
  userId: string;