diff --git a/guides/ai-transport/.env.example b/guides/ai-transport/.env.example new file mode 100644 index 0000000000..b81e5dbda9 --- /dev/null +++ b/guides/ai-transport/.env.example @@ -0,0 +1,10 @@ +# Ably API key (required for all guides) +ABLY_API_KEY= + +# OpenAI API key (required for openai-* guides) +OPENAI_API_KEY= + +# Anthropic API key (required for anthropic-* and lang-graph-* guides) +ANTHROPIC_API_KEY= + +# Vercel AI SDK uses the underlying provider's key (e.g. OPENAI_API_KEY) diff --git a/guides/ai-transport/.gitignore b/guides/ai-transport/.gitignore new file mode 100644 index 0000000000..713d5006da --- /dev/null +++ b/guides/ai-transport/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env diff --git a/guides/ai-transport/README.md b/guides/ai-transport/README.md new file mode 100644 index 0000000000..49009fd285 --- /dev/null +++ b/guides/ai-transport/README.md @@ -0,0 +1,73 @@ +# AI Transport Guides - E2E Code + +Full, runnable implementations for each AI Transport guide. Each guide directory contains language-specific subdirectories with publisher (agent) and subscriber (client) code. + +## Structure + +``` +/ + javascript/ # JavaScript/TypeScript implementation + src/publisher.ts + src/subscriber.ts + test/e2e.test.ts + python/ # (future) + java/ # (future) +``` + +## Guides + +| Guide | Provider | Pattern | Languages | +|-------|----------|---------|-----------| +| `openai-message-per-token` | OpenAI | Message per token | JavaScript | +| `openai-message-per-response` | OpenAI | Message per response | JavaScript | +| `anthropic-message-per-token` | Anthropic | Message per token | JavaScript | +| `anthropic-message-per-response` | Anthropic | Message per response | JavaScript | +| `vercel-message-per-token` | Vercel AI SDK | Message per token | JavaScript | +| `vercel-message-per-response` | Vercel AI SDK | Message per response | JavaScript | +| `lang-graph-message-per-token` | LangGraph | Message per token | JavaScript | +| `lang-graph-message-per-response` | LangGraph | Message per response | JavaScript | + +## Streaming patterns + +- **Message per token**: Publisher sends discrete `start`, `token`, and `stop` events. Subscriber reconstructs the full response by appending tokens correlated by `responseId`. +- **Message per response**: Publisher creates a single message and appends each token to it. Subscriber handles `message.create` and `message.append` actions, with the full response available in message history. + +## Prerequisites + +- Node.js 20+ +- API keys for the relevant providers (see `.env.example`) + +## Setup + +```bash +cd guides/ai-transport +cp .env.example .env +# Fill in your API keys in .env + +yarn install +``` + +## Running a guide + +Each guide has a publisher (streams from LLM to Ably) and a subscriber (reads from Ably): + +```bash +# Terminal 1 - start the subscriber +npx tsx /javascript/src/subscriber.ts + +# Terminal 2 - start the publisher +npx tsx /javascript/src/publisher.ts +``` + +## Running tests + +```bash +# All guides +yarn test + +# Watch mode +yarn test:watch + +# Single guide +npx vitest run +``` diff --git a/guides/ai-transport/anthropic-message-per-response/javascript/package.json b/guides/ai-transport/anthropic-message-per-response/javascript/package.json new file mode 100644 index 0000000000..ca8aaea8a2 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-response/javascript/package.json @@ -0,0 +1,14 @@ +{ + "name": "guide-anthropic-message-per-response", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.71" + } +} diff --git a/guides/ai-transport/anthropic-message-per-response/javascript/src/publisher.ts b/guides/ai-transport/anthropic-message-per-response/javascript/src/publisher.ts new file mode 100644 index 0000000000..143e2be744 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-response/javascript/src/publisher.ts @@ -0,0 +1,53 @@ +import Anthropic from '@anthropic-ai/sdk'; +import Ably from 'ably'; + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + const anthropic = new Anthropic(); + let msgSerial: string | null = null; + let textBlockIndex: number | null = null; + + const stream = await anthropic.messages.create({ + model: 'claude-sonnet-4-5-20250929', + max_tokens: 1024, + messages: [{ role: 'user', content: prompt }], + stream: true, + }); + + for await (const event of stream) { + switch (event.type) { + case 'message_start': { + const result = await channel.publish({ name: 'response', data: '' }); + msgSerial = result.serials[0]; + break; + } + + case 'content_block_start': + if (event.content_block.type === 'text') { + textBlockIndex = event.index; + } + break; + + case 'content_block_delta': + if (event.index === textBlockIndex && event.delta.type === 'text_delta' && msgSerial) { + channel.appendMessage({ serial: msgSerial, data: event.delta.text }); + } + break; + + case 'message_stop': + break; + } + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('ai:anthropic-mpr-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/anthropic-message-per-response/javascript/src/subscriber.ts b/guides/ai-transport/anthropic-message-per-response/javascript/src/subscriber.ts new file mode 100644 index 0000000000..d57e24ab12 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-response/javascript/src/subscriber.ts @@ -0,0 +1,61 @@ +import Ably from 'ably'; + +// Subscribe to a channel and reconstruct streamed responses using message actions. +// Returns a promise that resolves with the full response text when no new appends +// arrive for 2 seconds (indicating the stream is complete). +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + let lastSerial: string | null = null; + let doneTimer: ReturnType | null = null; + + const resetTimer = () => { + if (doneTimer) clearTimeout(doneTimer); + doneTimer = setTimeout(() => { + if (lastSerial) { + const finalText = responses.get(lastSerial) || ''; + resolve(finalText); + } + }, 3000); + }; + + channel.subscribe((message: Ably.Message) => { + switch (message.action) { + case 'message.create': + console.log('\n[Response started]', message.serial); + responses.set(message.serial, message.data as string); + lastSerial = message.serial; + resetTimer(); + break; + + case 'message.append': { + const current = responses.get(message.serial) || ''; + responses.set(message.serial, current + (message.data as string)); + process.stdout.write(message.data as string); + resetTimer(); + break; + } + + case 'message.update': + responses.set(message.serial, message.data as string); + console.log('\n[Response updated with full content]'); + resetTimer(); + break; + } + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('ai:anthropic-mpr-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/anthropic-message-per-response/javascript/test/e2e.test.ts b/guides/ai-transport/anthropic-message-per-response/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..a3162774ed --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-response/javascript/test/e2e.test.ts @@ -0,0 +1,76 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('anthropic-message-per-response', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `ai:test-anthropic-mpr-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes initial message and appends tokens', async () => { + const channel = subscriberClient.channels.get(channelName + '-actions'); + const pubChannel = publisherClient.channels.get(channelName + '-actions'); + const actions: { action: string; data?: string; serial?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + actions.push({ + action: message.action!, + data: message.data as string | undefined, + serial: message.serial, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + expect(actions[0].action).toBe('message.create'); + const appendActions = actions.filter((a) => a.action === 'message.append'); + expect(appendActions.length).toBeGreaterThan(0); + // All actions should share the same serial + const serials = new Set(actions.map((a) => a.serial)); + expect(serials.size).toBe(1); + }); + + it('subscriber reconstructs the full response from append actions', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('appended tokens concatenate to match the full response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const appendedTokens: string[] = []; + + await channel.subscribe((message: Ably.Message) => { + if (message.action === 'message.append') { + appendedTokens.push(message.data as string); + } + }); + + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = appendedTokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/anthropic-message-per-response/javascript/tsconfig.json b/guides/ai-transport/anthropic-message-per-response/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-response/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/anthropic-message-per-token/javascript/package.json b/guides/ai-transport/anthropic-message-per-token/javascript/package.json new file mode 100644 index 0000000000..4170180553 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-token/javascript/package.json @@ -0,0 +1,14 @@ +{ + "name": "guide-anthropic-message-per-token", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.71" + } +} diff --git a/guides/ai-transport/anthropic-message-per-token/javascript/src/publisher.ts b/guides/ai-transport/anthropic-message-per-token/javascript/src/publisher.ts new file mode 100644 index 0000000000..8d6a2d5407 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-token/javascript/src/publisher.ts @@ -0,0 +1,64 @@ +import Anthropic from '@anthropic-ai/sdk'; +import Ably from 'ably'; + +async function processEvent( + event: Anthropic.MessageStreamEvent, + channel: Ably.RealtimeChannel, + state: { responseId: string | null }, +) { + switch (event.type) { + case 'message_start': + state.responseId = event.message.id; + channel.publish({ + name: 'start', + extras: { headers: { responseId: state.responseId } }, + }); + break; + + case 'content_block_delta': + if (event.delta.type === 'text_delta') { + channel.publish({ + name: 'token', + data: event.delta.text, + extras: { headers: { responseId: state.responseId } }, + }); + } + break; + + case 'message_stop': + await channel.publish({ + name: 'stop', + extras: { headers: { responseId: state.responseId } }, + }); + break; + } +} + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + const anthropic = new Anthropic(); + const state = { responseId: null as string | null }; + + const stream = await anthropic.messages.create({ + model: 'claude-sonnet-4-5-20250929', + max_tokens: 1024, + messages: [{ role: 'user', content: prompt }], + stream: true, + }); + + for await (const event of stream) { + await processEvent(event, channel, state); + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('anthropic-mpt-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/anthropic-message-per-token/javascript/src/subscriber.ts b/guides/ai-transport/anthropic-message-per-token/javascript/src/subscriber.ts new file mode 100644 index 0000000000..8358f4edfb --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-token/javascript/src/subscriber.ts @@ -0,0 +1,42 @@ +import Ably from 'ably'; + +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + + channel.subscribe('start', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + console.log('\n[Response started]', responseId); + responses.set(responseId, ''); + }); + + channel.subscribe('token', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const token = message.data as string; + const currentText = responses.get(responseId) || ''; + responses.set(responseId, currentText + token); + process.stdout.write(token); + }); + + channel.subscribe('stop', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const finalText = responses.get(responseId) || ''; + console.log('\n[Response completed]', responseId); + resolve(finalText); + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('anthropic-mpt-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/anthropic-message-per-token/javascript/test/e2e.test.ts b/guides/ai-transport/anthropic-message-per-token/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..2a1efbfefe --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-token/javascript/test/e2e.test.ts @@ -0,0 +1,73 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('anthropic-message-per-token', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `test-anthropic-mpt-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes start, token, and stop events in order', async () => { + const channel = subscriberClient.channels.get(channelName + '-lifecycle'); + const pubChannel = publisherClient.channels.get(channelName + '-lifecycle'); + const events: { name: string; data?: string; responseId?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + events.push({ + name: message.name!, + data: message.data as string | undefined, + responseId: message.extras?.headers?.responseId, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + expect(events[0].name).toBe('start'); + expect(events[events.length - 1].name).toBe('stop'); + const tokenEvents = events.filter((e) => e.name === 'token'); + expect(tokenEvents.length).toBeGreaterThan(0); + const responseIds = new Set(events.map((e) => e.responseId)); + expect(responseIds.size).toBe(1); + expect(responseIds.values().next().value).toBeTruthy(); + }); + + it('subscriber reconstructs the full response from token events', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('token data concatenates to match the complete response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const tokens: string[] = []; + await channel.subscribe('token', (message: Ably.Message) => { + tokens.push(message.data as string); + }); + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = tokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/anthropic-message-per-token/javascript/tsconfig.json b/guides/ai-transport/anthropic-message-per-token/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/anthropic-message-per-token/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/lang-graph-message-per-response/javascript/package.json b/guides/ai-transport/lang-graph-message-per-response/javascript/package.json new file mode 100644 index 0000000000..4d751fd84f --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-response/javascript/package.json @@ -0,0 +1,16 @@ +{ + "name": "guide-lang-graph-message-per-response", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "@langchain/langgraph": "^0.2", + "@langchain/anthropic": "^0.3", + "@langchain/core": "^0.3" + } +} diff --git a/guides/ai-transport/lang-graph-message-per-response/javascript/src/publisher.ts b/guides/ai-transport/lang-graph-message-per-response/javascript/src/publisher.ts new file mode 100644 index 0000000000..58ba92f0f6 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-response/javascript/src/publisher.ts @@ -0,0 +1,56 @@ +import { ChatAnthropic } from '@langchain/anthropic'; +import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; +import Ably from 'ably'; + +const model = new ChatAnthropic({ model: 'claude-sonnet-4-5-20250929' }); + +const StateAnnotation = Annotation.Root({ + messages: Annotation({ + reducer: (x, y) => x.concat(y), + default: () => [], + }), +}); + +const graph = new StateGraph(StateAnnotation) + .addNode('agent', async (state) => { + const response = await model.invoke(state.messages); + return { messages: [response] }; + }) + .addEdge(START, 'agent') + .addEdge('agent', END); + +const app = graph.compile(); + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + let msgSerial: string | null = null; + + const stream = await app.stream( + { messages: [{ role: 'user', content: prompt }] }, + { streamMode: 'messages' }, + ); + + for await (const [messageChunk, _metadata] of stream) { + if (!msgSerial && messageChunk?.id) { + const result = await channel.publish({ name: 'response', data: '' }); + msgSerial = result.serials[0]; + } + + const content = messageChunk?.content; + if (content && msgSerial) { + channel.appendMessage({ serial: msgSerial, data: content }); + } + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('ai:langgraph-mpr-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/lang-graph-message-per-response/javascript/src/subscriber.ts b/guides/ai-transport/lang-graph-message-per-response/javascript/src/subscriber.ts new file mode 100644 index 0000000000..c9ba750fd2 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-response/javascript/src/subscriber.ts @@ -0,0 +1,61 @@ +import Ably from 'ably'; + +// Subscribe to a channel and reconstruct streamed responses using message actions. +// Returns a promise that resolves with the full response text when no new appends +// arrive for 2 seconds (indicating the stream is complete). +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + let lastSerial: string | null = null; + let doneTimer: ReturnType | null = null; + + const resetTimer = () => { + if (doneTimer) clearTimeout(doneTimer); + doneTimer = setTimeout(() => { + if (lastSerial) { + const finalText = responses.get(lastSerial) || ''; + resolve(finalText); + } + }, 3000); + }; + + channel.subscribe((message: Ably.Message) => { + switch (message.action) { + case 'message.create': + console.log('\n[Response started]', message.serial); + responses.set(message.serial, message.data as string); + lastSerial = message.serial; + resetTimer(); + break; + + case 'message.append': { + const current = responses.get(message.serial) || ''; + responses.set(message.serial, current + (message.data as string)); + process.stdout.write(message.data as string); + resetTimer(); + break; + } + + case 'message.update': + responses.set(message.serial, message.data as string); + console.log('\n[Response updated with full content]'); + resetTimer(); + break; + } + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('ai:langgraph-mpr-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/lang-graph-message-per-response/javascript/test/e2e.test.ts b/guides/ai-transport/lang-graph-message-per-response/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..10d7dc2163 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-response/javascript/test/e2e.test.ts @@ -0,0 +1,76 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('lang-graph-message-per-response', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `ai:test-lg-mpr-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes initial message and appends tokens', async () => { + const channel = subscriberClient.channels.get(channelName + '-actions'); + const pubChannel = publisherClient.channels.get(channelName + '-actions'); + const actions: { action: string; data?: string; serial?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + actions.push({ + action: message.action!, + data: message.data as string | undefined, + serial: message.serial, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + expect(actions[0].action).toBe('message.create'); + const appendActions = actions.filter((a) => a.action === 'message.append'); + expect(appendActions.length).toBeGreaterThan(0); + // All actions should share the same serial + const serials = new Set(actions.map((a) => a.serial)); + expect(serials.size).toBe(1); + }); + + it('subscriber reconstructs the full response from append actions', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('appended tokens concatenate to match the full response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const appendedTokens: string[] = []; + + await channel.subscribe((message: Ably.Message) => { + if (message.action === 'message.append') { + appendedTokens.push(message.data as string); + } + }); + + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = appendedTokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/lang-graph-message-per-response/javascript/tsconfig.json b/guides/ai-transport/lang-graph-message-per-response/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-response/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/lang-graph-message-per-token/javascript/package.json b/guides/ai-transport/lang-graph-message-per-token/javascript/package.json new file mode 100644 index 0000000000..125378263d --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-token/javascript/package.json @@ -0,0 +1,16 @@ +{ + "name": "guide-lang-graph-message-per-token", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "@langchain/langgraph": "^0.2", + "@langchain/anthropic": "^0.3", + "@langchain/core": "^0.3" + } +} diff --git a/guides/ai-transport/lang-graph-message-per-token/javascript/src/publisher.ts b/guides/ai-transport/lang-graph-message-per-token/javascript/src/publisher.ts new file mode 100644 index 0000000000..51c5a62df8 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-token/javascript/src/publisher.ts @@ -0,0 +1,68 @@ +import { ChatAnthropic } from '@langchain/anthropic'; +import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; +import Ably from 'ably'; + +const model = new ChatAnthropic({ model: 'claude-sonnet-4-5-20250929' }); + +const StateAnnotation = Annotation.Root({ + messages: Annotation({ + reducer: (x, y) => x.concat(y), + default: () => [], + }), +}); + +const graph = new StateGraph(StateAnnotation) + .addNode('agent', async (state) => { + const response = await model.invoke(state.messages); + return { messages: [response] }; + }) + .addEdge(START, 'agent') + .addEdge('agent', END); + +const app = graph.compile(); + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + let responseId: string | null = null; + + const stream = await app.stream( + { messages: [{ role: 'user', content: prompt }] }, + { streamMode: 'messages' }, + ); + + for await (const [messageChunk, _metadata] of stream) { + if (!responseId && messageChunk?.id) { + responseId = messageChunk.id; + channel.publish({ + name: 'start', + extras: { headers: { responseId } }, + }); + } + + const content = messageChunk?.content; + if (content) { + channel.publish({ + name: 'token', + data: content, + extras: { headers: { responseId } }, + }); + } + } + + await channel.publish({ + name: 'stop', + extras: { headers: { responseId } }, + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('langgraph-mpt-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/lang-graph-message-per-token/javascript/src/subscriber.ts b/guides/ai-transport/lang-graph-message-per-token/javascript/src/subscriber.ts new file mode 100644 index 0000000000..6c423d77c2 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-token/javascript/src/subscriber.ts @@ -0,0 +1,42 @@ +import Ably from 'ably'; + +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + + channel.subscribe('start', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + console.log('\n[Response started]', responseId); + responses.set(responseId, ''); + }); + + channel.subscribe('token', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const token = message.data as string; + const currentText = responses.get(responseId) || ''; + responses.set(responseId, currentText + token); + process.stdout.write(token); + }); + + channel.subscribe('stop', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const finalText = responses.get(responseId) || ''; + console.log('\n[Response completed]', responseId); + resolve(finalText); + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('langgraph-mpt-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/lang-graph-message-per-token/javascript/test/e2e.test.ts b/guides/ai-transport/lang-graph-message-per-token/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..518bcc1df0 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-token/javascript/test/e2e.test.ts @@ -0,0 +1,73 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('lang-graph-message-per-token', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `test-lg-mpt-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes start, token, and stop events in order', async () => { + const channel = subscriberClient.channels.get(channelName + '-lifecycle'); + const pubChannel = publisherClient.channels.get(channelName + '-lifecycle'); + const events: { name: string; data?: string; responseId?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + events.push({ + name: message.name!, + data: message.data as string | undefined, + responseId: message.extras?.headers?.responseId, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + expect(events[0].name).toBe('start'); + expect(events[events.length - 1].name).toBe('stop'); + const tokenEvents = events.filter((e) => e.name === 'token'); + expect(tokenEvents.length).toBeGreaterThan(0); + const responseIds = new Set(events.map((e) => e.responseId)); + expect(responseIds.size).toBe(1); + expect(responseIds.values().next().value).toBeTruthy(); + }); + + it('subscriber reconstructs the full response from token events', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('token data concatenates to match the complete response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const tokens: string[] = []; + await channel.subscribe('token', (message: Ably.Message) => { + tokens.push(message.data as string); + }); + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = tokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/lang-graph-message-per-token/javascript/tsconfig.json b/guides/ai-transport/lang-graph-message-per-token/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/lang-graph-message-per-token/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/openai-message-per-response/javascript/package.json b/guides/ai-transport/openai-message-per-response/javascript/package.json new file mode 100644 index 0000000000..2b741fd2ce --- /dev/null +++ b/guides/ai-transport/openai-message-per-response/javascript/package.json @@ -0,0 +1,14 @@ +{ + "name": "guide-openai-message-per-response", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "openai": "^4" + } +} diff --git a/guides/ai-transport/openai-message-per-response/javascript/src/publisher.ts b/guides/ai-transport/openai-message-per-response/javascript/src/publisher.ts new file mode 100644 index 0000000000..9d9d91604d --- /dev/null +++ b/guides/ai-transport/openai-message-per-response/javascript/src/publisher.ts @@ -0,0 +1,52 @@ +import OpenAI from 'openai'; +import Ably from 'ably'; + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + const openai = new OpenAI(); + let msgSerial: string | null = null; + let messageItemId: string | null = null; + + const stream = await openai.responses.create({ + model: 'gpt-4o-mini', + input: prompt, + stream: true, + }); + + for await (const event of stream) { + switch (event.type) { + case 'response.created': { + const result = await channel.publish({ name: 'response', data: '' }); + msgSerial = result.serials[0]; + break; + } + + case 'response.output_item.added': + if (event.item.type === 'message') { + messageItemId = event.item.id; + } + break; + + case 'response.output_text.delta': + if (event.item_id === messageItemId && msgSerial) { + channel.appendMessage({ serial: msgSerial, data: event.delta }); + } + break; + + case 'response.completed': + break; + } + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('ai:openai-mpr-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/openai-message-per-response/javascript/src/subscriber.ts b/guides/ai-transport/openai-message-per-response/javascript/src/subscriber.ts new file mode 100644 index 0000000000..4e91c9d02f --- /dev/null +++ b/guides/ai-transport/openai-message-per-response/javascript/src/subscriber.ts @@ -0,0 +1,61 @@ +import Ably from 'ably'; + +// Subscribe to a channel and reconstruct streamed responses using message actions. +// Returns a promise that resolves with the full response text when no new appends +// arrive for 2 seconds (indicating the stream is complete). +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + let lastSerial: string | null = null; + let doneTimer: ReturnType | null = null; + + const resetTimer = () => { + if (doneTimer) clearTimeout(doneTimer); + doneTimer = setTimeout(() => { + if (lastSerial) { + const finalText = responses.get(lastSerial) || ''; + resolve(finalText); + } + }, 3000); + }; + + channel.subscribe((message: Ably.Message) => { + switch (message.action) { + case 'message.create': + console.log('\n[Response started]', message.serial); + responses.set(message.serial, message.data as string); + lastSerial = message.serial; + resetTimer(); + break; + + case 'message.append': { + const current = responses.get(message.serial) || ''; + responses.set(message.serial, current + (message.data as string)); + process.stdout.write(message.data as string); + resetTimer(); + break; + } + + case 'message.update': + responses.set(message.serial, message.data as string); + console.log('\n[Response updated with full content]'); + resetTimer(); + break; + } + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('ai:openai-mpr-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/openai-message-per-response/javascript/test/e2e.test.ts b/guides/ai-transport/openai-message-per-response/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..e63bc9a462 --- /dev/null +++ b/guides/ai-transport/openai-message-per-response/javascript/test/e2e.test.ts @@ -0,0 +1,76 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('openai-message-per-response', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `ai:test-openai-mpr-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes initial message and appends tokens', async () => { + const channel = subscriberClient.channels.get(channelName + '-actions'); + const pubChannel = publisherClient.channels.get(channelName + '-actions'); + const actions: { action: string; data?: string; serial?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + actions.push({ + action: message.action!, + data: message.data as string | undefined, + serial: message.serial, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + expect(actions[0].action).toBe('message.create'); + const appendActions = actions.filter((a) => a.action === 'message.append'); + expect(appendActions.length).toBeGreaterThan(0); + // All actions should share the same serial + const serials = new Set(actions.map((a) => a.serial)); + expect(serials.size).toBe(1); + }); + + it('subscriber reconstructs the full response from append actions', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('appended tokens concatenate to match the full response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const appendedTokens: string[] = []; + + await channel.subscribe((message: Ably.Message) => { + if (message.action === 'message.append') { + appendedTokens.push(message.data as string); + } + }); + + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = appendedTokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/openai-message-per-response/javascript/tsconfig.json b/guides/ai-transport/openai-message-per-response/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/openai-message-per-response/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/openai-message-per-token/javascript/package.json b/guides/ai-transport/openai-message-per-token/javascript/package.json new file mode 100644 index 0000000000..37b29bff0e --- /dev/null +++ b/guides/ai-transport/openai-message-per-token/javascript/package.json @@ -0,0 +1,14 @@ +{ + "name": "guide-openai-message-per-token", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "openai": "^4" + } +} diff --git a/guides/ai-transport/openai-message-per-token/javascript/src/publisher.ts b/guides/ai-transport/openai-message-per-token/javascript/src/publisher.ts new file mode 100644 index 0000000000..5f3d10fe75 --- /dev/null +++ b/guides/ai-transport/openai-message-per-token/javascript/src/publisher.ts @@ -0,0 +1,87 @@ +import OpenAI from 'openai'; +import Ably from 'ably'; +import 'dotenv/config'; + +// Process each streaming event and publish to Ably. +// Returns a promise for the 'stop' event so the caller can await it. +async function processEvent( + event: OpenAI.Responses.ResponseStreamEvent, + channel: Ably.RealtimeChannel, + state: { responseId: string | null; messageItemId: string | null }, +) { + switch (event.type) { + case 'response.created': + state.responseId = event.response.id; + channel.publish({ + name: 'start', + extras: { + headers: { responseId: state.responseId }, + }, + }); + break; + + case 'response.output_item.added': + if (event.item.type === 'message') { + state.messageItemId = event.item.id; + } + break; + + case 'response.output_text.delta': + if (event.item_id === state.messageItemId) { + channel.publish({ + name: 'token', + data: event.delta, + extras: { + headers: { responseId: state.responseId }, + }, + }); + } + break; + + case 'response.completed': + // Await the final publish so all messages are flushed before returning + await channel.publish({ + name: 'stop', + extras: { + headers: { responseId: state.responseId }, + }, + }); + break; + } +} + +// Stream a response from OpenAI and publish tokens to Ably +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + const openai = new OpenAI(); + const state = { responseId: null as string | null, messageItemId: null as string | null }; + + const stream = await openai.responses.create({ + model: 'gpt-4o-mini', + input: prompt, + stream: true, + }); + + for await (const event of stream) { + await processEvent(event, channel, state); + } +} + +// Run as a standalone script +async function main() { + const realtime = new Ably.Realtime({ + key: process.env.ABLY_API_KEY, + echoMessages: false, + }); + + const channel = realtime.channels.get('openai-mpt-guide'); + + await publish(channel, 'Tell me a short joke'); + + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/openai-message-per-token/javascript/src/subscriber.ts b/guides/ai-transport/openai-message-per-token/javascript/src/subscriber.ts new file mode 100644 index 0000000000..4d19ef17d9 --- /dev/null +++ b/guides/ai-transport/openai-message-per-token/javascript/src/subscriber.ts @@ -0,0 +1,53 @@ +import Ably from 'ably'; +import 'dotenv/config'; + +// Subscribe to a channel and reconstruct streamed responses. +// Returns a promise that resolves with the full response text when a stop event is received. +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + + channel.subscribe('start', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + console.log('\n[Response started]', responseId); + responses.set(responseId, ''); + }); + + channel.subscribe('token', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const token = message.data as string; + + const currentText = responses.get(responseId) || ''; + responses.set(responseId, currentText + token); + + process.stdout.write(token); + }); + + channel.subscribe('stop', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const finalText = responses.get(responseId) || ''; + console.log('\n[Response completed]', responseId); + resolve(finalText); + }); + }); +} + +// Run as a standalone script +async function main() { + const realtime = new Ably.Realtime({ + key: process.env.ABLY_API_KEY, + }); + + const channel = realtime.channels.get('openai-mpt-guide'); + + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/openai-message-per-token/javascript/test/e2e.test.ts b/guides/ai-transport/openai-message-per-token/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..286046426d --- /dev/null +++ b/guides/ai-transport/openai-message-per-token/javascript/test/e2e.test.ts @@ -0,0 +1,105 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('openai-message-per-token', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `test-openai-mpt-${Date.now()}`; + + publisherClient = new Ably.Realtime({ + key: process.env.ABLY_API_KEY, + echoMessages: false, + }); + + subscriberClient = new Ably.Realtime({ + key: process.env.ABLY_API_KEY, + }); + }); + + afterAll(async () => { + // Allow pending publishes to flush before closing + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes start, token, and stop events in order', async () => { + const channel = subscriberClient.channels.get(channelName + '-lifecycle'); + const pubChannel = publisherClient.channels.get(channelName + '-lifecycle'); + + const events: { name: string; data?: string; responseId?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + events.push({ + name: message.name!, + data: message.data as string | undefined, + responseId: message.extras?.headers?.responseId, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + + // Wait for events to propagate + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // First event must be 'start' + expect(events[0].name).toBe('start'); + + // Last event must be 'stop' + expect(events[events.length - 1].name).toBe('stop'); + + // Middle events must all be 'token' + const tokenEvents = events.filter((e) => e.name === 'token'); + expect(tokenEvents.length).toBeGreaterThan(0); + + // All events must share the same responseId + const responseIds = new Set(events.map((e) => e.responseId)); + expect(responseIds.size).toBe(1); + expect(responseIds.values().next().value).toBeTruthy(); + }); + + it('subscriber reconstructs the full response from token events', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + + const responsePromise = subscribe(subChannel); + + // Ensure subscriber is attached before publishing + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await publish(pubChannel, 'Reply with exactly: Hello world'); + + const fullResponse = await responsePromise; + + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('token data concatenates to match the complete response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + + const tokens: string[] = []; + + await channel.subscribe('token', (message: Ably.Message) => { + tokens.push(message.data as string); + }); + + const responsePromise = subscribe(channel); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await publish(pubChannel, 'Reply with exactly: Test'); + + const fullResponse = await responsePromise; + + // Concatenated tokens should equal the subscriber's reconstructed response + const concatenated = tokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/openai-message-per-token/javascript/tsconfig.json b/guides/ai-transport/openai-message-per-token/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/openai-message-per-token/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/package.json b/guides/ai-transport/package.json new file mode 100644 index 0000000000..8f29318749 --- /dev/null +++ b/guides/ai-transport/package.json @@ -0,0 +1,32 @@ +{ + "name": "ai-transport-guides", + "version": "0.1.0", + "private": true, + "type": "module", + "engines": { + "node": ">=20.0.0" + }, + "workspaces": [ + "openai-message-per-token/javascript", + "openai-message-per-response/javascript", + "anthropic-message-per-token/javascript", + "anthropic-message-per-response/javascript", + "vercel-message-per-token/javascript", + "vercel-message-per-response/javascript", + "lang-graph-message-per-token/javascript", + "lang-graph-message-per-response/javascript" + ], + "scripts": { + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "ably": "^2.17.0", + "dotenv": "^16.4.5" + }, + "devDependencies": { + "tsx": "^4.19.0", + "typescript": "^5", + "vitest": "^3.0.0" + } +} diff --git a/guides/ai-transport/tsconfig.base.json b/guides/ai-transport/tsconfig.base.json new file mode 100644 index 0000000000..10dc38349c --- /dev/null +++ b/guides/ai-transport/tsconfig.base.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "isolatedModules": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/guides/ai-transport/vercel-message-per-response/javascript/package.json b/guides/ai-transport/vercel-message-per-response/javascript/package.json new file mode 100644 index 0000000000..9f4184b334 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-response/javascript/package.json @@ -0,0 +1,15 @@ +{ + "name": "guide-vercel-message-per-response", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "ai": "^6", + "@ai-sdk/openai": "^2" + } +} diff --git a/guides/ai-transport/vercel-message-per-response/javascript/src/publisher.ts b/guides/ai-transport/vercel-message-per-response/javascript/src/publisher.ts new file mode 100644 index 0000000000..79dd60b720 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-response/javascript/src/publisher.ts @@ -0,0 +1,44 @@ +import { streamText } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import Ably from 'ably'; + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + let msgSerial: string | null = null; + + const result = streamText({ + model: openai('gpt-4o-mini'), + prompt, + }); + + for await (const event of result.fullStream) { + switch (event.type) { + case 'text-start': { + const publishResult = await channel.publish({ name: 'response', data: '' }); + msgSerial = publishResult.serials[0]; + break; + } + + case 'text-delta': + if (msgSerial) { + channel.appendMessage({ serial: msgSerial, data: (event as any).text }); + } + break; + + case 'text-end': + break; + } + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('ai:vercel-mpr-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/vercel-message-per-response/javascript/src/subscriber.ts b/guides/ai-transport/vercel-message-per-response/javascript/src/subscriber.ts new file mode 100644 index 0000000000..7fa402d9ae --- /dev/null +++ b/guides/ai-transport/vercel-message-per-response/javascript/src/subscriber.ts @@ -0,0 +1,61 @@ +import Ably from 'ably'; + +// Subscribe to a channel and reconstruct streamed responses using message actions. +// Returns a promise that resolves with the full response text when no new appends +// arrive for 2 seconds (indicating the stream is complete). +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + let lastSerial: string | null = null; + let doneTimer: ReturnType | null = null; + + const resetTimer = () => { + if (doneTimer) clearTimeout(doneTimer); + doneTimer = setTimeout(() => { + if (lastSerial) { + const finalText = responses.get(lastSerial) || ''; + resolve(finalText); + } + }, 3000); + }; + + channel.subscribe((message: Ably.Message) => { + switch (message.action) { + case 'message.create': + console.log('\n[Response started]', message.serial); + responses.set(message.serial, message.data as string); + lastSerial = message.serial; + resetTimer(); + break; + + case 'message.append': { + const current = responses.get(message.serial) || ''; + responses.set(message.serial, current + (message.data as string)); + process.stdout.write(message.data as string); + resetTimer(); + break; + } + + case 'message.update': + responses.set(message.serial, message.data as string); + console.log('\n[Response updated with full content]'); + resetTimer(); + break; + } + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('ai:vercel-mpr-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/vercel-message-per-response/javascript/test/e2e.test.ts b/guides/ai-transport/vercel-message-per-response/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..78fe8d1b32 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-response/javascript/test/e2e.test.ts @@ -0,0 +1,76 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('vercel-message-per-response', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `ai:test-vercel-mpr-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes initial message and appends tokens', async () => { + const channel = subscriberClient.channels.get(channelName + '-actions'); + const pubChannel = publisherClient.channels.get(channelName + '-actions'); + const actions: { action: string; data?: string; serial?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + actions.push({ + action: message.action!, + data: message.data as string | undefined, + serial: message.serial, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 3000)); + + expect(actions[0].action).toBe('message.create'); + const appendActions = actions.filter((a) => a.action === 'message.append'); + expect(appendActions.length).toBeGreaterThan(0); + // All actions should share the same serial + const serials = new Set(actions.map((a) => a.serial)); + expect(serials.size).toBe(1); + }); + + it('subscriber reconstructs the full response from append actions', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('appended tokens concatenate to match the full response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const appendedTokens: string[] = []; + + await channel.subscribe((message: Ably.Message) => { + if (message.action === 'message.append') { + appendedTokens.push(message.data as string); + } + }); + + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = appendedTokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/vercel-message-per-response/javascript/tsconfig.json b/guides/ai-transport/vercel-message-per-response/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-response/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/vercel-message-per-token/javascript/package.json b/guides/ai-transport/vercel-message-per-token/javascript/package.json new file mode 100644 index 0000000000..9bd0df936a --- /dev/null +++ b/guides/ai-transport/vercel-message-per-token/javascript/package.json @@ -0,0 +1,15 @@ +{ + "name": "guide-vercel-message-per-token", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "start:publisher": "tsx src/publisher.ts", + "start:subscriber": "tsx src/subscriber.ts", + "test": "vitest run" + }, + "dependencies": { + "ai": "^6", + "@ai-sdk/openai": "^2" + } +} diff --git a/guides/ai-transport/vercel-message-per-token/javascript/src/publisher.ts b/guides/ai-transport/vercel-message-per-token/javascript/src/publisher.ts new file mode 100644 index 0000000000..b96a0644c3 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-token/javascript/src/publisher.ts @@ -0,0 +1,60 @@ +import { streamText } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import Ably from 'ably'; + +async function processEvent( + event: { type: string; id?: string; text?: string }, + channel: Ably.RealtimeChannel, + state: { responseId: string | null }, +) { + switch (event.type) { + case 'text-start': + state.responseId = event.id || null; + channel.publish({ + name: 'start', + extras: { headers: { responseId: state.responseId } }, + }); + break; + + case 'text-delta': + channel.publish({ + name: 'token', + data: event.text, + extras: { headers: { responseId: state.responseId } }, + }); + break; + + case 'text-end': + await channel.publish({ + name: 'stop', + extras: { headers: { responseId: state.responseId } }, + }); + break; + } +} + +export async function publish(channel: Ably.RealtimeChannel, prompt: string) { + const state = { responseId: null as string | null }; + + const result = streamText({ + model: openai('gpt-4o-mini'), + prompt, + }); + + for await (const event of result.fullStream) { + await processEvent(event as { type: string; id?: string; text?: string }, channel, state); + } +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + const channel = realtime.channels.get('vercel-mpt-guide'); + await publish(channel, 'Tell me a short joke'); + console.log('Done streaming. Closing connection...'); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/vercel-message-per-token/javascript/src/subscriber.ts b/guides/ai-transport/vercel-message-per-token/javascript/src/subscriber.ts new file mode 100644 index 0000000000..07e6981332 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-token/javascript/src/subscriber.ts @@ -0,0 +1,42 @@ +import Ably from 'ably'; + +export function subscribe(channel: Ably.RealtimeChannel): Promise { + return new Promise((resolve) => { + const responses = new Map(); + + channel.subscribe('start', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + console.log('\n[Response started]', responseId); + responses.set(responseId, ''); + }); + + channel.subscribe('token', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const token = message.data as string; + const currentText = responses.get(responseId) || ''; + responses.set(responseId, currentText + token); + process.stdout.write(token); + }); + + channel.subscribe('stop', (message: Ably.Message) => { + const responseId = message.extras?.headers?.responseId; + const finalText = responses.get(responseId) || ''; + console.log('\n[Response completed]', responseId); + resolve(finalText); + }); + }); +} + +async function main() { + const realtime = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + const channel = realtime.channels.get('vercel-mpt-guide'); + console.log('Subscriber ready, waiting for tokens...'); + const response = await subscribe(channel); + console.log('\nFull response:', response); + realtime.close(); +} + +import { fileURLToPath } from 'url'; +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main().catch(console.error); +} diff --git a/guides/ai-transport/vercel-message-per-token/javascript/test/e2e.test.ts b/guides/ai-transport/vercel-message-per-token/javascript/test/e2e.test.ts new file mode 100644 index 0000000000..3c183ab76c --- /dev/null +++ b/guides/ai-transport/vercel-message-per-token/javascript/test/e2e.test.ts @@ -0,0 +1,73 @@ +import Ably from 'ably'; +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { publish } from '../src/publisher.js'; +import { subscribe } from '../src/subscriber.js'; + +describe('vercel-message-per-token', () => { + let publisherClient: Ably.Realtime; + let subscriberClient: Ably.Realtime; + let channelName: string; + + beforeAll(() => { + channelName = `test-vercel-mpt-${Date.now()}`; + publisherClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY, echoMessages: false }); + subscriberClient = new Ably.Realtime({ key: process.env.ABLY_API_KEY }); + }); + + afterAll(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + publisherClient?.close(); + subscriberClient?.close(); + }); + + it('publishes start, token, and stop events in order', async () => { + const channel = subscriberClient.channels.get(channelName + '-lifecycle'); + const pubChannel = publisherClient.channels.get(channelName + '-lifecycle'); + const events: { name: string; data?: string; responseId?: string }[] = []; + + await channel.subscribe((message: Ably.Message) => { + events.push({ + name: message.name!, + data: message.data as string | undefined, + responseId: message.extras?.headers?.responseId, + }); + }); + + await publish(pubChannel, 'Reply with exactly: OK'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + + expect(events[0].name).toBe('start'); + expect(events[events.length - 1].name).toBe('stop'); + const tokenEvents = events.filter((e) => e.name === 'token'); + expect(tokenEvents.length).toBeGreaterThan(0); + const responseIds = new Set(events.map((e) => e.responseId)); + expect(responseIds.size).toBe(1); + expect(responseIds.values().next().value).toBeTruthy(); + }); + + it('subscriber reconstructs the full response from token events', async () => { + const subChannel = subscriberClient.channels.get(channelName + '-reconstruct'); + const pubChannel = publisherClient.channels.get(channelName + '-reconstruct'); + const responsePromise = subscribe(subChannel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Hello world'); + const fullResponse = await responsePromise; + expect(fullResponse.length).toBeGreaterThan(0); + expect(fullResponse.toLowerCase()).toContain('hello'); + }); + + it('token data concatenates to match the complete response', async () => { + const channel = subscriberClient.channels.get(channelName + '-concat'); + const pubChannel = publisherClient.channels.get(channelName + '-concat'); + const tokens: string[] = []; + await channel.subscribe('token', (message: Ably.Message) => { + tokens.push(message.data as string); + }); + const responsePromise = subscribe(channel); + await new Promise((resolve) => setTimeout(resolve, 1000)); + await publish(pubChannel, 'Reply with exactly: Test'); + const fullResponse = await responsePromise; + const concatenated = tokens.join(''); + expect(concatenated).toBe(fullResponse); + }); +}); diff --git a/guides/ai-transport/vercel-message-per-token/javascript/tsconfig.json b/guides/ai-transport/vercel-message-per-token/javascript/tsconfig.json new file mode 100644 index 0000000000..6ece4d3313 --- /dev/null +++ b/guides/ai-transport/vercel-message-per-token/javascript/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "test/**/*.ts"] +} diff --git a/guides/ai-transport/vitest.config.ts b/guides/ai-transport/vitest.config.ts new file mode 100644 index 0000000000..e785262498 --- /dev/null +++ b/guides/ai-transport/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + testTimeout: 30_000, + include: ['*/*/test/**/*.test.ts'], + setupFiles: ['./vitest.setup.ts'], + }, +}); diff --git a/guides/ai-transport/vitest.setup.ts b/guides/ai-transport/vitest.setup.ts new file mode 100644 index 0000000000..3c83d9f599 --- /dev/null +++ b/guides/ai-transport/vitest.setup.ts @@ -0,0 +1,2 @@ +import dotenv from 'dotenv'; +dotenv.config(); diff --git a/guides/ai-transport/yarn.lock b/guides/ai-transport/yarn.lock new file mode 100644 index 0000000000..0d5aeb63e9 --- /dev/null +++ b/guides/ai-transport/yarn.lock @@ -0,0 +1,1556 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ably/msgpack-js@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@ably/msgpack-js/-/msgpack-js-0.4.1.tgz#81526bdea5362ca57c112356899a5e506a34b760" + integrity sha512-Sjxj6SOr17hExAVrsycN7u6oV4PhZcK7Z2S8dM71CH/butgO47cSo/TL6FJPCXUyDAzKkOWjMUpJGyZkEpyu4Q== + dependencies: + bops "^1.0.1" + +"@ai-sdk/gateway@3.0.46": + version "3.0.46" + resolved "https://registry.yarnpkg.com/@ai-sdk/gateway/-/gateway-3.0.46.tgz#e329bebbf66cfc16f2a5c616a40d45e9232a2d72" + integrity sha512-zH1UbNRjG5woOXXFOrVCZraqZuFTtmPvLardMGcgLkzpxKV0U3tAGoyWKSZ862H+eBJfI/Hf2yj/zzGJcCkycg== + dependencies: + "@ai-sdk/provider" "3.0.8" + "@ai-sdk/provider-utils" "4.0.15" + "@vercel/oidc" "3.1.0" + +"@ai-sdk/openai@^2": + version "2.0.91" + resolved "https://registry.yarnpkg.com/@ai-sdk/openai/-/openai-2.0.91.tgz#1c615ee29db62355be261c2da324b5a85169798f" + integrity sha512-lozfRHfSTHg5/UliQjTDcOtISYGbEpt4FS/6QM5PcLmhdT0HmROllaBmG7+JaK+uqFtDXZGgMIpz3bqB9nzqCQ== + dependencies: + "@ai-sdk/provider" "2.0.1" + "@ai-sdk/provider-utils" "3.0.21" + +"@ai-sdk/provider-utils@3.0.21": + version "3.0.21" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-3.0.21.tgz#b98175079d2787f0dda7027d74482f4ae1acfbc8" + integrity sha512-veuMwTLxsgh31Jjn0SnBABnM1f7ebHhRWcV2ZuY3hP3iJDCZ8VXBaYqcHXoOQDqUXTCas08sKQcHyWK+zl882Q== + dependencies: + "@ai-sdk/provider" "2.0.1" + "@standard-schema/spec" "^1.0.0" + eventsource-parser "^3.0.6" + +"@ai-sdk/provider-utils@4.0.15": + version "4.0.15" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz#d585c7c89cfdf13697a40be5768ecd907a251585" + integrity sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w== + dependencies: + "@ai-sdk/provider" "3.0.8" + "@standard-schema/spec" "^1.1.0" + eventsource-parser "^3.0.6" + +"@ai-sdk/provider@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-2.0.1.tgz#4aba415f1815da33a7a81e5f41a0219af53278c0" + integrity sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng== + dependencies: + json-schema "^0.4.0" + +"@ai-sdk/provider@3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-3.0.8.tgz#fd7fac7533c03534ac1d3fb710a6b96e2aa00263" + integrity sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ== + dependencies: + json-schema "^0.4.0" + +"@anthropic-ai/sdk@^0.65.0": + version "0.65.0" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.65.0.tgz#3f464fe2029eacf8e7e7fb8197579d00c8ca7502" + integrity sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw== + dependencies: + json-schema-to-ts "^3.1.1" + +"@anthropic-ai/sdk@^0.71": + version "0.71.2" + resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.71.2.tgz#1e3e08a7b2c3129828480a3d0ca4487472fdde3d" + integrity sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ== + dependencies: + json-schema-to-ts "^3.1.1" + +"@babel/runtime@^7.18.3": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== + +"@cfworker/json-schema@^4.0.2": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6" + integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== + +"@esbuild/aix-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz#815b39267f9bffd3407ea6c376ac32946e24f8d2" + integrity sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg== + +"@esbuild/android-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz#19b882408829ad8e12b10aff2840711b2da361e8" + integrity sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg== + +"@esbuild/android-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.3.tgz#90be58de27915efa27b767fcbdb37a4470627d7b" + integrity sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA== + +"@esbuild/android-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.3.tgz#d7dcc976f16e01a9aaa2f9b938fbec7389f895ac" + integrity sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ== + +"@esbuild/darwin-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz#9f6cac72b3a8532298a6a4493ed639a8988e8abd" + integrity sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg== + +"@esbuild/darwin-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz#ac61d645faa37fd650340f1866b0812e1fb14d6a" + integrity sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg== + +"@esbuild/freebsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz#b8625689d73cf1830fe58c39051acdc12474ea1b" + integrity sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w== + +"@esbuild/freebsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz#07be7dd3c9d42fe0eccd2ab9f9ded780bc53bead" + integrity sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA== + +"@esbuild/linux-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz#bf31918fe5c798586460d2b3d6c46ed2c01ca0b6" + integrity sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg== + +"@esbuild/linux-arm@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz#28493ee46abec1dc3f500223cd9f8d2df08f9d11" + integrity sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw== + +"@esbuild/linux-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz#750752a8b30b43647402561eea764d0a41d0ee29" + integrity sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg== + +"@esbuild/linux-loong64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz#a5a92813a04e71198c50f05adfaf18fc1e95b9ed" + integrity sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA== + +"@esbuild/linux-mips64el@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz#deb45d7fd2d2161eadf1fbc593637ed766d50bb1" + integrity sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw== + +"@esbuild/linux-ppc64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz#6f39ae0b8c4d3d2d61a65b26df79f6e12a1c3d78" + integrity sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA== + +"@esbuild/linux-riscv64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz#4c5c19c3916612ec8e3915187030b9df0b955c1d" + integrity sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ== + +"@esbuild/linux-s390x@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz#9ed17b3198fa08ad5ccaa9e74f6c0aff7ad0156d" + integrity sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw== + +"@esbuild/linux-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz#12383dcbf71b7cf6513e58b4b08d95a710bf52a5" + integrity sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA== + +"@esbuild/netbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz#dd0cb2fa543205fcd931df44f4786bfcce6df7d7" + integrity sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA== + +"@esbuild/netbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz#028ad1807a8e03e155153b2d025b506c3787354b" + integrity sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA== + +"@esbuild/openbsd-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz#e3c16ff3490c9b59b969fffca87f350ffc0e2af5" + integrity sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw== + +"@esbuild/openbsd-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz#c5a4693fcb03d1cbecbf8b422422468dfc0d2a8b" + integrity sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ== + +"@esbuild/openharmony-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz#082082444f12db564a0775a41e1991c0e125055e" + integrity sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g== + +"@esbuild/sunos-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz#5ab036c53f929e8405c4e96e865a424160a1b537" + integrity sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA== + +"@esbuild/win32-arm64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz#38de700ef4b960a0045370c171794526e589862e" + integrity sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA== + +"@esbuild/win32-ia32@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz#451b93dc03ec5d4f38619e6cd64d9f9eff06f55c" + integrity sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q== + +"@esbuild/win32-x64@0.27.3": + version "0.27.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz#0eaf705c941a218a43dba8e09f1df1d6cd2f1f17" + integrity sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA== + +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@langchain/anthropic@^0.3": + version "0.3.34" + resolved "https://registry.yarnpkg.com/@langchain/anthropic/-/anthropic-0.3.34.tgz#ff131b9b612a76d7e97d960058efe3f0ccad8179" + integrity sha512-8bOW1A2VHRCjbzdYElrjxutKNs9NSIxYRGtR+OJWVzluMqoKKh2NmmFrpPizEyqCUEG2tTq5xt6XA1lwfqMJRA== + dependencies: + "@anthropic-ai/sdk" "^0.65.0" + fast-xml-parser "^4.4.1" + +"@langchain/core@^0.3": + version "0.3.80" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.80.tgz#c494a6944e53ab28bf32dc531e257b17cfc8f797" + integrity sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA== + dependencies: + "@cfworker/json-schema" "^4.0.2" + ansi-styles "^5.0.0" + camelcase "6" + decamelize "1.2.0" + js-tiktoken "^1.0.12" + langsmith "^0.3.67" + mustache "^4.2.0" + p-queue "^6.6.2" + p-retry "4" + uuid "^10.0.0" + zod "^3.25.32" + zod-to-json-schema "^3.22.3" + +"@langchain/langgraph-checkpoint@~0.0.17": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.18.tgz#2f7a9cdeda948ccc8d312ba9463810709d71d0b8" + integrity sha512-IS7zJj36VgY+4pf8ZjsVuUWef7oTwt1y9ylvwu0aLuOn1d0fg05Om9DLm3v2GZ2Df6bhLV1kfWAM0IAl9O5rQQ== + dependencies: + uuid "^10.0.0" + +"@langchain/langgraph-sdk@~0.0.32": + version "0.0.112" + resolved "https://registry.yarnpkg.com/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.112.tgz#3186919b60e3381aa8aa32ea9b9c39df1f02a9fd" + integrity sha512-/9W5HSWCqYgwma6EoOspL4BGYxGxeJP6lIquPSF4FA0JlKopaUv58ucZC3vAgdJyCgg6sorCIV/qg7SGpEcCLw== + dependencies: + "@types/json-schema" "^7.0.15" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + +"@langchain/langgraph@^0.2": + version "0.2.74" + resolved "https://registry.yarnpkg.com/@langchain/langgraph/-/langgraph-0.2.74.tgz#37367a1e8bafda3548037a91449a69a84f285def" + integrity sha512-oHpEi5sTZTPaeZX1UnzfM2OAJ21QGQrwReTV6+QnX7h8nDCBzhtipAw1cK616S+X8zpcVOjgOtJuaJhXa4mN8w== + dependencies: + "@langchain/langgraph-checkpoint" "~0.0.17" + "@langchain/langgraph-sdk" "~0.0.32" + uuid "^10.0.0" + zod "^3.23.8" + +"@opentelemetry/api@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== + +"@rollup/rollup-android-arm-eabi@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz#add5e608d4e7be55bc3ca3d962490b8b1890e088" + integrity sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg== + +"@rollup/rollup-android-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz#10bd0382b73592beee6e9800a69401a29da625c4" + integrity sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w== + +"@rollup/rollup-darwin-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz#1e99ab04c0b8c619dd7bbde725ba2b87b55bfd81" + integrity sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg== + +"@rollup/rollup-darwin-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz#69e741aeb2839d2e8f0da2ce7a33d8bd23632423" + integrity sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w== + +"@rollup/rollup-freebsd-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz#3736c232a999c7bef7131355d83ebdf9651a0839" + integrity sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug== + +"@rollup/rollup-freebsd-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz#227dcb8f466684070169942bd3998901c9bfc065" + integrity sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q== + +"@rollup/rollup-linux-arm-gnueabihf@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz#ba004b30df31b724f99ce66e7128248bea17cb0c" + integrity sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw== + +"@rollup/rollup-linux-arm-musleabihf@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz#6929f3e07be6b6da5991f63c6b68b3e473d0a65a" + integrity sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw== + +"@rollup/rollup-linux-arm64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz#06e89fd4a25d21fe5575d60b6f913c0e65297bfa" + integrity sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g== + +"@rollup/rollup-linux-arm64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz#fddabf395b90990d5194038e6cd8c00156ed8ac0" + integrity sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q== + +"@rollup/rollup-linux-loong64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz#04c10bb764bbf09a3c1bd90432e92f58d6603c36" + integrity sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA== + +"@rollup/rollup-linux-loong64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz#f2450361790de80581d8687ea19142d8a4de5c0f" + integrity sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw== + +"@rollup/rollup-linux-ppc64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz#0474f4667259e407eee1a6d38e29041b708f6a30" + integrity sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w== + +"@rollup/rollup-linux-ppc64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz#9f32074819eeb1ddbe51f50ea9dcd61a6745ec33" + integrity sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw== + +"@rollup/rollup-linux-riscv64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz#3fdb9d4b1e29fb6b6a6da9f15654d42eb77b99b2" + integrity sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A== + +"@rollup/rollup-linux-riscv64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz#1de780d64e6be0e3e8762035c22e0d8ea68df8ed" + integrity sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw== + +"@rollup/rollup-linux-s390x-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz#1da022ffd2d9e9f0fd8344ea49e113001fbcac64" + integrity sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg== + +"@rollup/rollup-linux-x64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz#78c16eef9520bd10e1ea7a112593bb58e2842622" + integrity sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg== + +"@rollup/rollup-linux-x64-musl@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz#a7598591b4d9af96cb3167b50a5bf1e02dfea06c" + integrity sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw== + +"@rollup/rollup-openbsd-x64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz#c51d48c07cd6c466560e5bed934aec688ce02614" + integrity sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw== + +"@rollup/rollup-openharmony-arm64@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz#f09921d0b2a0b60afbf3586d2a7a7f208ba6df17" + integrity sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ== + +"@rollup/rollup-win32-arm64-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz#08d491717135376e4a99529821c94ecd433d5b36" + integrity sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ== + +"@rollup/rollup-win32-ia32-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz#b0c12aac1104a8b8f26a5e0098e5facbb3e3964a" + integrity sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew== + +"@rollup/rollup-win32-x64-gnu@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz#b9cccef26f5e6fdc013bf3c0911a3c77428509d0" + integrity sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ== + +"@rollup/rollup-win32-x64-msvc@4.57.1": + version "4.57.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz#a03348e7b559c792b6277cc58874b89ef46e1e72" + integrity sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA== + +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@standard-schema/spec@^1.0.0", "@standard-schema/spec@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/chai@^5.2.2": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" + integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== + dependencies: + "@types/deep-eql" "*" + assertion-error "^2.0.1" + +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + +"@types/estree@1.0.8", "@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/http-cache-semantics@*": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#f6a7788f438cbfde15f29acad46512b4c01913b3" + integrity sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/node-fetch@^2.6.4": + version "2.6.13" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.13.tgz#e0c9b7b5edbdb1b50ce32c127e85e880872d56ee" + integrity sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw== + dependencies: + "@types/node" "*" + form-data "^4.0.4" + +"@types/node@*": + version "25.2.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.2.3.tgz#9c18245be768bdb4ce631566c7da303a5c99a7f8" + integrity sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ== + dependencies: + undici-types "~7.16.0" + +"@types/node@^18.11.18": + version "18.19.130" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.130.tgz#da4c6324793a79defb7a62cba3947ec5add00d59" + integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== + dependencies: + undici-types "~5.26.4" + +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + +"@vercel/oidc@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vercel/oidc/-/oidc-3.1.0.tgz#066caee449b84079f33c7445fc862464fe10ec32" + integrity sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w== + +"@vitest/expect@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433" + integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3" + integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ== + dependencies: + "@vitest/spy" "3.2.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4" + integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766" + integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ== + dependencies: + "@vitest/utils" "3.2.4" + pathe "^2.0.3" + strip-literal "^3.0.0" + +"@vitest/snapshot@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c" + integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ== + dependencies: + "@vitest/pretty-format" "3.2.4" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599" + integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw== + dependencies: + tinyspy "^4.0.3" + +"@vitest/utils@3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea" + integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA== + dependencies: + "@vitest/pretty-format" "3.2.4" + loupe "^3.1.4" + tinyrainbow "^2.0.0" + +ably@^2.17.0: + version "2.17.1" + resolved "https://registry.yarnpkg.com/ably/-/ably-2.17.1.tgz#7fe6877ba141ee70d8856523ec3a21d6716c1916" + integrity sha512-70yfXHoM7JtJD/8FCtPD1gkWW0f+AJqbJp0PsqDAqiyxFB8cPFY+FuKHgNTYb8eRHKXq8hT1xiDphUcY0+GHnA== + dependencies: + "@ably/msgpack-js" "^0.4.0" + dequal "^2.0.3" + fastestsmallesttextencoderdecoder "^1.0.22" + got "^11.8.5" + ulid "^2.3.0" + ws "^8.17.1" + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +agentkeepalive@^4.2.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" + integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== + dependencies: + humanize-ms "^1.2.1" + +ai@^6: + version "6.0.86" + resolved "https://registry.yarnpkg.com/ai/-/ai-6.0.86.tgz#cee563e8a0d9b87f8fdda764f190e7dd3f81ba00" + integrity sha512-U2W2LBCHA/pr0Ui7vmmsjBiLEzBbZF3yVHNy7Rbzn7IX+SvoQPFM5rN74hhfVzZoE8zBuGD4nLLk+j0elGacvQ== + dependencies: + "@ai-sdk/gateway" "3.0.46" + "@ai-sdk/provider" "3.0.8" + "@ai-sdk/provider-utils" "4.0.15" + "@opentelemetry/api" "1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +base64-js@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.0.2.tgz#474211c95e6cf2a547db461e4f6778b51d08fa65" + integrity sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg== + +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bops@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bops/-/bops-1.0.1.tgz#502aaf00ee119db1dbae088e3df4bea2e241dbcc" + integrity sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag== + dependencies: + base64-js "1.0.2" + to-utf8 "0.0.1" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +camelcase@6: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chai@^5.2.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06" + integrity sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.3.tgz#2427361117b70cca8dc89680ead32b157019caf5" + integrity sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA== + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +console-table-printer@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.15.0.tgz#5c808204640b8f024d545bde8aabe5d344dfadc1" + integrity sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw== + dependencies: + simple-wcswidth "^1.1.2" + +debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +decamelize@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +dotenv@^16.4.5: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +end-of-stream@^1.1.0: + version "1.4.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" + integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg== + dependencies: + once "^1.4.0" + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +esbuild@^0.27.0, esbuild@~0.27.0: + version "0.27.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.3.tgz#5859ca8e70a3af956b26895ce4954d7e73bd27a8" + integrity sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.3" + "@esbuild/android-arm" "0.27.3" + "@esbuild/android-arm64" "0.27.3" + "@esbuild/android-x64" "0.27.3" + "@esbuild/darwin-arm64" "0.27.3" + "@esbuild/darwin-x64" "0.27.3" + "@esbuild/freebsd-arm64" "0.27.3" + "@esbuild/freebsd-x64" "0.27.3" + "@esbuild/linux-arm" "0.27.3" + "@esbuild/linux-arm64" "0.27.3" + "@esbuild/linux-ia32" "0.27.3" + "@esbuild/linux-loong64" "0.27.3" + "@esbuild/linux-mips64el" "0.27.3" + "@esbuild/linux-ppc64" "0.27.3" + "@esbuild/linux-riscv64" "0.27.3" + "@esbuild/linux-s390x" "0.27.3" + "@esbuild/linux-x64" "0.27.3" + "@esbuild/netbsd-arm64" "0.27.3" + "@esbuild/netbsd-x64" "0.27.3" + "@esbuild/openbsd-arm64" "0.27.3" + "@esbuild/openbsd-x64" "0.27.3" + "@esbuild/openharmony-arm64" "0.27.3" + "@esbuild/sunos-x64" "0.27.3" + "@esbuild/win32-arm64" "0.27.3" + "@esbuild/win32-ia32" "0.27.3" + "@esbuild/win32-x64" "0.27.3" + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +eventsource-parser@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +expect-type@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68" + integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA== + +fast-xml-parser@^4.4.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz#c54d6b35aa0f23dc1ea60b6c884340c006dc6efb" + integrity sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig== + dependencies: + strnum "^1.1.1" + +fastestsmallesttextencoderdecoder@^1.0.22: + version "1.0.22" + resolved "https://registry.yarnpkg.com/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz#59b47e7b965f45258629cc6c127bf783281c5e93" + integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw== + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + +form-data@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-tsconfig@^4.7.5: + version "4.13.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.6.tgz#2fbfda558a98a691a798f123afd95915badce876" + integrity sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw== + dependencies: + resolve-pkg-maps "^1.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-cache-semantics@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz#205f4db64f8562b76a4ff9235aa5279839a09dd5" + integrity sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ== + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + +js-tiktoken@^1.0.12: + version "1.0.21" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.21.tgz#368a9957591a30a62997dd0c4cf30866f00f8221" + integrity sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g== + dependencies: + base64-js "^1.5.1" + +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-to-ts@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz#81f3acaf5a34736492f6f5f51870ef9ece1ca853" + integrity sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g== + dependencies: + "@babel/runtime" "^7.18.3" + ts-algebra "^2.0.0" + +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +langsmith@^0.3.67: + version "0.3.87" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.87.tgz#f1c991c93a5d4d226a31671be7e4443b4b8673b1" + integrity sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q== + dependencies: + "@types/uuid" "^10.0.0" + chalk "^4.1.2" + console-table-printer "^2.12.1" + p-queue "^6.6.2" + semver "^7.6.3" + uuid "^10.0.0" + +loupe@^3.1.0, loupe@^3.1.4: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.1.tgz#0095cf56dc5b7a9a7c08ff5b1a8796ec8ad17e76" + integrity sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +magic-string@^0.30.17: + version "0.30.21" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +ms@^2.0.0, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +openai@^4: + version "4.104.0" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.104.0.tgz#c489765dc051b95019845dab64b0e5207cae4d30" + integrity sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-retry@4: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" + integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +pump@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d" + integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rollup@^4.43.0: + version "4.57.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.57.1.tgz#947f70baca32db2b9c594267fe9150aa316e5a88" + integrity sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.57.1" + "@rollup/rollup-android-arm64" "4.57.1" + "@rollup/rollup-darwin-arm64" "4.57.1" + "@rollup/rollup-darwin-x64" "4.57.1" + "@rollup/rollup-freebsd-arm64" "4.57.1" + "@rollup/rollup-freebsd-x64" "4.57.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.57.1" + "@rollup/rollup-linux-arm-musleabihf" "4.57.1" + "@rollup/rollup-linux-arm64-gnu" "4.57.1" + "@rollup/rollup-linux-arm64-musl" "4.57.1" + "@rollup/rollup-linux-loong64-gnu" "4.57.1" + "@rollup/rollup-linux-loong64-musl" "4.57.1" + "@rollup/rollup-linux-ppc64-gnu" "4.57.1" + "@rollup/rollup-linux-ppc64-musl" "4.57.1" + "@rollup/rollup-linux-riscv64-gnu" "4.57.1" + "@rollup/rollup-linux-riscv64-musl" "4.57.1" + "@rollup/rollup-linux-s390x-gnu" "4.57.1" + "@rollup/rollup-linux-x64-gnu" "4.57.1" + "@rollup/rollup-linux-x64-musl" "4.57.1" + "@rollup/rollup-openbsd-x64" "4.57.1" + "@rollup/rollup-openharmony-arm64" "4.57.1" + "@rollup/rollup-win32-arm64-msvc" "4.57.1" + "@rollup/rollup-win32-ia32-msvc" "4.57.1" + "@rollup/rollup-win32-x64-gnu" "4.57.1" + "@rollup/rollup-win32-x64-msvc" "4.57.1" + fsevents "~2.3.2" + +semver@^7.6.3: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +simple-wcswidth@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz#66722f37629d5203f9b47c5477b1225b85d6525b" + integrity sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.9.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" + integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== + +strip-literal@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032" + integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + dependencies: + js-tokens "^9.0.1" + +strnum@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.1.2.tgz#57bca4fbaa6f271081715dbc9ed7cee5493e28e4" + integrity sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinyglobby@^0.2.14, tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +tinypool@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591" + integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg== + +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.4.tgz#d77a002fb53a88aa1429b419c1c92492e0c81f78" + integrity sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q== + +to-utf8@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/to-utf8/-/to-utf8-0.0.1.tgz#d17aea72ff2fba39b9e43601be7b3ff72e089852" + integrity sha512-zks18/TWT1iHO3v0vFp5qLKOG27m67ycq/Y7a7cTiRuUNlc4gf3HGnkRgMv0NyhnfTamtkYBJl+YeD1/j07gBQ== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-algebra@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ts-algebra/-/ts-algebra-2.0.0.tgz#4e3e0953878f26518fce7f6bb115064a65388b7a" + integrity sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw== + +tsx@^4.19.0: + version "4.21.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.21.0.tgz#32aa6cf17481e336f756195e6fe04dae3e6308b1" + integrity sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw== + dependencies: + esbuild "~0.27.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + +typescript@^5: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +ulid@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.4.0.tgz#9d9ee22e63f4390ee1bcd9ad09fca39d8ae0afed" + integrity sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +vite-node@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07" + integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg== + dependencies: + cac "^6.7.14" + debug "^4.4.1" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": + version "7.3.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.1.tgz#7f6cfe8fb9074138605e822a75d9d30b814d6507" + integrity sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA== + dependencies: + esbuild "^0.27.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" + integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A== + dependencies: + "@types/chai" "^5.2.2" + "@vitest/expect" "3.2.4" + "@vitest/mocker" "3.2.4" + "@vitest/pretty-format" "^3.2.4" + "@vitest/runner" "3.2.4" + "@vitest/snapshot" "3.2.4" + "@vitest/spy" "3.2.4" + "@vitest/utils" "3.2.4" + chai "^5.2.0" + debug "^4.4.1" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + picomatch "^4.0.2" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.14" + tinypool "^1.1.1" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node "3.2.4" + why-is-node-running "^2.3.0" + +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.17.1: + version "8.19.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.19.0.tgz#ddc2bdfa5b9ad860204f5a72a4863a8895fd8c8b" + integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== + +zod-to-json-schema@^3.22.3: + version "3.25.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba" + integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + +zod@^3.23.8, zod@^3.25.32: + version "3.25.76" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== diff --git a/jest.config.js b/jest.config.js index 09d4f81d9f..9e3892d5ab 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,7 @@ module.exports = { }, coveragePathIgnorePatterns: ['src/styles/svg'], testPathIgnorePatterns: [`node_modules`, `\\.cache`, `.*/public`], - modulePathIgnorePatterns: ['/examples/'], + modulePathIgnorePatterns: ['/examples/', '/guides/'], // NOTE: This is a workaround for compilation issues with .d.ts files transformIgnorePatterns: [ `node_modules/(?!(gatsby|gatsby-script|use-keyboard-shortcut|react-medium-image-zoom|@react-hook/media-query|@mdx-js/react|@ably/ui/core|until-async)/)`,