@mastra/factory 0.9.0 → 0.10.0-alpha.10

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 (129) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/auth.js +1 -1
  4. package/dist/auth.js.map +1 -1
  5. package/dist/factory.d.ts +8 -0
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +24 -8
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -1
  12. package/dist/integrations/github/integration.d.ts.map +1 -1
  13. package/dist/integrations/github/integration.js +1 -0
  14. package/dist/integrations/github/integration.js.map +1 -1
  15. package/dist/integrations/github/provenance.d.ts +7 -2
  16. package/dist/integrations/github/provenance.d.ts.map +1 -1
  17. package/dist/integrations/github/provenance.js +16 -2
  18. package/dist/integrations/github/provenance.js.map +1 -1
  19. package/dist/integrations/github/routes.d.ts +3 -0
  20. package/dist/integrations/github/routes.d.ts.map +1 -1
  21. package/dist/integrations/github/routes.js +165 -6
  22. package/dist/integrations/github/routes.js.map +1 -1
  23. package/dist/integrations/github/rules.d.ts.map +1 -1
  24. package/dist/integrations/github/rules.js +193 -127
  25. package/dist/integrations/github/rules.js.map +1 -1
  26. package/dist/integrations/linear/routes.d.ts.map +1 -1
  27. package/dist/integrations/linear/routes.js +50 -0
  28. package/dist/integrations/linear/routes.js.map +1 -1
  29. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  30. package/dist/integrations/platform/github/integration.js +1 -0
  31. package/dist/integrations/platform/github/integration.js.map +1 -1
  32. package/dist/routes/attention.d.ts +17 -0
  33. package/dist/routes/attention.d.ts.map +1 -0
  34. package/dist/routes/attention.js +305 -0
  35. package/dist/routes/attention.js.map +1 -0
  36. package/dist/routes/intake.d.ts.map +1 -1
  37. package/dist/routes/intake.js +11 -2
  38. package/dist/routes/intake.js.map +1 -1
  39. package/dist/routes/surface.d.ts +2 -3
  40. package/dist/routes/surface.d.ts.map +1 -1
  41. package/dist/routes/surface.js +61 -43
  42. package/dist/routes/surface.js.map +1 -1
  43. package/dist/routes/work-items.d.ts.map +1 -1
  44. package/dist/routes/work-items.js +35 -14
  45. package/dist/routes/work-items.js.map +1 -1
  46. package/dist/rules/defaults.js +5 -5
  47. package/dist/rules/defaults.js.map +1 -1
  48. package/dist/rules/dispatch-errors.d.ts +13 -0
  49. package/dist/rules/dispatch-errors.d.ts.map +1 -0
  50. package/dist/rules/dispatch-errors.js +77 -0
  51. package/dist/rules/dispatch-errors.js.map +1 -0
  52. package/dist/rules/dispatcher.d.ts +9 -0
  53. package/dist/rules/dispatcher.d.ts.map +1 -1
  54. package/dist/rules/dispatcher.js +118 -45
  55. package/dist/rules/dispatcher.js.map +1 -1
  56. package/dist/rules/processor.d.ts.map +1 -1
  57. package/dist/rules/processor.js +30 -14
  58. package/dist/rules/processor.js.map +1 -1
  59. package/dist/rules/terminal-cleanup.d.ts +1 -1
  60. package/dist/rules/terminal-cleanup.d.ts.map +1 -1
  61. package/dist/rules/terminal-cleanup.js +2 -2
  62. package/dist/rules/terminal-cleanup.js.map +1 -1
  63. package/dist/rules/transition-service.d.ts +2 -1
  64. package/dist/rules/transition-service.d.ts.map +1 -1
  65. package/dist/rules/transition-service.js +1 -1
  66. package/dist/rules/transition-service.js.map +1 -1
  67. package/dist/rules/types.d.ts +2 -0
  68. package/dist/rules/types.d.ts.map +1 -1
  69. package/dist/rules/types.js +9 -1
  70. package/dist/rules/types.js.map +1 -1
  71. package/dist/secret-encryption.d.ts +25 -0
  72. package/dist/secret-encryption.d.ts.map +1 -0
  73. package/dist/secret-encryption.js +96 -0
  74. package/dist/secret-encryption.js.map +1 -0
  75. package/dist/session/factory-session.d.ts +13 -3
  76. package/dist/session/factory-session.d.ts.map +1 -1
  77. package/dist/session/factory-session.js +15 -6
  78. package/dist/session/factory-session.js.map +1 -1
  79. package/dist/session/first-exec-capture.d.ts +16 -10
  80. package/dist/session/first-exec-capture.d.ts.map +1 -1
  81. package/dist/session/first-exec-capture.js +74 -14
  82. package/dist/session/first-exec-capture.js.map +1 -1
  83. package/dist/session/first-message-capture.d.ts +14 -10
  84. package/dist/session/first-message-capture.d.ts.map +1 -1
  85. package/dist/session/first-message-capture.js +17 -11
  86. package/dist/session/first-message-capture.js.map +1 -1
  87. package/dist/session/session-title.d.ts +7 -0
  88. package/dist/session/session-title.d.ts.map +1 -0
  89. package/dist/session/session-title.js +14 -0
  90. package/dist/session/session-title.js.map +1 -0
  91. package/dist/session/thread-title-mirror.d.ts +35 -0
  92. package/dist/session/thread-title-mirror.d.ts.map +1 -0
  93. package/dist/session/thread-title-mirror.js +51 -0
  94. package/dist/session/thread-title-mirror.js.map +1 -0
  95. package/dist/storage/domains/credentials/base.d.ts +3 -1
  96. package/dist/storage/domains/credentials/base.d.ts.map +1 -1
  97. package/dist/storage/domains/credentials/base.js +33 -15
  98. package/dist/storage/domains/credentials/base.js.map +1 -1
  99. package/dist/storage/domains/custom-providers/base.d.ts +3 -1
  100. package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
  101. package/dist/storage/domains/custom-providers/base.js +45 -25
  102. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  103. package/dist/storage/domains/intake/base.d.ts +6 -5
  104. package/dist/storage/domains/intake/base.d.ts.map +1 -1
  105. package/dist/storage/domains/intake/base.js +21 -13
  106. package/dist/storage/domains/intake/base.js.map +1 -1
  107. package/dist/storage/domains/integrations/base.d.ts +3 -1
  108. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  109. package/dist/storage/domains/integrations/base.js +28 -12
  110. package/dist/storage/domains/integrations/base.js.map +1 -1
  111. package/dist/storage/domains/source-control/base.d.ts +8 -0
  112. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  113. package/dist/storage/domains/source-control/base.js +14 -2
  114. package/dist/storage/domains/source-control/base.js.map +1 -1
  115. package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
  116. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  117. package/dist/storage/domains/source-control/inmemory.js +7 -0
  118. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  119. package/dist/storage/domains/work-items/base.d.ts +115 -12
  120. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  121. package/dist/storage/domains/work-items/base.js +497 -43
  122. package/dist/storage/domains/work-items/base.js.map +1 -1
  123. package/dist/work-item-branch.d.ts +23 -0
  124. package/dist/work-item-branch.d.ts.map +1 -0
  125. package/dist/work-item-branch.js +39 -0
  126. package/dist/work-item-branch.js.map +1 -0
  127. package/factory-skills/factory-rereview/SKILL.md +2 -0
  128. package/factory-skills/factory-review/SKILL.md +2 -0
  129. package/package.json +7 -7
@@ -1 +1 @@
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
+ {"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":"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"}
@@ -1,5 +1,6 @@
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';
3
4
  /**
4
5
  * A user-defined OpenAI-compatible provider. The DB-backed counterpart of the
5
6
  * local `settings.json` `customProviders` entries: org-scoped rows keyed by the
@@ -29,7 +30,8 @@ export interface UpsertCustomProviderInput {
29
30
  export declare const CUSTOM_PROVIDERS_SCHEMA: CollectionSchema;
30
31
  export declare class CustomProvidersStorage extends FactoryStorageDomain {
31
32
  #private;
32
- constructor();
33
+ private readonly encryption;
34
+ constructor(encryption?: FactorySecretEncryption);
33
35
  init(): Promise<void>;
34
36
  dangerouslyClearAll(): Promise<void>;
35
37
  /**
@@ -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;;;;;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
+ {"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,3 +1,4 @@
1
+ import { createPlaintextFactorySecretEncryption } from "../../../secret-encryption.js";
1
2
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
2
3
  //#region src/storage/domains/custom-providers/base.ts
3
4
  const CUSTOM_PROVIDERS_SCHEMA = {
@@ -22,26 +23,16 @@ const CUSTOM_PROVIDERS_SCHEMA = {
22
23
  columns: ["org_id", "provider_id"]
23
24
  }]
24
25
  };
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
- }
39
26
  var CustomProvidersStorage = class extends FactoryStorageDomain {
40
- constructor() {
27
+ encryption;
28
+ constructor(encryption = createPlaintextFactorySecretEncryption()) {
41
29
  super("custom-providers");
30
+ this.encryption = encryption;
42
31
  }
43
32
  async init() {
44
33
  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)));
45
36
  }
46
37
  async dangerouslyClearAll() {
47
38
  await this.ops.deleteMany("custom_providers", {});
@@ -49,6 +40,31 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
49
40
  get #db() {
50
41
  return this.ops;
51
42
  }
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
+ }
52
68
  /**
53
69
  * Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
54
70
  * the settings.json upsert semantics (no key retention: an absent `apiKey`
@@ -59,6 +75,7 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
59
75
  */
60
76
  async upsert({ orgId, userId, input, previousProviderId }) {
61
77
  const now = /* @__PURE__ */ new Date();
78
+ const encryptedApiKey = await this.#encryptApiKey(input.apiKey);
62
79
  const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
63
80
  if (renameFrom) {
64
81
  if (!await this.#db.findOne("custom_providers", {
@@ -72,17 +89,18 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
72
89
  provider_id: input.providerId,
73
90
  name: input.name,
74
91
  url: input.url,
75
- api_key: input.apiKey ?? null,
92
+ api_key: encryptedApiKey,
76
93
  models: input.models,
77
94
  updated_at: now
78
95
  }));
79
- if (renamed) return toRecord(renamed);
96
+ if (renamed) return this.#toRecord(renamed);
80
97
  }
81
98
  }
82
99
  const record = await this.#write({
83
100
  orgId,
84
101
  userId,
85
102
  input,
103
+ encryptedApiKey,
86
104
  now
87
105
  });
88
106
  if (renameFrom) await this.#db.deleteMany("custom_providers", {
@@ -97,40 +115,42 @@ var CustomProvidersStorage = class extends FactoryStorageDomain {
97
115
  * the single row) instead of one failing on the unique index, and a row
98
116
  * deleted mid-flight is recreated rather than reported as a stale success.
99
117
  */
100
- async #write({ orgId, userId, input, now }) {
118
+ async #write({ orgId, userId, input, encryptedApiKey, now }) {
101
119
  const updateExisting = () => this.#db.updateAtomic("custom_providers", {
102
120
  org_id: orgId,
103
121
  provider_id: input.providerId
104
122
  }, () => ({
105
123
  name: input.name,
106
124
  url: input.url,
107
- api_key: input.apiKey ?? null,
125
+ api_key: encryptedApiKey,
108
126
  models: input.models,
109
127
  updated_at: now
110
128
  }));
111
129
  const updated = await updateExisting();
112
- if (updated) return toRecord(updated);
130
+ if (updated) return this.#toRecord(updated);
113
131
  try {
114
- return toRecord(await this.#db.insertOne("custom_providers", {
132
+ const row = await this.#db.insertOne("custom_providers", {
115
133
  org_id: orgId,
116
134
  created_by: userId,
117
135
  provider_id: input.providerId,
118
136
  name: input.name,
119
137
  url: input.url,
120
- api_key: input.apiKey ?? null,
138
+ api_key: encryptedApiKey,
121
139
  models: input.models,
122
140
  created_at: now,
123
141
  updated_at: now
124
- }));
142
+ });
143
+ return this.#toRecord(row);
125
144
  } catch (error) {
126
145
  if (!(error instanceof UniqueViolationError)) throw error;
127
146
  const row = await updateExisting();
128
147
  if (!row) throw error;
129
- return toRecord(row);
148
+ return this.#toRecord(row);
130
149
  }
131
150
  }
132
151
  async list({ orgId }) {
133
- return (await this.#db.findMany("custom_providers", { org_id: orgId }, { orderBy: [["name", "asc"]] })).map(toRecord);
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)));
134
154
  }
135
155
  async delete({ orgId, providerId }) {
136
156
  return await this.#db.deleteMany("custom_providers", {
@@ -1 +1 @@
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":";;AA+BA,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,SAAS,SAAS,KAAgD;CAChE,OAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,WAAW,IAAI;EACf,YAAY,IAAI;EAChB,MAAM,IAAI;EACV,KAAK,IAAI;EACT,QAAQ,IAAI;EACZ,QAAQ,MAAM,QAAQ,IAAI,MAAM,IAAI,IAAI,SAAS,CAAC;EAClD,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,cAAc;EACZ,MAAM,kBAAkB;CAC1B;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,uBAAuB,CAAC;CACxD;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,oBAAoB,CAAC,CAAC;CAClD;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;;;;;;;;;CAUA,MAAM,OAAO,EACX,OACA,QACA,OACA,sBAMgC;EAChC,MAAM,sBAAM,IAAI,KAAK;EACrB,MAAM,aAAa,sBAAsB,uBAAuB,MAAM,aAAa,qBAAqB,KAAA;EAExG,IAAI,YAKE;OAAA,CAAC,MAJgB,KAAKA,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,MAAM,UAAU;KACzB,QAAQ,MAAM;KACd,YAAY;IACd,EACF;IACA,IAAI,SAAS,OAAO,SAAS,OAAO;GAEtC;;EAMF,MAAM,SAAS,MAAM,KAAKC,OAAO;GAAE;GAAO;GAAQ;GAAO;EAAI,CAAC;EAC9D,IAAI,YACF,MAAM,KAAKD,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO,aAAa;EAAW,CAAC;EAE1F,OAAO;CACT;;;;;;;CAQA,MAAMC,OAAO,EACX,OACA,QACA,OACA,OAMgC;EAChC,MAAM,uBACJ,KAAKD,IAAI,aACP,oBACA;GAAE,QAAQ;GAAO,aAAa,MAAM;EAAW,UACxC;GACL,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,SAAS,MAAM,UAAU;GACzB,QAAQ,MAAM;GACd,YAAY;EACd,EACF;EAEF,MAAM,UAAU,MAAM,eAAe;EACrC,IAAI,SAAS,OAAO,SAAS,OAAO;EAEpC,IAAI;GAYF,OAAO,SAAS,MAXE,KAAKA,IAAI,UAA+B,oBAAoB;IAC5E,QAAQ;IACR,YAAY;IACZ,aAAa,MAAM;IACnB,MAAM,MAAM;IACZ,KAAK,MAAM;IACX,SAAS,MAAM,UAAU;IACzB,QAAQ,MAAM;IACd,YAAY;IACZ,YAAY;GACd,CAAC,CACkB;EACrB,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GAEpD,MAAM,MAAM,MAAM,eAAe;GACjC,IAAI,CAAC,KAAK,MAAM;GAChB,OAAO,SAAS,GAAG;EACrB;CACF;CAEA,MAAM,KAAK,EAAE,SAA6D;EAMxE,QAAO,MALY,KAAKA,IAAI,SAC1B,oBACA,EAAE,QAAQ,MAAM,GAChB,EAAE,SAAS,CAAC,CAAC,QAAQ,KAAK,CAAC,EAAE,CAC/B,EAAA,CACY,IAAI,QAAQ;CAC1B;CAEA,MAAM,OAAO,EAAE,OAAO,cAAuE;EAC3F,OAAQ,MAAM,KAAKA,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO,aAAa;EAAW,CAAC,IAAK;CACvG;AACF"}
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":"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"}
@@ -59,15 +59,16 @@ export declare class IntakeStorage extends FactoryStorageDomain {
59
59
  integrationId: string;
60
60
  factoryProjectId: string;
61
61
  }): Promise<string[]>;
62
- /**
63
- * Points a source at a Factory project, replacing any existing binding.
64
- * A `null` project clears the binding.
65
- */
62
+ clearBinding({ orgId, integrationId, sourceId, }: {
63
+ orgId: string;
64
+ integrationId: string;
65
+ sourceId: string;
66
+ }): Promise<IntakeSourceBinding | null>;
66
67
  setBinding({ orgId, integrationId, sourceId, factoryProjectId, userId, }: {
67
68
  orgId: string;
68
69
  integrationId: string;
69
70
  sourceId: string;
70
- factoryProjectId: string | null;
71
+ factoryProjectId: string;
71
72
  userId?: string;
72
73
  }): Promise<void>;
73
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/intake/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,YAAiB,CAAC;AAEtD,eAAO,MAAM,sBAAsB,EAAE,gBAWpC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,EAAE,gBAmB3C,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAQD,qBAAa,aAAc,SAAQ,oBAAoB;;;IAK/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IASpC,SAAS,CAAC,EACd,KAAK,EACL,MAAM,EACN,cAAc,GACf,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,GAAG,OAAO,CAAC,YAAY,CAAC;IAYnB,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAanH,+EAA+E;IACzE,YAAY,CAAC,EACjB,KAAK,EACL,aAAa,GACd,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAYlC,4EAA4E;IACtE,kBAAkB,CAAC,EACvB,KAAK,EACL,aAAa,EACb,gBAAgB,GACjB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IASrB;;;OAGG;IACG,UAAU,CAAC,EACf,KAAK,EACL,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,IAAI,CAAC;CAuBlB"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/intake/base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,YAAiB,CAAC;AAEtD,eAAO,MAAM,sBAAsB,EAAE,gBAWpC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,EAAE,gBAmB3C,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAeD,qBAAa,aAAc,SAAQ,oBAAoB;;;IAK/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IASpC,SAAS,CAAC,EACd,KAAK,EACL,MAAM,EACN,cAAc,GACf,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,GAAG,OAAO,CAAC,YAAY,CAAC;IAYnB,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAanH,+EAA+E;IACzE,YAAY,CAAC,EACjB,KAAK,EACL,aAAa,GACd,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAQlC,4EAA4E;IACtE,kBAAkB,CAAC,EACvB,KAAK,EACL,aAAa,EACb,gBAAgB,GACjB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IASf,YAAY,CAAC,EACjB,KAAK,EACL,aAAa,EACb,QAAQ,GACT,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAajC,UAAU,CAAC,EACf,KAAK,EACL,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,IAAI,CAAC;CAmBlB"}
@@ -59,6 +59,13 @@ const INTAKE_SOURCE_BINDINGS_SCHEMA = {
59
59
  ]
60
60
  }]
61
61
  };
62
+ function toIntakeSourceBinding(row) {
63
+ return {
64
+ integrationId: row.integration_id,
65
+ sourceId: row.source_id,
66
+ factoryProjectId: row.factory_project_id
67
+ };
68
+ }
62
69
  var IntakeStorage = class extends FactoryStorageDomain {
63
70
  constructor() {
64
71
  super("intake");
@@ -115,11 +122,7 @@ var IntakeStorage = class extends FactoryStorageDomain {
115
122
  return (await this.#db.findMany("intake_source_bindings", {
116
123
  org_id: orgId,
117
124
  ...integrationId ? { integration_id: integrationId } : {}
118
- })).map((row) => ({
119
- integrationId: row.integration_id,
120
- sourceId: row.source_id,
121
- factoryProjectId: row.factory_project_id
122
- }));
125
+ })).map(toIntakeSourceBinding);
123
126
  }
124
127
  /** Source ids bound to one Factory project. Empty means "nothing bound". */
125
128
  async listBoundSourceIds({ orgId, integrationId, factoryProjectId }) {
@@ -129,20 +132,25 @@ var IntakeStorage = class extends FactoryStorageDomain {
129
132
  factory_project_id: factoryProjectId
130
133
  })).map((row) => row.source_id);
131
134
  }
132
- /**
133
- * Points a source at a Factory project, replacing any existing binding.
134
- * A `null` project clears the binding.
135
- */
135
+ async clearBinding({ orgId, integrationId, sourceId }) {
136
+ const where = {
137
+ org_id: orgId,
138
+ integration_id: integrationId,
139
+ source_id: sourceId
140
+ };
141
+ return this.storage.withTransaction(async (ops) => {
142
+ const row = await ops.findOne("intake_source_bindings", where);
143
+ if (!row) return null;
144
+ await ops.deleteMany("intake_source_bindings", where);
145
+ return toIntakeSourceBinding(row);
146
+ }, { isolationLevel: "serializable" });
147
+ }
136
148
  async setBinding({ orgId, integrationId, sourceId, factoryProjectId, userId }) {
137
149
  const where = {
138
150
  org_id: orgId,
139
151
  integration_id: integrationId,
140
152
  source_id: sourceId
141
153
  };
142
- if (factoryProjectId === null) {
143
- await this.#db.deleteMany("intake_source_bindings", where);
144
- return;
145
- }
146
154
  const now = /* @__PURE__ */ new Date();
147
155
  const patch = {
148
156
  factory_project_id: factoryProjectId,
@@ -1 +1 @@
1
- {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/intake/base.ts"],"sourcesContent":["/**\n * Per-user intake selections for every configured intake integration.\n *\n * Integration ids are dynamic. Each integration contributes provider-neutral\n * sources through `FactoryIntegration.intake`; this domain only persists which\n * source ids the user selected.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface IntakeSelection {\n enabled: boolean;\n /** Provider-owned source ids; `null` means nothing is selected. */\n sourceIds: string[] | null;\n}\n\nexport type IntakeConfig = Record<string, IntakeSelection>;\n\nexport const DEFAULT_INTAKE_CONFIG: IntakeConfig = {};\n\nexport const INTAKE_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_settings',\n columns: {\n id: { type: 'uuid-pk' },\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: [{ name: 'intake_settings_org_user_unique', columns: ['org_id', 'user_id'] }],\n};\n\n/**\n * Binds an intake source to the Factory project its items belong to.\n *\n * Intake selections are per user and org-wide, so they cannot say *where* an\n * ingested item should land. GitHub items are naturally scoped by their linked\n * repository; providers without that link (Linear) need this explicit binding\n * so viewing one project's board cannot materialize another project's items.\n */\nexport const INTAKE_SOURCE_BINDINGS_SCHEMA: CollectionSchema = {\n name: 'intake_source_bindings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n integration_id: { type: 'text' },\n source_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_source_bindings_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_source_bindings_org_integration_source_unique',\n columns: ['org_id', 'integration_id', 'source_id'],\n },\n ],\n};\n\nexport interface IntakeSourceBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n}\n\ntype IntakeSourceBindingRow = {\n integration_id: string;\n source_id: string;\n factory_project_id: string;\n};\n\nexport class IntakeStorage extends FactoryStorageDomain {\n constructor() {\n super('intake');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([INTAKE_SETTINGS_SCHEMA, INTAKE_SOURCE_BINDINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('intake_settings', {});\n await this.ops.deleteMany('intake_source_bindings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async getConfig({\n orgId,\n userId,\n integrationIds,\n }: {\n orgId: string;\n userId: string;\n /**\n * When provided, the result contains exactly these integration ids, with\n * unset integrations defaulting to `{ enabled: true, sourceIds: null }`.\n */\n integrationIds?: string[];\n }): Promise<IntakeConfig> {\n const row = await this.#db.findOne<{ config: IntakeConfig }>('intake_settings', {\n org_id: orgId,\n user_id: userId,\n });\n const saved = structuredClone(row?.config ?? DEFAULT_INTAKE_CONFIG);\n if (!integrationIds) return saved;\n return Object.fromEntries(\n integrationIds.map(integrationId => [integrationId, saved[integrationId] ?? { enabled: true, sourceIds: null }]),\n );\n }\n\n async saveConfig({ orgId, userId, config }: { orgId: string; userId: string; config: IntakeConfig }): Promise<void> {\n const now = new Date();\n const where = { org_id: orgId, user_id: userId };\n const updated = await this.#db.updateMany('intake_settings', where, { config, updated_at: now });\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_settings', { ...where, config, created_at: now, updated_at: now });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_settings', where, { config, updated_at: now });\n }\n }\n\n /** Every source binding in the org, optionally narrowed to one integration. */\n async listBindings({\n orgId,\n integrationId,\n }: {\n orgId: string;\n integrationId?: string;\n }): Promise<IntakeSourceBinding[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(row => ({\n integrationId: row.integration_id,\n sourceId: row.source_id,\n factoryProjectId: row.factory_project_id,\n }));\n }\n\n /** Source ids bound to one Factory project. Empty means \"nothing bound\". */\n async listBoundSourceIds({\n orgId,\n integrationId,\n factoryProjectId,\n }: {\n orgId: string;\n integrationId: string;\n factoryProjectId: string;\n }): Promise<string[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n factory_project_id: factoryProjectId,\n });\n return rows.map(row => row.source_id);\n }\n\n /**\n * Points a source at a Factory project, replacing any existing binding.\n * A `null` project clears the binding.\n */\n async setBinding({\n orgId,\n integrationId,\n sourceId,\n factoryProjectId,\n userId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n factoryProjectId: string | null;\n userId?: string;\n }): Promise<void> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n if (factoryProjectId === null) {\n await this.#db.deleteMany('intake_source_bindings', where);\n return;\n }\n const now = new Date();\n const patch = { factory_project_id: factoryProjectId, updated_at: now };\n const updated = await this.#db.updateMany('intake_source_bindings', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_source_bindings', {\n ...where,\n factory_project_id: factoryProjectId,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_source_bindings', where, patch);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/intake/base.ts"],"sourcesContent":["/**\n * Per-user intake selections for every configured intake integration.\n *\n * Integration ids are dynamic. Each integration contributes provider-neutral\n * sources through `FactoryIntegration.intake`; this domain only persists which\n * source ids the user selected.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface IntakeSelection {\n enabled: boolean;\n /** Provider-owned source ids; `null` means nothing is selected. */\n sourceIds: string[] | null;\n}\n\nexport type IntakeConfig = Record<string, IntakeSelection>;\n\nexport const DEFAULT_INTAKE_CONFIG: IntakeConfig = {};\n\nexport const INTAKE_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_settings',\n columns: {\n id: { type: 'uuid-pk' },\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: [{ name: 'intake_settings_org_user_unique', columns: ['org_id', 'user_id'] }],\n};\n\n/**\n * Binds an intake source to the Factory project its items belong to.\n *\n * Intake selections are per user and org-wide, so they cannot say *where* an\n * ingested item should land. GitHub items are naturally scoped by their linked\n * repository; providers without that link (Linear) need this explicit binding\n * so viewing one project's board cannot materialize another project's items.\n */\nexport const INTAKE_SOURCE_BINDINGS_SCHEMA: CollectionSchema = {\n name: 'intake_source_bindings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n integration_id: { type: 'text' },\n source_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_source_bindings_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_source_bindings_org_integration_source_unique',\n columns: ['org_id', 'integration_id', 'source_id'],\n },\n ],\n};\n\nexport interface IntakeSourceBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n}\n\ntype IntakeSourceBindingRow = {\n integration_id: string;\n source_id: string;\n factory_project_id: string;\n};\nfunction toIntakeSourceBinding(row: IntakeSourceBindingRow): IntakeSourceBinding {\n return {\n integrationId: row.integration_id,\n sourceId: row.source_id,\n factoryProjectId: row.factory_project_id,\n };\n}\n\nexport class IntakeStorage extends FactoryStorageDomain {\n constructor() {\n super('intake');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([INTAKE_SETTINGS_SCHEMA, INTAKE_SOURCE_BINDINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('intake_settings', {});\n await this.ops.deleteMany('intake_source_bindings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async getConfig({\n orgId,\n userId,\n integrationIds,\n }: {\n orgId: string;\n userId: string;\n /**\n * When provided, the result contains exactly these integration ids, with\n * unset integrations defaulting to `{ enabled: true, sourceIds: null }`.\n */\n integrationIds?: string[];\n }): Promise<IntakeConfig> {\n const row = await this.#db.findOne<{ config: IntakeConfig }>('intake_settings', {\n org_id: orgId,\n user_id: userId,\n });\n const saved = structuredClone(row?.config ?? DEFAULT_INTAKE_CONFIG);\n if (!integrationIds) return saved;\n return Object.fromEntries(\n integrationIds.map(integrationId => [integrationId, saved[integrationId] ?? { enabled: true, sourceIds: null }]),\n );\n }\n\n async saveConfig({ orgId, userId, config }: { orgId: string; userId: string; config: IntakeConfig }): Promise<void> {\n const now = new Date();\n const where = { org_id: orgId, user_id: userId };\n const updated = await this.#db.updateMany('intake_settings', where, { config, updated_at: now });\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_settings', { ...where, config, created_at: now, updated_at: now });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_settings', where, { config, updated_at: now });\n }\n }\n\n /** Every source binding in the org, optionally narrowed to one integration. */\n async listBindings({\n orgId,\n integrationId,\n }: {\n orgId: string;\n integrationId?: string;\n }): Promise<IntakeSourceBinding[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(toIntakeSourceBinding);\n }\n\n /** Source ids bound to one Factory project. Empty means \"nothing bound\". */\n async listBoundSourceIds({\n orgId,\n integrationId,\n factoryProjectId,\n }: {\n orgId: string;\n integrationId: string;\n factoryProjectId: string;\n }): Promise<string[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n factory_project_id: factoryProjectId,\n });\n return rows.map(row => row.source_id);\n }\n\n async clearBinding({\n orgId,\n integrationId,\n sourceId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n }): Promise<IntakeSourceBinding | null> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n return this.storage.withTransaction(\n async ops => {\n const row = await ops.findOne<IntakeSourceBindingRow>('intake_source_bindings', where);\n if (!row) return null;\n await ops.deleteMany('intake_source_bindings', where);\n return toIntakeSourceBinding(row);\n },\n { isolationLevel: 'serializable' },\n );\n }\n\n async setBinding({\n orgId,\n integrationId,\n sourceId,\n factoryProjectId,\n userId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n userId?: string;\n }): Promise<void> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n const now = new Date();\n const patch = { factory_project_id: factoryProjectId, updated_at: now };\n const updated = await this.#db.updateMany('intake_source_bindings', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_source_bindings', {\n ...where,\n factory_project_id: factoryProjectId,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_source_bindings', where, patch);\n }\n }\n}\n"],"mappings":"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"}
@@ -20,6 +20,7 @@
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';
23
24
  export declare const INTEGRATION_CONNECTIONS_SCHEMA: CollectionSchema;
24
25
  export declare const INTEGRATION_SUBSCRIPTIONS_SCHEMA: CollectionSchema;
25
26
  export declare const INTEGRATION_SETTINGS_SCHEMA: CollectionSchema;
@@ -117,7 +118,8 @@ export interface IntegrationStorageHandle<TConnection = Record<string, unknown>,
117
118
  */
118
119
  export declare class IntegrationStorage extends FactoryStorageDomain {
119
120
  #private;
120
- constructor();
121
+ private readonly encryption;
122
+ constructor(encryption?: FactorySecretEncryption);
121
123
  init(): Promise<void>;
122
124
  dangerouslyClearAll(): Promise<void>;
123
125
  /** 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":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,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,eAAO,MAAM,8BAA8B,EAAE,gBAc5C,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,gBAqB9C,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,gBAczC,CAAC;AAEF,yEAAyE;AACzE,MAAM,WAAW,qBAAqB,CAAC,KAAK;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB,CAAC,KAAK;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,kCAAkC,CAAC,KAAK,IAAI;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,CAAC,EAAE,SAAS,KAAK,GAAG;IAAE,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,wBAAwB,CACvC,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE;QACX,0DAA0D;QAC1D,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC;QACvE,8FAA8F;QAC9F,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,IAAI,EAAE,WAAW,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GACvF,OAAO,CAAC,IAAI,CAAC,CAAC;QACjB;;;;WAIG;QACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,WAAW,GAAG,OAAO,CAAC,qBAAqB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC;QAClH,sEAAsE;QACtE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACzC,CAAC;IACF,aAAa,EAAE;QACb,MAAM,CAAC,KAAK,EAAE,kCAAkC,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,CAAC;QAClH,iGAAiG;QACjG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,uBAAuB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAC/G,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACpF,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACtG,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,+EAA+E;QAC/E,WAAW,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;KAChG,CAAC;IACF,QAAQ,EAAE;QACR,4DAA4D;QAC5D,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QAC9D,0EAA0E;QAC1E,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACvE,CAAC;CACH;AA4BD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,oBAAoB;;IAExD,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,GAAE,uBAAkE;IAK3F,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB1C,oDAAoD;IACpD,cAAc,CACZ,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,wBAAwB,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC;CAwL1F"}