Assistant MCP tests (#36049)

* feat: refactor and test mcp and model logic

* fix: remove get_project tool

* fix: remove additional get_project tool references
This commit is contained in:
Greg Richardson authored and GitHub committed 2025-06-02 16:26:30 -06:00
1 parent b85a2e79df
commit c5e35e0e38
15 files changed
+1155 -829

No files matched your search

+13 -9
View File
@@ -2,8 +2,16 @@ import { useDisallowHipaa } from 'hooks/misc/useDisallowHipaa'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { OPT_IN_TAGS } from 'lib/constants'
import { IS_PLATFORM } from 'lib/constants'
import { z } from 'zod'
export type AiOptInLevel = 'disabled' | 'schema' | 'schema_and_log' | 'schema_and_log_and_data'
export const aiOptInLevelSchema = z.enum([
'disabled',
'schema',
'schema_and_log',
'schema_and_log_and_data',
])
export type AiOptInLevel = z.infer<typeof aiOptInLevelSchema>
// Exported helper function
export const getAiOptInLevel = (tags: string[] | undefined): AiOptInLevel => {
@@ -11,19 +19,15 @@ export const getAiOptInLevel = (tags: string[] | undefined): AiOptInLevel => {
const hasData = tags?.includes(OPT_IN_TAGS.AI_DATA)
const hasLog = tags?.includes(OPT_IN_TAGS.AI_LOG)
let level: AiOptInLevel
if (hasData) {
level = 'schema_and_log_and_data'
return 'schema_and_log_and_data'
} else if (hasLog) {
level = 'schema_and_log'
return 'schema_and_log'
} else if (hasSql) {
level = 'schema'
return 'schema'
} else {
level = 'disabled'
return 'disabled'
}
return level
}
/**
+17
View File
@@ -0,0 +1,17 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
const credentialProvider = fromNodeProviderChain()
export const bedrock = createAmazonBedrock({
credentialProvider,
})
export async function checkAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
+72
View File
@@ -0,0 +1,72 @@
import { openai } from '@ai-sdk/openai'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as bedrockModule from './bedrock'
import { bedrock } from './bedrock'
import { getModel, modelsByProvider } from './model'
// Mock dependencies
vi.mock('@ai-sdk/openai', () => ({
openai: vi.fn(() => 'openai-model'),
}))
vi.mock('./bedrock', () => ({
bedrock: vi.fn(() => 'bedrock-model'),
checkAwsCredentials: vi.fn(),
}))
describe('getModel', () => {
const originalEnv = { ...process.env }
beforeEach(() => {
vi.resetAllMocks()
})
afterEach(() => {
process.env = { ...originalEnv }
})
it('should return bedrock model when AWS credentials are available and AWS_REGION is set', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true)
process.env.AWS_REGION = 'us-east-1'
const { model, error } = await getModel()
console.log('Model:', model, 'Error:', error)
expect(model).toEqual('bedrock-model')
expect(bedrock).toHaveBeenCalledWith(modelsByProvider.bedrock)
expect(error).toBeUndefined()
})
it('should return error when AWS credentials are available but AWS_REGION is not set', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(true)
delete process.env.AWS_REGION
const { error } = await getModel()
expect(error).toEqual(new Error('AWS_REGION is not set'))
})
it('should return OpenAI model when AWS credentials are not available but OPENAI_API_KEY is set', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false)
process.env.OPENAI_API_KEY = 'test-key'
const { model } = await getModel()
expect(model).toEqual('openai-model')
expect(openai).toHaveBeenCalledWith(modelsByProvider.openai)
})
it('should return error when neither AWS credentials nor OPENAI_API_KEY is available', async () => {
vi.mocked(bedrockModule.checkAwsCredentials).mockResolvedValue(false)
delete process.env.OPENAI_API_KEY
const { error } = await getModel()
expect(error).toEqual(
new Error(
'No valid AI model available. Please set up a local AWS profile (for Bedrock) or pass an OPENAI_API_KEY.'
)
)
})
})
+52
View File
@@ -0,0 +1,52 @@
import { openai } from '@ai-sdk/openai'
import { LanguageModel } from 'ai'
import { bedrock, checkAwsCredentials } from './bedrock'
export const modelsByProvider = {
bedrock: 'us.anthropic.claude-sonnet-4-20250514-v1:0',
openai: 'gpt-4.1-2025-04-14',
}
export type ModelSuccess = {
model: LanguageModel
error?: never
}
export type ModelError = {
model?: never
error: Error
}
export type ModelResponse = ModelSuccess | ModelError
/**
* Retrieves the appropriate AI model based on available credentials.
*/
export async function getModel(): Promise<ModelResponse> {
const hasAwsCredentials = await checkAwsCredentials()
const hasOpenAIKey = !!process.env.OPENAI_API_KEY
if (hasAwsCredentials) {
if (!process.env.AWS_REGION) {
return {
error: new Error('AWS_REGION is not set'),
}
}
return {
model: bedrock(modelsByProvider.bedrock),
}
}
if (hasOpenAIKey) {
return {
model: openai(modelsByProvider.openai),
}
}
return {
error: new Error(
'No valid AI model available. Please set up a local AWS profile (for Bedrock) or pass an OPENAI_API_KEY.'
),
}
}
+3 -2
View File
@@ -23,7 +23,8 @@
"build:deno-types": "tsx scripts/deno-types.ts"
},
"dependencies": {
"@ai-sdk/amazon-bedrock": "^2.2.8",
"@ai-sdk/amazon-bedrock": "^2.2.9",
"@ai-sdk/openai": "^1.3.22",
"@ai-sdk/react": "^1.2.12",
"@aws-sdk/credential-providers": "^3.804.0",
"@dagrejs/dagre": "^1.0.4",
@@ -60,7 +61,7 @@
"@vercel/flags": "^2.6.0",
"@vitejs/plugin-react": "^4.3.4",
"@zip.js/zip.js": "^2.7.29",
"ai": "^4.3.9",
"ai": "^4.3.16",
"ai-commands": "workspace:*",
"awesome-debounce-promise": "^2.1.0",
"common": "workspace:*",
+204 -231
View File
@@ -1,54 +1,19 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import pgMeta from '@supabase/pg-meta'
import { streamText } from 'ai'
import { IS_PLATFORM } from 'common'
import { source } from 'common-tags'
import { executeSql } from 'data/sql/execute-sql-query'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { queryPgMetaSelfHosted } from 'lib/self-hosted'
import { NextApiRequest, NextApiResponse } from 'next'
import { getTools } from '../sql/tools'
export const maxDuration = 30
const credentialProvider = fromNodeProviderChain()
const bedrock = createAmazonBedrock({
credentialProvider,
})
async function hasAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
const hasCredentials = await hasAwsCredentials()
const pgMetaSchemasList = pgMeta.schemas.list()
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.AWS_REGION) {
return new Response(
JSON.stringify({
error: 'AWS_REGION is not set',
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
)
}
if (!hasCredentials) {
return new Response(
JSON.stringify({
error:
'AWS credentials are not configured. Set up a local profile or add environment variables.',
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
)
}
const { method } = req
switch (method) {
@@ -72,6 +37,12 @@ export default wrapper
async function handlePost(req: NextApiRequest, res: NextApiResponse) {
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body
const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata
@@ -100,225 +71,226 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
: { result: [] }
const result = await streamText({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
model,
maxSteps: 5,
tools: getTools({ projectRef, connectionString, authorization, includeSchemaMetadata }),
system: `
VERY IMPORTANT RULES:
1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED TYPESCRIPT/JAVASCRIPT TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS.
2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool.
system: source`
VERY IMPORTANT RULES:
1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED TYPESCRIPT/JAVASCRIPT TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS.
2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool.
You are a Supabase Edge Functions expert helping a user edit their TypeScript/JavaScript code based on a selection and a prompt.
Your goal is to modify the selected code according to the user's prompt, using the available tools to understand the database schema if necessary.
You MUST respond ONLY with the modified code that should replace the user's selection. Do not explain the changes or the tool results in the final output.
You are a Supabase Edge Functions expert helping a user edit their TypeScript/JavaScript code based on a selection and a prompt.
Your goal is to modify the selected code according to the user's prompt, using the available tools to understand the database schema if necessary.
You MUST respond ONLY with the modified code that should replace the user's selection. Do not explain the changes or the tool results in the final output.
# Core Task: Modify Selected Code
- Focus solely on altering the provided TypeScript/JavaScript selection based on the user's instructions for a Supabase Edge Function.
- Use the \`getSchema\` tool if the function interacts with the database and you need to understand table structures or relationships.
# Core Task: Modify Selected Code
- Focus solely on altering the provided TypeScript/JavaScript selection based on the user's instructions for a Supabase Edge Function.
- Use the \`getSchema\` tool if the function interacts with the database and you need to understand table structures or relationships.
# Edge Function Guidelines:
You're an expert in writing TypeScript and Deno JavaScript runtime. Generate **high-quality Supabase Edge Functions** that adhere to the following best practices:
1. Try to use Web APIs and Deno's core APIs instead of external dependencies (eg: use fetch instead of Axios, use WebSockets API instead of node-ws)
2. Do NOT use bare specifiers when importing dependencies. If you need to use an external dependency, make sure it's prefixed with either \`npm:\` or \`jsr:\`. For example, \`@supabase/supabase-js\` should be written as \`npm:@supabase/supabase-js\`.
3. For external imports, always define a version. For example, \`npm:@express\` should be written as \`npm:express@4.18.2\`.
4. For external dependencies, importing via \`npm:\` and \`jsr:\` is preferred. Minimize the use of imports from \`@deno.land/x\` , \`esm.sh\` and \`@unpkg.com\` . If you have a package from one of those CDNs, you can replace the CDN hostname with \`npm:\` specifier.
5. You can also use Node built-in APIs. You will need to import them using \`node:\` specifier. For example, to import Node process: \`import process from "node:process"\`. Use Node APIs when you find gaps in Deno APIs.
6. Do NOT use \`import { serve } from "https://deno.land/std@0.168.0/http/server.ts"\`. Instead use the built-in \`Deno.serve\`.
7. Following environment variables (ie. secrets) are pre-populated in both local and hosted Supabase environments. Users don't need to manually set them:
* SUPABASE_URL
* SUPABASE_ANON_KEY
* SUPABASE_SERVICE_ROLE_KEY
* SUPABASE_DB_URL
8. To set other environment variables the user can go to project settings then edge functions to set them
9. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with \`/function-name\` so they are routed correctly.
10. File write operations are ONLY permitted on \`/tmp\` directory. You can use either Deno or Node File APIs.
11. Use \`EdgeRuntime.waitUntil(promise)\` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context.
# Edge Function Guidelines:
You're an expert in writing TypeScript and Deno JavaScript runtime. Generate **high-quality Supabase Edge Functions** that adhere to the following best practices:
1. Try to use Web APIs and Deno's core APIs instead of external dependencies (eg: use fetch instead of Axios, use WebSockets API instead of node-ws)
2. Do NOT use bare specifiers when importing dependencies. If you need to use an external dependency, make sure it's prefixed with either \`npm:\` or \`jsr:\`. For example, \`@supabase/supabase-js\` should be written as \`npm:@supabase/supabase-js\`.
3. For external imports, always define a version. For example, \`npm:@express\` should be written as \`npm:express@4.18.2\`.
4. For external dependencies, importing via \`npm:\` and \`jsr:\` is preferred. Minimize the use of imports from \`@deno.land/x\` , \`esm.sh\` and \`@unpkg.com\` . If you have a package from one of those CDNs, you can replace the CDN hostname with \`npm:\` specifier.
5. You can also use Node built-in APIs. You will need to import them using \`node:\` specifier. For example, to import Node process: \`import process from "node:process"\`. Use Node APIs when you find gaps in Deno APIs.
6. Do NOT use \`import { serve } from "https://deno.land/std@0.168.0/http/server.ts"\`. Instead use the built-in \`Deno.serve\`.
7. Following environment variables (ie. secrets) are pre-populated in both local and hosted Supabase environments. Users don't need to manually set them:
* SUPABASE_URL
* SUPABASE_ANON_KEY
* SUPABASE_SERVICE_ROLE_KEY
* SUPABASE_DB_URL
8. To set other environment variables the user can go to project settings then edge functions to set them
9. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with \`/function-name\` so they are routed correctly.
10. File write operations are ONLY permitted on \`/tmp\` directory. You can use either Deno or Node File APIs.
11. Use \`EdgeRuntime.waitUntil(promise)\` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context.
# Database Integration:
- Use the getSchema tool to understand the database structure when needed
- Reference existing tables and schemas to ensure edge functions work with the user's data model
- Use proper types that match the database schema
- When accessing the database:
- Use RLS policies appropriately for security
- Handle database errors gracefully
- Use efficient queries and proper indexing
- Consider rate limiting for resource-intensive operations
- Use connection pooling when appropriate
- Implement proper error handling for database operations
# Database Integration:
- Use the getSchema tool to understand the database structure when needed
- Reference existing tables and schemas to ensure edge functions work with the user's data model
- Use proper types that match the database schema
- When accessing the database:
- Use RLS policies appropriately for security
- Handle database errors gracefully
- Use efficient queries and proper indexing
- Consider rate limiting for resource-intensive operations
- Use connection pooling when appropriate
- Implement proper error handling for database operations
# Example Templates:
### Simple Hello World Function
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
interface reqPayload {
name: string;
}
console.info('server started');
Deno.serve(async (req: Request) => {
const { name }: reqPayload = await req.json();
const data = {
message: \`Hello \${name} from foo!\`,
};
return new Response(
JSON.stringify(data),
{ headers: { 'Content-Type': 'application/json', 'Connection': 'keep-alive' }}
);
});
\`\`\`
### Example Function using Node built-in API
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { randomBytes } from "node:crypto";
import { createServer } from "node:http";
import process from "node:process";
const generateRandomString = (length: number) => {
const buffer = randomBytes(length);
return buffer.toString('hex');
};
const randomString = generateRandomString(10);
console.log(randomString);
const server = createServer((req, res) => {
const message = \`Hello\`;
res.end(message);
});
server.listen(9999);
\`\`\`
### Using npm packages in Functions
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import express from "npm:express@4.18.2";
const app = express();
app.get(/(.*)/, (req, res) => {
res.send("Welcome to Supabase");
});
app.listen(8000);
\`\`\`
### Generate embeddings using built-in @Supabase.ai API
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
const model = new Supabase.ai.Session('gte-small');
Deno.serve(async (req: Request) => {
const params = new URL(req.url).searchParams;
const input = params.get('text');
const output = await model.run(input, { mean_pool: true, normalize: true });
return new Response(
JSON.stringify(output),
{
headers: {
'Content-Type': 'application/json',
'Connection': 'keep-alive',
},
},
);
});
\`\`\`
### Integrating with Supabase Auth
\`\`\`typescript
# Example Templates:
### Simple Hello World Function
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from 'jsr:@supabase/supabase-js@2'
import { corsHeaders } from '../_shared/cors.ts' // Assuming cors.ts is in a shared folder
interface reqPayload {
name: string;
}
console.log(\`Function "select-from-table-with-auth-rls" up and running!\`)
console.info('server started');
Deno.serve(async (req: Request) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
const { name }: reqPayload = await req.json();
const data = {
message: \`Hello \${name} from foo!\`,
};
try {
// Create a Supabase client with the Auth context of the logged in user.
const supabaseClient = createClient(
// Supabase API URL - env var exported by default.
Deno.env.get('SUPABASE_URL')!,
// Supabase API ANON KEY - env var exported by default.
Deno.env.get('SUPABASE_ANON_KEY')!,
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
)
return new Response(
JSON.stringify(data),
{ headers: { 'Content-Type': 'application/json', 'Connection': 'keep-alive' }}
);
});
\`\`\`
// First get the token from the Authorization header
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
throw new Error('Missing Authorization header')
### Example Function using Node built-in API
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { randomBytes } from "node:crypto";
import { createServer } from "node:http";
import process from "node:process";
const generateRandomString = (length: number) => {
const buffer = randomBytes(length);
return buffer.toString('hex');
};
const randomString = generateRandomString(10);
console.log(randomString);
const server = createServer((req, res) => {
const message = \`Hello\`;
res.end(message);
});
server.listen(9999);
\`\`\`
### Using npm packages in Functions
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import express from "npm:express@4.18.2";
const app = express();
app.get(/(.*)/, (req, res) => {
res.send("Welcome to Supabase");
});
app.listen(8000);
\`\`\`
### Generate embeddings using built-in @Supabase.ai API
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
const model = new Supabase.ai.Session('gte-small');
Deno.serve(async (req: Request) => {
const params = new URL(req.url).searchParams;
const input = params.get('text');
const output = await model.run(input, { mean_pool: true, normalize: true });
return new Response(
JSON.stringify(output),
{
headers: {
'Content-Type': 'application/json',
'Connection': 'keep-alive',
},
},
);
});
\`\`\`
### Integrating with Supabase Auth
\`\`\`typescript
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from 'jsr:@supabase/supabase-js@2'
import { corsHeaders } from '../_shared/cors.ts' // Assuming cors.ts is in a shared folder
console.log(\`Function "select-from-table-with-auth-rls" up and running!\`)
Deno.serve(async (req: Request) => {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
const token = authHeader.replace('Bearer ', '')
// Now we can get the session or user object
const {
data: { user }, error: userError
} = await supabaseClient.auth.getUser(token)
if (userError) throw userError
try {
// Create a Supabase client with the Auth context of the logged in user.
const supabaseClient = createClient(
// Supabase API URL - env var exported by default.
Deno.env.get('SUPABASE_URL')!,
// Supabase API ANON KEY - env var exported by default.
Deno.env.get('SUPABASE_ANON_KEY')!,
// Create client with Auth context of the user that called the function.
// This way your row-level-security (RLS) policies are applied.
{
global: {
headers: { Authorization: req.headers.get('Authorization')! },
},
}
)
// Example: Select data associated with the authenticated user
// Replace 'your_table' and 'user_id' with your actual table and column names
// const { data, error } = await supabaseClient.from('your_table').select('*').eq('user_id', user.id)
// if (error) throw error
// First get the token from the Authorization header
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
throw new Error('Missing Authorization header')
}
const token = authHeader.replace('Bearer ', '')
// Return some data (replace with your actual logic)
return new Response(JSON.stringify({ user/*, data*/ }), { // Uncomment data if you query
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 200,
})
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
})
}
})
// Now we can get the session or user object
const {
data: { user }, error: userError
} = await supabaseClient.auth.getUser(token)
if (userError) throw userError
// To invoke:
// curl -i --location --request POST 'http://localhost:54321/functions/v1/your-function-name' \\
// --header 'Authorization: Bearer <YOUR_USER_JWT>' \\
// --header 'Content-Type: application/json' \\
// --data '{"some":"payload"}' // Optional payload
\`\`\`
// Example: Select data associated with the authenticated user
// Replace 'your_table' and 'user_id' with your actual table and column names
// const { data, error } = await supabaseClient.from('your_table').select('*').eq('user_id', user.id)
// if (error) throw error
# Tool Usage:
- First look at the list of provided schemas if database interaction is needed.
- Use \`getSchema\` to understand the data model you're working with if the edge function needs to interact with user data.
- Check both the public and auth schemas to understand the authentication setup if relevant.
- The available database schema names are: ${schemas}
// Return some data (replace with your actual logic)
return new Response(JSON.stringify({ user/*, data*/ }), { // Uncomment data if you query
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 200,
})
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
})
}
})
# Response Format:
- Your response MUST be ONLY the modified TypeScript/JavaScript text intended to replace the user's selection.
- Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT.
- Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`).
- Avoid duplicating variable declarations, imports, or function definitions already present in the surrounding context.
- If there is no surrounding context (before or after), ensure your response is a complete, valid Deno Edge Function including necessary imports and setup.
// To invoke:
// curl -i --location --request POST 'http://localhost:54321/functions/v1/your-function-name' \\
// --header 'Authorization: Bearer <YOUR_USER_JWT>' \\
// --header 'Content-Type: application/json' \\
// --data '{"some":"payload"}' // Optional payload
\`\`\`
REMEMBER: ONLY OUTPUT THE CODE MODIFICATION.
# Tool Usage:
- First look at the list of provided schemas if database interaction is needed.
- Use \`getSchema\` to understand the data model you're working with if the edge function needs to interact with user data.
- Check both the public and auth schemas to understand the authentication setup if relevant.
- The available database schema names are: ${schemas}
# Response Format:
- Your response MUST be ONLY the modified TypeScript/JavaScript text intended to replace the user's selection.
- Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT.
- Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`).
- Avoid duplicating variable declarations, imports, or function definitions already present in the surrounding context.
- If there is no surrounding context (before or after), ensure your response is a complete, valid Deno Edge Function including necessary imports and setup.
REMEMBER: ONLY OUTPUT THE CODE MODIFICATION.
`,
messages: [
{
role: 'user',
content: `You are helping me write TypeScript/JavaScript code for an edge function.
content: source`
You are helping me write TypeScript/JavaScript code for an edge function.
Here is the context:
${textBeforeCursor}<selection>${selection}</selection>${textAfterCursor}
@@ -331,7 +303,8 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
6. Avoid duplicating variable declarations, imports, or function definitions when considering the full code
7. If there is no surrounding context (before or after), make sure your response is a complete valid Deno Edge Function including imports.
Modify the selected text now:`,
Modify the selected text now:
`,
},
],
})
+11 -12
View File
@@ -1,11 +1,10 @@
import { bedrock } from '@ai-sdk/amazon-bedrock'
import { streamText, tool } from 'ai'
import { source } from 'common-tags'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID
export const maxDuration = 30
const ServiceSchema = z.object({
@@ -47,12 +46,6 @@ const getTools = () => {
}
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!awsAccessKeyId) {
return res.status(400).json({
error: 'No AWS_ACCESS_KEY_ID set. Create this environment variable to use AI features.',
})
}
const { method } = req
switch (method) {
@@ -70,12 +63,18 @@ const wrapper = (req: NextApiRequest, res: NextApiResponse) =>
export default wrapper
async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { messages } = req.body
const result = await streamText({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
model,
maxSteps: 7,
system: `
system: source`
You are a Supabase expert who helps people set up their Supabase project. You specializes in database schema design. You are to help the user design a database schema for their application but also suggest Supabase services they should use.
When designing database schemas, follow these rules:
@@ -95,7 +94,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
4. Always respond with a short single paragraph of less than 80 words of what you changed and the current state of the schema.
If user requests to reset the database, call the reset tool.
`,
`,
messages,
tools: getTools(),
})
@@ -13,7 +13,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
case 'GET':
return handleGet(req, res)
default:
res.setHeader('Allow', ['POST'])
res.setHeader('Allow', ['GET'])
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
}
}
+77 -109
View File
@@ -1,59 +1,19 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import pgMeta from '@supabase/pg-meta'
import { streamText } from 'ai'
import { IS_PLATFORM } from 'common'
import { source } from 'common-tags'
import { executeSql } from 'data/sql/execute-sql-query'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { queryPgMetaSelfHosted } from 'lib/self-hosted'
import { NextApiRequest, NextApiResponse } from 'next'
import { getTools } from '../sql/tools'
export const maxDuration = 30
const credentialProvider = fromNodeProviderChain()
const bedrock = createAmazonBedrock({
credentialProvider,
})
async function hasAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
const hasCredentials = await hasAwsCredentials()
const pgMetaSchemasList = pgMeta.schemas.list()
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.AWS_REGION) {
return new Response(
JSON.stringify({
error: 'AWS_REGION is not set',
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
)
}
if (!hasCredentials) {
return new Response(
JSON.stringify({
error:
'AWS credentials are not configured. Set up a local profile or add environment variables.',
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
)
}
if (req.method !== 'POST') {
return new Response(
JSON.stringify({ data: null, error: { message: `Method ${req.method} Not Allowed` } }),
@@ -65,6 +25,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { completionMetadata, projectRef, connectionString, includeSchemaMetadata } = req.body
const { textBeforeCursor, textAfterCursor, language, prompt, selection } = completionMetadata
@@ -92,85 +58,86 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
)
: { result: [] }
const result = await streamText({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
const result = streamText({
model,
maxSteps: 5,
tools: getTools({ projectRef, connectionString, authorization, includeSchemaMetadata }),
system: `
VERY IMPORTANT RULES:
1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED SQL TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS.
2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool.
system: source`
VERY IMPORTANT RULES:
1. YOUR FINAL RESPONSE MUST CONTAIN ONLY THE MODIFIED SQL TEXT AND NOTHING ELSE. NO EXPLANATIONS, MARKDOWN, OR CODE BLOCKS.
2. WHEN USING TOOLS: Call them directly based on the instructions. DO NOT add any explanatory text or conversation before or between tool calls in the output stream. Your reasoning is internal; just call the tool.
You are a Supabase Postgres expert helping a user edit their SQL code based on a selection and a prompt.
Your goal is to modify the selected SQL according to the user's prompt, using the available tools to understand the schema and RLS policies if necessary.
You MUST respond ONLY with the modified SQL that should replace the user's selection. Do not explain the changes or the tool results in the final output.
You are a Supabase Postgres expert helping a user edit their SQL code based on a selection and a prompt.
Your goal is to modify the selected SQL according to the user's prompt, using the available tools to understand the schema and RLS policies if necessary.
You MUST respond ONLY with the modified SQL that should replace the user's selection. Do not explain the changes or the tool results in the final output.
# Core Task: Modify Selected SQL
- Focus solely on altering the provided SQL selection based on the user's instructions.
- Use the \`getSchemaTables\` tool to understand table structures relevant to the edit.
- Use the \`getRlsKnowledge\` tool to understand existing RLS policies if the edit involves them.
- Adhere strictly to the SQL generation guidelines below when modifying or creating SQL.
# Core Task: Modify Selected SQL
- Focus solely on altering the provided SQL selection based on the user's instructions.
- Use the \`getSchemaTables\` tool to understand table structures relevant to the edit.
- Use the \`getRlsKnowledge\` tool to understand existing RLS policies if the edit involves them.
- Adhere strictly to the SQL generation guidelines below when modifying or creating SQL.
# SQL Style:
- Generated/modified SQL must be valid Postgres SQL.
- Always use double apostrophes for escaped single quotes (e.g., 'Night''s watch').
- Always use semicolons at the end of SQL statements (unless modifying a fragment where it wouldn't fit).
- Use \`vector(384)\` for embedding/vector related queries.
- Prefer \`text\` over \`varchar\`.
- Prefer \`timestamp with time zone\` over \`date\`.
- Feel free to suggest corrections for suspected typos in the user's selection or prompt.
# SQL Style:
- Generated/modified SQL must be valid Postgres SQL.
- Always use double apostrophes for escaped single quotes (e.g., 'Night''s watch').
- Always use semicolons at the end of SQL statements (unless modifying a fragment where it wouldn't fit).
- Use \`vector(384)\` for embedding/vector related queries.
- Prefer \`text\` over \`varchar\`.
- Prefer \`timestamp with time zone\` over \`date\`.
- Feel free to suggest corrections for suspected typos in the user's selection or prompt.
# Best Practices & Object Generation (Apply when relevant to the edit):
- **Auth Schema**: The \`auth.users\` table stores user authentication data. If editing involves user data, consider if a \`public.profiles\` table linked to \`auth.users\` (via user_id referencing auth.users.id) is more appropriate for user-specific public data. Do not directly modify/query \`auth.users\` structure unless explicitly asked. Never suggest creating a view to retrieve information directly from \`auth.users\`.
- **Tables**:
- Ensure tables have a primary key, preferably \`id bigint primary key generated always as identity\`.
- Ensure Row Level Security (RLS) is enabled on tables (\`enable row level security\`). If creating a table snippet, mention the need for policies.
- Prefer defining foreign key references within the \`CREATE TABLE\` statement if adding one.
- If adding a foreign key, consider suggesting a separate \`CREATE INDEX\` statement for the foreign key column(s) to optimize joins.
- **Foreign Tables**: If the edit involves foreign tables, they should ideally be in a schema named \`private\`. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api.
- **Views**:
- Include \`with (security_invoker=on)\` immediately after \`CREATE VIEW view_name\` if creating/modifying a view definition.
- **Materialized Views**: If the edit involves materialized views, they should ideally be in the \`private\` schema. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api.
- **Extensions**:
- Extensions should be installed in the \`extensions\` schema or a dedicated schema, **never** in \`public\`.
- **RLS Policies**:
- When modifying policies using functions from the \`auth\` schema (like \`auth.uid()\`):
- Wrap the function call in parentheses: \`(select auth.uid())\`.
- Use \`CREATE POLICY\` or \`ALTER POLICY\`. Policy names should be descriptive text in double quotes.
- Specify roles using \`TO authenticated\` or \`TO anon\`.
- Use separate policies for SELECT, INSERT, UPDATE, DELETE actions. Do not use \`FOR ALL\`.
- Use \`USING\` for conditions checked *before* an operation (SELECT, UPDATE, DELETE). Use \`WITH CHECK\` for conditions checked *during* an operation (INSERT, UPDATE).
- SELECT: \`USING (condition)\`
- INSERT: \`WITH CHECK (condition)\`
- UPDATE: \`USING (condition) WITH CHECK (condition)\`
- DELETE: \`USING (condition)\`
- Prefer \`PERMISSIVE\` policies unless \`RESTRICTIVE\` is explicitly needed.
- Leverage Supabase helper functions: \`auth.uid()\`, \`auth.jwt()\` (\`app_metadata\` for authz, \`user_metadata\` is user-updatable).
- **Performance**: Indexes on columns used in RLS policies are crucial. Minimize joins within policy definitions.
- **Functions**:
- Use \`security definer\` for functions returning type \`trigger\`; otherwise, default to \`security invoker\`.
- Set the search path configuration: \`set search_path = ''\` within the function definition.
- Use \`create or replace function\` when possible if modifying a function signature.
# Best Practices & Object Generation (Apply when relevant to the edit):
- **Auth Schema**: The \`auth.users\` table stores user authentication data. If editing involves user data, consider if a \`public.profiles\` table linked to \`auth.users\` (via user_id referencing auth.users.id) is more appropriate for user-specific public data. Do not directly modify/query \`auth.users\` structure unless explicitly asked. Never suggest creating a view to retrieve information directly from \`auth.users\`.
- **Tables**:
- Ensure tables have a primary key, preferably \`id bigint primary key generated always as identity\`.
- Ensure Row Level Security (RLS) is enabled on tables (\`enable row level security\`). If creating a table snippet, mention the need for policies.
- Prefer defining foreign key references within the \`CREATE TABLE\` statement if adding one.
- If adding a foreign key, consider suggesting a separate \`CREATE INDEX\` statement for the foreign key column(s) to optimize joins.
- **Foreign Tables**: If the edit involves foreign tables, they should ideally be in a schema named \`private\`. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api.
- **Views**:
- Include \`with (security_invoker=on)\` immediately after \`CREATE VIEW view_name\` if creating/modifying a view definition.
- **Materialized Views**: If the edit involves materialized views, they should ideally be in the \`private\` schema. Mention the security risk (RLS bypass) and link: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api.
- **Extensions**:
- Extensions should be installed in the \`extensions\` schema or a dedicated schema, **never** in \`public\`.
- **RLS Policies**:
- When modifying policies using functions from the \`auth\` schema (like \`auth.uid()\`):
- Wrap the function call in parentheses: \`(select auth.uid())\`.
- Use \`CREATE POLICY\` or \`ALTER POLICY\`. Policy names should be descriptive text in double quotes.
- Specify roles using \`TO authenticated\` or \`TO anon\`.
- Use separate policies for SELECT, INSERT, UPDATE, DELETE actions. Do not use \`FOR ALL\`.
- Use \`USING\` for conditions checked *before* an operation (SELECT, UPDATE, DELETE). Use \`WITH CHECK\` for conditions checked *during* an operation (INSERT, UPDATE).
- SELECT: \`USING (condition)\`
- INSERT: \`WITH CHECK (condition)\`
- UPDATE: \`USING (condition) WITH CHECK (condition)\`
- DELETE: \`USING (condition)\`
- Prefer \`PERMISSIVE\` policies unless \`RESTRICTIVE\` is explicitly needed.
- Leverage Supabase helper functions: \`auth.uid()\`, \`auth.jwt()\` (\`app_metadata\` for authz, \`user_metadata\` is user-updatable).
- **Performance**: Indexes on columns used in RLS policies are crucial. Minimize joins within policy definitions.
- **Functions**:
- Use \`security definer\` for functions returning type \`trigger\`; otherwise, default to \`security invoker\`.
- Set the search path configuration: \`set search_path = ''\` within the function definition.
- Use \`create or replace function\` when possible if modifying a function signature.
# Tool Usage:
- Before generating the final SQL modification:
- Use \`getSchemaTables\` if you need to retrieve information about tables in relevant schemas (usually \`public\`, potentially \`auth\` if user-related).
- Use \`getRlsKnowledge\` if you need to retrieve existing RLS policies and guidelines if the edit concerns policies.
- The available database schema names are: ${schemas}
# Tool Usage:
- Before generating the final SQL modification:
- Use \`getSchemaTables\` if you need to retrieve information about tables in relevant schemas (usually \`public\`, potentially \`auth\` if user-related).
- Use \`getRlsKnowledge\` if you need to retrieve existing RLS policies and guidelines if the edit concerns policies.
- The available database schema names are: ${schemas}
# Response Format:
- Your response MUST be ONLY the modified SQL text intended to replace the user's selection.
- Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT.
- Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`).
- Avoid duplicating SQL keywords already present in the surrounding context.
- If there is no surrounding context, ensure your response is a complete, valid SQL statement.
# Response Format:
- Your response MUST be ONLY the modified SQL text intended to replace the user's selection.
- Do NOT include explanations, markdown formatting, or code blocks. NO MATTER WHAT.
- Ensure the modified text integrates naturally with the surrounding code provided (\`textBeforeCursor\` and \`textAfterCursor\`).
- Avoid duplicating SQL keywords already present in the surrounding context.
- If there is no surrounding context, ensure your response is a complete, valid SQL statement.
REMEMBER: ONLY OUTPUT THE SQL MODIFICATION.
REMEMBER: ONLY OUTPUT THE SQL MODIFICATION.
`,
messages: [
{
role: 'user',
content: `You are helping me edit some pgsql code.
content: source`
You are helping me edit some pgsql code.
Here is the context:
${textBeforeCursor}<selection>${selection}</selection>${textAfterCursor}
@@ -186,7 +153,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
6. Avoid duplicating SQL keywords (SELECT, FROM, WHERE, etc) when considering the full statement
7. If there is no surrounding context (before or after), make sure your response is a complete valid SQL statement that can be run and resolves the prompt.
Modify the selected text now:`,
Modify the selected text now:
`,
},
],
})
+39 -61
View File
@@ -1,45 +1,15 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import { generateObject } from 'ai'
import { source } from 'common-tags'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'
import apiWrapper from 'lib/api/apiWrapper'
const credentialProvider = fromNodeProviderChain()
const bedrock = createAmazonBedrock({
credentialProvider,
})
async function hasAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
const hasCredentials = await hasAwsCredentials()
const cronSchema = z.object({
cron_expression: z.string().describe('The generated cron expression.'),
})
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.AWS_REGION) {
return res.status(500).json({
error: 'AWS_REGION is not set',
})
}
if (!hasCredentials) {
return res.status(500).json({
error:
'AWS credentials are not configured. Set up a local profile or add environment variables.',
})
}
const { method } = req
switch (method) {
@@ -63,43 +33,51 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
}
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const result = await generateObject({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
model,
schema: cronSchema,
prompt: `You are a cron syntax expert. Your purpose is to convert natural language time descriptions into valid cron expressions for pg_cron.
prompt: source`
You are a cron syntax expert. Your purpose is to convert natural language time descriptions into valid cron expressions for pg_cron.
Rules for responses:
- For standard intervals (minutes and above), output cron expressions in the 5-field format supported by pg_cron
- For second-based intervals, use the special pg_cron "x seconds" syntax
- Do not provide any explanation of what the cron expression does
- Do not ask for clarification if you need it. Just output the cron expression.
Rules for responses:
- For standard intervals (minutes and above), output cron expressions in the 5-field format supported by pg_cron
- For second-based intervals, use the special pg_cron "x seconds" syntax
- Do not provide any explanation of what the cron expression does
- Do not ask for clarification if you need it. Just output the cron expression.
Example input: "Every Monday at 3am"
Example output: 0 3 * * 1
Example input: "Every Monday at 3am"
Example output: 0 3 * * 1
Example input: "Every 30 seconds"
Example output: 30 seconds
Example input: "Every 30 seconds"
Example output: 30 seconds
Additional examples:
- Every minute: * * * * *
- Every 5 minutes: */5 * * * *
- Every first of the month, at 00:00: 0 0 1 * *
- Every night at midnight: 0 0 * * *
- Every Monday at 2am: 0 2 * * 1
- Every 15 seconds: 15 seconds
- Every 45 seconds: 45 seconds
Additional examples:
- Every minute: * * * * *
- Every 5 minutes: */5 * * * *
- Every first of the month, at 00:00: 0 0 1 * *
- Every night at midnight: 0 0 * * *
- Every Monday at 2am: 0 2 * * 1
- Every 15 seconds: 15 seconds
- Every 45 seconds: 45 seconds
Field order for standard cron:
- minute (0-59)
- hour (0-23)
- day (1-31)
- month (1-12)
- weekday (0-6, Sunday=0)
Field order for standard cron:
- minute (0-59)
- hour (0-23)
- day (1-31)
- month (1-12)
- weekday (0-6, Sunday=0)
Important: pg_cron uses "x seconds" for second-based intervals, not "x * * * *".
If the user asks for seconds, do not use the 5-field format, instead use "x seconds".
Important: pg_cron uses "x seconds" for second-based intervals, not "x * * * *".
If the user asks for seconds, do not use the 5-field format, instead use "x seconds".
Here is the user's prompt: ${prompt}`,
Here is the user's prompt: ${prompt}
`,
temperature: 0,
})
+122 -203
View File
@@ -1,49 +1,24 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import crypto from 'crypto'
import { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'
import { streamText, tool, type Tool } from 'ai'
import { AiOptInLevel } from 'hooks/misc/useOrgOptedIntoAi'
import { streamText, tool, ToolSet } from 'ai'
import { source } from 'common-tags'
import { aiOptInLevelSchema } from 'hooks/misc/useOrgOptedIntoAi'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { createSupabaseMCPClient } from './supabase-mcp'
import {
createSupabaseMCPClient,
expectedToolsSchema,
filterToolsByOptInLevel,
transformToolResult,
} from './supabase-mcp'
export const maxDuration = 30
const credentialProvider = fromNodeProviderChain()
const bedrock = createAmazonBedrock({
credentialProvider,
})
async function hasAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
const hasCredentials = await hasAwsCredentials()
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req
if (!process.env.AWS_REGION) {
return res.status(500).json({
error: 'AWS_REGION is not set',
})
}
if (!hasCredentials) {
return res.status(500).json({
error:
'AWS credentials are not configured. Set up a local profile or add environment variables.',
})
}
switch (method) {
case 'POST':
return handlePost(req, res)
@@ -58,21 +33,15 @@ const wrapper = (req: NextApiRequest, res: NextApiResponse) =>
export default wrapper
const requestBodySchema = z.object({
messages: z.array(z.any()),
projectRef: z.string(),
aiOptInLevel: aiOptInLevelSchema,
schema: z.string().optional(),
table: z.string().optional(),
})
async function handlePost(req: NextApiRequest, res: NextApiResponse) {
const { messages, projectRef, aiOptInLevel, schema, table } = req.body as {
messages: any[] // Use stronger types if available
projectRef: string
aiOptInLevel: AiOptInLevel
schema?: string
table?: string
}
if (!projectRef) {
return res.status(400).json({
error: 'Missing project_ref in query parameters',
})
}
const authorization = req.headers.authorization
const accessToken = authorization?.replace('Bearer ', '')
@@ -80,175 +49,122 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
return res.status(401).json({ error: 'Authorization token is required' })
}
let mcpTools: Record<string, Tool<any, unknown>> = {}
let wrappedExecuteSqlTool: Tool<any, unknown> | undefined = undefined
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const { data, error: parseError } = requestBodySchema.safeParse(req.body)
if (parseError) {
return res.status(400).json({
error: 'Invalid request body',
issues: parseError.issues,
})
}
const { messages, projectRef, aiOptInLevel, schema, table } = data
try {
const privacyMessage =
"You don't have permission to use this tool. This is an organization-wide setting requiring you to opt-in. Please choose your preferred data sharing level in your organization's settings. Supabase Assistant uses Amazon Bedrock, which does not store or log your prompts and completions, use them to train AWS models, or distribute them to third parties. By default, no data is shared. Granting permission allows Supabase to send information (like schema, logs, or data, depending on your chosen level) to Bedrock solely to generate responses."
const condensedPrivacyMessage =
'Requires opting in to sending data to Bedrock which does not store, train on, or distribute it. You can opt in via organization settings.'
const createPrivacyMessageTool = (toolInstance: Tool<any, any>) => {
return {
...toolInstance,
description: `${toolInstance.description} (Note: ${condensedPrivacyMessage})`,
execute: async (_args: any, _context: any) => ({ status: privacyMessage }),
}
}
// Fetch MCP client and tools
const mcpClient = await createSupabaseMCPClient({
accessToken,
projectId: projectRef,
})
let availableMcpTools = await mcpClient.tools()
const availableMcpTools = await mcpClient.tools()
let allAllowedTools = [
'list_tables',
'list_extensions',
'list_edge_functions',
'list_branches',
'get_project',
'execute_sql',
'get_logs',
]
// Validate that the expected tools are available
const { data: validatedTools, error: validationError } =
expectedToolsSchema.safeParse(availableMcpTools)
// Filter out tools that should never be available across any permission level
availableMcpTools = Object.fromEntries(
Object.entries(availableMcpTools).filter(([key]) => allAllowedTools.includes(key))
)
// Build allowed tools based on permission level
let allowedTools: string[] = []
// For schema and above permission levels
if (
aiOptInLevel === 'schema' ||
aiOptInLevel === 'schema_and_log' ||
aiOptInLevel === 'schema_and_log_and_data'
) {
allowedTools = [
'list_tables',
'list_extensions',
'list_edge_functions',
'list_branches',
'get_project',
]
if (validationError) {
console.error('MCP tools validation error:', validationError)
return res.status(500).json({
error: 'Internal error: MCP tools validation failed',
issues: validationError.issues,
})
}
// For schema_and_log permission level, add log access tools
if (aiOptInLevel === 'schema_and_log' || aiOptInLevel === 'schema_and_log_and_data') {
allowedTools = [...allowedTools, 'get_logs']
}
// Modify the execute_sql tool to add manualToolCallId
const modifiedMcpTools = {
...availableMcpTools,
execute_sql: transformToolResult(validatedTools.execute_sql, (result) => {
const manualToolCallId = `manual_${crypto.randomUUID()}`
// For schema_and_log_and_data permission level, add data access tools
if (aiOptInLevel === 'schema_and_log_and_data') {
allowedTools = [...allowedTools, 'execute_sql']
}
// Process tools based on permission level
mcpTools = Object.fromEntries(
Object.entries(availableMcpTools).map(([key, toolInstance]) => {
if (allowedTools.includes(key)) {
return [key, toolInstance]
if (typeof result === 'object') {
return { ...result, manualToolCallId }
} else {
console.warn('execute_sql result is not an object, cannot add manualToolCallId')
return {
error: 'Internal error: Unexpected tool result format',
manualToolCallId,
}
}
return [key, createPrivacyMessageTool(toolInstance)]
})
)
// Wrap execute_sql only if it's allowed and exists
const originalExecuteSqlTool = mcpTools.execute_sql as Tool<any, any> | undefined
if (originalExecuteSqlTool && allowedTools.includes('execute_sql')) {
wrappedExecuteSqlTool = tool({
...originalExecuteSqlTool,
execute: async (args, context) => {
if (!originalExecuteSqlTool.execute) {
throw new Error('Original execute_sql tool has no execute function.')
}
const originalResult = await originalExecuteSqlTool.execute(args, context)
const manualToolCallId = `manual_${crypto.randomUUID()}`
if (originalResult && typeof originalResult === 'object') {
;(originalResult as any).manualToolCallId = manualToolCallId
} else {
console.warn('execute_sql result is not an object, cannot add manualToolCallId')
return {
error: 'Internal error: Unexpected tool result format',
manualToolCallId: manualToolCallId,
}
}
return originalResult
},
})
} else {
console.warn(
'execute_sql tool not found in mcpTools or not allowed at this permission level.'
)
}),
}
const display_query = tool({
description:
'Displays SQL query results (table or chart) or renders SQL for write/DDL operations. Use this for all query display needs. Optionally references a previous execute_sql call via manualToolCallId for displaying SELECT results.',
parameters: z.object({
manualToolCallId: z
.string()
.optional()
.describe(
'The manual ID from the corresponding execute_sql result (for SELECT queries).'
),
sql: z.string().describe('The SQL query.'),
label: z
.string()
.describe(
'The title or label for this query block (e.g., "Users Over Time", "Create Users Table").'
),
view: z
.enum(['table', 'chart'])
.optional()
.describe(
'Display mode for SELECT results: table or chart. Required if manualToolCallId is provided.'
),
xAxis: z.string().optional().describe('Key for the x-axis (required if view is chart).'),
yAxis: z.string().optional().describe('Key for the y-axis (required if view is chart).'),
runQuery: z
.boolean()
.optional()
.describe(
'Whether to automatically run the query. Set to true for read-only queries when manualToolCallId does not exist due to permissions. Should be false for write/DDL operations.'
),
}),
execute: async (args) => {
const statusMessage = args.manualToolCallId
? 'Tool call sent to client for rendering SELECT results.'
: 'Tool call sent to client for rendering write/DDL query.'
return { status: statusMessage }
},
})
// Filter tools based on the AI opt-in level
const mcpTools = filterToolsByOptInLevel(modifiedMcpTools, aiOptInLevel)
const display_edge_function = tool({
description: 'Renders the code for a Supabase Edge Function for the user to deploy manually.',
parameters: z.object({
name: z
.string()
.describe('The URL-friendly name of the Edge Function (e.g., "my-function").'),
code: z.string().describe('The TypeScript code for the Edge Function.'),
}),
execute: async (args) => {
return { status: 'Tool call sent to client for rendering.' }
},
})
const allTools = {
// Combine MCP tools with custom tools
const tools: ToolSet = {
...mcpTools,
...(wrappedExecuteSqlTool && { execute_sql: wrappedExecuteSqlTool }),
display_query,
display_edge_function,
display_query: tool({
description:
'Displays SQL query results (table or chart) or renders SQL for write/DDL operations. Use this for all query display needs. Optionally references a previous execute_sql call via manualToolCallId for displaying SELECT results.',
parameters: z.object({
manualToolCallId: z
.string()
.optional()
.describe(
'The manual ID from the corresponding execute_sql result (for SELECT queries).'
),
sql: z.string().describe('The SQL query.'),
label: z
.string()
.describe(
'The title or label for this query block (e.g., "Users Over Time", "Create Users Table").'
),
view: z
.enum(['table', 'chart'])
.optional()
.describe(
'Display mode for SELECT results: table or chart. Required if manualToolCallId is provided.'
),
xAxis: z.string().optional().describe('Key for the x-axis (required if view is chart).'),
yAxis: z.string().optional().describe('Key for the y-axis (required if view is chart).'),
runQuery: z
.boolean()
.optional()
.describe(
'Whether to automatically run the query. Set to true for read-only queries when manualToolCallId does not exist due to permissions. Should be false for write/DDL operations.'
),
}),
execute: async (args) => {
const statusMessage = args.manualToolCallId
? 'Tool call sent to client for rendering SELECT results.'
: 'Tool call sent to client for rendering write/DDL query.'
return { status: statusMessage }
},
}),
display_edge_function: tool({
description:
'Renders the code for a Supabase Edge Function for the user to deploy manually.',
parameters: z.object({
name: z
.string()
.describe('The URL-friendly name of the Edge Function (e.g., "my-function").'),
code: z.string().describe('The TypeScript code for the Edge Function.'),
}),
execute: async () => {
return { status: 'Tool call sent to client for rendering.' }
},
}),
}
let systemPrompt = `
const system = source`
The current project is ${projectRef}.
You are a Supabase Postgres expert. Your goal is to generate SQL or Edge Function code based on user requests, using specific tools for rendering.
@@ -392,16 +308,19 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) {
`
const result = streamText({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
model,
maxSteps: 10,
system: systemPrompt.trim(),
system,
messages,
tools: allTools,
tools,
})
result.pipeDataStreamToResponse(res)
} catch (error: any) {
} catch (error) {
console.error('Error in handlePost:', error)
return res.status(500).json({ message: error.message })
if (error instanceof Error) {
return res.status(500).json({ message: error.message })
}
return res.status(500).json({ message: 'An unexpected error occurred.' })
}
}
@@ -0,0 +1,198 @@
import { Tool, ToolExecutionOptions, ToolSet } from 'ai'
import { describe, expect, it, vitest } from 'vitest'
import { z } from 'zod'
import {
expectedToolsSchema,
filterToolsByOptInLevel,
getAllowedTools,
transformToolResult,
} from './supabase-mcp'
describe('getAllowedTools', () => {
it('should return empty array for disabled opt-in level', () => {
const tools = getAllowedTools('disabled')
expect(tools).toEqual([])
})
it('should return schema tools for schema opt-in level', () => {
const tools = getAllowedTools('schema')
expect(tools).toContain('list_tables')
expect(tools).toContain('list_extensions')
expect(tools).toContain('list_edge_functions')
expect(tools).toContain('list_branches')
expect(tools).not.toContain('get_logs')
expect(tools).not.toContain('execute_sql')
})
it('should return schema and log tools for schema_and_log opt-in level', () => {
const tools = getAllowedTools('schema_and_log')
expect(tools).toContain('list_tables')
expect(tools).toContain('list_extensions')
expect(tools).toContain('list_edge_functions')
expect(tools).toContain('list_branches')
expect(tools).toContain('get_logs')
expect(tools).not.toContain('execute_sql')
})
it('should return all tools for schema_and_log_and_data opt-in level', () => {
const tools = getAllowedTools('schema_and_log_and_data')
expect(tools).toContain('list_tables')
expect(tools).toContain('list_extensions')
expect(tools).toContain('list_edge_functions')
expect(tools).toContain('list_branches')
expect(tools).toContain('get_logs')
expect(tools).toContain('execute_sql')
})
})
describe('filterToolsByOptInLevel', () => {
const mockTools: ToolSet = {
list_tables: { execute: vitest.fn() },
list_extensions: { execute: vitest.fn() },
list_edge_functions: { execute: vitest.fn() },
list_branches: { execute: vitest.fn() },
get_logs: { execute: vitest.fn() },
execute_sql: { execute: vitest.fn() },
other: { execute: vitest.fn() }, // This tool should be filtered out
} as unknown as ToolSet
const stubResultSchema = z.object({
status: z.string(),
})
async function checkStub(name: string, tool: Tool<any, any>) {
if (!tool.execute) {
throw new Error(`Tool ${name} does not have an execute function`)
}
const result: { status: string } = await (tool.execute as any)()
const parsedResult = stubResultSchema.safeParse(result)
return (
parsedResult.success &&
parsedResult.data.status.includes("You don't have permission to use this tool")
)
}
async function expectStubsFor(tools: ToolSet, expectedStubTools: string[]) {
for (const toolName in tools) {
const tool = tools[toolName]
const shouldBeStub = expectedStubTools.includes(toolName)
const hasStub = await checkStub(toolName, tool)
expect(hasStub).toBe(shouldBeStub)
}
}
it('should filter out tools not in tool whitelist', async () => {
const tools = filterToolsByOptInLevel(mockTools, 'disabled')
expect(tools).not.toHaveProperty('other')
})
it('should stub all functions for disabled opt-in level', async () => {
const tools = filterToolsByOptInLevel(mockTools, 'disabled')
await expectStubsFor(tools, [
'list_tables',
'list_extensions',
'list_edge_functions',
'list_branches',
'get_logs',
'execute_sql',
])
})
it('should stub log and execute tools for schema opt-in level', async () => {
const tools = filterToolsByOptInLevel(mockTools, 'schema')
await expectStubsFor(tools, ['get_logs', 'execute_sql'])
})
it('should stub execute tool for schema_and_log opt-in level', async () => {
const tools = filterToolsByOptInLevel(mockTools, 'schema_and_log')
await expectStubsFor(tools, ['execute_sql'])
})
it('should not stub any tools for schema_and_log_and_data opt-in level', async () => {
const tools = filterToolsByOptInLevel(mockTools, 'schema_and_log_and_data')
await expectStubsFor(tools, [])
})
})
describe('transformToolResult', () => {
it('should wrap a tool with a result transformation function', async () => {
const originalResult = { data: 'original' }
const mockTool = {
description: 'Test tool',
execute: vitest.fn().mockResolvedValue(originalResult),
} as unknown as Tool<any, typeof originalResult>
const transformFn = vitest.fn((result: typeof originalResult) => ({
data: `${result.data} - transformed`,
}))
const transformedTool = transformToolResult(mockTool, transformFn)
// Tool properties should be preserved
expect(transformedTool.description).toBe(mockTool.description)
// Execute the transformed tool
const args = { key: 'value' }
const options = {} as ToolExecutionOptions
if (!transformedTool.execute) {
throw new Error('Transformed tool does not have an execute function')
}
const result = await transformedTool.execute(args, options)
// Original tool should have been called with the same arguments
expect(mockTool.execute).toHaveBeenCalledWith(args, options)
// Transform function should have been called with the original result
expect(transformFn).toHaveBeenCalledWith(originalResult)
// Final result should be the transformed value
expect(result).toEqual({ data: 'original - transformed' })
})
it('should throw an error if tool is null', () => {
expect(() => transformToolResult(null as any, () => ({}))).toThrow('Tool is required')
})
it('should throw an error if tool does not have an execute function', () => {
const invalidTool = { name: 'invalid' } as any
expect(() => transformToolResult(invalidTool, () => ({}))).toThrow(
'Tool does not have an execute function'
)
})
})
describe('expectedToolsSchema', () => {
it('should validate the expected tools schema', () => {
const validTools = {
list_tables: {},
list_extensions: {},
list_edge_functions: {},
list_branches: {},
get_logs: {},
execute_sql: {},
other: {},
}
const validationResult = expectedToolsSchema.safeParse(validTools)
expect(validationResult.success).toBe(true)
// Test with missing tool
const invalidTools = { ...validTools }
delete (invalidTools as any).execute_sql
const invalidValidationResult = expectedToolsSchema.safeParse(invalidTools)
expect(invalidValidationResult.success).toBe(false)
})
})
+110 -2
View File
@@ -1,7 +1,14 @@
import { StreamTransport } from '@supabase/mcp-utils'
import { experimental_createMCPClient as createMCPClient } from 'ai'
import { createSupabaseMcpServer } from '@supabase/mcp-server-supabase'
import { StreamTransport } from '@supabase/mcp-utils'
import {
experimental_createMCPClient as createMCPClient,
Tool,
ToolExecutionOptions,
ToolSet,
} from 'ai'
import { AiOptInLevel } from 'hooks/misc/useOrgOptedIntoAi'
import { API_URL } from 'lib/constants'
import { z } from 'zod'
export async function createSupabaseMCPClient({
accessToken,
@@ -36,3 +43,104 @@ export async function createSupabaseMCPClient({
return client
}
const basicToolSchema = z.custom<Tool>((value) => typeof value === 'object')
/**
* Schema to validate that the expected tools are available from the Supabase MCP.
*
* Note that tool structure itself is not validated, only that the tools exist.
*/
export const expectedToolsSchema = z.object({
list_tables: basicToolSchema,
list_extensions: basicToolSchema,
list_edge_functions: basicToolSchema,
list_branches: basicToolSchema,
get_logs: basicToolSchema,
execute_sql: basicToolSchema,
})
export const toolWhitelist = Object.keys(expectedToolsSchema.shape)
export function createPrivacyMessageTool(toolInstance: Tool<any, any>) {
const privacyMessage =
"You don't have permission to use this tool. This is an organization-wide setting requiring you to opt-in. Please choose your preferred data sharing level in your organization's settings. Supabase Assistant uses Amazon Bedrock, which does not store or log your prompts and completions, use them to train AWS models, or distribute them to third parties. By default, no data is shared. Granting permission allows Supabase to send information (like schema, logs, or data, depending on your chosen level) to Bedrock solely to generate responses."
const condensedPrivacyMessage =
'Requires opting in to sending data to Bedrock which does not store, train on, or distribute it. You can opt in via organization settings.'
return {
...toolInstance,
description: `${toolInstance.description} (Note: ${condensedPrivacyMessage})`,
execute: async (_args: any, _context: any) => ({ status: privacyMessage }),
}
}
export function filterToolsByOptInLevel(tools: ToolSet, aiOptInLevel: AiOptInLevel) {
// Get allowed tools based on the AI opt-in level
const allowedTools = getAllowedTools(aiOptInLevel)
// Filter the tools to only include those that are allowed
return Object.fromEntries(
Object.entries(tools)
.filter(([key]) => toolWhitelist.includes(key))
.map(([key, toolInstance]) => {
if (allowedTools.includes(key)) {
return [key, toolInstance]
}
// If the tool is not allowed, provide a stub that returns a privacy message
return [key, createPrivacyMessageTool(toolInstance)]
})
)
}
/**
* Transforms the result of a tool execution to a new output.
*/
export function transformToolResult<OriginalResult, NewResult>(
tool: Tool<any, OriginalResult>,
execute: (result: OriginalResult) => NewResult
): Tool<any, NewResult> {
if (!tool) {
throw new Error('Tool is required')
}
if (!tool.execute) {
throw new Error('Tool does not have an execute function')
}
// Intercept the tool to add a custom execute function
return {
...tool,
execute: async (args: any, options: ToolExecutionOptions) => {
const result = await tool.execute!(args, options)
return execute(result)
},
} as Tool<any, NewResult>
}
export function getAllowedTools(aiOptInLevel: AiOptInLevel) {
// Build allowed tools based on permission level
const allowedTools: string[] = []
// For schema and above permission levels
if (
aiOptInLevel === 'schema' ||
aiOptInLevel === 'schema_and_log' ||
aiOptInLevel === 'schema_and_log_and_data'
) {
allowedTools.push('list_tables', 'list_extensions', 'list_edge_functions', 'list_branches')
}
// For schema_and_log permission level, add log access tools
if (aiOptInLevel === 'schema_and_log' || aiOptInLevel === 'schema_and_log_and_data') {
allowedTools.push('get_logs')
}
// For schema_and_log_and_data permission level, add data access tools
if (aiOptInLevel === 'schema_and_log_and_data') {
allowedTools.push('execute_sql')
}
return allowedTools
}
+15 -37
View File
@@ -1,26 +1,9 @@
import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock'
import { fromNodeProviderChain } from '@aws-sdk/credential-providers'
import { generateObject } from 'ai'
import { source } from 'common-tags'
import { getModel } from 'lib/ai/model'
import apiWrapper from 'lib/api/apiWrapper'
import { NextApiRequest, NextApiResponse } from 'next'
import { z } from 'zod'
import apiWrapper from 'lib/api/apiWrapper'
const credentialProvider = fromNodeProviderChain()
const bedrock = createAmazonBedrock({
credentialProvider,
})
async function hasAwsCredentials() {
try {
const credentials = await credentialProvider()
return !!credentials
} catch (error) {
return false
}
}
const hasCredentials = await hasAwsCredentials()
const titleSchema = z.object({
title: z
@@ -32,19 +15,6 @@ const titleSchema = z.object({
})
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.AWS_REGION) {
return res.status(500).json({
error: 'AWS_REGION is not set',
})
}
if (!hasCredentials) {
return res.status(500).json({
error:
'AWS credentials are not configured. Set up a local profile or add environment variables.',
})
}
const { method } = req
switch (method) {
@@ -68,14 +38,22 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
}
try {
const { model, error: modelError } = await getModel()
if (modelError) {
return res.status(500).json({ error: modelError.message })
}
const result = await generateObject({
model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
model,
schema: titleSchema,
prompt: `Generate a short title and summarized description for this Postgres SQL snippet:
prompt: source`
Generate a short title and summarized description for this Postgres SQL snippet:
${sql}
${sql}
The description should describe why this table was created (eg. "Table to track todos") or what the query does.`,
The description should describe why this table was created (eg. "Table to track todos") or what the query does.
`,
temperature: 0,
})
+221 -162
View File
@@ -200,7 +200,7 @@ importers:
version: 1.6.0
tailwindcss:
specifier: ^3.3.0
version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))
version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))
tsconfig:
specifier: workspace:*
version: link:../../packages/tsconfig
@@ -572,8 +572,11 @@ importers:
apps/studio:
dependencies:
'@ai-sdk/amazon-bedrock':
specifier: ^2.2.8
specifier: ^2.2.9
version: 2.2.9(zod@3.23.8)
'@ai-sdk/openai':
specifier: ^1.3.22
version: 1.3.22(zod@3.23.8)
'@ai-sdk/react':
specifier: ^1.2.12
version: 1.2.12(react@18.3.1)(zod@3.23.8)
@@ -683,8 +686,8 @@ importers:
specifier: ^2.7.29
version: 2.7.30
ai:
specifier: ^4.3.9
version: 4.3.15(react@18.3.1)(zod@3.23.8)
specifier: ^4.3.16
version: 4.3.16(react@18.3.1)(zod@3.23.8)
ai-commands:
specifier: workspace:*
version: link:../../packages/ai-commands
@@ -994,7 +997,7 @@ importers:
version: 4.4.2
'@vitest/coverage-v8':
specifier: ^3.0.9
version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))
version: 3.0.9(supports-color@8.1.1)(vitest@3.0.9)
'@vitest/ui':
specifier: ^3.0.0
version: 3.0.4(vitest@3.0.9)
@@ -1259,7 +1262,7 @@ importers:
version: 14.2.3
'@next/mdx':
specifier: ^14.2.3
version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1))
version: 14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1))
'@octokit/auth-app':
specifier: ^7.0.0
version: 7.1.5
@@ -1497,7 +1500,7 @@ importers:
version: 2.49.3
ai:
specifier: ^3.4.33
version: 3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.13(typescript@5.5.2))(zod@3.23.8)
version: 3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.2.0(svelte@5.33.10))(svelte@5.33.10)(vue@3.5.16(typescript@5.5.2))(zod@3.23.8)
common-tags:
specifier: ^1.8.2
version: 1.8.2
@@ -1655,10 +1658,10 @@ importers:
version: 0.1.9
'@tailwindcss/forms':
specifier: ^0.5.0
version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))
version: 0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))
'@tailwindcss/typography':
specifier: ^0.5.9
version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))
version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))
deepmerge:
specifier: ^4.2.2
version: 4.3.1
@@ -1671,10 +1674,10 @@ importers:
devDependencies:
tailwindcss:
specifier: ^3.4.1
version: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))
version: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))
tailwindcss-animate:
specifier: ^1.0.6
version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))
version: 1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))
packages/eslint-config-supabase:
dependencies:
@@ -2221,6 +2224,12 @@ packages:
peerDependencies:
zod: ^3.0.0
'@ai-sdk/openai@1.3.22':
resolution: {integrity: sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.0.0
'@ai-sdk/provider-utils@1.0.22':
resolution: {integrity: sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==}
engines: {node: '>=18'}
@@ -2646,6 +2655,10 @@ packages:
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.24.7':
resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
engines: {node: '>=6.9.0'}
@@ -2654,6 +2667,10 @@ packages:
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.27.1':
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.25.9':
resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
@@ -2681,6 +2698,11 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/parser@7.27.3':
resolution: {integrity: sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/plugin-syntax-decorators@7.25.9':
resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==}
engines: {node: '>=6.9.0'}
@@ -2759,6 +2781,10 @@ packages:
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
engines: {node: '>=6.9.0'}
'@babel/types@7.27.3':
resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==}
engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@1.0.2':
resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
engines: {node: '>=18'}
@@ -7507,6 +7533,11 @@ packages:
'@supabase/supabase-js@2.49.3':
resolution: {integrity: sha512-42imTuAm9VEQGlXT0O6zrSwNnsIblU1eieqrAWj8HSmFaYkxepk/IuUVw1M5hKelk0ZYlqDKNwRErI1rF1EL4w==}
'@sveltejs/acorn-typescript@1.0.5':
resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
peerDependencies:
acorn: ^8.9.0
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
@@ -8333,34 +8364,34 @@ packages:
'@vitest/utils@3.0.9':
resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==}
'@vue/compiler-core@3.5.13':
resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
'@vue/compiler-core@3.5.16':
resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==}
'@vue/compiler-dom@3.5.13':
resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
'@vue/compiler-dom@3.5.16':
resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==}
'@vue/compiler-sfc@3.5.13':
resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
'@vue/compiler-sfc@3.5.16':
resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==}
'@vue/compiler-ssr@3.5.13':
resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
'@vue/compiler-ssr@3.5.16':
resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==}
'@vue/reactivity@3.5.13':
resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
'@vue/reactivity@3.5.16':
resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==}
'@vue/runtime-core@3.5.13':
resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
'@vue/runtime-core@3.5.16':
resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==}
'@vue/runtime-dom@3.5.13':
resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
'@vue/runtime-dom@3.5.16':
resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==}
'@vue/server-renderer@3.5.13':
resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
'@vue/server-renderer@3.5.16':
resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==}
peerDependencies:
vue: 3.5.13
vue: 3.5.16
'@vue/shared@3.5.13':
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
'@vue/shared@3.5.16':
resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==}
'@webassemblyjs/ast@1.14.1':
resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==}
@@ -8538,8 +8569,8 @@ packages:
zod:
optional: true
ai@4.3.15:
resolution: {integrity: sha512-TYKRzbWg6mx/pmTadlAEIhuQtzfHUV0BbLY72+zkovXwq/9xhcH24IlQmkyBpElK6/4ArS0dHdOOtR1jOPVwtg==}
ai@4.3.16:
resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==}
engines: {node: '>=18'}
peerDependencies:
react: ^18 || ^19 || ^19.0.0-rc
@@ -9206,9 +9237,6 @@ packages:
code-block-writer@13.0.1:
resolution: {integrity: sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==}
code-red@1.0.4:
resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==}
codemirror-graphql@2.0.10:
resolution: {integrity: sha512-rC9NxibCsSzWtCQjHLfwKCkyYdGv2BT/BCgyDoKPrc/e7aGiyLyeT0fB60d+0imwlvhX3lIHncl6JMz2YxQ/jg==}
peerDependencies:
@@ -9510,10 +9538,6 @@ packages:
resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
engines: {node: '>=8.0.0'}
css-tree@2.3.1:
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
@@ -10327,6 +10351,9 @@ packages:
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
hasBin: true
esm-env@1.2.2:
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
esniff@2.0.1:
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
engines: {node: '>=0.10'}
@@ -10344,6 +10371,9 @@ packages:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
esrap@1.4.6:
resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
@@ -12445,9 +12475,6 @@ packages:
mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
mdurl@1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
@@ -15213,10 +15240,10 @@ packages:
resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
engines: {node: '>= 8'}
sswr@2.1.0:
resolution: {integrity: sha512-Cqc355SYlTAaUt8iDPaC/4DPPXK925PePLMxyBKuWd5kKc5mwsG3nT9+Mq2tyguL5s7b4Jg+IRMpTRsNTAfpSQ==}
sswr@2.2.0:
resolution: {integrity: sha512-clTszLPZkmycALTHD1mXGU+mOtA/MIoLgS1KGTTzFNVm9rytQVykgRaP+z1zl572cz0bTqj4rFVoC2N+IGK4Sg==}
peerDependencies:
svelte: ^4.0.0 || ^5.0.0-next.0
svelte: ^4.0.0 || ^5.0.0
stack-generator@2.0.10:
resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==}
@@ -15464,9 +15491,9 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
svelte@4.2.19:
resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
engines: {node: '>=16'}
svelte@5.33.10:
resolution: {integrity: sha512-/yArPQIBoQS2p86LKnvJywOXkVHeEXnFgrDPSxkEfIAEkykopYuy2bF6UUqHG4IbZlJD6OurLxJT8Kn7kTk9WA==}
engines: {node: '>=18'}
svgson@5.3.1:
resolution: {integrity: sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==}
@@ -15490,8 +15517,8 @@ packages:
swrev@4.0.0:
resolution: {integrity: sha512-LqVcOHSB4cPGgitD1riJ1Hh4vdmITOp+BkmfmXRh4hSF/t7EnS4iD+SOTmq7w5pPm/SiPeto4ADbKS6dHUDWFA==}
swrv@1.0.4:
resolution: {integrity: sha512-zjEkcP8Ywmj+xOJW3lIT65ciY/4AL4e/Or7Gj0MzU3zBJNMdJiT8geVZhINavnlHRMMCcJLHhraLTAiDOTmQ9g==}
swrv@1.1.0:
resolution: {integrity: sha512-pjllRDr2s0iTwiE5Isvip51dZGR7GjLH1gCSVyE8bQnbAx6xackXsFdojau+1O5u98yHF5V73HQGOFxKUXO9gQ==}
peerDependencies:
vue: '>=3.2.26 < 4'
@@ -16427,8 +16454,8 @@ packages:
vscode-languageserver-types@3.17.5:
resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
vue@3.5.13:
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
vue@3.5.16:
resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -16693,15 +16720,13 @@ packages:
yup@1.4.0:
resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==}
zimmerframe@1.1.2:
resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
zod-to-json-schema@3.23.5:
resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==}
peerDependencies:
zod: ^3.23.3
zod-to-json-schema@3.24.5:
resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
peerDependencies:
@@ -16755,6 +16780,12 @@ snapshots:
'@ai-sdk/provider-utils': 1.0.22(zod@3.23.8)
zod: 3.23.8
'@ai-sdk/openai@1.3.22(zod@3.23.8)':
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.23.8)
zod: 3.23.8
'@ai-sdk/provider-utils@1.0.22(zod@3.23.8)':
dependencies:
'@ai-sdk/provider': 0.0.26
@@ -16806,13 +16837,13 @@ snapshots:
transitivePeerDependencies:
- zod
'@ai-sdk/svelte@0.0.57(svelte@4.2.19)(zod@3.23.8)':
'@ai-sdk/svelte@0.0.57(svelte@5.33.10)(zod@3.23.8)':
dependencies:
'@ai-sdk/provider-utils': 1.0.22(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.50(zod@3.23.8)
sswr: 2.1.0(svelte@4.2.19)
sswr: 2.2.0(svelte@5.33.10)
optionalDependencies:
svelte: 4.2.19
svelte: 5.33.10
transitivePeerDependencies:
- zod
@@ -16822,7 +16853,7 @@ snapshots:
'@ai-sdk/provider-utils': 1.0.22(zod@3.23.8)
json-schema: 0.4.0
secure-json-parse: 2.7.0
zod-to-json-schema: 3.23.5(zod@3.23.8)
zod-to-json-schema: 3.24.5(zod@3.23.8)
optionalDependencies:
zod: 3.23.8
@@ -16833,13 +16864,13 @@ snapshots:
zod: 3.23.8
zod-to-json-schema: 3.24.5(zod@3.23.8)
'@ai-sdk/vue@0.0.59(vue@3.5.13(typescript@5.5.2))(zod@3.23.8)':
'@ai-sdk/vue@0.0.59(vue@3.5.16(typescript@5.5.2))(zod@3.23.8)':
dependencies:
'@ai-sdk/provider-utils': 1.0.22(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.50(zod@3.23.8)
swrv: 1.0.4(vue@3.5.13(typescript@5.5.2))
swrv: 1.1.0(vue@3.5.16(typescript@5.5.2))
optionalDependencies:
vue: 3.5.13(typescript@5.5.2)
vue: 3.5.16(typescript@5.5.2)
transitivePeerDependencies:
- zod
@@ -16871,13 +16902,13 @@ snapshots:
'@aws-crypto/crc32@3.0.0':
dependencies:
'@aws-crypto/util': 3.0.0
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.804.0
tslib: 1.14.1
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
'@aws-sdk/types': 3.468.0
'@aws-sdk/types': 3.804.0
tslib: 2.8.1
'@aws-crypto/ie11-detection@3.0.0':
@@ -17742,10 +17773,14 @@ snapshots:
'@babel/helper-string-parser@7.25.9': {}
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.24.7': {}
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-identifier@7.27.1': {}
'@babel/helper-validator-option@7.25.9': {}
'@babel/helpers@7.26.10':
@@ -17772,6 +17807,10 @@ snapshots:
dependencies:
'@babel/types': 7.27.0
'@babel/parser@7.27.3':
dependencies:
'@babel/types': 7.27.3
'@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.10(supports-color@8.1.1))':
dependencies:
'@babel/core': 7.26.10(supports-color@8.1.1)
@@ -17880,6 +17919,11 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@babel/types@7.27.3':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
'@bcoe/v8-coverage@1.0.2': {}
'@bundled-es-modules/cookie@2.0.1':
@@ -19590,6 +19634,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2))':
dependencies:
'@mdx-js/mdx': 2.3.0(supports-color@8.1.1)
source-map: 0.7.4
webpack: 5.94.0(esbuild@0.25.2)
transitivePeerDependencies:
- supports-color
optional: true
'@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0)':
dependencies:
'@mdx-js/mdx': 2.3.0(supports-color@8.1.1)
@@ -19772,11 +19825,11 @@ snapshots:
dependencies:
glob: 10.3.10
'@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1))(@mdx-js/react@2.3.0(react@18.3.1))':
'@next/mdx@14.2.3(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2)))(@mdx-js/react@2.3.0(react@18.3.1))':
dependencies:
source-map: 0.7.4
optionalDependencies:
'@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0)
'@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0(esbuild@0.25.2))
'@mdx-js/react': 2.3.0(react@18.3.1)
'@next/mdx@15.3.1(@mdx-js/loader@2.3.0(supports-color@8.1.1)(webpack@5.94.0))(@mdx-js/react@2.3.0(react@18.3.1))':
@@ -23755,6 +23808,10 @@ snapshots:
- bufferutil
- utf-8-validate
'@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)':
dependencies:
acorn: 8.14.1
'@swc/counter@0.1.3': {}
'@swc/helpers@0.5.15':
@@ -23775,6 +23832,11 @@ snapshots:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))
'@tailwindcss/forms@0.5.6(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))':
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))
'@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2)))':
dependencies:
lodash.castarray: 4.4.0
@@ -23783,6 +23845,14 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))
'@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2)))':
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))
'@tanstack/directive-functions-plugin@1.114.12(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)':
dependencies:
'@babel/code-frame': 7.26.2
@@ -25068,24 +25138,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
debug: 4.4.0(supports-color@8.1.1)
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1)
istanbul-reports: 3.1.7
magic-string: 0.30.17
magicast: 0.3.5
std-env: 3.8.1
test-exclude: 7.0.1
tinyrainbow: 2.0.0
vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)
transitivePeerDependencies:
- supports-color
'@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9(@types/node@20.12.11)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.7.3(@types/node@20.12.11)(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))':
dependencies:
'@ampproject/remapping': 2.3.0
@@ -25122,6 +25174,24 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitest/coverage-v8@3.0.9(supports-color@8.1.1)(vitest@3.0.9)':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
debug: 4.4.0(supports-color@8.1.1)
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1)
istanbul-reports: 3.1.7
magic-string: 0.30.17
magicast: 0.3.5
std-env: 3.8.1
test-exclude: 7.0.1
tinyrainbow: 2.0.0
vitest: 3.0.9(@types/node@20.12.11)(@vitest/ui@3.0.4)(jiti@2.4.2)(jsdom@20.0.3(supports-color@8.1.1))(msw@2.4.11(typescript@5.5.2))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5)
transitivePeerDependencies:
- supports-color
'@vitest/expect@3.0.9':
dependencies:
'@vitest/spy': 3.0.9
@@ -25202,59 +25272,59 @@ snapshots:
loupe: 3.1.3
tinyrainbow: 2.0.0
'@vue/compiler-core@3.5.13':
'@vue/compiler-core@3.5.16':
dependencies:
'@babel/parser': 7.27.0
'@vue/shared': 3.5.13
'@babel/parser': 7.27.3
'@vue/shared': 3.5.16
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
'@vue/compiler-dom@3.5.13':
'@vue/compiler-dom@3.5.16':
dependencies:
'@vue/compiler-core': 3.5.13
'@vue/shared': 3.5.13
'@vue/compiler-core': 3.5.16
'@vue/shared': 3.5.16
'@vue/compiler-sfc@3.5.13':
'@vue/compiler-sfc@3.5.16':
dependencies:
'@babel/parser': 7.27.0
'@vue/compiler-core': 3.5.13
'@vue/compiler-dom': 3.5.13
'@vue/compiler-ssr': 3.5.13
'@vue/shared': 3.5.13
'@babel/parser': 7.27.3
'@vue/compiler-core': 3.5.16
'@vue/compiler-dom': 3.5.16
'@vue/compiler-ssr': 3.5.16
'@vue/shared': 3.5.16
estree-walker: 2.0.2
magic-string: 0.30.17
postcss: 8.5.3
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.13':
'@vue/compiler-ssr@3.5.16':
dependencies:
'@vue/compiler-dom': 3.5.13
'@vue/shared': 3.5.13
'@vue/compiler-dom': 3.5.16
'@vue/shared': 3.5.16
'@vue/reactivity@3.5.13':
'@vue/reactivity@3.5.16':
dependencies:
'@vue/shared': 3.5.13
'@vue/shared': 3.5.16
'@vue/runtime-core@3.5.13':
'@vue/runtime-core@3.5.16':
dependencies:
'@vue/reactivity': 3.5.13
'@vue/shared': 3.5.13
'@vue/reactivity': 3.5.16
'@vue/shared': 3.5.16
'@vue/runtime-dom@3.5.13':
'@vue/runtime-dom@3.5.16':
dependencies:
'@vue/reactivity': 3.5.13
'@vue/runtime-core': 3.5.13
'@vue/shared': 3.5.13
'@vue/reactivity': 3.5.16
'@vue/runtime-core': 3.5.16
'@vue/shared': 3.5.16
csstype: 3.1.3
'@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.5.2))':
'@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.5.2))':
dependencies:
'@vue/compiler-ssr': 3.5.13
'@vue/shared': 3.5.13
vue: 3.5.13(typescript@5.5.2)
'@vue/compiler-ssr': 3.5.16
'@vue/shared': 3.5.16
vue: 3.5.16(typescript@5.5.2)
'@vue/shared@3.5.13': {}
'@vue/shared@3.5.16': {}
'@webassemblyjs/ast@1.14.1':
dependencies:
@@ -25442,32 +25512,32 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
ai@3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.5.13(typescript@5.5.2))(zod@3.23.8):
ai@3.4.33(openai@4.71.1(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.2.0(svelte@5.33.10))(svelte@5.33.10)(vue@3.5.16(typescript@5.5.2))(zod@3.23.8):
dependencies:
'@ai-sdk/provider': 0.0.26
'@ai-sdk/provider-utils': 1.0.22(zod@3.23.8)
'@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.23.8)
'@ai-sdk/solid': 0.0.54(zod@3.23.8)
'@ai-sdk/svelte': 0.0.57(svelte@4.2.19)(zod@3.23.8)
'@ai-sdk/svelte': 0.0.57(svelte@5.33.10)(zod@3.23.8)
'@ai-sdk/ui-utils': 0.0.50(zod@3.23.8)
'@ai-sdk/vue': 0.0.59(vue@3.5.13(typescript@5.5.2))(zod@3.23.8)
'@ai-sdk/vue': 0.0.59(vue@3.5.16(typescript@5.5.2))(zod@3.23.8)
'@opentelemetry/api': 1.9.0
eventsource-parser: 1.1.2
json-schema: 0.4.0
jsondiffpatch: 0.6.0
secure-json-parse: 2.7.0
zod-to-json-schema: 3.23.5(zod@3.23.8)
zod-to-json-schema: 3.24.5(zod@3.23.8)
optionalDependencies:
openai: 4.71.1(encoding@0.1.13)(zod@3.23.8)
react: 18.3.1
sswr: 2.1.0(svelte@4.2.19)
svelte: 4.2.19
sswr: 2.2.0(svelte@5.33.10)
svelte: 5.33.10
zod: 3.23.8
transitivePeerDependencies:
- solid-js
- vue
ai@4.3.15(react@18.3.1)(zod@3.23.8):
ai@4.3.16(react@18.3.1)(zod@3.23.8):
dependencies:
'@ai-sdk/provider': 1.1.3
'@ai-sdk/provider-utils': 2.2.8(zod@3.23.8)
@@ -26242,14 +26312,6 @@ snapshots:
code-block-writer@13.0.1: {}
code-red@1.0.4:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
'@types/estree': 1.0.5
acorn: 8.14.1
estree-walker: 3.0.3
periscopic: 3.1.0
codemirror-graphql@2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0):
dependencies:
'@codemirror/language': 6.11.0
@@ -26566,11 +26628,6 @@ snapshots:
mdn-data: 2.0.14
source-map: 0.6.1
css-tree@2.3.1:
dependencies:
mdn-data: 2.0.30
source-map-js: 1.2.1
css-what@6.1.0: {}
css.escape@1.5.1: {}
@@ -27300,7 +27357,7 @@ snapshots:
debug: 4.4.0(supports-color@8.1.1)
enhanced-resolve: 5.17.1
eslint: 8.57.0(supports-color@8.1.1)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)
fast-glob: 3.3.2
get-tsconfig: 4.7.2
@@ -27312,7 +27369,7 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1):
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1):
dependencies:
debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:
@@ -27339,7 +27396,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0(supports-color@8.1.1)
eslint-import-resolver-node: 0.3.9(supports-color@8.1.1)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-plugin-import@2.29.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1))(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9(supports-color@8.1.1))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0(supports-color@8.1.1))(supports-color@8.1.1)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -27462,6 +27519,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
esm-env@1.2.2: {}
esniff@2.0.1:
dependencies:
d: 1.0.2
@@ -27481,6 +27540,10 @@ snapshots:
dependencies:
estraverse: 5.3.0
esrap@1.4.6:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
@@ -30179,8 +30242,6 @@ snapshots:
mdn-data@2.0.14: {}
mdn-data@2.0.30: {}
mdurl@1.0.1: {}
mdurl@2.0.0: {}
@@ -33876,9 +33937,9 @@ snapshots:
dependencies:
minipass: 3.3.6
sswr@2.1.0(svelte@4.2.19):
sswr@2.2.0(svelte@5.33.10):
dependencies:
svelte: 4.2.19
svelte: 5.33.10
swrev: 4.0.0
stack-generator@2.0.10:
@@ -34155,22 +34216,22 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
svelte@4.2.19:
svelte@5.33.10:
dependencies:
'@ampproject/remapping': 2.3.0
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping': 0.3.25
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1)
'@types/estree': 1.0.5
acorn: 8.14.1
aria-query: 5.3.2
axobject-query: 4.1.0
code-red: 1.0.4
css-tree: 2.3.1
estree-walker: 3.0.3
clsx: 2.1.1
esm-env: 1.2.2
esrap: 1.4.6
is-reference: 3.0.3
locate-character: 3.0.0
magic-string: 0.30.17
periscopic: 3.1.0
zimmerframe: 1.1.2
svgson@5.3.1:
dependencies:
@@ -34207,9 +34268,9 @@ snapshots:
swrev@4.0.0: {}
swrv@1.0.4(vue@3.5.13(typescript@5.5.2)):
swrv@1.1.0(vue@3.5.16(typescript@5.5.2)):
dependencies:
vue: 3.5.13(typescript@5.5.2)
vue: 3.5.16(typescript@5.5.2)
symbol-tree@3.2.4:
optional: true
@@ -34226,9 +34287,9 @@ snapshots:
tailwind-merge@1.14.0: {}
tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))):
tailwindcss-animate@1.0.7(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))):
dependencies:
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.5.2))
tailwindcss: 3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.5.2))
tailwindcss-radix@2.8.0: {}
@@ -35453,13 +35514,13 @@ snapshots:
vscode-languageserver-types@3.17.5: {}
vue@3.5.13(typescript@5.5.2):
vue@3.5.16(typescript@5.5.2):
dependencies:
'@vue/compiler-dom': 3.5.13
'@vue/compiler-sfc': 3.5.13
'@vue/runtime-dom': 3.5.13
'@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.5.2))
'@vue/shared': 3.5.13
'@vue/compiler-dom': 3.5.16
'@vue/compiler-sfc': 3.5.16
'@vue/runtime-dom': 3.5.16
'@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.5.2))
'@vue/shared': 3.5.16
optionalDependencies:
typescript: 5.5.2
@@ -35784,16 +35845,14 @@ snapshots:
toposort: 2.0.2
type-fest: 2.19.0
zimmerframe@1.1.2: {}
zip-stream@6.0.1:
dependencies:
archiver-utils: 5.0.2
compress-commons: 6.0.2
readable-stream: 4.6.0
zod-to-json-schema@3.23.5(zod@3.23.8):
dependencies:
zod: 3.23.8
zod-to-json-schema@3.24.5(zod@3.23.8):
dependencies:
zod: 3.23.8