@mastra/factory 0.9.0 → 0.10.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @mastra/factory
2
2
 
3
+ ## 0.10.0-alpha.2
4
+
5
+ ### Patch Changes
6
+
7
+ - add installable PWA metadata and device icons to the Factory UI ([#22051](https://github.com/mastra-ai/mastra/pull/22051))
8
+
9
+ - Updated dependencies [[`e737014`](https://github.com/mastra-ai/mastra/commit/e737014e0fc7035759762bb5b48baef1d6c0f6a7), [`d6ce34a`](https://github.com/mastra-ai/mastra/commit/d6ce34aeceb06ddf3d595a1eed5cc74f481a46a1), [`e6f8450`](https://github.com/mastra-ai/mastra/commit/e6f845074d478527026b18d85031b23353e1d0a4)]:
10
+ - @mastra/core@1.62.0-alpha.2
11
+ - @mastra/code-sdk@1.4.1-alpha.2
12
+
13
+ ## 0.10.0-alpha.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [[`f95f468`](https://github.com/mastra-ai/mastra/commit/f95f468cf1e7c2b924a13826494f98b8f2ccd581)]:
18
+ - @mastra/core@1.61.1-alpha.1
19
+ - @mastra/code-sdk@1.4.1-alpha.1
20
+
21
+ ## 0.10.0-alpha.0
22
+
23
+ ### Minor Changes
24
+
25
+ - Improved the Factory audit log with a density timeline, category filters, responsive rows, and automatic history loading. Intake binding changes now appear in the affected project's audit history. ([#22023](https://github.com/mastra-ai/mastra/pull/22023))
26
+
27
+ ```ts
28
+ const response = await fetch(`/web/factory/projects/${factoryProjectId}/audit?actions=factory.run.started&limit=50`);
29
+ const page = await response.json();
30
+ ```
31
+
32
+ ### Patch Changes
33
+
34
+ - Factory skill playbooks in Settings › Agent › Skills now render as formatted markdown instead of a wall of plain text, with a toggle on hover to read the raw SKILL.md source. Long skills scroll inside the card rather than stretching the page. ([#22018](https://github.com/mastra-ai/mastra/pull/22018))
35
+
36
+ - Updated dependencies [[`1e47b75`](https://github.com/mastra-ai/mastra/commit/1e47b7520cab4cfaa8daed52f17e2e6d14ff7539)]:
37
+ - @mastra/core@1.61.1-alpha.0
38
+ - @mastra/code-sdk@1.4.1-alpha.0
39
+
3
40
  ## 0.9.0
4
41
 
5
42
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/auth.js CHANGED
@@ -499,7 +499,7 @@ function createFactoryAuthGate(provider) {
499
499
  if (c.req.method === "POST" && SIGNATURE_VERIFYING_CHANNEL_WEBHOOK.test(path)) return next();
500
500
  if (c.req.method === "GET" && (path === "/connect/slack" || path.startsWith("/connect/slack/"))) return next();
501
501
  if (c.req.method === "GET" && path === "/login") return c.redirect(`/signin${new URL(c.req.url).search}`);
502
- if (path === "/signin" || path.startsWith("/assets/") || path === "/manifest.webmanifest" || path === "/mastra.svg" || c.req.method === "GET" && SESSION_FAVICON_PATHS.has(path)) return next();
502
+ if (path === "/signin" || path.startsWith("/assets/") || path === "/manifest.webmanifest" || path === "/mastra.svg" || path === "/pwa-192.png" || path === "/pwa-512.png" || path === "/apple-touch-icon.png" || c.req.method === "GET" && SESSION_FAVICON_PATHS.has(path)) return next();
503
503
  const token = getBearerToken(c.req.header("Authorization"));
504
504
  const user = await timedAboveThreshold("auth.gate.authenticate", 1e3, () => authenticateRequest(provider, token, c.req.raw));
505
505
  if (user) {
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import { MastraAuthWorkos } from '@mastra/auth-workos';\nimport {\n registerApiRoute,\n isAuthHttpHandler,\n isCredentialsProvider,\n isOrganizationsProvider,\n isSessionProvider,\n isSSOProvider,\n} from '@mastra/core/server';\nimport type { ApiRoute, IMastraAuthProvider, ISessionProvider } from '@mastra/core/server';\nimport type { Context, Hono } from 'hono';\n\nimport type { RouteAuth } from './routes/route.js';\nimport { timedAboveThreshold } from './timing.js';\n\n/**\n * Provider-neutral factory auth gating for the MastraCode web server.\n *\n * When an auth provider is active (a `MastraAuthProvider` instance passed to\n * `MastraFactory`'s `auth` slot, or — back-compat for suites/paths that never\n * boot the factory — implied by the WorkOS env vars), every route on the web\n * server is placed behind it: unauthenticated browser navigations are\n * redirected to the SPA's `/signin` page, API/XHR calls receive a 401, and a\n * small set of public routes stay reachable while signed out — the provider's\n * `/auth/*` routes plus `/auth/me`, the `/signin` page, its `/assets/*` bundle,\n * and the SPA manifest metadata. When no provider is active, `mountFactoryAuth` is a no-op and the server\n * behaves exactly as it does without auth.\n *\n * Provider specifics stay in the providers (`@mastra/auth-workos`,\n * `@mastra/auth-better-auth`, or any custom `IMastraAuthProvider`); this\n * module composes them capability-first via the core type guards:\n * - `authenticateToken` — session/bearer validation (all providers)\n * - `ISSOProvider` — hosted-login `/auth/login`, `/auth/callback`, `/auth/logout`\n * - `IAuthHttpHandler` — provider-owned `/auth/api/*` endpoints (better-auth)\n * - `IOrganizationsProvider` — personal-org bootstrap + admin checks\n * - `ICredentialsProvider.isSignUpEnabled` — SPA sign-up affordance\n * - `getClearSessionHeaders` — session cookie clearing on logout\n */\n\n/** Minimal shape of the signed-in user surfaced to the SPA (no tokens). */\nexport interface FactoryAuthUser {\n /** Stable WorkOS user id used to scope per-user data (GitHub installs etc.). */\n workosId?: string;\n /** Provider user id; WorkOS shapes may use `workosId` instead (see {@link workosId}). */\n id?: string;\n email?: string;\n name?: string;\n /** Provider-supplied profile picture URL, when the auth provider exposes one. */\n avatarUrl?: string;\n /**\n * Organization id. The org is the top-level tenant: it owns the GitHub\n * App installation and connected projects, while each user inside the org gets\n * isolated building instances. Absent for personal (no-org) accounts.\n */\n organizationId?: string;\n}\n\n/**\n * Tenant identity: the org is the top-level tenant, and each user inside it is\n * an isolated builder. Agent state, worktrees and sandboxes are scoped per\n * `(orgId, userId)`. Personal (no-org) users have `orgId === undefined`.\n */\nexport interface FactoryAuthTenant {\n /** Organization id, or `undefined` for personal (no-org) accounts. */\n orgId?: string;\n /** Stable provider user id. */\n userId: string;\n}\n\n/**\n * Validate that a `returnTo` value is a safe same-site path, to prevent\n * open-redirect attacks. Only absolute local paths (`/foo`) are allowed;\n * protocol-relative (`//evil.com`) and absolute URLs are rejected.\n */\nexport function sanitizeReturnTo(raw: string | undefined): string {\n if (!raw) return '/';\n if (!raw.startsWith('/')) return '/';\n // Reject protocol-relative URLs like \"//evil.com\" and \"/\\evil.com\".\n if (raw.startsWith('//') || raw.startsWith('/\\\\')) return '/';\n return raw;\n}\n\n/** Extract a bearer token from the Authorization header, if present. */\nexport function getBearerToken(authorization: string | undefined): string {\n if (!authorization) return '';\n const match = /^Bearer\\s+(.+)$/i.exec(authorization);\n return match?.[1] ?? '';\n}\n\n/**\n * Whether the SPA is served cross-origin from this API (platform deploy). When\n * `MASTRACODE_ALLOWED_ORIGINS` is set the browser talks to us cross-site, so\n * session cookies must be `SameSite=None; Secure` for the browser to send them.\n * Same-origin local dev leaves this unset and keeps the stricter `SameSite=Lax`.\n */\nexport function isCrossSiteAuth(): boolean {\n return Boolean(process.env.MASTRACODE_ALLOWED_ORIGINS?.trim());\n}\n\n/** Hono context variables set by the auth gate. */\nexport interface FactoryAuthVariables {\n factoryAuthUser: FactoryAuthUser;\n}\n\n/** Context key under which the gate stashes the authenticated user. */\nconst FACTORY_AUTH_USER_KEY = 'factoryAuthUser';\n\n/**\n * Read the authenticated user the gate stashed on the context, or\n * `undefined` when unauthenticated / auth disabled. Used by downstream routes\n * (e.g. GitHub) to scope rows per user.\n */\nexport function getFactoryAuthUser(c: Context): FactoryAuthUser | undefined {\n return c.get(FACTORY_AUTH_USER_KEY) as FactoryAuthUser | undefined;\n}\n\n/**\n * Read the authenticated user off a request context, normalizing whatever the\n * active auth provider put there.\n *\n * The server's auth layer writes the provider's `authenticateToken` result into\n * the request context's `user` slot verbatim, so the value's shape follows the\n * provider: WorkOS writes a flat user, better-auth writes a `{ session, user }`\n * wrapper whose org lives on the session. Reading that slot as a\n * {@link FactoryAuthUser} therefore yields `undefined` for both the id and the\n * org under better-auth, which reads as \"this session belongs to somebody else\"\n * at every ownership check. Normalize on the way in instead.\n */\nexport function getFactoryAuthUserFromContext(\n requestContext: { get: (key: string) => unknown } | undefined,\n): FactoryAuthUser | undefined {\n if (!requestContext || typeof requestContext.get !== 'function') return undefined;\n return toFactoryAuthUser(requestContext.get('user')) ?? undefined;\n}\n\n/** Resolve the stable user id from an authenticated user shape. */\nexport function getFactoryAuthUserId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.workosId ?? user?.id;\n}\n\n/** Resolve the organization id from a user shape, if present. */\nexport function getFactoryAuthOrgId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.organizationId;\n}\n\n/**\n * Resolve the tenant identity `(orgId, userId)` from the authenticated user on\n * the context. Returns `undefined` when there is no signed-in user (auth\n * disabled or unauthenticated). `orgId` is `undefined` for personal accounts;\n * callers gate org-scoped GitHub features on its presence while agent state\n * falls back to a user-only tenant.\n */\nexport function factoryAuthTenant(c: Context): FactoryAuthTenant | undefined {\n const user = getFactoryAuthUser(c);\n const userId = getFactoryAuthUserId(user);\n if (!userId) return undefined;\n return { orgId: getFactoryAuthOrgId(user), userId };\n}\n\n/** True when both WorkOS credential env vars are present (legacy env gate). */\nfunction envWorkosConfigured(): boolean {\n return Boolean(process.env.WORKOS_API_KEY && process.env.WORKOS_CLIENT_ID);\n}\n\n/**\n * WorkOS provider implied by the `WORKOS_*` env vars — back-compat for test\n * suites exercised without booting the factory (route suites set `WORKOS_*`\n * directly and call {@link mountFactoryAuth} without an explicit provider).\n * `fetchMemberships: true` lets `authenticateToken` resolve `organizationId`\n * from a single membership when the JWT has no org claim — required so a\n * bootstrapped personal org resolves without re-auth.\n */\nfunction envFallbackAuthProvider(redirectUri: string | undefined): MastraAuthWorkos | undefined {\n if (!envWorkosConfigured()) return undefined;\n return new MastraAuthWorkos({\n redirectUri: redirectUri ?? process.env.WORKOS_REDIRECT_URI,\n fetchMemberships: true,\n });\n}\n\n/**\n * Map a provider `authenticateToken` result onto the neutral SPA user shape.\n *\n * Two result families exist today:\n * - flat provider users (WorkOS `WorkOSUser` et al.): `id`/`workosId`/`email`/\n * `name`/`organizationId` directly on the object;\n * - session-shaped results (better-auth `BetterAuthUser`): `{ session, user }`\n * with the active org on the session.\n */\nfunction toFactoryAuthUser(result: unknown): FactoryAuthUser | null {\n if (!result || typeof result !== 'object') return null;\n const record = result as Record<string, unknown>;\n\n // Session-shaped results: { session, user }. A result carrying both halves and\n // top-level identity fields is read as session-shaped: the session half is the\n // authenticated one, and preferring it keeps the org and the id from coming\n // from two different places.\n if (record.user && typeof record.user === 'object' && record.session && typeof record.session === 'object') {\n const user = record.user as { id?: unknown; email?: unknown; name?: unknown; avatarUrl?: unknown };\n const session = record.session as { activeOrganizationId?: unknown };\n if (typeof user.id !== 'string') return null;\n return {\n id: user.id,\n email: typeof user.email === 'string' ? user.email : undefined,\n name: typeof user.name === 'string' ? user.name : undefined,\n avatarUrl: typeof user.avatarUrl === 'string' ? user.avatarUrl : undefined,\n organizationId: typeof session.activeOrganizationId === 'string' ? session.activeOrganizationId : undefined,\n };\n }\n\n // Flat provider users.\n const flat = record as {\n id?: unknown;\n workosId?: unknown;\n email?: unknown;\n name?: unknown;\n avatarUrl?: unknown;\n organizationId?: unknown;\n };\n const id = typeof flat.id === 'string' ? flat.id : undefined;\n const workosId = typeof flat.workosId === 'string' ? flat.workosId : undefined;\n if (!id && !workosId) return null;\n return {\n id,\n workosId,\n email: typeof flat.email === 'string' ? flat.email : undefined,\n name: typeof flat.name === 'string' ? flat.name : undefined,\n avatarUrl: typeof flat.avatarUrl === 'string' ? flat.avatarUrl : undefined,\n organizationId: typeof flat.organizationId === 'string' ? flat.organizationId : undefined,\n };\n}\n\n/**\n * Resolve the authenticated user for a request via the provider. Never throws:\n * ordinary invalid/expired sessions resolve to `null`.\n */\nasync function authenticateRequest(\n provider: IMastraAuthProvider,\n token: string,\n raw: Request,\n): Promise<FactoryAuthUser | null> {\n try {\n const result = await provider.authenticateToken(token, raw);\n return toFactoryAuthUser(result);\n } catch {\n return null;\n }\n}\n\n/**\n * Bootstrap a personal org for no-org accounts so org-scoped features (GitHub\n * connect) work without leaving the app. Mutates the resolved user so the\n * current request sees the org immediately; subsequent requests resolve it via\n * the provider's own session/membership lookup (providers cache internally).\n * Best-effort: providers swallow their own bootstrap failures, and any\n * unexpected throw leaves the user no-org.\n */\nasync function ensureUserOrg(provider: IMastraAuthProvider, user: FactoryAuthUser): Promise<void> {\n if (getFactoryAuthOrgId(user)) return;\n if (!isOrganizationsProvider(provider)) return;\n const userId = getFactoryAuthUserId(user);\n if (!userId) return;\n try {\n const orgId = await provider.ensureOrganization(userId);\n if (orgId) user.organizationId = orgId;\n } catch {\n // Best-effort: the user stays no-org until a later request succeeds.\n }\n}\n\n/**\n * `Set-Cookie` values that clear the provider's session cookie(s), from the\n * provider's (possibly partial) `ISessionProvider.getClearSessionHeaders`.\n */\nfunction providerClearCookies(provider: IMastraAuthProvider): string[] {\n const getClearSessionHeaders = (provider as Partial<ISessionProvider>).getClearSessionHeaders;\n if (typeof getClearSessionHeaders !== 'function') return [];\n const headers = getClearSessionHeaders.call(provider) ?? {};\n const setCookie = headers['Set-Cookie'];\n if (!setCookie) return [];\n // A provider may join several clearing cookies into one header value.\n return setCookie.split(/,(?=\\s*[^;=,\\s]+=)/).map(cookie => cookie.trim());\n}\n\n/**\n * Fail-closed authorization for organization-level administrative mutations.\n * The caller must belong to the same active organization and the provider must\n * explicitly confirm an admin/owner role.\n */\nexport async function isOrganizationAdmin(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n organizationId: string,\n): Promise<boolean> {\n const user = await ensureFactoryAuthUser(provider, c);\n if (!user || user.organizationId !== organizationId || !provider || !isOrganizationsProvider(provider)) {\n return false;\n }\n const userId = getFactoryAuthUserId(user);\n if (!userId) return false;\n try {\n return await provider.isOrganizationAdmin(organizationId, userId);\n } catch {\n return false;\n }\n}\n\n/**\n * Build the factory's implementation of the `RouteAuth` seam over the\n * resolved provider (`undefined` = auth disabled). Constructed once per boot\n * by `MastraFactory.prepare()` and handed to factory route modules at\n * construction — they never import the factory auth module directly.\n */\nexport function createFactoryRouteAuth(provider: IMastraAuthProvider | undefined): RouteAuth {\n return {\n enabled: () => provider !== undefined,\n ensureUser: (c: Context) => ensureFactoryAuthUser(provider, c),\n tenant: (c: Context) => factoryAuthTenant(c),\n isOrganizationAdmin: (c: Context, organizationId: string) => isOrganizationAdmin(provider, c, organizationId),\n };\n}\n\n/** True when the given provider is WorkOS. Gates WorkOS-only capabilities. */\nexport function isWorkOSAuth(provider: IMastraAuthProvider | undefined): boolean {\n return provider instanceof MastraAuthWorkos;\n}\n\n/**\n * The raw WorkOS provider, for features that need the WorkOS client directly\n * (audit-log export, Admin Portal links). Callers must gate on\n * {@link isWorkOSAuth} first — throws when the provider is not WorkOS.\n */\nexport function getWorkOSProvider(provider: IMastraAuthProvider | undefined): MastraAuthWorkos {\n if (provider instanceof MastraAuthWorkos) return provider;\n throw new Error('WorkOS provider requested but the active factory auth provider is not WorkOS');\n}\n\n/**\n * Resolve the authenticated user for a request, stashing it on the context.\n *\n * The gate only authenticates non-`/auth/*` requests via the `Authorization`\n * header, so cookie-based browser navigations to public `/auth/*` routes (the\n * GitHub connect/callback flow) arrive without a gate-stashed user. This reads\n * the session cookie from the raw request the same way `/auth/me` does,\n * caches the result on the context, and returns it so downstream helpers like\n * {@link factoryAuthTenant} work uniformly on both gated and public routes.\n *\n * Returns `undefined` when there is no valid session (or auth is disabled).\n */\nexport async function ensureFactoryAuthUser(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n): Promise<FactoryAuthUser | undefined> {\n const existing = getFactoryAuthUser(c);\n if (existing) return existing;\n if (!provider) return undefined;\n\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n if (!user) return undefined;\n\n await ensureUserOrg(provider, user);\n\n c.set(FACTORY_AUTH_USER_KEY, user);\n return user;\n}\n\nexport interface MountFactoryAuthOptions {\n /**\n * Explicit auth provider to mount. When omitted, falls back to a WorkOS\n * provider implied by the `WORKOS_*` env vars (back-compat for suites that\n * never boot the factory).\n */\n provider?: IMastraAuthProvider;\n /**\n * Absolute URL the identity provider redirects back to after login (WorkOS\n * env-fallback path only). Defaults to the `WORKOS_REDIRECT_URI` env var.\n */\n redirectUri?: string;\n /** Browser-facing origin used to derive the SSO callback URL. */\n publicUrl?: string;\n}\n\n/**\n * Decide whether a request is a top-level browser navigation (which should be\n * redirected to `/signin`) versus an API/XHR call (which should get a 401 JSON\n * response the SPA can react to).\n */\nfunction isNavigationRequest(path: string, accept: string | undefined): boolean {\n if (path.startsWith('/api/')) return false;\n return (accept ?? '').includes('text/html');\n}\n\n/**\n * Handle the provider-neutral `/auth/me` route: validate the session with the\n * active provider and report the signed-in user (no tokens) to the SPA.\n * `/auth/me` is public (the gate skips `/auth/*`), so it validates the session\n * itself rather than reading a value the gate would have stashed.\n */\nasync function handleAuthMe(provider: IMastraAuthProvider, c: Context): Promise<Response> {\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n // Provider identity for the SPA: `/signin` renders the hosted-login button\n // for WorkOS and an email/password form for better-auth (with sign-up hidden\n // when the provider disables it).\n const signUpDisabled = isCredentialsProvider(provider) && provider.isSignUpEnabled?.() === false;\n const meta = { provider: provider.name, ...(signUpDisabled ? { signUpDisabled: true } : {}) };\n if (!user) {\n return c.json({ authenticated: false, user: null, ...meta });\n }\n // Resolve the org the same way gated requests do (providers cache, so this\n // is a lookup — not a create — after first bootstrap).\n await ensureUserOrg(provider, user);\n return c.json({\n authenticated: true,\n user: {\n userId: getFactoryAuthUserId(user),\n email: user.email,\n name: user.name,\n organizationId: user.organizationId,\n },\n ...meta,\n });\n}\n\n/**\n * Encode a validated returnTo path into the OAuth `state` parameter.\n *\n * Pipe format (`uuid|encodedPath`) is the contract `MastraAuthStudio` parses\n * to forward the path as the platform's `post_login_redirect`; a JSON blob\n * here silently degrades every post-login redirect to `/`.\n */\nfunction encodeState(returnTo: string): string {\n return `${crypto.randomUUID()}|${encodeURIComponent(returnTo)}`;\n}\n\n/** Decode the OAuth `state` parameter back into a sanitized returnTo path. */\nfunction decodeState(state: string | undefined): string {\n if (!state) return '/';\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n return sanitizeReturnTo(decodeURIComponent(state.slice(pipeIndex + 1)));\n } catch {\n return '/';\n }\n }\n return '/';\n}\n\n/**\n * Short-lived cookie stashing the post-login destination across the hosted\n * OAuth round-trip. Providers/platforms differ in whether they echo `state`\n * back to the callback, so the cookie is the reliable channel; `state` (when\n * echoed) takes precedence only if the cookie is missing.\n */\nconst RETURN_TO_COOKIE = 'mastra_factory_return_to';\n\nfunction returnToCookieHeader(returnTo: string): string {\n const crossSite = isCrossSiteAuth() ? '; SameSite=None; Secure' : '; SameSite=Lax';\n return `${RETURN_TO_COOKIE}=${encodeURIComponent(returnTo)}; Path=/; Max-Age=600; HttpOnly${crossSite}`;\n}\n\nfunction clearReturnToCookieHeader(): string {\n const crossSite = isCrossSiteAuth() ? '; SameSite=None; Secure' : '; SameSite=Lax';\n return `${RETURN_TO_COOKIE}=; Path=/; Max-Age=0; HttpOnly${crossSite}`;\n}\n\nfunction readReturnToCookie(c: Context): string | undefined {\n const header = c.req.header('Cookie');\n if (!header) return undefined;\n for (const part of header.split(';')) {\n const [name, ...rest] = part.trim().split('=');\n if (name === RETURN_TO_COOKIE) {\n try {\n return decodeURIComponent(rest.join('='));\n } catch {\n return undefined;\n }\n }\n }\n return undefined;\n}\n\n/** HTTP methods supported for public auth routes. */\ntype AuthRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'ALL';\n\n/** A public `/auth/*` route derived from the provider's capabilities. */\ninterface AuthRouteSpec {\n path: string;\n method: AuthRouteMethod;\n handler: (c: Context) => Response | Promise<Response>;\n}\n\n/**\n * Derive the public `/auth/*` routes from the provider's capabilities:\n *\n * - `IAuthHttpHandler` → `ALL /auth/api/*` proxy to the provider's own HTTP\n * surface (better-auth sign-in/up/out/session — what the SPA's\n * email/password form posts to).\n * - `ISSOProvider` → hosted-login `GET /auth/login` / `GET /auth/callback` /\n * `GET /auth/logout` (returnTo preserved through the OAuth `state` param).\n * - handler-shaped, non-SSO providers → `GET /auth/login` redirects to the\n * SPA's `/signin` form, `GET /auth/logout` revokes via the provider's\n * sign-out endpoint and clears the session cookie.\n */\nfunction providerAuthRoutes(provider: IMastraAuthProvider, publicUrl?: string): AuthRouteSpec[] {\n const routes: AuthRouteSpec[] = [];\n\n if (isAuthHttpHandler(provider)) {\n routes.push({\n path: '/auth/api/*',\n method: 'ALL',\n handler: c => provider.handleAuthRequest(c.req.raw),\n });\n }\n\n if (isSSOProvider(provider)) {\n routes.push(\n {\n path: '/auth/login',\n method: 'GET',\n handler: async c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n const state = encodeState(returnTo);\n // Build the callback URL from the browser-facing public origin so\n // the OAuth round-trip lands back on the SPA's origin (in dev the\n // SPA is on :5173 and Vite proxies /auth/* to the API on :4111 —\n // deriving from c.req.url would use :4111 and the post-callback\n // redirect to `/` would miss the SPA). Providers that ignore the\n // caller's URI in favor of their own config (e.g. MastraAuthWorkos\n // with an explicit `redirectUri` option) still take precedence.\n const redirectUri = publicUrl ? new URL('/auth/callback', publicUrl).toString() : '';\n const loginUrl = await provider.getLoginUrl(redirectUri, state);\n for (const cookie of (await provider.getLoginCookies?.(redirectUri, state)) ?? []) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n // Stash the destination in a cookie too: not every provider/platform\n // echoes `state` back to the callback.\n if (returnTo !== '/') {\n c.header('Set-Cookie', returnToCookieHeader(returnTo), { append: true });\n }\n return c.redirect(loginUrl);\n },\n },\n {\n path: '/auth/callback',\n method: 'GET',\n handler: async c => {\n const code = c.req.query('code');\n const stateReturnTo = decodeState(c.req.query('state'));\n const cookieReturnTo = sanitizeReturnTo(readReturnToCookie(c));\n const returnTo = cookieReturnTo !== '/' ? cookieReturnTo : stateReturnTo;\n c.header('Set-Cookie', clearReturnToCookieHeader(), { append: true });\n const idpError = c.req.query('error');\n if (idpError) {\n // IdP denial (e.g. access_denied for a non-org-member): bouncing to\n // /auth/login would re-enter the IdP in a redirect loop.\n const query = new URLSearchParams({ error: idpError.slice(0, 64) });\n const description = c.req.query('error_description');\n if (description) query.set('error_description', description.slice(0, 256));\n if (returnTo !== '/') query.set('returnTo', returnTo);\n return c.redirect(`/signin?${query.toString()}`);\n }\n if (!code) {\n return c.redirect('/auth/login');\n }\n try {\n const result = await provider.handleCallback(code, c.req.query('state') ?? '');\n if (result.cookies?.length) {\n // Provider populated cookies directly (e.g. WorkOS AuthKit builds\n // its own sealed session cookie inside handleCallback).\n for (const cookie of result.cookies) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } else if (isSessionProvider(provider) && result.tokens) {\n // Fallback for providers that expose ISessionProvider but leave\n // cookie construction to the server (e.g. MastraAuthStudio, which\n // returns just the sealed session as accessToken so\n // getSessionHeaders can scope the cookie to this deployment's\n // domain via MASTRA_COOKIE_DOMAIN / sharedApiUrl auto-detection).\n // Mirrors packages/server/src/server/handlers/auth.ts:492-503.\n const resultUser = result.user as { id: string; organizationId?: string };\n const session = await provider.createSession(resultUser.id, {\n accessToken: result.tokens.accessToken,\n refreshToken: result.tokens.refreshToken,\n expiresAt: result.tokens.expiresAt,\n organizationId: resultUser.organizationId,\n });\n for (const [key, value] of Object.entries(provider.getSessionHeaders(session))) {\n c.header(key, value, { append: true });\n }\n }\n return c.redirect(returnTo);\n } catch {\n // Code exchange failed (expired/replayed code, misconfig). Send the\n // user back to login rather than surfacing a raw error.\n return c.redirect('/auth/login');\n }\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n let logoutUrl: string | null = null;\n try {\n logoutUrl = (await provider.getLogoutUrl?.('/', c.req.raw)) ?? null;\n } catch {\n logoutUrl = null;\n }\n // Clear the session cookie regardless of whether the provider\n // returned a logout URL.\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect(logoutUrl ?? '/');\n },\n },\n );\n } else if (isAuthHttpHandler(provider)) {\n routes.push(\n {\n // Hosted-login equivalent: no hosted page, so send the browser to the\n // SPA's /signin form, preserving returnTo.\n path: '/auth/login',\n method: 'GET',\n handler: c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n // Revoke the session server-side through the provider's own sign-out\n // endpoint and forward its clearing cookies; fall back to our clear\n // cookies regardless.\n try {\n const origin = new URL(c.req.url).origin;\n const response = await provider.handleAuthRequest(\n new Request(`${origin}/auth/api/sign-out`, { method: 'POST', headers: c.req.raw.headers }),\n );\n for (const cookie of response.headers.getSetCookie()) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } catch {\n // No/invalid session: nothing to revoke.\n }\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect('/');\n },\n },\n );\n }\n\n return routes;\n}\n\n/**\n * Register the public `/auth/*` routes on a Hono app: the capability-derived\n * provider routes (login/callback/logout/provider APIs) plus the\n * provider-neutral `/auth/me`. Split out from `mountFactoryAuth` so both the local\n * Hono server and the platform Mastra entry can reuse the exact same handlers.\n */\nexport function registerAuthRoutes(\n app: Hono<any>,\n provider: IMastraAuthProvider,\n options: { publicUrl?: string } = {},\n): void {\n for (const route of providerAuthRoutes(provider, options.publicUrl)) {\n const methods = route.method === 'ALL' ? ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] : [route.method];\n app.on(methods, route.path, c => route.handler(c));\n }\n app.get('/auth/me', c => handleAuthMe(provider, c));\n}\n\n/**\n * Build the public `/auth/*` routes (provider routes + `/auth/me`) as Mastra\n * `server.apiRoutes`. Used by the platform Mastra entry (`src/mastra/index.ts`),\n * which can't register plain Hono routes on the deployer-generated app the way\n * the local server does via {@link registerAuthRoutes}.\n *\n * Handlers are identical to {@link registerAuthRoutes}. All are `requiresAuth: false`\n * (they must be reachable while unauthenticated), and the gate middleware skips\n * `/auth/*` so it never blocks them. `/auth/*` is not under `/api`, so it is a\n * valid custom-route path.\n */\nexport function buildAuthRoutes(provider: IMastraAuthProvider, options: { publicUrl?: string } = {}): ApiRoute[] {\n return [\n // `registerApiRoute` handlers see @mastra/core's bundled hono Context type,\n // which is structurally identical to (but nominally distinct from) the\n // local hono version the route handlers are typed against — cast across\n // the seam.\n ...providerAuthRoutes(provider, options.publicUrl).map(route =>\n registerApiRoute(route.path, {\n method: route.method,\n requiresAuth: false,\n handler: c => route.handler(c as unknown as Context),\n }),\n ),\n registerApiRoute('/auth/me', {\n method: 'GET',\n requiresAuth: false,\n handler: c => handleAuthMe(provider, c as unknown as Context),\n }),\n ];\n}\n\n/**\n * Channel webhook paths whose adapter verifies the platform's request signature,\n * making the delivery self-authenticating. Add a platform here only once its\n * adapter rejects unsigned or mis-signed requests.\n */\nconst SIGNATURE_VERIFYING_CHANNEL_WEBHOOK = /^\\/api\\/agent-controllers\\/[^/]+\\/channels\\/slack\\/webhook$/;\n\n// Fetched by tabs that may already be signed out. Enumerated, not prefix-matched,\n// so a future route under the same prefix does not inherit the pass.\nconst SESSION_FAVICON_PATHS = new Set([\n '/favicon-session-initializing.svg',\n '/favicon-session-working.svg',\n '/favicon-session-awaiting.svg',\n '/favicon-session-error.svg',\n]);\n\n/**\n * Build the auth gate as a plain Hono middleware handler `(c, next)`. Protects\n * everything that is not a public `/auth/*` route: authenticated requests stash\n * the user on the context and continue; unauthenticated navigations redirect to\n * login and XHR/API calls get a 401 JSON. Shared by the local Hono server\n * (`mountFactoryAuth`) and the platform Mastra entry (`server.middleware`).\n */\nexport function createFactoryAuthGate(provider: IMastraAuthProvider) {\n return async (c: Context, next: () => Promise<void>): Promise<Response | void> => {\n const path = c.req.path;\n if (path.startsWith('/auth/')) {\n return next();\n }\n if (c.req.method === 'POST' && path === '/web/github/webhook') {\n return next();\n }\n // Inbound chat-channel webhooks (Slack events) carry no user session: they\n // authenticate by platform signature, the adapter verifying the request\n // against its signing secret. The routes declare `requiresAuth: false`, but\n // this gate is `use()` middleware — it runs before route matching, so that\n // metadata is not readable here and the path needs an explicit pass.\n //\n // The platform is allowlisted rather than matched as a wildcard: a pass\n // keyed on path shape alone would silently extend to any future adapter,\n // including one that does not verify signatures. Controller id stays a\n // wildcard because it is whatever the host registered.\n if (c.req.method === 'POST' && SIGNATURE_VERIFYING_CHANNEL_WEBHOOK.test(path)) {\n return next();\n }\n // The Slack account-linking deep link and the Sign-in-with-Slack OIDC\n // start/callback do their own auth (friendly login-redirect for signed-out\n // visitors; the OIDC callback authenticates via its signed `state`) — see\n // connect-route.ts.\n if (c.req.method === 'GET' && (path === '/connect/slack' || path.startsWith('/connect/slack/'))) {\n return next();\n }\n // The platform's deploy-auth flow lands IdP denials on `/login`\n // (`error=access_denied&error_description=...`); the SPA serves sign-in at\n // `/signin`, so forward the query there instead of burying it in returnTo.\n if (c.req.method === 'GET' && path === '/login') {\n return c.redirect(`/signin${new URL(c.req.url).search}`);\n }\n // The SPA sign-in page, its static bundle, and browser-fetched metadata\n // must be reachable while signed out; no user is stashed, so `/api/*`\n // stays protected.\n if (\n path === '/signin' ||\n path.startsWith('/assets/') ||\n path === '/manifest.webmanifest' ||\n path === '/mastra.svg' ||\n (c.req.method === 'GET' && SESSION_FAVICON_PATHS.has(path))\n ) {\n return next();\n }\n\n const token = getBearerToken(c.req.header('Authorization'));\n // A slow verification here delays EVERY protected request — surface\n // outliers so auth-backend latency is attributable from server logs.\n const user = await timedAboveThreshold('auth.gate.authenticate', 1_000, () =>\n authenticateRequest(provider, token, c.req.raw),\n );\n\n if (user) {\n // Bootstrap a personal org for no-org accounts so the org id resolves on\n // this request (see ensureFactoryAuthUser for the rationale).\n await ensureUserOrg(provider, user);\n c.set(FACTORY_AUTH_USER_KEY, user);\n c.get('requestContext')?.set('user', user);\n return next();\n }\n\n if (isNavigationRequest(path, c.req.header('Accept'))) {\n const url = new URL(c.req.url);\n const returnTo = sanitizeReturnTo(url.pathname + url.search);\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n }\n\n return c.json({ error: 'unauthorized' }, 401);\n };\n}\n\n/**\n * Mount factory auth gating onto the host app. No-op when auth is disabled\n * (no provider active).\n *\n * Must be called before the Mastra adapter routes, the `/web/*` routes, and\n * the static UI handlers so the gate covers every request. Composes the shared\n * `registerAuthRoutes` + `createFactoryAuthGate` factories so the local Hono server\n * and the platform Mastra entry stay behavior-identical.\n */\nexport function mountFactoryAuth(app: Hono<any>, options: MountFactoryAuthOptions = {}): boolean {\n const provider = options.provider ?? envFallbackAuthProvider(options.redirectUri);\n if (!provider) return false;\n\n registerAuthRoutes(app, provider, { publicUrl: options.publicUrl });\n app.use('*', createFactoryAuthGate(provider));\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import { MastraAuthWorkos } from '@mastra/auth-workos';\nimport {\n registerApiRoute,\n isAuthHttpHandler,\n isCredentialsProvider,\n isOrganizationsProvider,\n isSessionProvider,\n isSSOProvider,\n} from '@mastra/core/server';\nimport type { ApiRoute, IMastraAuthProvider, ISessionProvider } from '@mastra/core/server';\nimport type { Context, Hono } from 'hono';\n\nimport type { RouteAuth } from './routes/route.js';\nimport { timedAboveThreshold } from './timing.js';\n\n/**\n * Provider-neutral factory auth gating for the MastraCode web server.\n *\n * When an auth provider is active (a `MastraAuthProvider` instance passed to\n * `MastraFactory`'s `auth` slot, or — back-compat for suites/paths that never\n * boot the factory — implied by the WorkOS env vars), every route on the web\n * server is placed behind it: unauthenticated browser navigations are\n * redirected to the SPA's `/signin` page, API/XHR calls receive a 401, and a\n * small set of public routes stay reachable while signed out — the provider's\n * `/auth/*` routes plus `/auth/me`, the `/signin` page, its `/assets/*` bundle,\n * and the SPA manifest metadata. When no provider is active, `mountFactoryAuth` is a no-op and the server\n * behaves exactly as it does without auth.\n *\n * Provider specifics stay in the providers (`@mastra/auth-workos`,\n * `@mastra/auth-better-auth`, or any custom `IMastraAuthProvider`); this\n * module composes them capability-first via the core type guards:\n * - `authenticateToken` — session/bearer validation (all providers)\n * - `ISSOProvider` — hosted-login `/auth/login`, `/auth/callback`, `/auth/logout`\n * - `IAuthHttpHandler` — provider-owned `/auth/api/*` endpoints (better-auth)\n * - `IOrganizationsProvider` — personal-org bootstrap + admin checks\n * - `ICredentialsProvider.isSignUpEnabled` — SPA sign-up affordance\n * - `getClearSessionHeaders` — session cookie clearing on logout\n */\n\n/** Minimal shape of the signed-in user surfaced to the SPA (no tokens). */\nexport interface FactoryAuthUser {\n /** Stable WorkOS user id used to scope per-user data (GitHub installs etc.). */\n workosId?: string;\n /** Provider user id; WorkOS shapes may use `workosId` instead (see {@link workosId}). */\n id?: string;\n email?: string;\n name?: string;\n /** Provider-supplied profile picture URL, when the auth provider exposes one. */\n avatarUrl?: string;\n /**\n * Organization id. The org is the top-level tenant: it owns the GitHub\n * App installation and connected projects, while each user inside the org gets\n * isolated building instances. Absent for personal (no-org) accounts.\n */\n organizationId?: string;\n}\n\n/**\n * Tenant identity: the org is the top-level tenant, and each user inside it is\n * an isolated builder. Agent state, worktrees and sandboxes are scoped per\n * `(orgId, userId)`. Personal (no-org) users have `orgId === undefined`.\n */\nexport interface FactoryAuthTenant {\n /** Organization id, or `undefined` for personal (no-org) accounts. */\n orgId?: string;\n /** Stable provider user id. */\n userId: string;\n}\n\n/**\n * Validate that a `returnTo` value is a safe same-site path, to prevent\n * open-redirect attacks. Only absolute local paths (`/foo`) are allowed;\n * protocol-relative (`//evil.com`) and absolute URLs are rejected.\n */\nexport function sanitizeReturnTo(raw: string | undefined): string {\n if (!raw) return '/';\n if (!raw.startsWith('/')) return '/';\n // Reject protocol-relative URLs like \"//evil.com\" and \"/\\evil.com\".\n if (raw.startsWith('//') || raw.startsWith('/\\\\')) return '/';\n return raw;\n}\n\n/** Extract a bearer token from the Authorization header, if present. */\nexport function getBearerToken(authorization: string | undefined): string {\n if (!authorization) return '';\n const match = /^Bearer\\s+(.+)$/i.exec(authorization);\n return match?.[1] ?? '';\n}\n\n/**\n * Whether the SPA is served cross-origin from this API (platform deploy). When\n * `MASTRACODE_ALLOWED_ORIGINS` is set the browser talks to us cross-site, so\n * session cookies must be `SameSite=None; Secure` for the browser to send them.\n * Same-origin local dev leaves this unset and keeps the stricter `SameSite=Lax`.\n */\nexport function isCrossSiteAuth(): boolean {\n return Boolean(process.env.MASTRACODE_ALLOWED_ORIGINS?.trim());\n}\n\n/** Hono context variables set by the auth gate. */\nexport interface FactoryAuthVariables {\n factoryAuthUser: FactoryAuthUser;\n}\n\n/** Context key under which the gate stashes the authenticated user. */\nconst FACTORY_AUTH_USER_KEY = 'factoryAuthUser';\n\n/**\n * Read the authenticated user the gate stashed on the context, or\n * `undefined` when unauthenticated / auth disabled. Used by downstream routes\n * (e.g. GitHub) to scope rows per user.\n */\nexport function getFactoryAuthUser(c: Context): FactoryAuthUser | undefined {\n return c.get(FACTORY_AUTH_USER_KEY) as FactoryAuthUser | undefined;\n}\n\n/**\n * Read the authenticated user off a request context, normalizing whatever the\n * active auth provider put there.\n *\n * The server's auth layer writes the provider's `authenticateToken` result into\n * the request context's `user` slot verbatim, so the value's shape follows the\n * provider: WorkOS writes a flat user, better-auth writes a `{ session, user }`\n * wrapper whose org lives on the session. Reading that slot as a\n * {@link FactoryAuthUser} therefore yields `undefined` for both the id and the\n * org under better-auth, which reads as \"this session belongs to somebody else\"\n * at every ownership check. Normalize on the way in instead.\n */\nexport function getFactoryAuthUserFromContext(\n requestContext: { get: (key: string) => unknown } | undefined,\n): FactoryAuthUser | undefined {\n if (!requestContext || typeof requestContext.get !== 'function') return undefined;\n return toFactoryAuthUser(requestContext.get('user')) ?? undefined;\n}\n\n/** Resolve the stable user id from an authenticated user shape. */\nexport function getFactoryAuthUserId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.workosId ?? user?.id;\n}\n\n/** Resolve the organization id from a user shape, if present. */\nexport function getFactoryAuthOrgId(user: FactoryAuthUser | undefined): string | undefined {\n return user?.organizationId;\n}\n\n/**\n * Resolve the tenant identity `(orgId, userId)` from the authenticated user on\n * the context. Returns `undefined` when there is no signed-in user (auth\n * disabled or unauthenticated). `orgId` is `undefined` for personal accounts;\n * callers gate org-scoped GitHub features on its presence while agent state\n * falls back to a user-only tenant.\n */\nexport function factoryAuthTenant(c: Context): FactoryAuthTenant | undefined {\n const user = getFactoryAuthUser(c);\n const userId = getFactoryAuthUserId(user);\n if (!userId) return undefined;\n return { orgId: getFactoryAuthOrgId(user), userId };\n}\n\n/** True when both WorkOS credential env vars are present (legacy env gate). */\nfunction envWorkosConfigured(): boolean {\n return Boolean(process.env.WORKOS_API_KEY && process.env.WORKOS_CLIENT_ID);\n}\n\n/**\n * WorkOS provider implied by the `WORKOS_*` env vars — back-compat for test\n * suites exercised without booting the factory (route suites set `WORKOS_*`\n * directly and call {@link mountFactoryAuth} without an explicit provider).\n * `fetchMemberships: true` lets `authenticateToken` resolve `organizationId`\n * from a single membership when the JWT has no org claim — required so a\n * bootstrapped personal org resolves without re-auth.\n */\nfunction envFallbackAuthProvider(redirectUri: string | undefined): MastraAuthWorkos | undefined {\n if (!envWorkosConfigured()) return undefined;\n return new MastraAuthWorkos({\n redirectUri: redirectUri ?? process.env.WORKOS_REDIRECT_URI,\n fetchMemberships: true,\n });\n}\n\n/**\n * Map a provider `authenticateToken` result onto the neutral SPA user shape.\n *\n * Two result families exist today:\n * - flat provider users (WorkOS `WorkOSUser` et al.): `id`/`workosId`/`email`/\n * `name`/`organizationId` directly on the object;\n * - session-shaped results (better-auth `BetterAuthUser`): `{ session, user }`\n * with the active org on the session.\n */\nfunction toFactoryAuthUser(result: unknown): FactoryAuthUser | null {\n if (!result || typeof result !== 'object') return null;\n const record = result as Record<string, unknown>;\n\n // Session-shaped results: { session, user }. A result carrying both halves and\n // top-level identity fields is read as session-shaped: the session half is the\n // authenticated one, and preferring it keeps the org and the id from coming\n // from two different places.\n if (record.user && typeof record.user === 'object' && record.session && typeof record.session === 'object') {\n const user = record.user as { id?: unknown; email?: unknown; name?: unknown; avatarUrl?: unknown };\n const session = record.session as { activeOrganizationId?: unknown };\n if (typeof user.id !== 'string') return null;\n return {\n id: user.id,\n email: typeof user.email === 'string' ? user.email : undefined,\n name: typeof user.name === 'string' ? user.name : undefined,\n avatarUrl: typeof user.avatarUrl === 'string' ? user.avatarUrl : undefined,\n organizationId: typeof session.activeOrganizationId === 'string' ? session.activeOrganizationId : undefined,\n };\n }\n\n // Flat provider users.\n const flat = record as {\n id?: unknown;\n workosId?: unknown;\n email?: unknown;\n name?: unknown;\n avatarUrl?: unknown;\n organizationId?: unknown;\n };\n const id = typeof flat.id === 'string' ? flat.id : undefined;\n const workosId = typeof flat.workosId === 'string' ? flat.workosId : undefined;\n if (!id && !workosId) return null;\n return {\n id,\n workosId,\n email: typeof flat.email === 'string' ? flat.email : undefined,\n name: typeof flat.name === 'string' ? flat.name : undefined,\n avatarUrl: typeof flat.avatarUrl === 'string' ? flat.avatarUrl : undefined,\n organizationId: typeof flat.organizationId === 'string' ? flat.organizationId : undefined,\n };\n}\n\n/**\n * Resolve the authenticated user for a request via the provider. Never throws:\n * ordinary invalid/expired sessions resolve to `null`.\n */\nasync function authenticateRequest(\n provider: IMastraAuthProvider,\n token: string,\n raw: Request,\n): Promise<FactoryAuthUser | null> {\n try {\n const result = await provider.authenticateToken(token, raw);\n return toFactoryAuthUser(result);\n } catch {\n return null;\n }\n}\n\n/**\n * Bootstrap a personal org for no-org accounts so org-scoped features (GitHub\n * connect) work without leaving the app. Mutates the resolved user so the\n * current request sees the org immediately; subsequent requests resolve it via\n * the provider's own session/membership lookup (providers cache internally).\n * Best-effort: providers swallow their own bootstrap failures, and any\n * unexpected throw leaves the user no-org.\n */\nasync function ensureUserOrg(provider: IMastraAuthProvider, user: FactoryAuthUser): Promise<void> {\n if (getFactoryAuthOrgId(user)) return;\n if (!isOrganizationsProvider(provider)) return;\n const userId = getFactoryAuthUserId(user);\n if (!userId) return;\n try {\n const orgId = await provider.ensureOrganization(userId);\n if (orgId) user.organizationId = orgId;\n } catch {\n // Best-effort: the user stays no-org until a later request succeeds.\n }\n}\n\n/**\n * `Set-Cookie` values that clear the provider's session cookie(s), from the\n * provider's (possibly partial) `ISessionProvider.getClearSessionHeaders`.\n */\nfunction providerClearCookies(provider: IMastraAuthProvider): string[] {\n const getClearSessionHeaders = (provider as Partial<ISessionProvider>).getClearSessionHeaders;\n if (typeof getClearSessionHeaders !== 'function') return [];\n const headers = getClearSessionHeaders.call(provider) ?? {};\n const setCookie = headers['Set-Cookie'];\n if (!setCookie) return [];\n // A provider may join several clearing cookies into one header value.\n return setCookie.split(/,(?=\\s*[^;=,\\s]+=)/).map(cookie => cookie.trim());\n}\n\n/**\n * Fail-closed authorization for organization-level administrative mutations.\n * The caller must belong to the same active organization and the provider must\n * explicitly confirm an admin/owner role.\n */\nexport async function isOrganizationAdmin(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n organizationId: string,\n): Promise<boolean> {\n const user = await ensureFactoryAuthUser(provider, c);\n if (!user || user.organizationId !== organizationId || !provider || !isOrganizationsProvider(provider)) {\n return false;\n }\n const userId = getFactoryAuthUserId(user);\n if (!userId) return false;\n try {\n return await provider.isOrganizationAdmin(organizationId, userId);\n } catch {\n return false;\n }\n}\n\n/**\n * Build the factory's implementation of the `RouteAuth` seam over the\n * resolved provider (`undefined` = auth disabled). Constructed once per boot\n * by `MastraFactory.prepare()` and handed to factory route modules at\n * construction — they never import the factory auth module directly.\n */\nexport function createFactoryRouteAuth(provider: IMastraAuthProvider | undefined): RouteAuth {\n return {\n enabled: () => provider !== undefined,\n ensureUser: (c: Context) => ensureFactoryAuthUser(provider, c),\n tenant: (c: Context) => factoryAuthTenant(c),\n isOrganizationAdmin: (c: Context, organizationId: string) => isOrganizationAdmin(provider, c, organizationId),\n };\n}\n\n/** True when the given provider is WorkOS. Gates WorkOS-only capabilities. */\nexport function isWorkOSAuth(provider: IMastraAuthProvider | undefined): boolean {\n return provider instanceof MastraAuthWorkos;\n}\n\n/**\n * The raw WorkOS provider, for features that need the WorkOS client directly\n * (audit-log export, Admin Portal links). Callers must gate on\n * {@link isWorkOSAuth} first — throws when the provider is not WorkOS.\n */\nexport function getWorkOSProvider(provider: IMastraAuthProvider | undefined): MastraAuthWorkos {\n if (provider instanceof MastraAuthWorkos) return provider;\n throw new Error('WorkOS provider requested but the active factory auth provider is not WorkOS');\n}\n\n/**\n * Resolve the authenticated user for a request, stashing it on the context.\n *\n * The gate only authenticates non-`/auth/*` requests via the `Authorization`\n * header, so cookie-based browser navigations to public `/auth/*` routes (the\n * GitHub connect/callback flow) arrive without a gate-stashed user. This reads\n * the session cookie from the raw request the same way `/auth/me` does,\n * caches the result on the context, and returns it so downstream helpers like\n * {@link factoryAuthTenant} work uniformly on both gated and public routes.\n *\n * Returns `undefined` when there is no valid session (or auth is disabled).\n */\nexport async function ensureFactoryAuthUser(\n provider: IMastraAuthProvider | undefined,\n c: Context,\n): Promise<FactoryAuthUser | undefined> {\n const existing = getFactoryAuthUser(c);\n if (existing) return existing;\n if (!provider) return undefined;\n\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n if (!user) return undefined;\n\n await ensureUserOrg(provider, user);\n\n c.set(FACTORY_AUTH_USER_KEY, user);\n return user;\n}\n\nexport interface MountFactoryAuthOptions {\n /**\n * Explicit auth provider to mount. When omitted, falls back to a WorkOS\n * provider implied by the `WORKOS_*` env vars (back-compat for suites that\n * never boot the factory).\n */\n provider?: IMastraAuthProvider;\n /**\n * Absolute URL the identity provider redirects back to after login (WorkOS\n * env-fallback path only). Defaults to the `WORKOS_REDIRECT_URI` env var.\n */\n redirectUri?: string;\n /** Browser-facing origin used to derive the SSO callback URL. */\n publicUrl?: string;\n}\n\n/**\n * Decide whether a request is a top-level browser navigation (which should be\n * redirected to `/signin`) versus an API/XHR call (which should get a 401 JSON\n * response the SPA can react to).\n */\nfunction isNavigationRequest(path: string, accept: string | undefined): boolean {\n if (path.startsWith('/api/')) return false;\n return (accept ?? '').includes('text/html');\n}\n\n/**\n * Handle the provider-neutral `/auth/me` route: validate the session with the\n * active provider and report the signed-in user (no tokens) to the SPA.\n * `/auth/me` is public (the gate skips `/auth/*`), so it validates the session\n * itself rather than reading a value the gate would have stashed.\n */\nasync function handleAuthMe(provider: IMastraAuthProvider, c: Context): Promise<Response> {\n const token = getBearerToken(c.req.header('Authorization'));\n const user = await authenticateRequest(provider, token, c.req.raw);\n // Provider identity for the SPA: `/signin` renders the hosted-login button\n // for WorkOS and an email/password form for better-auth (with sign-up hidden\n // when the provider disables it).\n const signUpDisabled = isCredentialsProvider(provider) && provider.isSignUpEnabled?.() === false;\n const meta = { provider: provider.name, ...(signUpDisabled ? { signUpDisabled: true } : {}) };\n if (!user) {\n return c.json({ authenticated: false, user: null, ...meta });\n }\n // Resolve the org the same way gated requests do (providers cache, so this\n // is a lookup — not a create — after first bootstrap).\n await ensureUserOrg(provider, user);\n return c.json({\n authenticated: true,\n user: {\n userId: getFactoryAuthUserId(user),\n email: user.email,\n name: user.name,\n organizationId: user.organizationId,\n },\n ...meta,\n });\n}\n\n/**\n * Encode a validated returnTo path into the OAuth `state` parameter.\n *\n * Pipe format (`uuid|encodedPath`) is the contract `MastraAuthStudio` parses\n * to forward the path as the platform's `post_login_redirect`; a JSON blob\n * here silently degrades every post-login redirect to `/`.\n */\nfunction encodeState(returnTo: string): string {\n return `${crypto.randomUUID()}|${encodeURIComponent(returnTo)}`;\n}\n\n/** Decode the OAuth `state` parameter back into a sanitized returnTo path. */\nfunction decodeState(state: string | undefined): string {\n if (!state) return '/';\n const pipeIndex = state.indexOf('|');\n if (pipeIndex !== -1) {\n try {\n return sanitizeReturnTo(decodeURIComponent(state.slice(pipeIndex + 1)));\n } catch {\n return '/';\n }\n }\n return '/';\n}\n\n/**\n * Short-lived cookie stashing the post-login destination across the hosted\n * OAuth round-trip. Providers/platforms differ in whether they echo `state`\n * back to the callback, so the cookie is the reliable channel; `state` (when\n * echoed) takes precedence only if the cookie is missing.\n */\nconst RETURN_TO_COOKIE = 'mastra_factory_return_to';\n\nfunction returnToCookieHeader(returnTo: string): string {\n const crossSite = isCrossSiteAuth() ? '; SameSite=None; Secure' : '; SameSite=Lax';\n return `${RETURN_TO_COOKIE}=${encodeURIComponent(returnTo)}; Path=/; Max-Age=600; HttpOnly${crossSite}`;\n}\n\nfunction clearReturnToCookieHeader(): string {\n const crossSite = isCrossSiteAuth() ? '; SameSite=None; Secure' : '; SameSite=Lax';\n return `${RETURN_TO_COOKIE}=; Path=/; Max-Age=0; HttpOnly${crossSite}`;\n}\n\nfunction readReturnToCookie(c: Context): string | undefined {\n const header = c.req.header('Cookie');\n if (!header) return undefined;\n for (const part of header.split(';')) {\n const [name, ...rest] = part.trim().split('=');\n if (name === RETURN_TO_COOKIE) {\n try {\n return decodeURIComponent(rest.join('='));\n } catch {\n return undefined;\n }\n }\n }\n return undefined;\n}\n\n/** HTTP methods supported for public auth routes. */\ntype AuthRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'ALL';\n\n/** A public `/auth/*` route derived from the provider's capabilities. */\ninterface AuthRouteSpec {\n path: string;\n method: AuthRouteMethod;\n handler: (c: Context) => Response | Promise<Response>;\n}\n\n/**\n * Derive the public `/auth/*` routes from the provider's capabilities:\n *\n * - `IAuthHttpHandler` → `ALL /auth/api/*` proxy to the provider's own HTTP\n * surface (better-auth sign-in/up/out/session — what the SPA's\n * email/password form posts to).\n * - `ISSOProvider` → hosted-login `GET /auth/login` / `GET /auth/callback` /\n * `GET /auth/logout` (returnTo preserved through the OAuth `state` param).\n * - handler-shaped, non-SSO providers → `GET /auth/login` redirects to the\n * SPA's `/signin` form, `GET /auth/logout` revokes via the provider's\n * sign-out endpoint and clears the session cookie.\n */\nfunction providerAuthRoutes(provider: IMastraAuthProvider, publicUrl?: string): AuthRouteSpec[] {\n const routes: AuthRouteSpec[] = [];\n\n if (isAuthHttpHandler(provider)) {\n routes.push({\n path: '/auth/api/*',\n method: 'ALL',\n handler: c => provider.handleAuthRequest(c.req.raw),\n });\n }\n\n if (isSSOProvider(provider)) {\n routes.push(\n {\n path: '/auth/login',\n method: 'GET',\n handler: async c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n const state = encodeState(returnTo);\n // Build the callback URL from the browser-facing public origin so\n // the OAuth round-trip lands back on the SPA's origin (in dev the\n // SPA is on :5173 and Vite proxies /auth/* to the API on :4111 —\n // deriving from c.req.url would use :4111 and the post-callback\n // redirect to `/` would miss the SPA). Providers that ignore the\n // caller's URI in favor of their own config (e.g. MastraAuthWorkos\n // with an explicit `redirectUri` option) still take precedence.\n const redirectUri = publicUrl ? new URL('/auth/callback', publicUrl).toString() : '';\n const loginUrl = await provider.getLoginUrl(redirectUri, state);\n for (const cookie of (await provider.getLoginCookies?.(redirectUri, state)) ?? []) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n // Stash the destination in a cookie too: not every provider/platform\n // echoes `state` back to the callback.\n if (returnTo !== '/') {\n c.header('Set-Cookie', returnToCookieHeader(returnTo), { append: true });\n }\n return c.redirect(loginUrl);\n },\n },\n {\n path: '/auth/callback',\n method: 'GET',\n handler: async c => {\n const code = c.req.query('code');\n const stateReturnTo = decodeState(c.req.query('state'));\n const cookieReturnTo = sanitizeReturnTo(readReturnToCookie(c));\n const returnTo = cookieReturnTo !== '/' ? cookieReturnTo : stateReturnTo;\n c.header('Set-Cookie', clearReturnToCookieHeader(), { append: true });\n const idpError = c.req.query('error');\n if (idpError) {\n // IdP denial (e.g. access_denied for a non-org-member): bouncing to\n // /auth/login would re-enter the IdP in a redirect loop.\n const query = new URLSearchParams({ error: idpError.slice(0, 64) });\n const description = c.req.query('error_description');\n if (description) query.set('error_description', description.slice(0, 256));\n if (returnTo !== '/') query.set('returnTo', returnTo);\n return c.redirect(`/signin?${query.toString()}`);\n }\n if (!code) {\n return c.redirect('/auth/login');\n }\n try {\n const result = await provider.handleCallback(code, c.req.query('state') ?? '');\n if (result.cookies?.length) {\n // Provider populated cookies directly (e.g. WorkOS AuthKit builds\n // its own sealed session cookie inside handleCallback).\n for (const cookie of result.cookies) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } else if (isSessionProvider(provider) && result.tokens) {\n // Fallback for providers that expose ISessionProvider but leave\n // cookie construction to the server (e.g. MastraAuthStudio, which\n // returns just the sealed session as accessToken so\n // getSessionHeaders can scope the cookie to this deployment's\n // domain via MASTRA_COOKIE_DOMAIN / sharedApiUrl auto-detection).\n // Mirrors packages/server/src/server/handlers/auth.ts:492-503.\n const resultUser = result.user as { id: string; organizationId?: string };\n const session = await provider.createSession(resultUser.id, {\n accessToken: result.tokens.accessToken,\n refreshToken: result.tokens.refreshToken,\n expiresAt: result.tokens.expiresAt,\n organizationId: resultUser.organizationId,\n });\n for (const [key, value] of Object.entries(provider.getSessionHeaders(session))) {\n c.header(key, value, { append: true });\n }\n }\n return c.redirect(returnTo);\n } catch {\n // Code exchange failed (expired/replayed code, misconfig). Send the\n // user back to login rather than surfacing a raw error.\n return c.redirect('/auth/login');\n }\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n let logoutUrl: string | null = null;\n try {\n logoutUrl = (await provider.getLogoutUrl?.('/', c.req.raw)) ?? null;\n } catch {\n logoutUrl = null;\n }\n // Clear the session cookie regardless of whether the provider\n // returned a logout URL.\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect(logoutUrl ?? '/');\n },\n },\n );\n } else if (isAuthHttpHandler(provider)) {\n routes.push(\n {\n // Hosted-login equivalent: no hosted page, so send the browser to the\n // SPA's /signin form, preserving returnTo.\n path: '/auth/login',\n method: 'GET',\n handler: c => {\n const returnTo = sanitizeReturnTo(c.req.query('returnTo'));\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n },\n },\n {\n path: '/auth/logout',\n method: 'GET',\n handler: async c => {\n // Revoke the session server-side through the provider's own sign-out\n // endpoint and forward its clearing cookies; fall back to our clear\n // cookies regardless.\n try {\n const origin = new URL(c.req.url).origin;\n const response = await provider.handleAuthRequest(\n new Request(`${origin}/auth/api/sign-out`, { method: 'POST', headers: c.req.raw.headers }),\n );\n for (const cookie of response.headers.getSetCookie()) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n } catch {\n // No/invalid session: nothing to revoke.\n }\n for (const cookie of providerClearCookies(provider)) {\n c.header('Set-Cookie', cookie, { append: true });\n }\n return c.redirect('/');\n },\n },\n );\n }\n\n return routes;\n}\n\n/**\n * Register the public `/auth/*` routes on a Hono app: the capability-derived\n * provider routes (login/callback/logout/provider APIs) plus the\n * provider-neutral `/auth/me`. Split out from `mountFactoryAuth` so both the local\n * Hono server and the platform Mastra entry can reuse the exact same handlers.\n */\nexport function registerAuthRoutes(\n app: Hono<any>,\n provider: IMastraAuthProvider,\n options: { publicUrl?: string } = {},\n): void {\n for (const route of providerAuthRoutes(provider, options.publicUrl)) {\n const methods = route.method === 'ALL' ? ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] : [route.method];\n app.on(methods, route.path, c => route.handler(c));\n }\n app.get('/auth/me', c => handleAuthMe(provider, c));\n}\n\n/**\n * Build the public `/auth/*` routes (provider routes + `/auth/me`) as Mastra\n * `server.apiRoutes`. Used by the platform Mastra entry (`src/mastra/index.ts`),\n * which can't register plain Hono routes on the deployer-generated app the way\n * the local server does via {@link registerAuthRoutes}.\n *\n * Handlers are identical to {@link registerAuthRoutes}. All are `requiresAuth: false`\n * (they must be reachable while unauthenticated), and the gate middleware skips\n * `/auth/*` so it never blocks them. `/auth/*` is not under `/api`, so it is a\n * valid custom-route path.\n */\nexport function buildAuthRoutes(provider: IMastraAuthProvider, options: { publicUrl?: string } = {}): ApiRoute[] {\n return [\n // `registerApiRoute` handlers see @mastra/core's bundled hono Context type,\n // which is structurally identical to (but nominally distinct from) the\n // local hono version the route handlers are typed against — cast across\n // the seam.\n ...providerAuthRoutes(provider, options.publicUrl).map(route =>\n registerApiRoute(route.path, {\n method: route.method,\n requiresAuth: false,\n handler: c => route.handler(c as unknown as Context),\n }),\n ),\n registerApiRoute('/auth/me', {\n method: 'GET',\n requiresAuth: false,\n handler: c => handleAuthMe(provider, c as unknown as Context),\n }),\n ];\n}\n\n/**\n * Channel webhook paths whose adapter verifies the platform's request signature,\n * making the delivery self-authenticating. Add a platform here only once its\n * adapter rejects unsigned or mis-signed requests.\n */\nconst SIGNATURE_VERIFYING_CHANNEL_WEBHOOK = /^\\/api\\/agent-controllers\\/[^/]+\\/channels\\/slack\\/webhook$/;\n\n// Fetched by tabs that may already be signed out. Enumerated, not prefix-matched,\n// so a future route under the same prefix does not inherit the pass.\nconst SESSION_FAVICON_PATHS = new Set([\n '/favicon-session-initializing.svg',\n '/favicon-session-working.svg',\n '/favicon-session-awaiting.svg',\n '/favicon-session-error.svg',\n]);\n\n/**\n * Build the auth gate as a plain Hono middleware handler `(c, next)`. Protects\n * everything that is not a public `/auth/*` route: authenticated requests stash\n * the user on the context and continue; unauthenticated navigations redirect to\n * login and XHR/API calls get a 401 JSON. Shared by the local Hono server\n * (`mountFactoryAuth`) and the platform Mastra entry (`server.middleware`).\n */\nexport function createFactoryAuthGate(provider: IMastraAuthProvider) {\n return async (c: Context, next: () => Promise<void>): Promise<Response | void> => {\n const path = c.req.path;\n if (path.startsWith('/auth/')) {\n return next();\n }\n if (c.req.method === 'POST' && path === '/web/github/webhook') {\n return next();\n }\n // Inbound chat-channel webhooks (Slack events) carry no user session: they\n // authenticate by platform signature, the adapter verifying the request\n // against its signing secret. The routes declare `requiresAuth: false`, but\n // this gate is `use()` middleware — it runs before route matching, so that\n // metadata is not readable here and the path needs an explicit pass.\n //\n // The platform is allowlisted rather than matched as a wildcard: a pass\n // keyed on path shape alone would silently extend to any future adapter,\n // including one that does not verify signatures. Controller id stays a\n // wildcard because it is whatever the host registered.\n if (c.req.method === 'POST' && SIGNATURE_VERIFYING_CHANNEL_WEBHOOK.test(path)) {\n return next();\n }\n // The Slack account-linking deep link and the Sign-in-with-Slack OIDC\n // start/callback do their own auth (friendly login-redirect for signed-out\n // visitors; the OIDC callback authenticates via its signed `state`) — see\n // connect-route.ts.\n if (c.req.method === 'GET' && (path === '/connect/slack' || path.startsWith('/connect/slack/'))) {\n return next();\n }\n // The platform's deploy-auth flow lands IdP denials on `/login`\n // (`error=access_denied&error_description=...`); the SPA serves sign-in at\n // `/signin`, so forward the query there instead of burying it in returnTo.\n if (c.req.method === 'GET' && path === '/login') {\n return c.redirect(`/signin${new URL(c.req.url).search}`);\n }\n // The SPA sign-in page, its static bundle, and browser-fetched metadata\n // must be reachable while signed out; no user is stashed, so `/api/*`\n // stays protected.\n if (\n path === '/signin' ||\n path.startsWith('/assets/') ||\n path === '/manifest.webmanifest' ||\n path === '/mastra.svg' ||\n path === '/pwa-192.png' ||\n path === '/pwa-512.png' ||\n path === '/apple-touch-icon.png' ||\n (c.req.method === 'GET' && SESSION_FAVICON_PATHS.has(path))\n ) {\n return next();\n }\n\n const token = getBearerToken(c.req.header('Authorization'));\n // A slow verification here delays EVERY protected request — surface\n // outliers so auth-backend latency is attributable from server logs.\n const user = await timedAboveThreshold('auth.gate.authenticate', 1_000, () =>\n authenticateRequest(provider, token, c.req.raw),\n );\n\n if (user) {\n // Bootstrap a personal org for no-org accounts so the org id resolves on\n // this request (see ensureFactoryAuthUser for the rationale).\n await ensureUserOrg(provider, user);\n c.set(FACTORY_AUTH_USER_KEY, user);\n c.get('requestContext')?.set('user', user);\n return next();\n }\n\n if (isNavigationRequest(path, c.req.header('Accept'))) {\n const url = new URL(c.req.url);\n const returnTo = sanitizeReturnTo(url.pathname + url.search);\n return c.redirect(`/signin?returnTo=${encodeURIComponent(returnTo)}`);\n }\n\n return c.json({ error: 'unauthorized' }, 401);\n };\n}\n\n/**\n * Mount factory auth gating onto the host app. No-op when auth is disabled\n * (no provider active).\n *\n * Must be called before the Mastra adapter routes, the `/web/*` routes, and\n * the static UI handlers so the gate covers every request. Composes the shared\n * `registerAuthRoutes` + `createFactoryAuthGate` factories so the local Hono server\n * and the platform Mastra entry stay behavior-identical.\n */\nexport function mountFactoryAuth(app: Hono<any>, options: MountFactoryAuthOptions = {}): boolean {\n const provider = options.provider ?? envFallbackAuthProvider(options.redirectUri);\n if (!provider) return false;\n\n registerAuthRoutes(app, provider, { publicUrl: options.publicUrl });\n app.use('*', createFactoryAuthGate(provider));\n return true;\n}\n"],"mappings":";;;;;;;;;AA0EA,SAAgB,iBAAiB,KAAiC;CAChE,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI,CAAC,IAAI,WAAW,GAAG,GAAG,OAAO;CAEjC,IAAI,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,KAAK,GAAG,OAAO;CAC1D,OAAO;AACT;;AAGA,SAAgB,eAAe,eAA2C;CACxE,IAAI,CAAC,eAAe,OAAO;CAE3B,OADc,mBAAmB,KAAK,aAC3B,CAAC,GAAG,MAAM;AACvB;;;;;;;AAQA,SAAgB,kBAA2B;CACzC,OAAO,QAAQ,QAAQ,IAAI,4BAA4B,KAAK,CAAC;AAC/D;;AAQA,MAAM,wBAAwB;;;;;;AAO9B,SAAgB,mBAAmB,GAAyC;CAC1E,OAAO,EAAE,IAAI,qBAAqB;AACpC;;;;;;;;;;;;;AAcA,SAAgB,8BACd,gBAC6B;CAC7B,IAAI,CAAC,kBAAkB,OAAO,eAAe,QAAQ,YAAY,OAAO,KAAA;CACxE,OAAO,kBAAkB,eAAe,IAAI,MAAM,CAAC,KAAK,KAAA;AAC1D;;AAGA,SAAgB,qBAAqB,MAAuD;CAC1F,OAAO,MAAM,YAAY,MAAM;AACjC;;AAGA,SAAgB,oBAAoB,MAAuD;CACzF,OAAO,MAAM;AACf;;;;;;;;AASA,SAAgB,kBAAkB,GAA2C;CAC3E,MAAM,OAAO,mBAAmB,CAAC;CACjC,MAAM,SAAS,qBAAqB,IAAI;CACxC,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,OAAO;EAAE,OAAO,oBAAoB,IAAI;EAAG;CAAO;AACpD;;AAGA,SAAS,sBAA+B;CACtC,OAAO,QAAQ,QAAQ,IAAI,kBAAkB,QAAQ,IAAI,gBAAgB;AAC3E;;;;;;;;;AAUA,SAAS,wBAAwB,aAA+D;CAC9F,IAAI,CAAC,oBAAoB,GAAG,OAAO,KAAA;CACnC,OAAO,IAAI,iBAAiB;EAC1B,aAAa,eAAe,QAAQ,IAAI;EACxC,kBAAkB;CACpB,CAAC;AACH;;;;;;;;;;AAWA,SAAS,kBAAkB,QAAyC;CAClE,IAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO;CAClD,MAAM,SAAS;CAMf,IAAI,OAAO,QAAQ,OAAO,OAAO,SAAS,YAAY,OAAO,WAAW,OAAO,OAAO,YAAY,UAAU;EAC1G,MAAM,OAAO,OAAO;EACpB,MAAM,UAAU,OAAO;EACvB,IAAI,OAAO,KAAK,OAAO,UAAU,OAAO;EACxC,OAAO;GACL,IAAI,KAAK;GACT,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;GACrD,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;GAClD,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY,KAAA;GACjE,gBAAgB,OAAO,QAAQ,yBAAyB,WAAW,QAAQ,uBAAuB,KAAA;EACpG;CACF;CAGA,MAAM,OAAO;CAQb,MAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,KAAA;CACnD,MAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,KAAA;CACrE,IAAI,CAAC,MAAM,CAAC,UAAU,OAAO;CAC7B,OAAO;EACL;EACA;EACA,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;EACrD,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;EAClD,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY,KAAA;EACjE,gBAAgB,OAAO,KAAK,mBAAmB,WAAW,KAAK,iBAAiB,KAAA;CAClF;AACF;;;;;AAMA,eAAe,oBACb,UACA,OACA,KACiC;CACjC,IAAI;EAEF,OAAO,kBAAkB,MADJ,SAAS,kBAAkB,OAAO,GAAG,CAC3B;CACjC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;AAUA,eAAe,cAAc,UAA+B,MAAsC;CAChG,IAAI,oBAAoB,IAAI,GAAG;CAC/B,IAAI,CAAC,wBAAwB,QAAQ,GAAG;CACxC,MAAM,SAAS,qBAAqB,IAAI;CACxC,IAAI,CAAC,QAAQ;CACb,IAAI;EACF,MAAM,QAAQ,MAAM,SAAS,mBAAmB,MAAM;EACtD,IAAI,OAAO,KAAK,iBAAiB;CACnC,QAAQ,CAER;AACF;;;;;AAMA,SAAS,qBAAqB,UAAyC;CACrE,MAAM,yBAA0B,SAAuC;CACvE,IAAI,OAAO,2BAA2B,YAAY,OAAO,CAAC;CAE1D,MAAM,aADU,uBAAuB,KAAK,QAAQ,KAAK,CAAC,EAAA,CAChC;CAC1B,IAAI,CAAC,WAAW,OAAO,CAAC;CAExB,OAAO,UAAU,MAAM,oBAAoB,CAAC,CAAC,KAAI,WAAU,OAAO,KAAK,CAAC;AAC1E;;;;;;AAOA,eAAsB,oBACpB,UACA,GACA,gBACkB;CAClB,MAAM,OAAO,MAAM,sBAAsB,UAAU,CAAC;CACpD,IAAI,CAAC,QAAQ,KAAK,mBAAmB,kBAAkB,CAAC,YAAY,CAAC,wBAAwB,QAAQ,GACnG,OAAO;CAET,MAAM,SAAS,qBAAqB,IAAI;CACxC,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI;EACF,OAAO,MAAM,SAAS,oBAAoB,gBAAgB,MAAM;CAClE,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;AAQA,SAAgB,uBAAuB,UAAsD;CAC3F,OAAO;EACL,eAAe,aAAa,KAAA;EAC5B,aAAa,MAAe,sBAAsB,UAAU,CAAC;EAC7D,SAAS,MAAe,kBAAkB,CAAC;EAC3C,sBAAsB,GAAY,mBAA2B,oBAAoB,UAAU,GAAG,cAAc;CAC9G;AACF;;AAGA,SAAgB,aAAa,UAAoD;CAC/E,OAAO,oBAAoB;AAC7B;;;;;;AAOA,SAAgB,kBAAkB,UAA6D;CAC7F,IAAI,oBAAoB,kBAAkB,OAAO;CACjD,MAAM,IAAI,MAAM,8EAA8E;AAChG;;;;;;;;;;;;;AAcA,eAAsB,sBACpB,UACA,GACsC;CACtC,MAAM,WAAW,mBAAmB,CAAC;CACrC,IAAI,UAAU,OAAO;CACrB,IAAI,CAAC,UAAU,OAAO,KAAA;CAGtB,MAAM,OAAO,MAAM,oBAAoB,UADzB,eAAe,EAAE,IAAI,OAAO,eAAe,CACJ,GAAG,EAAE,IAAI,GAAG;CACjE,IAAI,CAAC,MAAM,OAAO,KAAA;CAElB,MAAM,cAAc,UAAU,IAAI;CAElC,EAAE,IAAI,uBAAuB,IAAI;CACjC,OAAO;AACT;;;;;;AAuBA,SAAS,oBAAoB,MAAc,QAAqC;CAC9E,IAAI,KAAK,WAAW,OAAO,GAAG,OAAO;CACrC,QAAQ,UAAU,GAAA,CAAI,SAAS,WAAW;AAC5C;;;;;;;AAQA,eAAe,aAAa,UAA+B,GAA+B;CAExF,MAAM,OAAO,MAAM,oBAAoB,UADzB,eAAe,EAAE,IAAI,OAAO,eAAe,CACJ,GAAG,EAAE,IAAI,GAAG;CAIjE,MAAM,iBAAiB,sBAAsB,QAAQ,KAAK,SAAS,kBAAkB,MAAM;CAC3F,MAAM,OAAO;EAAE,UAAU,SAAS;EAAM,GAAI,iBAAiB,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAAG;CAC5F,IAAI,CAAC,MACH,OAAO,EAAE,KAAK;EAAE,eAAe;EAAO,MAAM;EAAM,GAAG;CAAK,CAAC;CAI7D,MAAM,cAAc,UAAU,IAAI;CAClC,OAAO,EAAE,KAAK;EACZ,eAAe;EACf,MAAM;GACJ,QAAQ,qBAAqB,IAAI;GACjC,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,gBAAgB,KAAK;EACvB;EACA,GAAG;CACL,CAAC;AACH;;;;;;;;AASA,SAAS,YAAY,UAA0B;CAC7C,OAAO,GAAG,OAAO,WAAW,EAAE,GAAG,mBAAmB,QAAQ;AAC9D;;AAGA,SAAS,YAAY,OAAmC;CACtD,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,YAAY,MAAM,QAAQ,GAAG;CACnC,IAAI,cAAc,IAChB,IAAI;EACF,OAAO,iBAAiB,mBAAmB,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC;CACxE,QAAQ;EACN,OAAO;CACT;CAEF,OAAO;AACT;;;;;;;AAQA,MAAM,mBAAmB;AAEzB,SAAS,qBAAqB,UAA0B;CACtD,MAAM,YAAY,gBAAgB,IAAI,4BAA4B;CAClE,OAAO,GAAG,iBAAiB,GAAG,mBAAmB,QAAQ,EAAE,iCAAiC;AAC9F;AAEA,SAAS,4BAAoC;CAC3C,MAAM,YAAY,gBAAgB,IAAI,4BAA4B;CAClE,OAAO,GAAG,iBAAiB,gCAAgC;AAC7D;AAEA,SAAS,mBAAmB,GAAgC;CAC1D,MAAM,SAAS,EAAE,IAAI,OAAO,QAAQ;CACpC,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,KAAK,MAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;EACpC,MAAM,CAAC,MAAM,GAAG,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG;EAC7C,IAAI,SAAS,kBACX,IAAI;GACF,OAAO,mBAAmB,KAAK,KAAK,GAAG,CAAC;EAC1C,QAAQ;GACN;EACF;CAEJ;AAEF;;;;;;;;;;;;;AAwBA,SAAS,mBAAmB,UAA+B,WAAqC;CAC9F,MAAM,SAA0B,CAAC;CAEjC,IAAI,kBAAkB,QAAQ,GAC5B,OAAO,KAAK;EACV,MAAM;EACN,QAAQ;EACR,UAAS,MAAK,SAAS,kBAAkB,EAAE,IAAI,GAAG;CACpD,CAAC;CAGH,IAAI,cAAc,QAAQ,GACxB,OAAO,KACL;EACE,MAAM;EACN,QAAQ;EACR,SAAS,OAAM,MAAK;GAClB,MAAM,WAAW,iBAAiB,EAAE,IAAI,MAAM,UAAU,CAAC;GACzD,MAAM,QAAQ,YAAY,QAAQ;GAQlC,MAAM,cAAc,YAAY,IAAI,IAAI,kBAAkB,SAAS,CAAC,CAAC,SAAS,IAAI;GAClF,MAAM,WAAW,MAAM,SAAS,YAAY,aAAa,KAAK;GAC9D,KAAK,MAAM,UAAW,MAAM,SAAS,kBAAkB,aAAa,KAAK,KAAM,CAAC,GAC9E,EAAE,OAAO,cAAc,QAAQ,EAAE,QAAQ,KAAK,CAAC;GAIjD,IAAI,aAAa,KACf,EAAE,OAAO,cAAc,qBAAqB,QAAQ,GAAG,EAAE,QAAQ,KAAK,CAAC;GAEzE,OAAO,EAAE,SAAS,QAAQ;EAC5B;CACF,GACA;EACE,MAAM;EACN,QAAQ;EACR,SAAS,OAAM,MAAK;GAClB,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM;GAC/B,MAAM,gBAAgB,YAAY,EAAE,IAAI,MAAM,OAAO,CAAC;GACtD,MAAM,iBAAiB,iBAAiB,mBAAmB,CAAC,CAAC;GAC7D,MAAM,WAAW,mBAAmB,MAAM,iBAAiB;GAC3D,EAAE,OAAO,cAAc,0BAA0B,GAAG,EAAE,QAAQ,KAAK,CAAC;GACpE,MAAM,WAAW,EAAE,IAAI,MAAM,OAAO;GACpC,IAAI,UAAU;IAGZ,MAAM,QAAQ,IAAI,gBAAgB,EAAE,OAAO,SAAS,MAAM,GAAG,EAAE,EAAE,CAAC;IAClE,MAAM,cAAc,EAAE,IAAI,MAAM,mBAAmB;IACnD,IAAI,aAAa,MAAM,IAAI,qBAAqB,YAAY,MAAM,GAAG,GAAG,CAAC;IACzE,IAAI,aAAa,KAAK,MAAM,IAAI,YAAY,QAAQ;IACpD,OAAO,EAAE,SAAS,WAAW,MAAM,SAAS,GAAG;GACjD;GACA,IAAI,CAAC,MACH,OAAO,EAAE,SAAS,aAAa;GAEjC,IAAI;IACF,MAAM,SAAS,MAAM,SAAS,eAAe,MAAM,EAAE,IAAI,MAAM,OAAO,KAAK,EAAE;IAC7E,IAAI,OAAO,SAAS,QAGlB,KAAK,MAAM,UAAU,OAAO,SAC1B,EAAE,OAAO,cAAc,QAAQ,EAAE,QAAQ,KAAK,CAAC;SAE5C,IAAI,kBAAkB,QAAQ,KAAK,OAAO,QAAQ;KAOvD,MAAM,aAAa,OAAO;KAC1B,MAAM,UAAU,MAAM,SAAS,cAAc,WAAW,IAAI;MAC1D,aAAa,OAAO,OAAO;MAC3B,cAAc,OAAO,OAAO;MAC5B,WAAW,OAAO,OAAO;MACzB,gBAAgB,WAAW;KAC7B,CAAC;KACD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,kBAAkB,OAAO,CAAC,GAC3E,EAAE,OAAO,KAAK,OAAO,EAAE,QAAQ,KAAK,CAAC;IAEzC;IACA,OAAO,EAAE,SAAS,QAAQ;GAC5B,QAAQ;IAGN,OAAO,EAAE,SAAS,aAAa;GACjC;EACF;CACF,GACA;EACE,MAAM;EACN,QAAQ;EACR,SAAS,OAAM,MAAK;GAClB,IAAI,YAA2B;GAC/B,IAAI;IACF,YAAa,MAAM,SAAS,eAAe,KAAK,EAAE,IAAI,GAAG,KAAM;GACjE,QAAQ;IACN,YAAY;GACd;GAGA,KAAK,MAAM,UAAU,qBAAqB,QAAQ,GAChD,EAAE,OAAO,cAAc,QAAQ,EAAE,QAAQ,KAAK,CAAC;GAEjD,OAAO,EAAE,SAAS,aAAa,GAAG;EACpC;CACF,CACF;MACK,IAAI,kBAAkB,QAAQ,GACnC,OAAO,KACL;EAGE,MAAM;EACN,QAAQ;EACR,UAAS,MAAK;GACZ,MAAM,WAAW,iBAAiB,EAAE,IAAI,MAAM,UAAU,CAAC;GACzD,OAAO,EAAE,SAAS,oBAAoB,mBAAmB,QAAQ,GAAG;EACtE;CACF,GACA;EACE,MAAM;EACN,QAAQ;EACR,SAAS,OAAM,MAAK;GAIlB,IAAI;IACF,MAAM,SAAS,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC;IAClC,MAAM,WAAW,MAAM,SAAS,kBAC9B,IAAI,QAAQ,GAAG,OAAO,qBAAqB;KAAE,QAAQ;KAAQ,SAAS,EAAE,IAAI,IAAI;IAAQ,CAAC,CAC3F;IACA,KAAK,MAAM,UAAU,SAAS,QAAQ,aAAa,GACjD,EAAE,OAAO,cAAc,QAAQ,EAAE,QAAQ,KAAK,CAAC;GAEnD,QAAQ,CAER;GACA,KAAK,MAAM,UAAU,qBAAqB,QAAQ,GAChD,EAAE,OAAO,cAAc,QAAQ,EAAE,QAAQ,KAAK,CAAC;GAEjD,OAAO,EAAE,SAAS,GAAG;EACvB;CACF,CACF;CAGF,OAAO;AACT;;;;;;;AAQA,SAAgB,mBACd,KACA,UACA,UAAkC,CAAC,GAC7B;CACN,KAAK,MAAM,SAAS,mBAAmB,UAAU,QAAQ,SAAS,GAAG;EACnE,MAAM,UAAU,MAAM,WAAW,QAAQ;GAAC;GAAO;GAAQ;GAAO;GAAU;EAAO,IAAI,CAAC,MAAM,MAAM;EAClG,IAAI,GAAG,SAAS,MAAM,OAAM,MAAK,MAAM,QAAQ,CAAC,CAAC;CACnD;CACA,IAAI,IAAI,aAAY,MAAK,aAAa,UAAU,CAAC,CAAC;AACpD;;;;;;;;;;;;AAaA,SAAgB,gBAAgB,UAA+B,UAAkC,CAAC,GAAe;CAC/G,OAAO,CAKL,GAAG,mBAAmB,UAAU,QAAQ,SAAS,CAAC,CAAC,KAAI,UACrD,iBAAiB,MAAM,MAAM;EAC3B,QAAQ,MAAM;EACd,cAAc;EACd,UAAS,MAAK,MAAM,QAAQ,CAAuB;CACrD,CAAC,CACH,GACA,iBAAiB,YAAY;EAC3B,QAAQ;EACR,cAAc;EACd,UAAS,MAAK,aAAa,UAAU,CAAuB;CAC9D,CAAC,CACH;AACF;;;;;;AAOA,MAAM,sCAAsC;AAI5C,MAAM,wCAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;AACF,CAAC;;;;;;;;AASD,SAAgB,sBAAsB,UAA+B;CACnE,OAAO,OAAO,GAAY,SAAwD;EAChF,MAAM,OAAO,EAAE,IAAI;EACnB,IAAI,KAAK,WAAW,QAAQ,GAC1B,OAAO,KAAK;EAEd,IAAI,EAAE,IAAI,WAAW,UAAU,SAAS,uBACtC,OAAO,KAAK;EAYd,IAAI,EAAE,IAAI,WAAW,UAAU,oCAAoC,KAAK,IAAI,GAC1E,OAAO,KAAK;EAMd,IAAI,EAAE,IAAI,WAAW,UAAU,SAAS,oBAAoB,KAAK,WAAW,iBAAiB,IAC3F,OAAO,KAAK;EAKd,IAAI,EAAE,IAAI,WAAW,SAAS,SAAS,UACrC,OAAO,EAAE,SAAS,UAAU,IAAI,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ;EAKzD,IACE,SAAS,aACT,KAAK,WAAW,UAAU,KAC1B,SAAS,2BACT,SAAS,iBACT,SAAS,kBACT,SAAS,kBACT,SAAS,2BACR,EAAE,IAAI,WAAW,SAAS,sBAAsB,IAAI,IAAI,GAEzD,OAAO,KAAK;EAGd,MAAM,QAAQ,eAAe,EAAE,IAAI,OAAO,eAAe,CAAC;EAG1D,MAAM,OAAO,MAAM,oBAAoB,0BAA0B,WAC/D,oBAAoB,UAAU,OAAO,EAAE,IAAI,GAAG,CAChD;EAEA,IAAI,MAAM;GAGR,MAAM,cAAc,UAAU,IAAI;GAClC,EAAE,IAAI,uBAAuB,IAAI;GACjC,EAAE,IAAI,gBAAgB,CAAC,EAAE,IAAI,QAAQ,IAAI;GACzC,OAAO,KAAK;EACd;EAEA,IAAI,oBAAoB,MAAM,EAAE,IAAI,OAAO,QAAQ,CAAC,GAAG;GACrD,MAAM,MAAM,IAAI,IAAI,EAAE,IAAI,GAAG;GAC7B,MAAM,WAAW,iBAAiB,IAAI,WAAW,IAAI,MAAM;GAC3D,OAAO,EAAE,SAAS,oBAAoB,mBAAmB,QAAQ,GAAG;EACtE;EAEA,OAAO,EAAE,KAAK,EAAE,OAAO,eAAe,GAAG,GAAG;CAC9C;AACF;;;;;;;;;;AAWA,SAAgB,iBAAiB,KAAgB,UAAmC,CAAC,GAAY;CAC/F,MAAM,WAAW,QAAQ,YAAY,wBAAwB,QAAQ,WAAW;CAChF,IAAI,CAAC,UAAU,OAAO;CAEtB,mBAAmB,KAAK,UAAU,EAAE,WAAW,QAAQ,UAAU,CAAC;CAClE,IAAI,IAAI,KAAK,sBAAsB,QAAQ,CAAC;CAC5C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/routes/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;CACnD;AAYD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,KAAK,EAAE,YAAY,CAAC;IACpB,sCAAsC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;IAClF,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,UAAU,aAAa;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAWtE;AAaD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAyBpE;AAmBD,qBAAa,YAAa,SAAQ,KAAK,CAAC,gBAAgB,CAAC;;IAgBvD,MAAM,IAAI,QAAQ,EAAE;CAqKrB"}
1
+ {"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/routes/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC,CAAC;CACnD;AAYD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,KAAK,EAAE,YAAY,CAAC;IACpB,sCAAsC;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;KAAE,CAAC;IAClF,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,UAAU,aAAa;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAWtE;AAaD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,IAAI,CAyBpE;AAmBD,qBAAa,YAAa,SAAQ,KAAK,CAAC,gBAAgB,CAAC;;IAgBvD,MAAM,IAAI,QAAQ,EAAE;CAsLrB"}
@@ -164,15 +164,24 @@ var IntakeRoutes = class extends Route {
164
164
  })) return c.json({ error: "factory_project_not_found" }, 404);
165
165
  }
166
166
  await intake.ensureReady();
167
- await intake.setBinding({
167
+ let auditFactoryProjectId = binding.factoryProjectId;
168
+ if (binding.factoryProjectId === null) auditFactoryProjectId = (await intake.clearBinding({
169
+ orgId: tenant.orgId,
170
+ integrationId: binding.integrationId,
171
+ sourceId: binding.sourceId
172
+ }))?.factoryProjectId ?? null;
173
+ else await intake.setBinding({
168
174
  orgId: tenant.orgId,
169
175
  userId: tenant.userId,
170
- ...binding
176
+ integrationId: binding.integrationId,
177
+ sourceId: binding.sourceId,
178
+ factoryProjectId: binding.factoryProjectId
171
179
  });
172
180
  await audit.emit({
173
181
  context: loose(c),
174
182
  input: {
175
183
  action: "factory.intake.binding_updated",
184
+ ...auditFactoryProjectId ? { factoryProjectId: auditFactoryProjectId } : {},
176
185
  targets: [{
177
186
  type: "intake_source",
178
187
  id: `${binding.integrationId}:${binding.sourceId}`
@@ -1 +1 @@
1
- {"version":3,"file":"intake.js","names":["#resolveTenant"],"sources":["../../src/routes/intake.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { Intake, IntakeItem } from '../capabilities/intake.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { IntakeConfig, IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nexport interface IntakeIntegration {\n id: string;\n intake: Pick<Intake, 'listSources' | 'listItems'>;\n}\n\ninterface AggregatedIntakeItem extends Omit<IntakeItem, 'source'> {\n integrationId: string;\n externalSource: {\n integrationId: string;\n type: string;\n externalId: string;\n url?: string;\n };\n}\n\nexport interface IntakeRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Intake selection domain handle. */\n intake: IntakeStorage;\n /** Factory project domain handle, used to validate binding targets. */\n projects?: { get(input: { orgId: string; id: string }): Promise<unknown | null> };\n integrations?: IntakeIntegration[];\n}\n\ninterface ParsedBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string | null;\n}\n\n/** Validate a binding request body, rejecting unknown shapes. */\nexport function parseIntakeBinding(body: unknown): ParsedBinding | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const { integrationId, sourceId, factoryProjectId } = body as Record<string, unknown>;\n const isId = (value: unknown) => typeof value === 'string' && value.length > 0 && value.length <= 256;\n if (!isId(integrationId) || !isId(sourceId)) return null;\n if (factoryProjectId !== null && !isId(factoryProjectId)) return null;\n return {\n integrationId: integrationId as string,\n sourceId: sourceId as string,\n factoryProjectId: factoryProjectId as string | null,\n };\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction sanitizeIdList(value: unknown): string[] | null | undefined {\n if (value === null) return null;\n if (!Array.isArray(value) || value.length > 200) return undefined;\n const ids = value.filter((item): item is string => typeof item === 'string' && item.length > 0 && item.length <= 256);\n return ids.length === value.length && new Set(ids).size === ids.length ? ids : undefined;\n}\n\n/** Validate a request body into an intake config, rejecting unknown shapes. */\nexport function parseIntakeConfig(body: unknown): IntakeConfig | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const entries = Object.entries(body);\n if (entries.length > 50) return null;\n\n // Null-prototype so an `__proto__` key lands as a real entry instead of silently\n // reassigning the prototype and disappearing from the validation below.\n const config: IntakeConfig = Object.create(null);\n for (const [integrationId, value] of entries) {\n if (\n !integrationId ||\n integrationId.length > 128 ||\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value)\n ) {\n return null;\n }\n const selection = value as { enabled?: unknown; sourceIds?: unknown };\n if (typeof selection.enabled !== 'boolean') return null;\n const sourceIds = sanitizeIdList(selection.sourceIds ?? null);\n if (sourceIds === undefined) return null;\n config[integrationId] = { enabled: selection.enabled, sourceIds };\n }\n return config;\n}\n\nfunction encodeCursor(cursors: Record<string, string>): string | null {\n return Object.keys(cursors).length > 0 ? Buffer.from(JSON.stringify(cursors)).toString('base64url') : null;\n}\n\nfunction decodeCursor(value: string | undefined): Record<string, string> | null {\n if (!value) return {};\n try {\n const parsed = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const entries = Object.entries(parsed);\n if (entries.some(([key, cursor]) => !key || typeof cursor !== 'string')) return null;\n return Object.fromEntries(entries) as Record<string, string>;\n } catch {\n return null;\n }\n}\n\nexport class IntakeRoutes extends Route<IntakeRoutesDeps> {\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'Intake configuration requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n const { audit, intake, projects, integrations = [] } = this.deps;\n const integrationIds = integrations.map(integration => integration.id);\n\n return [\n registerApiRoute('/web/intake/config', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n return c.json({ config });\n },\n }),\n registerApiRoute('/web/intake/config', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const config = parseIntakeConfig(body);\n if (!config) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n\n const registeredConfig: IntakeConfig = Object.create(null);\n for (const [integrationId, selection] of Object.entries(config)) {\n if (integrationIds.includes(integrationId)) {\n registeredConfig[integrationId] = selection;\n continue;\n }\n if (selection.enabled || selection.sourceIds?.length) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n }\n\n await intake.ensureReady();\n await intake.saveConfig({ ...tenant, config: registeredConfig });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: [{ type: 'intake_config', id: tenant.orgId }],\n metadata: Object.fromEntries(\n Object.entries(registeredConfig).map(([integrationId, selection]) => [\n integrationId,\n { enabled: selection.enabled, sources: selection.sourceIds?.length ?? null },\n ]),\n ),\n },\n });\n return c.json({ config: registeredConfig });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const binding = parseIntakeBinding(body);\n if (!binding || !integrationIds.includes(binding.integrationId)) {\n return c.json({ error: 'invalid_binding' }, 400);\n }\n if (binding.factoryProjectId && projects) {\n const project = await projects.get({ orgId: tenant.orgId, id: binding.factoryProjectId });\n if (!project) return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n await intake.setBinding({ orgId: tenant.orgId, userId: tenant.userId, ...binding });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.binding_updated',\n targets: [{ type: 'intake_source', id: `${binding.integrationId}:${binding.sourceId}` }],\n metadata: { factoryProjectId: binding.factoryProjectId },\n },\n });\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/sources', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const pages = await Promise.all(\n integrations.map(async integration => ({\n integrationId: integration.id,\n sources: await integration.intake.listSources(tenant),\n })),\n );\n return c.json({\n sources: pages.flatMap(page =>\n page.sources.map(source => ({ integrationId: page.integrationId, ...source })),\n ),\n });\n },\n }),\n registerApiRoute('/web/intake/items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const cursors = decodeCursor(c.req.query('cursor'));\n if (!cursors) return c.json({ error: 'invalid_cursor' }, 400);\n\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n const items: AggregatedIntakeItem[] = [];\n const nextCursors: Record<string, string> = {};\n for (const integration of integrations) {\n const selection = config[integration.id];\n if (!selection?.enabled || !selection.sourceIds?.length) continue;\n const page = await integration.intake.listItems({\n ...tenant,\n sourceIds: selection.sourceIds,\n ...(cursors[integration.id] ? { cursor: cursors[integration.id] } : {}),\n });\n items.push(\n ...page.items.map(item => {\n const { source, ...candidate } = item;\n return {\n ...candidate,\n integrationId: integration.id,\n externalSource: { integrationId: integration.id, ...source },\n };\n }),\n );\n if (page.nextCursor) nextCursors[integration.id] = page.nextCursor;\n }\n return c.json({ items, nextCursor: encodeCursor(nextCursors) });\n },\n }),\n ];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"intake.js","names":["#resolveTenant"],"sources":["../../src/routes/intake.ts"],"sourcesContent":["import type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { Intake, IntakeItem } from '../capabilities/intake.js';\nimport type { AuditEmitter } from '../storage/domains/audit/domain.js';\nimport type { IntakeConfig, IntakeStorage } from '../storage/domains/intake/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\nexport interface IntakeIntegration {\n id: string;\n intake: Pick<Intake, 'listSources' | 'listItems'>;\n}\n\ninterface AggregatedIntakeItem extends Omit<IntakeItem, 'source'> {\n integrationId: string;\n externalSource: {\n integrationId: string;\n type: string;\n externalId: string;\n url?: string;\n };\n}\n\nexport interface IntakeRoutesDeps extends RouteDependencies {\n audit: AuditEmitter;\n /** Intake selection domain handle. */\n intake: IntakeStorage;\n /** Factory project domain handle, used to validate binding targets. */\n projects?: { get(input: { orgId: string; id: string }): Promise<unknown | null> };\n integrations?: IntakeIntegration[];\n}\n\ninterface ParsedBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string | null;\n}\n\n/** Validate a binding request body, rejecting unknown shapes. */\nexport function parseIntakeBinding(body: unknown): ParsedBinding | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const { integrationId, sourceId, factoryProjectId } = body as Record<string, unknown>;\n const isId = (value: unknown) => typeof value === 'string' && value.length > 0 && value.length <= 256;\n if (!isId(integrationId) || !isId(sourceId)) return null;\n if (factoryProjectId !== null && !isId(factoryProjectId)) return null;\n return {\n integrationId: integrationId as string,\n sourceId: sourceId as string,\n factoryProjectId: factoryProjectId as string | null,\n };\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction sanitizeIdList(value: unknown): string[] | null | undefined {\n if (value === null) return null;\n if (!Array.isArray(value) || value.length > 200) return undefined;\n const ids = value.filter((item): item is string => typeof item === 'string' && item.length > 0 && item.length <= 256);\n return ids.length === value.length && new Set(ids).size === ids.length ? ids : undefined;\n}\n\n/** Validate a request body into an intake config, rejecting unknown shapes. */\nexport function parseIntakeConfig(body: unknown): IntakeConfig | null {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return null;\n const entries = Object.entries(body);\n if (entries.length > 50) return null;\n\n // Null-prototype so an `__proto__` key lands as a real entry instead of silently\n // reassigning the prototype and disappearing from the validation below.\n const config: IntakeConfig = Object.create(null);\n for (const [integrationId, value] of entries) {\n if (\n !integrationId ||\n integrationId.length > 128 ||\n typeof value !== 'object' ||\n value === null ||\n Array.isArray(value)\n ) {\n return null;\n }\n const selection = value as { enabled?: unknown; sourceIds?: unknown };\n if (typeof selection.enabled !== 'boolean') return null;\n const sourceIds = sanitizeIdList(selection.sourceIds ?? null);\n if (sourceIds === undefined) return null;\n config[integrationId] = { enabled: selection.enabled, sourceIds };\n }\n return config;\n}\n\nfunction encodeCursor(cursors: Record<string, string>): string | null {\n return Object.keys(cursors).length > 0 ? Buffer.from(JSON.stringify(cursors)).toString('base64url') : null;\n}\n\nfunction decodeCursor(value: string | undefined): Record<string, string> | null {\n if (!value) return {};\n try {\n const parsed = JSON.parse(Buffer.from(value, 'base64url').toString('utf8')) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const entries = Object.entries(parsed);\n if (entries.some(([key, cursor]) => !key || typeof cursor !== 'string')) return null;\n return Object.fromEntries(entries) as Record<string, string>;\n } catch {\n return null;\n }\n}\n\nexport class IntakeRoutes extends Route<IntakeRoutesDeps> {\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'Intake configuration requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n routes(): ApiRoute[] {\n const { audit, intake, projects, integrations = [] } = this.deps;\n const integrationIds = integrations.map(integration => integration.id);\n\n return [\n registerApiRoute('/web/intake/config', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n return c.json({ config });\n },\n }),\n registerApiRoute('/web/intake/config', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const config = parseIntakeConfig(body);\n if (!config) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n\n const registeredConfig: IntakeConfig = Object.create(null);\n for (const [integrationId, selection] of Object.entries(config)) {\n if (integrationIds.includes(integrationId)) {\n registeredConfig[integrationId] = selection;\n continue;\n }\n if (selection.enabled || selection.sourceIds?.length) {\n return c.json({ error: 'invalid_config' }, 400);\n }\n }\n\n await intake.ensureReady();\n await intake.saveConfig({ ...tenant, config: registeredConfig });\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.config_updated',\n targets: [{ type: 'intake_config', id: tenant.orgId }],\n metadata: Object.fromEntries(\n Object.entries(registeredConfig).map(([integrationId, selection]) => [\n integrationId,\n { enabled: selection.enabled, sources: selection.sourceIds?.length ?? null },\n ]),\n ),\n },\n });\n return c.json({ config: registeredConfig });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n await intake.ensureReady();\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/bindings', {\n method: 'PUT',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n\n let body: unknown;\n try {\n body = await c.req.json();\n } catch {\n return c.json({ error: 'Invalid JSON body' }, 400);\n }\n const binding = parseIntakeBinding(body);\n if (!binding || !integrationIds.includes(binding.integrationId)) {\n return c.json({ error: 'invalid_binding' }, 400);\n }\n if (binding.factoryProjectId && projects) {\n const project = await projects.get({ orgId: tenant.orgId, id: binding.factoryProjectId });\n if (!project) return c.json({ error: 'factory_project_not_found' }, 404);\n }\n\n await intake.ensureReady();\n let auditFactoryProjectId = binding.factoryProjectId;\n if (binding.factoryProjectId === null) {\n const previousBinding = await intake.clearBinding({\n orgId: tenant.orgId,\n integrationId: binding.integrationId,\n sourceId: binding.sourceId,\n });\n auditFactoryProjectId = previousBinding?.factoryProjectId ?? null;\n } else {\n await intake.setBinding({\n orgId: tenant.orgId,\n userId: tenant.userId,\n integrationId: binding.integrationId,\n sourceId: binding.sourceId,\n factoryProjectId: binding.factoryProjectId,\n });\n }\n await audit.emit({\n context: loose(c),\n input: {\n action: 'factory.intake.binding_updated',\n ...(auditFactoryProjectId ? { factoryProjectId: auditFactoryProjectId } : {}),\n targets: [{ type: 'intake_source', id: `${binding.integrationId}:${binding.sourceId}` }],\n metadata: { factoryProjectId: binding.factoryProjectId },\n },\n });\n return c.json({ bindings: await intake.listBindings({ orgId: tenant.orgId }) });\n },\n }),\n registerApiRoute('/web/intake/sources', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const pages = await Promise.all(\n integrations.map(async integration => ({\n integrationId: integration.id,\n sources: await integration.intake.listSources(tenant),\n })),\n );\n return c.json({\n sources: pages.flatMap(page =>\n page.sources.map(source => ({ integrationId: page.integrationId, ...source })),\n ),\n });\n },\n }),\n registerApiRoute('/web/intake/items', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const tenant = await this.#resolveTenant(loose(c));\n if ('response' in tenant) return tenant.response;\n const cursors = decodeCursor(c.req.query('cursor'));\n if (!cursors) return c.json({ error: 'invalid_cursor' }, 400);\n\n await intake.ensureReady();\n const config = await intake.getConfig({ ...tenant, integrationIds });\n const items: AggregatedIntakeItem[] = [];\n const nextCursors: Record<string, string> = {};\n for (const integration of integrations) {\n const selection = config[integration.id];\n if (!selection?.enabled || !selection.sourceIds?.length) continue;\n const page = await integration.intake.listItems({\n ...tenant,\n sourceIds: selection.sourceIds,\n ...(cursors[integration.id] ? { cursor: cursors[integration.id] } : {}),\n });\n items.push(\n ...page.items.map(item => {\n const { source, ...candidate } = item;\n return {\n ...candidate,\n integrationId: integration.id,\n externalSource: { integrationId: integration.id, ...source },\n };\n }),\n );\n if (page.nextCursor) nextCursors[integration.id] = page.nextCursor;\n }\n return c.json({ items, nextCursor: encodeCursor(nextCursors) });\n },\n }),\n ];\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/factory",
3
- "version": "0.9.0",
3
+ "version": "0.10.0-alpha.2",
4
4
  "description": "Mastra Software Factory module: the server core behind the Mastra Software Factory — storage domains, integrations, and surfaces for agent-powered software delivery",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -52,10 +52,10 @@
52
52
  "hono": "^4.12.8",
53
53
  "zod": "^4.3.6",
54
54
  "@mastra/auth-studio": "1.3.4",
55
- "@mastra/code-sdk": "1.4.0",
56
- "@mastra/auth-workos": "1.6.4",
55
+ "@mastra/code-sdk": "1.4.1-alpha.2",
56
+ "@mastra/core": "1.62.0-alpha.2",
57
57
  "@mastra/slack": "1.6.1",
58
- "@mastra/core": "1.61.0"
58
+ "@mastra/auth-workos": "1.6.4"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/node": "22.20.1",
@@ -66,7 +66,7 @@
66
66
  "vitest": "4.1.10",
67
67
  "@internal/lint": "0.0.125",
68
68
  "@mastra/libsql": "1.21.1",
69
- "@mastra/pg": "1.21.1",
69
+ "@mastra/pg": "1.22.0-alpha.0",
70
70
  "@internal/types-builder": "0.0.100"
71
71
  },
72
72
  "engines": {