From 8570d77052a120dbda5732f3034d4dbb05e30a04 Mon Sep 17 00:00:00 2001 From: xindeli Date: Thu, 17 Sep 2026 16:54:53 +0800 Subject: [PATCH 01/26] refactor(app): colocate shadcn UI under the Next.js app tree Root components/ and lib/ were leftover CLI defaults; keep primitives and cn() with the rest of the frontend. --- app/components/language-switch.tsx | 2 +- {components => app/components}/ui/button.tsx | 2 +- {components => app/components}/ui/dialog.tsx | 4 ++-- {components => app/components}/ui/tabs.tsx | 2 +- app/features/workspace/components/preview-frame.tsx | 2 +- app/features/workspace/components/site-header.tsx | 4 ++-- app/features/workspace/workspace-screen.tsx | 6 +++--- {lib => app/lib}/utils.ts | 0 components.json | 10 +++++----- tests/architecture.test.ts | 13 +++++++++++++ 10 files changed, 29 insertions(+), 16 deletions(-) rename {components => app/components}/ui/button.tsx (98%) rename {components => app/components}/ui/dialog.tsx (97%) rename {components => app/components}/ui/tabs.tsx (97%) rename {lib => app/lib}/utils.ts (100%) diff --git a/app/components/language-switch.tsx b/app/components/language-switch.tsx index bfc36a3..80117bd 100644 --- a/app/components/language-switch.tsx +++ b/app/components/language-switch.tsx @@ -1,6 +1,6 @@ 'use client'; -import { cn } from '@/lib/utils'; +import { cn } from '@/app/lib/utils'; import type { Locale } from '../i18n'; const OPTIONS: { value: Locale; label: string }[] = [ diff --git a/components/ui/button.tsx b/app/components/ui/button.tsx similarity index 98% rename from components/ui/button.tsx rename to app/components/ui/button.tsx index 33e984e..3aa4f37 100644 --- a/components/ui/button.tsx +++ b/app/components/ui/button.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from '@/lib/utils'; +import { cn } from '@/app/lib/utils'; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive", diff --git a/components/ui/dialog.tsx b/app/components/ui/dialog.tsx similarity index 97% rename from components/ui/dialog.tsx rename to app/components/ui/dialog.tsx index 2b8054d..892c50b 100644 --- a/components/ui/dialog.tsx +++ b/app/components/ui/dialog.tsx @@ -4,8 +4,8 @@ import * as React from "react" import { XIcon } from "lucide-react" import { Dialog as DialogPrimitive } from "radix-ui" -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" +import { cn } from "@/app/lib/utils" +import { Button } from "@/app/components/ui/button" function Dialog({ ...props diff --git a/components/ui/tabs.tsx b/app/components/ui/tabs.tsx similarity index 97% rename from components/ui/tabs.tsx rename to app/components/ui/tabs.tsx index 991a9ee..f74a2fc 100644 --- a/components/ui/tabs.tsx +++ b/app/components/ui/tabs.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import * as TabsPrimitive from '@radix-ui/react-tabs'; -import { cn } from '@/lib/utils'; +import { cn } from '@/app/lib/utils'; function Tabs({ className, diff --git a/app/features/workspace/components/preview-frame.tsx b/app/features/workspace/components/preview-frame.tsx index 6ab456b..abb72a5 100644 --- a/app/features/workspace/components/preview-frame.tsx +++ b/app/features/workspace/components/preview-frame.tsx @@ -1,7 +1,7 @@ 'use client'; import { memo } from 'react'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/app/components/ui/button'; export type PreviewViewport = 'desktop' | 'mobile'; diff --git a/app/features/workspace/components/site-header.tsx b/app/features/workspace/components/site-header.tsx index 37868f1..2d3393a 100644 --- a/app/features/workspace/components/site-header.tsx +++ b/app/features/workspace/components/site-header.tsx @@ -1,7 +1,7 @@ 'use client'; import { ArrowLeft, MessageCircle } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/app/components/ui/button'; import { Dialog, DialogClose, @@ -11,7 +11,7 @@ import { DialogHeader, DialogTitle, DialogTrigger, -} from '@/components/ui/dialog'; +} from '@/app/components/ui/dialog'; import { LanguageSwitch } from '@/app/components/language-switch'; import type { Locale, UiCopy } from '@/app/i18n'; diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 18220a8..1c0b43b 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -14,7 +14,7 @@ import { Rocket, Smartphone, } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/app/components/ui/button'; import { Dialog, DialogClose, @@ -23,8 +23,8 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from '@/components/ui/dialog'; -import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; +} from '@/app/components/ui/dialog'; +import { Tabs, TabsList, TabsTrigger } from '@/app/components/ui/tabs'; import { appendNarrationChunk, dropTrailingSummaryEcho, diff --git a/lib/utils.ts b/app/lib/utils.ts similarity index 100% rename from lib/utils.ts rename to app/lib/utils.ts diff --git a/components.json b/components.json index 77a378b..5d29f39 100644 --- a/components.json +++ b/components.json @@ -11,11 +11,11 @@ "prefix": "" }, "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" + "components": "@/app/components", + "utils": "@/app/lib/utils", + "ui": "@/app/components/ui", + "lib": "@/app/lib", + "hooks": "@/app/hooks" }, "iconLibrary": "lucide" } diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index a001f67..bb1cdba 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -13,6 +13,19 @@ async function sourceFiles(root: string): Promise { return nested.flat(); } +test('frontend UI and helpers live under app/, not the repo root', async () => { + const topLevel = await readdir('.', { withFileTypes: true }); + const directories = new Set(topLevel.filter((entry) => entry.isDirectory()).map((entry) => entry.name)); + assert.ok( + !directories.has('components'), + 'root components/ is leftover shadcn layout; keep UI under app/components/', + ); + assert.ok( + !directories.has('lib'), + 'root lib/ is leftover shadcn layout; keep helpers under app/lib/', + ); +}); + test('frontend never imports the agent runtime', async () => { for (const file of await sourceFiles('app')) { const source = await readFile(file, 'utf8'); From fb11a3c557fb77fec60b3a91d5749a3d66741671 Mon Sep 17 00:00:00 2001 From: xindeli Date: Thu, 17 Sep 2026 21:41:24 +0800 Subject: [PATCH 02/26] feat(models): add DeepSeek V4.1 Flash as the default The gateway now serves v4.1-flash; list it with the built-ins so the picker and agent fall back to it when AI_GATEWAY_MODEL is unset. --- .env.example | 2 +- shared/models.ts | 3 ++- tests/models.test.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 1a67889..84dc531 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ AI_GATEWAY_API_KEY= AI_GATEWAY_BASE_URL=https://ai-gateway.edgeone.link/v1 -AI_GATEWAY_MODEL=@makers/deepseek-v4-flash +AI_GATEWAY_MODEL=@makers/deepseek-v4.1-flash # Optional. Adds entries to the composer's model picker as `id|Label` pairs, # comma separated. Built-in models are already listed; use this for vendor models # whose key you bound in the console. One gateway key serves every entry, so this diff --git a/shared/models.ts b/shared/models.ts index 418f72a..cd7fd71 100644 --- a/shared/models.ts +++ b/shared/models.ts @@ -24,7 +24,7 @@ export type ModelOption = { }; /** Runs when the deployment configures nothing and the user picks nothing. */ -export const DEFAULT_MODEL = '@makers/deepseek-v4-flash'; +export const DEFAULT_MODEL = '@makers/deepseek-v4.1-flash'; /** * The models the platform serves without a vendor key. Free and rate limited, @@ -35,6 +35,7 @@ export const DEFAULT_MODEL = '@makers/deepseek-v4-flash'; * a deployment that has bound one adds it through EXTRA_MODELS_ENV_KEY. */ export const BUILT_IN_MODELS: readonly ModelOption[] = [ + { id: '@makers/deepseek-v4.1-flash', label: 'DeepSeek V4.1 Flash' }, { id: '@makers/deepseek-v4-flash', label: 'DeepSeek V4 Flash' }, { id: '@makers/deepseek-v4-pro', label: 'DeepSeek V4 Pro' }, { id: '@makers/hy3', label: 'Hunyuan 3' }, diff --git a/tests/models.test.ts b/tests/models.test.ts index 6e644ac..eb09164 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -71,7 +71,7 @@ test('an extra model that repeats a built-in does not appear twice', () => { assert.equal(catalog.length, BUILT_IN_MODELS.length); assert.equal( catalog.find((option) => option.id === DEFAULT_MODEL)?.label, - 'DeepSeek V4 Flash', + 'DeepSeek V4.1 Flash', ); }); From 8c27418e2d8ce3bae0a4762d16800d174c4abdd3 Mon Sep 17 00:00:00 2001 From: xindeli Date: Thu, 17 Sep 2026 21:42:49 +0800 Subject: [PATCH 03/26] fix(chat): show the API key card before waiting on a Blob snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pause reply was already on screen, but result — which gates the input — waited on sandbox.persist, including a second flush that could hang and fail. --- agents/_lib/pipelines/chat.ts | 11 +++++++++-- tests/gateway-prompt.test.ts | 21 +++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/agents/_lib/pipelines/chat.ts b/agents/_lib/pipelines/chat.ts index ab5c64b..70b9e41 100644 --- a/agents/_lib/pipelines/chat.ts +++ b/agents/_lib/pipelines/chat.ts @@ -319,11 +319,15 @@ export async function runChatPipeline( let fileTree: FileTreeItem[] = []; if (modelResult.projectTouched) { - await checkpoint.flush(); + // The Files panel can update without waiting for a Blob snapshot. Persist + // used to run here and again in finalizeTurn, so a slow or failing + // sandbox.persist held the result event — and the API key card, which is + // gated on it — for tens of seconds after the pause reply was already on + // screen. fileTree = await fileTreePush.flush('Failed to read the file list.'); } await finalizeTurn(pauseReply, 'completed', { - withSnapshot: modelResult.projectTouched, + withSnapshot: false, }); send({ type: 'result', @@ -346,6 +350,9 @@ export async function runChatPipeline( deployment: state.deployment, }, }); + if (modelResult.projectTouched) { + void checkpoint.flush(); + } return; } const sanitizedModelOutput = modelResult.success && modelResult.output diff --git a/tests/gateway-prompt.test.ts b/tests/gateway-prompt.test.ts index f18053c..ba03803 100644 --- a/tests/gateway-prompt.test.ts +++ b/tests/gateway-prompt.test.ts @@ -302,11 +302,24 @@ test('a turn waiting for the API key is completed, not a red error', async () => readFile('agents/_lib/pipelines/helpers.ts', 'utf8'), readFile('agents/_lib/prompt.ts', 'utf8'), ]); + const pause = chat.slice( + chat.indexOf('if (state.gatewayPromptPending)'), + chat.indexOf('const sanitizedModelOutput'), + ); + assert.match(helpers, /GATEWAY_CREDENTIALS_USER_REPLY/); - assert.match(chat, /if \(state\.gatewayPromptPending\)/); - assert.match(chat, /GATEWAY_CREDENTIALS_USER_REPLY\[replyLocale\]/); - assert.match(chat, /gatewayNeeded: true/); - assert.match(chat, /ok: true,\s*\n\s*reply: pauseReply/); + assert.match(pause, /GATEWAY_CREDENTIALS_USER_REPLY\[replyLocale\]/); + assert.match(pause, /gatewayNeeded: true/); + assert.match(pause, /ok: true,\s*\n\s*reply: pauseReply/); + // The card is gated on result/loading, so a Blob snapshot that hangs or + // fails must not sit in front of that event. Persist after it, unawaited. + assert.match(pause, /withSnapshot: false/); + assert.match(pause, /void checkpoint\.flush\(\)/); + assert.ok( + pause.indexOf("type: 'result'") < pause.indexOf('void checkpoint.flush()'), + 'result must go out before snapshot persist, or the card waits on Blob', + ); + assert.doesNotMatch(pause, /await checkpoint\.flush\(\)/); assert.match(prompt, /do not say the preview is ready/); assert.match(prompt, /preview and deploy must still run/); }); From 00e294a9c2a418ec065828a844ea00ed5761bb8d Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 10:52:54 +0800 Subject: [PATCH 04/26] refactor(session): keep conversation truth in the SDK transcript Store history as a single Blob-backed JSONL file behind a resident Query, split prompt/deploy/model onto their own routes, and drop the parallel stores and monoliths that duplicated that state. --- agents/_lib/agent.ts | 790 -------- .../_lib/makers/cli-deploy.ts | 24 +- .../_lib/makers/cli-dev.ts | 456 +---- .../compat/lint-script.ts} | 361 +--- agents/_lib/makers/compat/run.ts | 118 ++ agents/_lib/makers/compat/skill-rules.ts | 244 +++ .../declarations.ts} | 6 +- {shared => agents/_lib/makers}/npm-install.ts | 0 agents/_lib/makers/preview-proxy-source.ts | 459 +++++ .../makers-deploy.ts => makers/project.ts} | 0 agents/_lib/makers/session.ts | 61 + .../makers-token.ts => makers/token.ts} | 2 +- {shared => agents/_lib/makers}/tool-phase.ts | 0 agents/_lib/memory.ts | 241 --- agents/_lib/pipelines/index.ts | 8 - agents/_lib/pipelines/turn-lifecycle.ts | 105 - agents/_lib/pipelines/workspace.ts | 90 - agents/_lib/project/archive.ts | 4 +- agents/_lib/project/commands.ts | 4 +- .../_lib/{pipelines => project}/download.ts | 6 +- .../project/{gateway-prompt.ts => gateway.ts} | 4 +- agents/_lib/project/index.ts | 2 +- agents/_lib/project/persistence.ts | 19 +- agents/_lib/project/preview.ts | 49 +- .../file-read.ts => project/read.ts} | 6 +- .../_lib/project}/resume-file-cache.ts | 2 +- .../{pipelines => project}/resume-files.ts | 6 +- .../_lib/project}/sandbox-command.ts | 0 agents/_lib/project/scaffold.ts | 7 +- agents/_lib/project/state.ts | 2 +- agents/_lib/project/templates.ts | 2 +- agents/_lib/project/workspace.ts | 95 + agents/_lib/prompt.ts | 34 +- agents/_lib/runtime/context.ts | 57 + agents/_lib/runtime/merge.ts | 59 + agents/_lib/{utils => runtime}/request.ts | 13 - agents/_lib/{shared.ts => runtime/sse.ts} | 6 +- agents/_lib/session/live.ts | 724 +++++++ agents/_lib/session/projection.ts | 126 ++ agents/_lib/{pipelines => session}/resume.ts | 305 +-- agents/_lib/session/store.ts | 166 ++ .../_lib/{chat-tasks.ts => session/task.ts} | 135 +- agents/_lib/session/transcript.ts | 70 + agents/_lib/tools/assemble.ts | 178 ++ agents/_lib/tools/commands-wrap.ts | 89 +- agents/_lib/tools/project-tools.ts | 4 +- agents/_lib/tools/web-search-wrap.ts | 2 +- agents/_lib/{pipelines => turn}/chat.ts | 113 +- .../helpers.ts => turn/checkpoint.ts} | 7 +- agents/_lib/{pipelines => turn}/deploy.ts | 72 +- agents/_lib/turn/lifecycle.ts | 57 + agents/_lib/types.ts | 67 +- agents/_lib/utils/activity.ts | 142 -- agents/_lib/utils/narration.ts | 119 -- {shared => agents/_lib/utils}/shell.ts | 0 agents/_lib/utils/text.ts | 2 +- agents/_lib/utils/tool-phase.ts | 20 - agents/deploy.ts | 24 + agents/download.ts | 2 +- agents/file.ts | 2 +- agents/preview.ts | 2 +- agents/prompt.ts | 37 + agents/session-model.ts | 41 + agents/session.ts | 46 +- agents/stop.ts | 41 +- app/features/workspace/hooks/use-live-turn.ts | 598 ++++++ .../workspace/hooks/use-preview-surface.ts | 415 ++++ .../workspace/hooks/use-session-resume.ts | 302 +++ .../workspace/hooks/use-workspace-state.ts | 106 + app/features/workspace/workspace-api.ts | 53 +- app/features/workspace/workspace-screen.tsx | 1718 ++--------------- app/lib/assistant-timeline.ts | 125 +- app/lib/conversation.ts | 11 +- app/lib/tool-activity.ts | 362 +--- package-lock.json | 10 + package.json | 1 + shared/makers-url.ts | 19 + shared/protocol.ts | 10 +- shared/sanitize-assistant-text.ts | 73 - shared/timeline.ts | 643 ++++++ tests/activity.test.ts | 45 +- tests/app-shell.test.ts | 2 +- tests/architecture.test.ts | 55 +- tests/chat-stream.test.ts | 6 +- tests/commands-wrap.test.ts | 4 +- tests/deploy-task.test.ts | 99 +- tests/gateway-prompt.test.ts | 29 +- tests/makers-compat.test.ts | 12 +- tests/makers-declarations.test.ts | 9 +- tests/makers-deploy.test.ts | 51 +- tests/makers-dev.test.ts | 10 +- tests/makers-file-semantics.test.ts | 4 +- tests/makers-lint.test.ts | 10 +- tests/makers-sub-token.test.ts | 33 +- tests/narration.test.ts | 2 +- tests/npm-install.test.ts | 4 +- tests/preview-path.test.ts | 30 +- tests/project-templates.test.ts | 2 +- tests/prompt-single-source.test.ts | 39 +- tests/resume-file-cache.test.ts | 2 +- tests/route-consolidation.test.ts | 51 +- tests/sandbox-timeout.test.ts | 2 +- tests/sanitize-assistant-text.test.ts | 2 +- tests/sse-parser.test.ts | 6 +- tests/tool-activity.test.ts | 2 +- tests/tool-phase.test.ts | 2 +- tests/transcript.test.ts | 148 ++ tests/user-facing-reply.test.ts | 8 +- 108 files changed, 5523 insertions(+), 5527 deletions(-) delete mode 100644 agents/_lib/agent.ts rename shared/makers-deploy.ts => agents/_lib/makers/cli-deploy.ts (97%) rename shared/makers-dev.ts => agents/_lib/makers/cli-dev.ts (66%) rename agents/_lib/{project/makers-compat.ts => makers/compat/lint-script.ts} (58%) create mode 100644 agents/_lib/makers/compat/run.ts create mode 100644 agents/_lib/makers/compat/skill-rules.ts rename agents/_lib/{project/makers-declarations.ts => makers/declarations.ts} (98%) rename {shared => agents/_lib/makers}/npm-install.ts (100%) create mode 100644 agents/_lib/makers/preview-proxy-source.ts rename agents/_lib/{project/makers-deploy.ts => makers/project.ts} (100%) create mode 100644 agents/_lib/makers/session.ts rename agents/_lib/{project/makers-token.ts => makers/token.ts} (99%) rename {shared => agents/_lib/makers}/tool-phase.ts (100%) delete mode 100644 agents/_lib/memory.ts delete mode 100644 agents/_lib/pipelines/index.ts delete mode 100644 agents/_lib/pipelines/turn-lifecycle.ts delete mode 100644 agents/_lib/pipelines/workspace.ts rename agents/_lib/{pipelines => project}/download.ts (91%) rename agents/_lib/project/{gateway-prompt.ts => gateway.ts} (98%) rename agents/_lib/{pipelines/file-read.ts => project/read.ts} (92%) rename {shared => agents/_lib/project}/resume-file-cache.ts (97%) rename agents/_lib/{pipelines => project}/resume-files.ts (90%) rename {shared => agents/_lib/project}/sandbox-command.ts (100%) create mode 100644 agents/_lib/project/workspace.ts create mode 100644 agents/_lib/runtime/context.ts create mode 100644 agents/_lib/runtime/merge.ts rename agents/_lib/{utils => runtime}/request.ts (84%) rename agents/_lib/{shared.ts => runtime/sse.ts} (87%) create mode 100644 agents/_lib/session/live.ts create mode 100644 agents/_lib/session/projection.ts rename agents/_lib/{pipelines => session}/resume.ts (54%) create mode 100644 agents/_lib/session/store.ts rename agents/_lib/{chat-tasks.ts => session/task.ts} (72%) create mode 100644 agents/_lib/session/transcript.ts create mode 100644 agents/_lib/tools/assemble.ts rename agents/_lib/{pipelines => turn}/chat.ts (89%) rename agents/_lib/{pipelines/helpers.ts => turn/checkpoint.ts} (98%) rename agents/_lib/{pipelines => turn}/deploy.ts (88%) create mode 100644 agents/_lib/turn/lifecycle.ts delete mode 100644 agents/_lib/utils/activity.ts delete mode 100644 agents/_lib/utils/narration.ts rename {shared => agents/_lib/utils}/shell.ts (100%) delete mode 100644 agents/_lib/utils/tool-phase.ts create mode 100644 agents/deploy.ts create mode 100644 agents/prompt.ts create mode 100644 agents/session-model.ts create mode 100644 app/features/workspace/hooks/use-live-turn.ts create mode 100644 app/features/workspace/hooks/use-preview-surface.ts create mode 100644 app/features/workspace/hooks/use-session-resume.ts create mode 100644 app/features/workspace/hooks/use-workspace-state.ts create mode 100644 shared/makers-url.ts delete mode 100644 shared/sanitize-assistant-text.ts create mode 100644 shared/timeline.ts create mode 100644 tests/transcript.test.ts diff --git a/agents/_lib/agent.ts b/agents/_lib/agent.ts deleted file mode 100644 index 2b71edc..0000000 --- a/agents/_lib/agent.ts +++ /dev/null @@ -1,790 +0,0 @@ -import { - createSdkMcpServer, - query, - type Query, - type SDKMessage, - type SDKResultMessage, -} from '@anthropic-ai/claude-agent-sdk'; -import { - DEFAULT_PATH, - GATEWAY_CONVERSATION_ID_HEADER_NAME, - GATEWAY_QUOTA_BYPASS_HEADER, - GATEWAY_QUOTA_PROMPT_HEADER, - MAKERS_SKILL_NAMES, - SANDBOX_MCP_SERVER_NAME, -} from './constants.ts'; -import { - describeModelRun, - resolveConfiguredModel, - resolveRunningModelLabel, -} from './models.ts'; -import { wrapSandboxTools } from './tools/commands-wrap.ts'; -import { wrapWebSearchTool } from './tools/web-search-wrap.ts'; -import { - WEB_SEARCH_API_KEY_ENV, - isWebSearchConfigured, - isWebSearchToolName, -} from '../../shared/web-search.ts'; -import { - buildRequestGatewayCredentialsTool, - REQUEST_GATEWAY_CREDENTIALS_TOOL, -} from './project/gateway-prompt.ts'; -import { - buildProjectScaffoldTool, - buildWriteProjectFileTool, -} from './tools/project-tools.ts'; -import { buildLoadMakersSkillTool } from './tools/makers-skills.ts'; -import { buildPrompt, buildTurnPrompt } from './prompt.ts'; -import { resolveMakersProjectName } from './project/makers-deploy.ts'; -import type { - AgentProgressEvent, - CodingAgentResult, - ConversationMessage, - DeploymentInfo, - PreviewKind, - ProjectState, - ScaffoldLog, - StreamSend, -} from './types.ts'; -import { - detectFatalToolError, - sanitizeAssistantText, - truncateForStream, -} from './utils/text.ts'; -import { summarizeToolInput, summarizeToolOutput } from './utils/activity.ts'; -import { - resolveNarrationEmit, - sanitizeNarrationText, - type NarrationEmitState, -} from './utils/narration.ts'; -import { - isInstallCommand, - isMakersDeployCommand, - isPreviewCommand, - parseEchoedExitCode, - shortenToolName, -} from './utils/tool-phase.ts'; - -function pickEnvValue(context: any, key: string) { - const value = context?.env?.[key]; - return typeof value === 'string' ? value.trim() : ''; -} - -function sanitizeHeaderValue(value: string) { - return value.replace(/[\r\n]+/g, ' ').trim(); -} - -function buildAnthropicCustomHeaders(customHeaders: string, conversationId: string) { - const safeConversationId = sanitizeHeaderValue(conversationId); - return [ - customHeaders, - GATEWAY_QUOTA_BYPASS_HEADER, - GATEWAY_QUOTA_PROMPT_HEADER, - safeConversationId - ? `${GATEWAY_CONVERSATION_ID_HEADER_NAME}: ${safeConversationId}` - : '', - ].filter(Boolean).join('\n'); -} - -function extractSandboxCommand(input: unknown) { - const record = input && typeof input === 'object' ? input as Record : {}; - const command = typeof record.command === 'string' - ? record.command - : typeof record.cmd === 'string' - ? record.cmd - : ''; - return command.trim(); -} - -function isBrowserSandboxToolName(name: string) { - return name.toLowerCase().includes('browser'); -} - -function isGenericProjectWriteToolName(name: string) { - const normalized = name.toLowerCase(); - return normalized === 'files_write' - || normalized === 'write_files' - || normalized.endsWith('__files_write') - || normalized.endsWith('__write_files'); -} - -function extractVisibleNarrationDelta(event: SDKMessage) { - if (event.type !== 'stream_event') { - return ''; - } - const streamEvent = (event as any).event; - if (streamEvent?.type !== 'content_block_delta') { - return ''; - } - const delta = streamEvent.delta; - if (delta?.type === 'text_delta' && typeof delta.text === 'string') { - return sanitizeNarrationText(delta.text); - } - return ''; -} - -type StreamingToolUseBlock = { - id: string; - name: string; - inputJson: string; - input?: unknown; -}; - -function isToolUseContentBlock(block: unknown): block is { - type: string; - id?: string; - name?: string; - input?: unknown; -} { - const record = block && typeof block === 'object' - ? block as Record - : {}; - return record.type === 'tool_use' || record.type === 'mcp_tool_use'; -} - -function extractVisibleTextBlock(block: unknown) { - const record = block && typeof block === 'object' - ? block as Record - : {}; - if (record.type !== 'text' || typeof record.text !== 'string') { - return ''; - } - return sanitizeNarrationText(record.text); -} - -function parseToolInputJson(rawJson: string, fallback: unknown) { - if (!rawJson.trim()) { - return fallback ?? {}; - } - try { - return JSON.parse(rawJson); - } catch { - return fallback ?? {}; - } -} - -type ToolProgressPhase = 'scaffold' | 'code' | 'install' | 'preview' | 'link'; - -function inferToolProgress(name: string, input: unknown): { - phaseHint?: ToolProgressPhase; - fileCount?: number; -} { - const toolName = shortenToolName(name); - if (toolName === 'ensure_project_scaffold') { - return { phaseHint: 'scaffold' }; - } - if (toolName === 'files_write' || toolName === 'write_files' || toolName === 'files_make_dir' || toolName === 'files_remove') { - return { phaseHint: 'code' }; - } - if (toolName === 'write_project_file') { - return { phaseHint: 'code', fileCount: 1 }; - } - if (toolName === 'commands') { - const cmd = extractSandboxCommand(input); - if (isInstallCommand(cmd)) { - return { phaseHint: 'install' }; - } - if (isPreviewCommand(cmd) || isMakersDeployCommand(cmd)) { - return { phaseHint: 'preview' }; - } - } - return {}; -} - -export async function runCodingAgent( - context: any, - conversationId: string, - userMessage: string, - history: ConversationMessage[], - state: ProjectState, - isNewProject: boolean, - onScaffoldLog?: (log: ScaffoldLog) => void, - onProgress?: (event: AgentProgressEvent) => void, - // Fires after the scaffold succeeds (no argument) and after every - // write_project_file (with the file just written, so the pipeline can stream - // its content to the frontend instead of making it fetch the file back). - onProjectFilesChanged?: (file?: { path: string; content: string }) => void | Promise, - // Fires as soon as a direct Makers CLI command resolves a public URL so the - // UI can switch to the iframe without waiting for verification / finalize. - onPreviewReady?: (preview: { url?: string; sandboxDebugUrl?: string; kind?: PreviewKind }) => void, - // Deploy is durable product state, not an iframe preview. Stream each state - // transition independently so the UI can show running/success/failure. - onDeploymentStatus?: (deployment: DeploymentInfo) => void, - abortSignal?: AbortSignal, - // An object rather than a twelfth positional argument: the list above is long - // enough that a new slot would be easy to fill in the wrong order at one of - // the two call sites in the chat pipeline. - runOptions: { model?: string; send?: StreamSend } = {}, -): Promise { - // Prefer AI Gateway for model access, with backward-compatible Anthropic / DeepSeek config. - const apiKey = pickEnvValue(context, 'AI_GATEWAY_API_KEY') - || pickEnvValue(context, 'ANTHROPIC_API_KEY') - || pickEnvValue(context, 'DEEPSEEK_API_KEY'); - const authToken = pickEnvValue(context, 'ANTHROPIC_AUTH_TOKEN') - || pickEnvValue(context, 'DEEPSEEK_API_KEY'); - // A model picked in the composer outranks the deployment default. The choice - // was checked against this deployment's catalogue before it got here, so an - // unrecognized ID arrives as '' and the configured model still runs. - const model = (runOptions.model || '').trim() || resolveConfiguredModel(context); - const baseURL = pickEnvValue(context, 'AI_GATEWAY_BASE_URL') - || pickEnvValue(context, 'ANTHROPIC_BASE_URL') - || pickEnvValue(context, 'DEEPSEEK_BASE_URL') - || ''; - const customHeaders = pickEnvValue(context, 'ANTHROPIC_CUSTOM_HEADERS'); - const executablePath = pickEnvValue(context, 'CLAUDE_CODE_EXECUTABLE_PATH'); - - if (!apiKey && !authToken) { - return { - success: false, - output: null, - error: 'Missing AI_GATEWAY_API_KEY / ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / DEEPSEEK_API_KEY. The agent cannot call the model.', - projectTouched: false, - filesWritten: false, - wasCreated: false, - }; - } - - if (!baseURL) { - return { - success: false, - output: null, - error: 'Missing AI_GATEWAY_BASE_URL / ANTHROPIC_BASE_URL / DEEPSEEK_BASE_URL. The agent cannot call the model.', - projectTouched: false, - filesWritten: false, - wasCreated: false, - }; - } - - const sdkEnv: Record = { - ANTHROPIC_BASE_URL: baseURL, - ANTHROPIC_MODEL: model, - // @anthropic-ai/sdk injects ANTHROPIC_CUSTOM_HEADERS into each model request. - ANTHROPIC_CUSTOM_HEADERS: buildAnthropicCustomHeaders(customHeaders, conversationId), - PATH: pickEnvValue(context, 'PATH') || DEFAULT_PATH, - HOME: pickEnvValue(context, 'HOME') || '/tmp', - CLAUDE_CONFIG_DIR: pickEnvValue(context, 'CLAUDE_CONFIG_DIR') || '/tmp/.claude', - }; - - if (apiKey) { - sdkEnv.ANTHROPIC_API_KEY = apiKey; - } - if (authToken) { - sdkEnv.ANTHROPIC_AUTH_TOKEN = authToken; - } - if (!sdkEnv.ANTHROPIC_API_KEY && authToken) { - sdkEnv.ANTHROPIC_API_KEY = authToken; - } - - // The tool callbacks below flip these as work lands in the sandbox. They live - // outside the try so the catch path can still report what was touched: a - // stream error after a write must not tell the pipeline "nothing happened", - // or the turn finalizes with withState: false and the files are lost. - let projectTouched = false; - let filesWritten = false; - let previewTouched = false; - let deploymentTouched = false; - let wasCreated = false; - // Held out here so the finally can always detach the listener and stop the - // subprocess, including when the stream throws mid-turn. - const sdkAbortController = new AbortController(); - const abortSdkQuery = () => sdkAbortController.abort(); - abortSignal?.addEventListener('abort', abortSdkQuery, { once: true }); - let sdkQuery: Query | null = null; - - try { - if (abortSignal?.aborted) { - return { - success: false, - output: null, - error: null, - projectTouched: false, - filesWritten: false, - wasCreated: false, - stopped: true, - }; - } - const mcpServerName = SANDBOX_MCP_SERVER_NAME; - const makersProjectName = resolveMakersProjectName(context, state); - if (typeof context.tools?.toClaudeMcpServer !== 'function') { - throw new Error('The current Pages Agent Runtime is missing context.tools.toClaudeMcpServer. Please upgrade to a runtime that supports the new pages-agent-toolkit Tools API.'); - } - const edgeoneMcp = context.tools.toClaudeMcpServer(mcpServerName, { alwaysLoad: true }); - const scaffoldTool = buildProjectScaffoldTool( - context, - state, - onScaffoldLog, - ({ created }) => { - projectTouched = true; - wasCreated = created; - }, - ); - const handlePreviewPublished = (preview: { url?: string; sandboxDebugUrl?: string; kind?: PreviewKind }) => { - previewTouched = true; - if (preview.url) { - onPreviewReady?.(preview); - } - }; - const handleDeploymentStatus = (deployment: DeploymentInfo) => { - deploymentTouched = true; - onDeploymentStatus?.(deployment); - }; - // A tool that cannot serve a single query should not be advertised: the - // model spends a call to learn what the environment already knows. The two - // lists have to agree, so one predicate decides for both. - const webSearchAvailable = isWebSearchConfigured( - pickEnvValue(context, WEB_SEARCH_API_KEY_ENV), - ); - const offerSandboxTool = (name: string) => - !isBrowserSandboxToolName(name) - && !isGenericProjectWriteToolName(name) - && (webSearchAvailable || !isWebSearchToolName(name)); - const sandboxTools = wrapWebSearchTool(wrapSandboxTools( - edgeoneMcp.tools.filter((tool: { name: string }) => offerSandboxTool(tool.name)), - { - context, - state, - conversationId, - send: runOptions.send, - signal: abortSignal, - onPreviewReady: handlePreviewPublished, - onDeploymentStatus: handleDeploymentStatus, - }, - )); - const sandboxAllowedTools = edgeoneMcp.allowedTools.filter(offerSandboxTool); - const writeProjectFileTool = buildWriteProjectFileTool( - context, - state, - async ({ written, content }) => { - projectTouched = true; - filesWritten = true; - await onProjectFilesChanged?.({ path: written, content }); - }, - ); - const loadMakersSkillTool = buildLoadMakersSkillTool(); - const requestGatewayTool = buildRequestGatewayCredentialsTool({ - context, - state, - conversationId, - send: runOptions.send, - }); - const mcpTools = [ - ...sandboxTools, - scaffoldTool, - loadMakersSkillTool, - writeProjectFileTool, - requestGatewayTool, - ]; - const mcpAllowedTools = [ - ...sandboxAllowedTools, - `mcp__${mcpServerName}__ensure_project_scaffold`, - `mcp__${mcpServerName}__load_makers_skill`, - `mcp__${mcpServerName}__write_project_file`, - `mcp__${mcpServerName}__${REQUEST_GATEWAY_CREDENTIALS_TOOL}`, - 'Skill', - ]; - - const sandboxMcpServer = createSdkMcpServer({ - name: mcpServerName, - tools: mcpTools, - alwaysLoad: true, - }); - - const sdkOptions: Parameters[0]['options'] = { - model, - permissionMode: 'dontAsk', - maxTurns: 100, - // Built-in local Read/Write/Bash stay off. Skill is enabled so the model - // can load official Makers skills from .claude/skills/ on demand. - tools: ['Skill'], - skills: [...MAKERS_SKILL_NAMES], - includePartialMessages: true, - mcpServers: { - [mcpServerName]: sandboxMcpServer, - }, - allowedTools: mcpAllowedTools, - strictMcpConfig: true, - // Identical on every turn of a conversation, which is the point: the - // request and the history ride along as the turn's own message below. - systemPrompt: buildPrompt( - state, - isNewProject, - mcpServerName, - makersProjectName, - resolveRunningModelLabel(context, model), - webSearchAvailable, - ), - env: sdkEnv, - cwd: process.cwd(), - settingSources: ['project'], - abortController: sdkAbortController, - // The subprocess writes here only when something is wrong, and the turn - // fails without saying which layer broke. Unconditional: a flag nobody - // set is a log nobody has when it matters. - stderr: (data: string) => { - console.warn('[claude-code]', data.trimEnd()); - }, - }; - - if (executablePath) { - sdkOptions.pathToClaudeCodeExecutable = executablePath; - } - - sdkQuery = query({ - prompt: buildTurnPrompt(userMessage, history), - options: sdkOptions, - }); - - let resultMessage: SDKResultMessage | null = null; - // Sandbox infrastructure failures, such as EdgeOne LazySandbox routes returning - // Not Found, make all later tool calls fail. Retrying only consumes turns and - // pollutes context, so stop this query immediately with a clear upper-layer error. - let fatalError: string | null = null; - // Independently record tool_use_id -> tool context so tool_result events - // can update the correct progress step even when model providers stream - // partial tool inputs differently. - const toolContextById = new Map(); - const toolStartedAtById = new Map(); - const pendingToolUseBlocks = new Map(); - const emittedToolUseProgress = new Map(); - let narrationState: NarrationEmitState = { - currentTextBlock: '', - emittedNarration: '', - }; - const SCAFFOLD_TOOL_NAME = `mcp__${mcpServerName}__ensure_project_scaffold`; - // Push file_tree immediately at most once per turn after scaffold, avoiding duplicate find calls. - let scaffoldHandled = false; - - const emitNarration = (rawText: string, uuid: string, complete = false) => { - const resolved = resolveNarrationEmit(narrationState, rawText, complete); - narrationState = resolved.state; - if (!resolved.text) { - return; - } - onProgress?.({ - type: 'text_segment', - data: { - uuid, - text: resolved.text, - }, - }); - }; - - const emitToolUseProgress = (toolUse: { - id?: string; - name?: string; - input?: unknown; - }) => { - const toolName = typeof toolUse.name === 'string' ? toolUse.name : ''; - const toolUseId = typeof toolUse.id === 'string' ? toolUse.id : ''; - const shortToolName = shortenToolName(toolName); - const command = shortToolName === 'commands' ? extractSandboxCommand(toolUse.input) : ''; - const progress = typeof toolUse.name === 'string' - ? inferToolProgress(toolName, toolUse.input) - : {}; - const inputSummary = summarizeToolInput(toolName, toolUse.input, state.appDir); - const progressSignature = JSON.stringify({ - name: toolName, - command, - phaseHint: progress.phaseHint || '', - fileCount: progress.fileCount || 0, - inputSummary, - }); - if (toolUseId) { - const previousSignature = emittedToolUseProgress.get(toolUseId); - if (previousSignature === progressSignature) { - return; - } - emittedToolUseProgress.set(toolUseId, progressSignature); - } - // Tool calls end the current narration block. Clear the per-block window so - // the next assistant text is not compared against the previous sentence. - narrationState = { - ...narrationState, - currentTextBlock: '', - }; - - if (toolUseId && typeof toolUse.name === 'string') { - toolContextById.set(toolUseId, { - name: toolUse.name, - ...(command ? { command } : {}), - }); - } - const startedAt = toolUseId - ? toolStartedAtById.get(toolUseId) || Date.now() - : Date.now(); - if (toolUseId) toolStartedAtById.set(toolUseId, startedAt); - onProgress?.({ - type: 'tool_use', - data: { - id: toolUseId, - name: toolName, - ...(command ? { command } : {}), - ...progress, - inputSummary, - startedAt, - }, - }); - }; - - for await (const event of sdkQuery as AsyncIterable) { - if (abortSignal?.aborted) { - sdkAbortController.abort(); - break; - } - // Forward structured tool progress and high-level model narration. Tool - // input JSON and non-text stream deltas stay out of the UI. - if (event.type === 'stream_event') { - emitNarration( - extractVisibleNarrationDelta(event), - typeof event.uuid === 'string' ? event.uuid : '', - false, - ); - const streamEvent = (event as any).event; - if (streamEvent?.type === 'content_block_start') { - const contentBlock = streamEvent.content_block; - // Each new text block starts a fresh dedupe window so earlier narration - // cannot suppress later phrases that share a common suffix/substring. - if (contentBlock?.type === 'text') { - narrationState = { - ...narrationState, - currentTextBlock: '', - }; - } - if (isToolUseContentBlock(contentBlock) && typeof streamEvent.index === 'number') { - pendingToolUseBlocks.set(streamEvent.index, { - id: typeof contentBlock.id === 'string' ? contentBlock.id : '', - name: typeof contentBlock.name === 'string' ? contentBlock.name : '', - inputJson: '', - input: contentBlock.input, - }); - emitToolUseProgress({ - id: contentBlock.id, - name: contentBlock.name, - input: contentBlock.input, - }); - } - } else if (streamEvent?.type === 'content_block_delta') { - const delta = streamEvent.delta; - const pendingToolUse = typeof streamEvent.index === 'number' - ? pendingToolUseBlocks.get(streamEvent.index) - : undefined; - if ( - pendingToolUse - && delta?.type === 'input_json_delta' - && typeof delta.partial_json === 'string' - ) { - pendingToolUse.inputJson += delta.partial_json; - } - } else if (streamEvent?.type === 'content_block_stop') { - const pendingToolUse = typeof streamEvent.index === 'number' - ? pendingToolUseBlocks.get(streamEvent.index) - : undefined; - if (pendingToolUse) { - pendingToolUseBlocks.delete(streamEvent.index); - emitToolUseProgress({ - id: pendingToolUse.id, - name: pendingToolUse.name, - input: parseToolInputJson(pendingToolUse.inputJson, pendingToolUse.input), - }); - } - } - } else if (event.type === 'assistant') { - const blocks = (event as any).message?.content; - if (Array.isArray(blocks)) { - for (const b of blocks) { - emitNarration( - extractVisibleTextBlock(b), - typeof event.uuid === 'string' ? event.uuid : '', - true, - ); - if (isToolUseContentBlock(b)) { - emitToolUseProgress({ - id: b.id, - name: b.name, - input: b.input, - }); - } - } - } - } else if (event.type === 'user') { - const blocks = (event as any).message?.content; - if (Array.isArray(blocks)) { - for (const b of blocks) { - if (b?.type === 'tool_result') { - const text = Array.isArray(b.content) - ? b.content.map((c: any) => (typeof c?.text === 'string' ? c.text : '')).join(' ') - : (typeof b.content === 'string' ? b.content : ''); - const toolContext = toolContextById.get(b.tool_use_id); - const toolName = toolContext?.name || ''; - const echoedExit = parseEchoedExitCode(text); - const commandFailed = typeof echoedExit === 'number' && echoedExit !== 0; - const toolFailed = b.is_error === true || commandFailed; - onProgress?.({ - type: 'tool_result', - data: { - tool_use_id: typeof b.tool_use_id === 'string' ? b.tool_use_id : '', - toolName, - ...(toolContext?.command ? { command: toolContext.command } : {}), - ok: !toolFailed, - preview: truncateForStream(text, 500), - outputSummary: summarizeToolOutput(text, state.appDir, toolName), - status: toolFailed ? 'failed' : 'completed', - endedAt: Date.now(), - }, - }); - // Once ensure_project_scaffold succeeds, notify the outer pipeline to - // push file_tree so the Files panel does not wait for the whole runCodingAgent turn. - if ( - !scaffoldHandled - && toolName === SCAFFOLD_TOOL_NAME - && b.is_error !== true - ) { - scaffoldHandled = true; - try { - await onProjectFilesChanged?.(); - } catch (err) { - console.warn('[scaffold-done] onProjectFilesChanged failed', err); - } - } - // Detect sandbox infrastructure failures only on is_error=true tool - // results, avoiding false positives from normal text containing "Not Found". - if (b.is_error === true && !fatalError) { - const fatal = detectFatalToolError(text); - if (fatal) { - fatalError = `${fatal} (tool=${toolName})`; - console.warn('[fatal] aborting agent loop:', fatalError); - } - } - } - } - } - } - if (event.type === 'result') { - resultMessage = event; - break; - } - // Exit the loop immediately after a fatal error instead of waiting for more model turns. - if (fatalError) { - break; - } - } - - if (abortSignal?.aborted || sdkAbortController.signal.aborted) { - return { - success: false, - output: null, - error: null, - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - stopped: true, - }; - } - - // Fatal errors take priority over normal results, even if the SDK produced - // a result for this turn. - if (fatalError) { - return { - success: false, - output: null, - error: fatalError, - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - fatal: true, - }; - } - - if (!resultMessage) { - return { - success: false, - output: null, - error: 'The model stream ended without returning a result.', - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - }; - } - - // Whether the composer's model switch took effect is otherwise - // unobservable: asking the agent returns the priors of whichever model is - // answering, not this run. Logged for every finished run, failed included, - // because a substitution is a reason a run fails. - const modelRun = describeModelRun(model, resultMessage.modelUsage); - if (modelRun.mismatch) { - console.warn('[model]', `${modelRun.line} — the gateway served a model this turn did not request`); - } else { - console.info('[model]', modelRun.line); - } - - if (resultMessage.subtype !== 'success') { - return { - success: false, - output: null, - error: Array.isArray(resultMessage.errors) && resultMessage.errors.length > 0 - ? resultMessage.errors[0] - : 'Model execution failed.', - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - }; - } - - return { - success: true, - output: sanitizeAssistantText((resultMessage.result || '').trim()), - error: null, - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - }; - } catch(e) { - if (abortSignal?.aborted || (e instanceof Error && e.name === 'AbortError')) { - return { - success: false, - output: null, - error: null, - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - stopped: true, - }; - } - console.error(e); - const message = e instanceof Error ? e.message : String(e); - const fatal = detectFatalToolError(message); - return { - success: false, - output: null, - error: fatal || message || 'Execution failed.', - projectTouched, - filesWritten, - previewTouched, - deploymentTouched, - wasCreated, - ...(fatal ? { fatal: true } : {}), - }; - } finally { - abortSignal?.removeEventListener('abort', abortSdkQuery); - // Terminate the CLI subprocess and its MCP transports on every exit path. - // Breaking the loop on a fatal error leaves them running otherwise, which - // keeps consuming turns and billing after the response is already sent. - try { - sdkQuery?.close(); - } catch (err) { - console.warn('[agent] failed to close the SDK query', err); - } - } -} diff --git a/shared/makers-deploy.ts b/agents/_lib/makers/cli-deploy.ts similarity index 97% rename from shared/makers-deploy.ts rename to agents/_lib/makers/cli-deploy.ts index 688bc68..af15f9d 100644 --- a/shared/makers-deploy.ts +++ b/agents/_lib/makers/cli-deploy.ts @@ -3,12 +3,15 @@ * Keep this free of sandbox / React imports so tests and the frontend can share it. */ -import { buildMakersDevStopScript } from './makers-dev.ts'; +import { buildMakersDevStopScript } from './cli-dev.ts'; import { buildNpmCacheReclaimScript } from './npm-install.ts'; -import type { DeploymentInfo } from './protocol.ts'; -import { shellQuote } from './shell.ts'; +import type { DeploymentInfo } from '../../../shared/protocol.ts'; +import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; +import { shellQuote } from '../utils/shell.ts'; import { MAKERS_CLI_UNAVAILABLE_MESSAGE, isEdgeoneCliUnavailable } from './tool-phase.ts'; +export { isMakersDeployUrl }; + /** * Printed by the deploy command when the CLI reported a successful publish that * carried no client build. @@ -650,21 +653,6 @@ export function describeMakersDeployment( }; } -export function isMakersDeployUrl(url?: string | null): boolean { - if (!url) return false; - try { - const parsed = new URL(url); - if (parsed.pathname === '/preview/' || parsed.pathname.startsWith('/preview/')) { - return false; - } - return /(?:^|\.)edgeone\.(?:cool|ai|link)$/i.test(parsed.hostname) - || /(?:^|\.)pages\.edgeone\./i.test(parsed.hostname) - || /(?:^|\.)edgeone\.page$/i.test(parsed.hostname); - } catch { - return false; - } -} - export function redactSecret(text: string, secret: string) { if (!secret) return text; return text.split(secret).join('[redacted]'); diff --git a/shared/makers-dev.ts b/agents/_lib/makers/cli-dev.ts similarity index 66% rename from shared/makers-dev.ts rename to agents/_lib/makers/cli-dev.ts index 44c6830..ab6fa57 100644 --- a/shared/makers-dev.ts +++ b/agents/_lib/makers/cli-dev.ts @@ -9,7 +9,8 @@ import { createHash } from 'node:crypto'; import { buildNpmWarmupWaitScript } from './npm-install.ts'; -import { shellQuote } from './shell.ts'; +import { buildPreviewProxyTemplate, PROXY_REVISION_PLACEHOLDER } from './preview-proxy-source.ts'; +import { shellQuote } from '../utils/shell.ts'; export const PREVIEW_PROXY_SCRIPT_PATH = '/tmp/edgeone-preview-proxy.cjs'; @@ -158,8 +159,6 @@ export function buildMakersDevStopScript(makersPort: number, announce = '') { ].join('\n'); } -const PROXY_REVISION_PLACEHOLDER = '__PREVIEW_PROXY_REVISION__'; - function normalizePreviewPrefix(value: string) { const normalized = `/${value}`.replace(/\/+/g, '/').replace(/\/+$/, ''); return normalized === '/' ? '' : normalized; @@ -413,457 +412,6 @@ function buildPreviewProxy(listenPort: number, targetPort: number, prefix: strin return { script: template.replace(PROXY_REVISION_PLACEHOLDER, revision), revision }; } -function buildPreviewProxyTemplate( - listenPort: number, - targetPort: number, - prefix: string, -) { - const normalizedPrefix = normalizePreviewPrefix(prefix); - return `const http = require('node:http'); -const net = require('node:net'); - -const LISTEN_PORT = ${listenPort}; -const TARGET_PORT = ${targetPort}; -const PREFIX = ${JSON.stringify(normalizedPrefix)}; -const HEALTH_PATH = '/__edgeone_preview_proxy_health'; - -// Set once the upstream asks to be addressed with the prefix — see -// previewUpstreamClaimsPrefix in shared/makers-dev.ts. Until then the prefix is -// stripped, which is what makers dev and every framework with an asset-only -// prefix knob expect. -let prefixAware = false; - -// The 404-shaped form of the same claim, tried once per proxy — see -// previewPrefixProbe. Once, because an upstream that really does serve at the -// root answers a genuinely missing page with a 404 too, and re-asking on every -// one of those would double the requests to re-learn what the first probe -// already established. -let prefixProbed = false; - -function rewritePath(url) { - if (!url) return '/'; - if (prefixAware) return url; - if ( - PREFIX - && (url === PREFIX || url.startsWith(PREFIX + '/') || url.startsWith(PREFIX + '?')) - ) { - const next = url.slice(PREFIX.length); - if (!next || next === '/') return '/'; - return next.startsWith('/') ? next : '/' + next; - } - return url; -} - -// Mirrors previewCanonicalRedirect in shared/makers-dev.ts. -function canonicalRedirect(url) { - if (!PREFIX || !url) return null; - const queryStart = url.indexOf('?'); - const path = queryStart === -1 ? url : url.slice(0, queryStart); - if (path !== PREFIX) return null; - return PREFIX + '/' + (queryStart === -1 ? '' : url.slice(queryStart)); -} - -function rewriteLocation(value) { - if (!PREFIX || typeof value !== 'string' || !value.startsWith('/')) return value; - if (value === PREFIX || value.startsWith(PREFIX + '/')) return value; - return PREFIX + value; -} - -// Mirrors previewUpstreamClaimsPrefix in shared/makers-dev.ts. -function claimsPrefix(statusCode, location) { - if (!PREFIX) return false; - if (!statusCode || statusCode < 300 || statusCode >= 400) return false; - if (typeof location !== 'string' || !location) return false; - let path = location; - const schemeEnd = location.indexOf('://'); - if (schemeEnd !== -1) { - const afterHost = location.indexOf('/', schemeEnd + 3); - path = afterHost === -1 ? '/' : location.slice(afterHost); - } else if (location.charAt(0) !== '/') { - return false; - } - path = path.split('?')[0]; - return path === PREFIX || path.indexOf(PREFIX + '/') === 0; -} - -// Mirrors previewPrefixProbe in shared/makers-dev.ts. -function prefixProbe(requestUrl, forwardedPath, statusCode) { - if (!PREFIX || !requestUrl || !forwardedPath) return null; - if (statusCode !== 404) return null; - if (forwardedPath === requestUrl) return null; - const probePath = requestUrl.split('?')[0]; - if (probePath !== PREFIX && probePath.indexOf(PREFIX + '/') !== 0) return null; - return requestUrl; -} - -// Mirrors previewTrailingSlashFollow in shared/makers-dev.ts. -function trailingSlashFollow(forwardedPath, statusCode, location) { - if (!forwardedPath) return null; - if (!statusCode || statusCode < 300 || statusCode >= 400) return null; - if (typeof location !== 'string' || location.charAt(0) !== '/') return null; - const from = forwardedPath.split('?')[0]; - const to = location.split('?')[0]; - if (from === to) return null; - return withoutTrailingSlash(from) === withoutTrailingSlash(to) ? location : null; -} - -function withoutTrailingSlash(value) { - return value.length > 1 && value.charAt(value.length - 1) === '/' - ? value.slice(0, -1) - : value; -} - -function rewriteSetCookie(value) { - if (!PREFIX || typeof value !== 'string') return value; - return value.replace(/;\\s*Path=\\//gi, '; Path=' + PREFIX + '/'); -} - -// makers dev reaches its function runtime through http-proxy with xfwd -// enabled, and xfwd APPENDS to x-forwarded-proto rather than replacing it. A -// value from the sandbox gateway therefore arrives at the runtime as -// "http,http", which it concatenates into a request URL and hands to new -// Request(): ERR_INVALID_URL. The failure is then swallowed by an error -// handler that throws on its own, so nothing ever writes a response and the -// browser spins until the user gives up. Dropping the header here leaves xfwd -// setting the single value it would have set anyway, which is also what makes -// a direct curl to the CLI work today. -// The parent workspace frames this preview cross-origin, so it cannot read the -// iframe's location to fill its address bar. Nothing else can report the route -// either: makers dev serves the application, and a proxy is the only layer left -// that sees every document. This posts the real path — prefix included, which is -// what the parent strips for display and reuses when deep-linking a copied URL. -const TRACKER = ''; - -// Scanned as latin1 so one character is one byte and the match offset can index -// the buffer directly. -// -// is only the preferred landing place. A hand-written index.html may not -// have one, and the script above is now what makes in-app navigation work, so -// skipping those pages would leave exactly the simplest generated sites broken. -// The fallbacks stay below the doctype: above it the page drops into quirks -// mode, which changes how the whole document lays out. -function headInsertionPoint(buffer) { - const text = buffer.toString('latin1'); - for (const pattern of [/]*>/i, /]*>/i, /]*>/i]) { - const match = pattern.exec(text); - if (match) return match.index + match[0].length; - } - return -1; -} - -function acceptsHtml(req) { - const accept = req.headers.accept; - return typeof accept === 'string' && accept.includes('text/html'); -} - -function isHtmlResponse(headers) { - const type = headers['content-type']; - return typeof type === 'string' && type.toLowerCase().includes('text/html'); -} - -function forwardHeaders(req) { - const headers = { - ...req.headers, - host: '127.0.0.1:' + TARGET_PORT, - 'x-forwarded-prefix': PREFIX, - }; - delete headers['x-forwarded-proto']; - // TRACKER can only be spliced into an unencoded body. Asking for identity on - // navigations alone costs nothing on a loopback hop and leaves compression in - // place for the assets, which are what the encoding is actually worth. - if (acceptsHtml(req)) headers['accept-encoding'] = 'identity'; - return headers; -} - -// Buffer only up to the opening , then release and stream the rest -// untouched: a page that streams its body from a Suspense boundary has to keep -// arriving in pieces, and the shell carrying is already in the first one. -function injectTracker(upstream, res) { - const SCAN_LIMIT = 65536; - let pending = []; - let scanned = 0; - let injected = false; - - function splice(buffer) { - const at = headInsertionPoint(buffer); - // No to splice after. Prepending would land the script above the - // doctype and drop the page into quirks mode, so leave the body alone and - // let the address bar stay where it is. - if (at === -1) { - res.write(buffer); - return; - } - res.write(buffer.subarray(0, at)); - res.write(TRACKER); - res.write(buffer.subarray(at)); - } - - upstream.on('data', (chunk) => { - if (injected) { - res.write(chunk); - return; - } - pending.push(chunk); - scanned += chunk.length; - const buffer = Buffer.concat(pending); - if (headInsertionPoint(buffer) === -1 && scanned <= SCAN_LIMIT) return; - injected = true; - pending = []; - splice(buffer); - }); - upstream.on('end', () => { - if (!injected && pending.length) splice(Buffer.concat(pending)); - res.end(); - }); - upstream.on('error', () => res.end()); -} - -const server = http.createServer((req, res) => { - if ((req.url || '').split('?')[0] === HEALTH_PATH) { - res.writeHead(200, { - 'content-type': 'text/plain', - 'x-edgeone-preview-proxy': '${PROXY_REVISION_PLACEHOLDER}', - }); - res.end('ok'); - return; - } - - const canonical = canonicalRedirect(req.url); - if (canonical) { - res.writeHead(308, { location: canonical }); - res.end(); - return; - } - - forward(req, res, rewritePath(req.url), true, true, false); -}); - -function forward(req, res, path, mayRetry, mayFollow, probing) { - const headers = forwardHeaders(req); - const proxy = http.request({ - hostname: '127.0.0.1', - port: TARGET_PORT, - path, - method: req.method, - headers, - }, (upstream) => { - // The probe's answer, which is the half of previewPrefixProbe that decides. - // Anything but a second 404 means the prefixed path is a route the upstream - // knows, so it keeps the prefix from here on. Read before the branches - // below so a probe answered with a redirect still counts as knowing it. - if (probing && upstream.statusCode !== 404) prefixAware = true; - // The one response that means the prefix should not have been stripped. - // Retried rather than passed on, because handing the browser a redirect to - // a path this proxy still strips is the same request again: it would bounce - // between the two until the browser gave up. - if ( - mayRetry - && !prefixAware - && (req.method === 'GET' || req.method === 'HEAD') - && claimsPrefix(upstream.statusCode, upstream.headers.location) - ) { - prefixAware = true; - upstream.resume(); - forward(req, res, req.url || '/', false, true, false); - return; - } - // The same claim made as a 404, which is how Astro states it. Asked rather - // than concluded: the retry's status is what tells a base-mounted app apart - // from a page that is simply not there. - if ( - mayRetry - && !prefixAware - && !prefixProbed - && (req.method === 'GET' || req.method === 'HEAD') - && prefixProbe(req.url, path, upstream.statusCode) - ) { - prefixProbed = true; - upstream.resume(); - forward(req, res, req.url, false, true, true); - return; - } - // A redirect that only normalizes a trailing slash, settled here for the - // same reason — see previewTrailingSlashFollow. Once, and never from a - // follow of its own: an upstream that keeps normalizing is a loop this - // proxy would be holding open instead of the browser. - if (mayFollow && (req.method === 'GET' || req.method === 'HEAD')) { - const follow = trailingSlashFollow( - path, - upstream.statusCode, - upstream.headers.location, - ); - if (follow) { - upstream.resume(); - forward(req, res, follow, false, false, false); - return; - } - } - const responseHeaders = { ...upstream.headers }; - if (responseHeaders.location) { - responseHeaders.location = rewriteLocation(responseHeaders.location); - } - if (Array.isArray(responseHeaders['set-cookie'])) { - responseHeaders['set-cookie'] = responseHeaders['set-cookie'].map(rewriteSetCookie); - } - const injectable = isHtmlResponse(responseHeaders) - && !responseHeaders['content-encoding']; - // The body grows by TRACKER, so the declared length no longer holds. - // Dropping it hands the response to chunked encoding. - if (injectable) delete responseHeaders['content-length']; - res.writeHead(upstream.statusCode || 502, responseHeaders); - if (injectable) injectTracker(upstream, res); - else upstream.pipe(res); - }); - proxy.on('error', () => { - if (!res.headersSent) res.writeHead(502); - res.end('preview proxy error'); - }); - // Neither a retry nor a follow has a body left to send: both are reached - // only for a GET or a HEAD, and the request stream is already consumed. - if (mayRetry) req.pipe(proxy); - else proxy.end(); -} - -server.on('upgrade', (req, socket, head) => { - const path = rewritePath(req.url); - const headers = forwardHeaders(req); - const target = net.connect(TARGET_PORT, '127.0.0.1', () => { - const headerLines = Object.entries(headers).flatMap(([key, value]) => { - if (value == null) return []; - return [key + ': ' + (Array.isArray(value) ? value.join(', ') : value)]; - }); - target.write([ - (req.method || 'GET') + ' ' + path + ' HTTP/1.1', - ...headerLines, - '', - '', - ].join('\\r\\n')); - if (head && head.length) target.write(head); - target.pipe(socket); - socket.pipe(target); - }); - target.on('error', () => socket.destroy()); - socket.on('error', () => target.destroy()); -}); - -server.listen(LISTEN_PORT, '0.0.0.0'); -`; -} - export type MakersDevBackgroundOptions = { makersPort: number; previewPort: number; diff --git a/agents/_lib/project/makers-compat.ts b/agents/_lib/makers/compat/lint-script.ts similarity index 58% rename from agents/_lib/project/makers-compat.ts rename to agents/_lib/makers/compat/lint-script.ts index 030c9c8..d730a75 100644 --- a/agents/_lib/project/makers-compat.ts +++ b/agents/_lib/makers/compat/lint-script.ts @@ -1,251 +1,4 @@ -import { createHash } from 'node:crypto'; -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; -import type { ProjectState } from '../types.ts'; -import { runCommandCapturingExit } from './commands.ts'; - -export type MakersValidationRule = { - skill: string; - pathPatterns: string[]; - pattern: string; - message: string; -}; - -/** - * Where a framework's platform adapter goes and whether this project needs one. - * - * `serverOutput` is deliberately separate from `adapter`: the file that decides - * whether the app renders on a server is often not the file the adapter is - * wired into. React Router declares `ssr` in react-router.config.ts and takes - * its adapter as a vite.config.ts plugin, so one field cannot serve both. - */ -export type MakersFrameworkProfile = { - id: string; - label: string; - detect: string[]; - adapter: { - package: string; - /** - * The range to declare when this agent adds the adapter itself. Optional - * because it is the platform's to state, not this repo's: absent, the - * declaration falls back to `latest`, which resolves but pins nothing. - */ - version?: string; - configFiles: string[]; - /** - * A config file that, in a shape only it can be in, silences the others. - * - * SvelteKit is why this exists. It reads exactly one config and prefers the - * Vite one, so a single option passed to `sveltekit()` discards the whole of - * a sibling `svelte.config.js` — adapter included, with no warning from the - * build. Checking the first config file that happens to exist calls that - * project correct; checking `vite.config.ts` unconditionally calls a project - * that legitimately keeps its config in `svelte.config.js` broken. - */ - configOverride?: { - files: string[]; - pattern: string; - /** Appended to the error, because "wire it in here" is baffling on its own. */ - reason: string; - }; - required: 'always' | 'server-output'; - } | null; - serverOutput?: { - files: string[]; - default: 'server' | 'static'; - serverPattern?: string; - staticPattern?: string; - }; - outputDirectory: string; - unsupported: string[]; -}; - -const FRAMEWORK_PROFILE_SKILL = 'makers-frameworks'; - -// The profiles live in a fenced block inside the vendored skill rather than in -// its frontmatter: they are nested objects, and the frontmatter reader here is a -// line-oriented approximation of YAML that cannot represent them. -const FRAMEWORK_PROFILE_BLOCK = - /\s*```json\s*\r?\n([\s\S]*?)\r?\n```/; - -export function parseMakersFrameworkProfiles(source: string): MakersFrameworkProfile[] { - const block = source.match(FRAMEWORK_PROFILE_BLOCK)?.[1]; - if (!block) return []; - const parsed = JSON.parse(block) as MakersFrameworkProfile[]; - if (!Array.isArray(parsed)) { - throw new Error('makers-framework-profiles must be a JSON array'); - } - for (const profile of parsed) { - if (!profile.id || !Array.isArray(profile.detect) || profile.detect.length === 0) { - throw new Error(`framework profile ${profile.id || '(unnamed)'} needs an id and a detect list`); - } - // Compile every pattern at load time so a malformed vendored profile fails - // here, where the message names the profile, rather than inside the sandbox - // script as a syntax error with no attribution. - for (const pattern of [ - profile.serverOutput?.serverPattern, - profile.serverOutput?.staticPattern, - profile.adapter?.configOverride?.pattern, - ]) { - if (pattern) new RegExp(pattern); - } - } - return parsed; -} - -const VALIDATION_SKILLS = [ - 'makers-agents', - 'makers-cloud-functions', - 'makers-deploy', - 'makers-edge-functions', - 'makers-env-adaption', - 'makers-frameworks', - 'makers-middleware', - 'makers-storage', -] as const; - -export const SUPPORTED_MAKERS_AGENT_FRAMEWORKS = [ - 'claude-agent-sdk', - 'openai-agents-sdk', - 'langgraph', - 'crewai', - 'deepagents', -] as const; - -function parseFrontmatterScalar(value: string) { - const trimmed = value.trim(); - if (trimmed.startsWith('"') && trimmed.endsWith('"')) { - // A double-quoted YAML scalar is close enough to JSON to reuse the parser, - // but not close enough to trust it: one stray backslash in a vendored skill - // would otherwise take down every compatibility check with a SyntaxError. - try { - return JSON.parse(trimmed) as string; - } catch { - return trimmed.slice(1, -1); - } - } - if (trimmed.startsWith("'") && trimmed.endsWith("'")) { - return trimmed.slice(1, -1).replaceAll("''", "'"); - } - return trimmed; -} - -export function parseMakersSkillValidationRules( - skill: string, - source: string, -): MakersValidationRule[] { - const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]; - if (!frontmatter) return []; - - const pathPatterns: string[] = []; - const rules: Array<{ pattern: string; message: string }> = []; - let section: 'paths' | 'validate' | null = null; - let pendingPattern = ''; - - for (const line of frontmatter.split(/\r?\n/)) { - if (line === 'pathPatterns:') { - section = 'paths'; - continue; - } - if (line === 'validate:') { - section = 'validate'; - continue; - } - if (/^\S/.test(line)) { - section = null; - continue; - } - if (section === 'paths') { - const match = line.match(/^\s{2}-\s+(.+)$/); - if (match?.[1]) pathPatterns.push(parseFrontmatterScalar(match[1])); - continue; - } - if (section === 'validate') { - const patternMatch = line.match(/^\s{2}-\s+pattern:\s+(.+)$/); - if (patternMatch?.[1]) { - pendingPattern = parseFrontmatterScalar(patternMatch[1]); - continue; - } - const messageMatch = line.match(/^\s{4}message:\s+(.+)$/); - if (messageMatch?.[1] && pendingPattern) { - rules.push({ - pattern: pendingPattern, - message: parseFrontmatterScalar(messageMatch[1]), - }); - pendingPattern = ''; - } - } - } - - return rules.map((rule) => ({ - skill, - pathPatterns: [...pathPatterns], - ...rule, - })); -} - -function readVendoredSkill(skill: string) { - return readFile( - path.join( - process.cwd(), - '.claude', - 'skills', - 'edgeone-makers-tools', - 'references', - skill, - 'SKILL.md', - ), - 'utf8', - ); -} - -let frameworkProfilesPromise: Promise | undefined; - -export function loadMakersFrameworkProfiles(): Promise { - if (!frameworkProfilesPromise) { - const pending = readVendoredSkill(FRAMEWORK_PROFILE_SKILL).then((source) => { - const profiles = parseMakersFrameworkProfiles(source); - if (profiles.length === 0) { - throw new Error( - `${FRAMEWORK_PROFILE_SKILL} carries no framework profiles; the adapter check cannot run without them`, - ); - } - return profiles; - }); - frameworkProfilesPromise = pending.catch((error) => { - frameworkProfilesPromise = undefined; - throw error; - }); - } - return frameworkProfilesPromise; -} - -let validationRulesPromise: Promise | undefined; - -export function loadMakersValidationRules(): Promise { - if (!validationRulesPromise) { - const pending = Promise.all(VALIDATION_SKILLS.map(async (skill) => { - const source = await readVendoredSkill(skill); - return parseMakersSkillValidationRules(skill, source); - })).then((groups) => { - const rules = groups.flat(); - for (const rule of rules) { - // Fail at the source if an official vendored rule is malformed instead - // of silently dropping a compatibility check. - new RegExp(rule.pattern); - } - return rules; - }); - // Cache the success, not the attempt. Holding a rejected promise here would - // turn one unlucky read into a permanently broken compatibility check for - // every later turn on this warm instance. - validationRulesPromise = pending.catch((error) => { - validationRulesPromise = undefined; - throw error; - }); - } - return validationRulesPromise; -} +import { SUPPORTED_MAKERS_AGENT_FRAMEWORKS, type MakersFrameworkProfile, type MakersValidationRule } from './skill-rules.ts'; export function buildMakersCompatibilityScript( sourceRules: readonly MakersValidationRule[], @@ -706,115 +459,3 @@ process.stdout.write( ); `}`; } - -const COMPAT_SCRIPT_NAME = '.makers-compat-check.cjs'; - -/** - * Which script body each session already has on disk. - * - * The body is derived from the vendored skills, which are read once per - * process, so it is identical for every run of a session — and the lint runs at - * least twice a turn, once before the preview starts and once at verification. - * Re-sending it each time was a sandbox write buying nothing. - * - * Keyed by session because sessions do not share a sandbox, and paired with the - * retry below because a cache that outlives the file it describes is worse than - * no cache at all. - */ -const uploadedCompatScripts = new Map(); - -function compatScriptFingerprint(script: string) { - return createHash('sha256').update(script).digest('hex'); -} - -/** A sandbox recycled under us: the file is gone, so the lint never ran. */ -function compatScriptMissing(result: { stdout?: string; stderr?: string }) { - const output = `${result.stdout || ''}\n${result.stderr || ''}`; - return output.includes('MODULE_NOT_FOUND') - || (output.includes('Cannot find module') && output.includes(COMPAT_SCRIPT_NAME)); -} - -/** - * Run the lint so that a failure still arrives as a report. - * - * Exiting non-zero is how the lint says it found something, and it is also what - * throws away everything it found: the sandbox layer turns a failed shell into - * SANDBOX_UNKNOWN_ERROR and keeps neither stdout nor stderr, so the caller is - * handed "exit status 2" and nothing else. That reads like a broken sandbox - * rather than a project to fix — the run that prompted this spent one turn - * checking the CLI version and another guessing at a file before it landed on - * the one the lint had already named. Echoing the status keeps the shell - * successful, which is what lets the report travel as text. - */ -export function buildMakersCompatibilityCommand() { - return [ - 'set +e', - `node ../${COMPAT_SCRIPT_NAME}`, - 'echo EXIT:$?', - ].join('\n'); -} - -export async function runMakersCompatibilityCheck( - context: any, - state: ProjectState, -) { - const [rules, profiles] = await Promise.all([ - loadMakersValidationRules(), - loadMakersFrameworkProfiles(), - ]); - const script = buildMakersCompatibilityScript(rules, profiles); - const scriptPath = `${state.sessionDir}/${COMPAT_SCRIPT_NAME}`; - const fingerprint = compatScriptFingerprint(script); - const upload = async () => { - await context.sandbox.files.write(scriptPath, script); - uploadedCompatScripts.set(state.sessionDir, fingerprint); - }; - - if (uploadedCompatScripts.get(state.sessionDir) !== fingerprint) { - await upload(); - } - - const run = () => runCommandCapturingExit( - context, - buildMakersCompatibilityCommand(), - { cwd: state.appDir, timeout: 20 }, - ); - - const result = await run(); - if (result.exitCode !== 0 && compatScriptMissing(result)) { - // Not a project failure — the lint had nothing to run. Restore the file and - // ask again, because reporting this as a compatibility failure would send - // the model looking for a problem in code that was never examined. - uploadedCompatScripts.delete(state.sessionDir); - await upload(); - return run(); - } - return result; -} - -/** - * Keep fast, deterministic checks that the CLI cannot explain as clearly. - * - * The prefix rules here are about what the project must not contain: the host - * restores /preview/ in the browser, so root-absolute paths are correct, and - * what breaks is a path that carries the prefix already or a framework told to - * expect it. The exception is a subresource URL in a page nothing builds, which - * the parser fetches before the restoring shim can exist — that one has to be - * relative, and it is the only root-absolute path still rejected here. - * - * The adapter check is the exception in shape — it is about what the project - * must contain. It earns that because it is the only failure here that no other - * gate sees: preview, smoke test, and build all pass without the adapter, and - * the deployment is broken anyway. - */ -export async function assertMakersProjectCompatible( - context: any, - state: ProjectState, -) { - const result = await runMakersCompatibilityCheck(context, state); - if (result.exitCode !== 0) { - throw new Error( - `Makers compatibility check failed:\n${result.stderr || result.stdout}\nThis is the project lint, not the EdgeOne CLI: do not check the CLI version or inspect the environment. Fix only the reported project files, then rerun the same EdgeOne CLI command.`, - ); - } -} diff --git a/agents/_lib/makers/compat/run.ts b/agents/_lib/makers/compat/run.ts new file mode 100644 index 0000000..88a1f77 --- /dev/null +++ b/agents/_lib/makers/compat/run.ts @@ -0,0 +1,118 @@ +import { createHash } from 'node:crypto'; +import type { ProjectState } from '../../types.ts'; +import { runCommandCapturingExit } from '../../project/commands.ts'; +import { loadMakersFrameworkProfiles, loadMakersValidationRules } from './skill-rules.ts'; +import { buildMakersCompatibilityScript } from './lint-script.ts'; + +const COMPAT_SCRIPT_NAME = '.makers-compat-check.cjs'; + +/** + * Which script body each session already has on disk. + * + * The body is derived from the vendored skills, which are read once per + * process, so it is identical for every run of a session — and the lint runs at + * least twice a turn, once before the preview starts and once at verification. + * Re-sending it each time was a sandbox write buying nothing. + * + * Keyed by session because sessions do not share a sandbox, and paired with the + * retry below because a cache that outlives the file it describes is worse than + * no cache at all. + */ +const uploadedCompatScripts = new Map(); + +function compatScriptFingerprint(script: string) { + return createHash('sha256').update(script).digest('hex'); +} + +/** A sandbox recycled under us: the file is gone, so the lint never ran. */ +function compatScriptMissing(result: { stdout?: string; stderr?: string }) { + const output = `${result.stdout || ''}\n${result.stderr || ''}`; + return output.includes('MODULE_NOT_FOUND') + || (output.includes('Cannot find module') && output.includes(COMPAT_SCRIPT_NAME)); +} + +/** + * Run the lint so that a failure still arrives as a report. + * + * Exiting non-zero is how the lint says it found something, and it is also what + * throws away everything it found: the sandbox layer turns a failed shell into + * SANDBOX_UNKNOWN_ERROR and keeps neither stdout nor stderr, so the caller is + * handed "exit status 2" and nothing else. That reads like a broken sandbox + * rather than a project to fix — the run that prompted this spent one turn + * checking the CLI version and another guessing at a file before it landed on + * the one the lint had already named. Echoing the status keeps the shell + * successful, which is what lets the report travel as text. + */ +export function buildMakersCompatibilityCommand() { + return [ + 'set +e', + `node ../${COMPAT_SCRIPT_NAME}`, + 'echo EXIT:$?', + ].join('\n'); +} + +export async function runMakersCompatibilityCheck( + context: any, + state: ProjectState, +) { + const [rules, profiles] = await Promise.all([ + loadMakersValidationRules(), + loadMakersFrameworkProfiles(), + ]); + const script = buildMakersCompatibilityScript(rules, profiles); + const scriptPath = `${state.sessionDir}/${COMPAT_SCRIPT_NAME}`; + const fingerprint = compatScriptFingerprint(script); + const upload = async () => { + await context.sandbox.files.write(scriptPath, script); + uploadedCompatScripts.set(state.sessionDir, fingerprint); + }; + + if (uploadedCompatScripts.get(state.sessionDir) !== fingerprint) { + await upload(); + } + + const run = () => runCommandCapturingExit( + context, + buildMakersCompatibilityCommand(), + { cwd: state.appDir, timeout: 20 }, + ); + + const result = await run(); + if (result.exitCode !== 0 && compatScriptMissing(result)) { + // Not a project failure — the lint had nothing to run. Restore the file and + // ask again, because reporting this as a compatibility failure would send + // the model looking for a problem in code that was never examined. + uploadedCompatScripts.delete(state.sessionDir); + await upload(); + return run(); + } + return result; +} + +/** + * Keep fast, deterministic checks that the CLI cannot explain as clearly. + * + * The prefix rules here are about what the project must not contain: the host + * restores /preview/ in the browser, so root-absolute paths are correct, and + * what breaks is a path that carries the prefix already or a framework told to + * expect it. The exception is a subresource URL in a page nothing builds, which + * the parser fetches before the restoring shim can exist — that one has to be + * relative, and it is the only root-absolute path still rejected here. + * + * The adapter check is the exception in shape — it is about what the project + * must contain. It earns that because it is the only failure here that no other + * gate sees: preview, smoke test, and build all pass without the adapter, and + * the deployment is broken anyway. + */ +export async function assertMakersProjectCompatible( + context: any, + state: ProjectState, +) { + const result = await runMakersCompatibilityCheck(context, state); + if (result.exitCode !== 0) { + throw new Error( + `Makers compatibility check failed:\n${result.stderr || result.stdout}\nThis is the project lint, not the EdgeOne CLI: do not check the CLI version or inspect the environment. Fix only the reported project files, then rerun the same EdgeOne CLI command.`, + ); + } +} + diff --git a/agents/_lib/makers/compat/skill-rules.ts b/agents/_lib/makers/compat/skill-rules.ts new file mode 100644 index 0000000..7b66815 --- /dev/null +++ b/agents/_lib/makers/compat/skill-rules.ts @@ -0,0 +1,244 @@ +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; +export type MakersValidationRule = { + skill: string; + pathPatterns: string[]; + pattern: string; + message: string; +}; + +/** + * Where a framework's platform adapter goes and whether this project needs one. + * + * `serverOutput` is deliberately separate from `adapter`: the file that decides + * whether the app renders on a server is often not the file the adapter is + * wired into. React Router declares `ssr` in react-router.config.ts and takes + * its adapter as a vite.config.ts plugin, so one field cannot serve both. + */ +export type MakersFrameworkProfile = { + id: string; + label: string; + detect: string[]; + adapter: { + package: string; + /** + * The range to declare when this agent adds the adapter itself. Optional + * because it is the platform's to state, not this repo's: absent, the + * declaration falls back to `latest`, which resolves but pins nothing. + */ + version?: string; + configFiles: string[]; + /** + * A config file that, in a shape only it can be in, silences the others. + * + * SvelteKit is why this exists. It reads exactly one config and prefers the + * Vite one, so a single option passed to `sveltekit()` discards the whole of + * a sibling `svelte.config.js` — adapter included, with no warning from the + * build. Checking the first config file that happens to exist calls that + * project correct; checking `vite.config.ts` unconditionally calls a project + * that legitimately keeps its config in `svelte.config.js` broken. + */ + configOverride?: { + files: string[]; + pattern: string; + /** Appended to the error, because "wire it in here" is baffling on its own. */ + reason: string; + }; + required: 'always' | 'server-output'; + } | null; + serverOutput?: { + files: string[]; + default: 'server' | 'static'; + serverPattern?: string; + staticPattern?: string; + }; + outputDirectory: string; + unsupported: string[]; +}; + +const FRAMEWORK_PROFILE_SKILL = 'makers-frameworks'; + +// The profiles live in a fenced block inside the vendored skill rather than in +// its frontmatter: they are nested objects, and the frontmatter reader here is a +// line-oriented approximation of YAML that cannot represent them. +const FRAMEWORK_PROFILE_BLOCK = + /\s*```json\s*\r?\n([\s\S]*?)\r?\n```/; + +export function parseMakersFrameworkProfiles(source: string): MakersFrameworkProfile[] { + const block = source.match(FRAMEWORK_PROFILE_BLOCK)?.[1]; + if (!block) return []; + const parsed = JSON.parse(block) as MakersFrameworkProfile[]; + if (!Array.isArray(parsed)) { + throw new Error('makers-framework-profiles must be a JSON array'); + } + for (const profile of parsed) { + if (!profile.id || !Array.isArray(profile.detect) || profile.detect.length === 0) { + throw new Error(`framework profile ${profile.id || '(unnamed)'} needs an id and a detect list`); + } + // Compile every pattern at load time so a malformed vendored profile fails + // here, where the message names the profile, rather than inside the sandbox + // script as a syntax error with no attribution. + for (const pattern of [ + profile.serverOutput?.serverPattern, + profile.serverOutput?.staticPattern, + profile.adapter?.configOverride?.pattern, + ]) { + if (pattern) new RegExp(pattern); + } + } + return parsed; +} + +const VALIDATION_SKILLS = [ + 'makers-agents', + 'makers-cloud-functions', + 'makers-deploy', + 'makers-edge-functions', + 'makers-env-adaption', + 'makers-frameworks', + 'makers-middleware', + 'makers-storage', +] as const; + +export const SUPPORTED_MAKERS_AGENT_FRAMEWORKS = [ + 'claude-agent-sdk', + 'openai-agents-sdk', + 'langgraph', + 'crewai', + 'deepagents', +] as const; + +function parseFrontmatterScalar(value: string) { + const trimmed = value.trim(); + if (trimmed.startsWith('"') && trimmed.endsWith('"')) { + // A double-quoted YAML scalar is close enough to JSON to reuse the parser, + // but not close enough to trust it: one stray backslash in a vendored skill + // would otherwise take down every compatibility check with a SyntaxError. + try { + return JSON.parse(trimmed) as string; + } catch { + return trimmed.slice(1, -1); + } + } + if (trimmed.startsWith("'") && trimmed.endsWith("'")) { + return trimmed.slice(1, -1).replaceAll("''", "'"); + } + return trimmed; +} + +export function parseMakersSkillValidationRules( + skill: string, + source: string, +): MakersValidationRule[] { + const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]; + if (!frontmatter) return []; + + const pathPatterns: string[] = []; + const rules: Array<{ pattern: string; message: string }> = []; + let section: 'paths' | 'validate' | null = null; + let pendingPattern = ''; + + for (const line of frontmatter.split(/\r?\n/)) { + if (line === 'pathPatterns:') { + section = 'paths'; + continue; + } + if (line === 'validate:') { + section = 'validate'; + continue; + } + if (/^\S/.test(line)) { + section = null; + continue; + } + if (section === 'paths') { + const match = line.match(/^\s{2}-\s+(.+)$/); + if (match?.[1]) pathPatterns.push(parseFrontmatterScalar(match[1])); + continue; + } + if (section === 'validate') { + const patternMatch = line.match(/^\s{2}-\s+pattern:\s+(.+)$/); + if (patternMatch?.[1]) { + pendingPattern = parseFrontmatterScalar(patternMatch[1]); + continue; + } + const messageMatch = line.match(/^\s{4}message:\s+(.+)$/); + if (messageMatch?.[1] && pendingPattern) { + rules.push({ + pattern: pendingPattern, + message: parseFrontmatterScalar(messageMatch[1]), + }); + pendingPattern = ''; + } + } + } + + return rules.map((rule) => ({ + skill, + pathPatterns: [...pathPatterns], + ...rule, + })); +} + +function readVendoredSkill(skill: string) { + return readFile( + path.join( + process.cwd(), + '.claude', + 'skills', + 'edgeone-makers-tools', + 'references', + skill, + 'SKILL.md', + ), + 'utf8', + ); +} + +let frameworkProfilesPromise: Promise | undefined; + +export function loadMakersFrameworkProfiles(): Promise { + if (!frameworkProfilesPromise) { + const pending = readVendoredSkill(FRAMEWORK_PROFILE_SKILL).then((source) => { + const profiles = parseMakersFrameworkProfiles(source); + if (profiles.length === 0) { + throw new Error( + `${FRAMEWORK_PROFILE_SKILL} carries no framework profiles; the adapter check cannot run without them`, + ); + } + return profiles; + }); + frameworkProfilesPromise = pending.catch((error) => { + frameworkProfilesPromise = undefined; + throw error; + }); + } + return frameworkProfilesPromise; +} + +let validationRulesPromise: Promise | undefined; + +export function loadMakersValidationRules(): Promise { + if (!validationRulesPromise) { + const pending = Promise.all(VALIDATION_SKILLS.map(async (skill) => { + const source = await readVendoredSkill(skill); + return parseMakersSkillValidationRules(skill, source); + })).then((groups) => { + const rules = groups.flat(); + for (const rule of rules) { + // Fail at the source if an official vendored rule is malformed instead + // of silently dropping a compatibility check. + new RegExp(rule.pattern); + } + return rules; + }); + // Cache the success, not the attempt. Holding a rejected promise here would + // turn one unlucky read into a permanently broken compatibility check for + // every later turn on this warm instance. + validationRulesPromise = pending.catch((error) => { + validationRulesPromise = undefined; + throw error; + }); + } + return validationRulesPromise; +} diff --git a/agents/_lib/project/makers-declarations.ts b/agents/_lib/makers/declarations.ts similarity index 98% rename from agents/_lib/project/makers-declarations.ts rename to agents/_lib/makers/declarations.ts index 710c9da..b54b065 100644 --- a/agents/_lib/project/makers-declarations.ts +++ b/agents/_lib/makers/declarations.ts @@ -13,9 +13,9 @@ import { loadMakersFrameworkProfiles, SUPPORTED_MAKERS_AGENT_FRAMEWORKS, type MakersFrameworkProfile, -} from './makers-compat.ts'; -import { runSandboxCommand } from './commands.ts'; -import { readFileFromSandbox } from './fs.ts'; +} from './compat/skill-rules.ts'; +import { runSandboxCommand } from '../project/commands.ts'; +import { readFileFromSandbox } from '../project/fs.ts'; export type MakersAgentFramework = (typeof SUPPORTED_MAKERS_AGENT_FRAMEWORKS)[number]; diff --git a/shared/npm-install.ts b/agents/_lib/makers/npm-install.ts similarity index 100% rename from shared/npm-install.ts rename to agents/_lib/makers/npm-install.ts diff --git a/agents/_lib/makers/preview-proxy-source.ts b/agents/_lib/makers/preview-proxy-source.ts new file mode 100644 index 0000000..c6d0d77 --- /dev/null +++ b/agents/_lib/makers/preview-proxy-source.ts @@ -0,0 +1,459 @@ +/** Generated Node proxy source. Keep in sync with the TS helpers in cli-dev.ts. */ + +export const PROXY_REVISION_PLACEHOLDER = '__PREVIEW_PROXY_REVISION__'; + +function normalizePreviewPrefix(value: string) { + const normalized = `/${value}`.replace(/\/+/g, '/').replace(/\/+$/, ''); + return normalized === '/' ? '' : normalized; +} + +export function buildPreviewProxyTemplate( + listenPort: number, + targetPort: number, + prefix: string, +) { + const normalizedPrefix = normalizePreviewPrefix(prefix); + return `const http = require('node:http'); +const net = require('node:net'); + +const LISTEN_PORT = ${listenPort}; +const TARGET_PORT = ${targetPort}; +const PREFIX = ${JSON.stringify(normalizedPrefix)}; +const HEALTH_PATH = '/__edgeone_preview_proxy_health'; + +// Set once the upstream asks to be addressed with the prefix — see +// previewUpstreamClaimsPrefix in shared/makers-dev.ts. Until then the prefix is +// stripped, which is what makers dev and every framework with an asset-only +// prefix knob expect. +let prefixAware = false; + +// The 404-shaped form of the same claim, tried once per proxy — see +// previewPrefixProbe. Once, because an upstream that really does serve at the +// root answers a genuinely missing page with a 404 too, and re-asking on every +// one of those would double the requests to re-learn what the first probe +// already established. +let prefixProbed = false; + +function rewritePath(url) { + if (!url) return '/'; + if (prefixAware) return url; + if ( + PREFIX + && (url === PREFIX || url.startsWith(PREFIX + '/') || url.startsWith(PREFIX + '?')) + ) { + const next = url.slice(PREFIX.length); + if (!next || next === '/') return '/'; + return next.startsWith('/') ? next : '/' + next; + } + return url; +} + +// Mirrors previewCanonicalRedirect in shared/makers-dev.ts. +function canonicalRedirect(url) { + if (!PREFIX || !url) return null; + const queryStart = url.indexOf('?'); + const path = queryStart === -1 ? url : url.slice(0, queryStart); + if (path !== PREFIX) return null; + return PREFIX + '/' + (queryStart === -1 ? '' : url.slice(queryStart)); +} + +function rewriteLocation(value) { + if (!PREFIX || typeof value !== 'string' || !value.startsWith('/')) return value; + if (value === PREFIX || value.startsWith(PREFIX + '/')) return value; + return PREFIX + value; +} + +// Mirrors previewUpstreamClaimsPrefix in shared/makers-dev.ts. +function claimsPrefix(statusCode, location) { + if (!PREFIX) return false; + if (!statusCode || statusCode < 300 || statusCode >= 400) return false; + if (typeof location !== 'string' || !location) return false; + let path = location; + const schemeEnd = location.indexOf('://'); + if (schemeEnd !== -1) { + const afterHost = location.indexOf('/', schemeEnd + 3); + path = afterHost === -1 ? '/' : location.slice(afterHost); + } else if (location.charAt(0) !== '/') { + return false; + } + path = path.split('?')[0]; + return path === PREFIX || path.indexOf(PREFIX + '/') === 0; +} + +// Mirrors previewPrefixProbe in shared/makers-dev.ts. +function prefixProbe(requestUrl, forwardedPath, statusCode) { + if (!PREFIX || !requestUrl || !forwardedPath) return null; + if (statusCode !== 404) return null; + if (forwardedPath === requestUrl) return null; + const probePath = requestUrl.split('?')[0]; + if (probePath !== PREFIX && probePath.indexOf(PREFIX + '/') !== 0) return null; + return requestUrl; +} + +// Mirrors previewTrailingSlashFollow in shared/makers-dev.ts. +function trailingSlashFollow(forwardedPath, statusCode, location) { + if (!forwardedPath) return null; + if (!statusCode || statusCode < 300 || statusCode >= 400) return null; + if (typeof location !== 'string' || location.charAt(0) !== '/') return null; + const from = forwardedPath.split('?')[0]; + const to = location.split('?')[0]; + if (from === to) return null; + return withoutTrailingSlash(from) === withoutTrailingSlash(to) ? location : null; +} + +function withoutTrailingSlash(value) { + return value.length > 1 && value.charAt(value.length - 1) === '/' + ? value.slice(0, -1) + : value; +} + +function rewriteSetCookie(value) { + if (!PREFIX || typeof value !== 'string') return value; + return value.replace(/;\\s*Path=\\//gi, '; Path=' + PREFIX + '/'); +} + +// makers dev reaches its function runtime through http-proxy with xfwd +// enabled, and xfwd APPENDS to x-forwarded-proto rather than replacing it. A +// value from the sandbox gateway therefore arrives at the runtime as +// "http,http", which it concatenates into a request URL and hands to new +// Request(): ERR_INVALID_URL. The failure is then swallowed by an error +// handler that throws on its own, so nothing ever writes a response and the +// browser spins until the user gives up. Dropping the header here leaves xfwd +// setting the single value it would have set anyway, which is also what makes +// a direct curl to the CLI work today. +// The parent workspace frames this preview cross-origin, so it cannot read the +// iframe's location to fill its address bar. Nothing else can report the route +// either: makers dev serves the application, and a proxy is the only layer left +// that sees every document. This posts the real path — prefix included, which is +// what the parent strips for display and reuses when deep-linking a copied URL. +const TRACKER = ''; + +// Scanned as latin1 so one character is one byte and the match offset can index +// the buffer directly. +// +// is only the preferred landing place. A hand-written index.html may not +// have one, and the script above is now what makes in-app navigation work, so +// skipping those pages would leave exactly the simplest generated sites broken. +// The fallbacks stay below the doctype: above it the page drops into quirks +// mode, which changes how the whole document lays out. +function headInsertionPoint(buffer) { + const text = buffer.toString('latin1'); + for (const pattern of [/]*>/i, /]*>/i, /]*>/i]) { + const match = pattern.exec(text); + if (match) return match.index + match[0].length; + } + return -1; +} + +function acceptsHtml(req) { + const accept = req.headers.accept; + return typeof accept === 'string' && accept.includes('text/html'); +} + +function isHtmlResponse(headers) { + const type = headers['content-type']; + return typeof type === 'string' && type.toLowerCase().includes('text/html'); +} + +function forwardHeaders(req) { + const headers = { + ...req.headers, + host: '127.0.0.1:' + TARGET_PORT, + 'x-forwarded-prefix': PREFIX, + }; + delete headers['x-forwarded-proto']; + // TRACKER can only be spliced into an unencoded body. Asking for identity on + // navigations alone costs nothing on a loopback hop and leaves compression in + // place for the assets, which are what the encoding is actually worth. + if (acceptsHtml(req)) headers['accept-encoding'] = 'identity'; + return headers; +} + +// Buffer only up to the opening , then release and stream the rest +// untouched: a page that streams its body from a Suspense boundary has to keep +// arriving in pieces, and the shell carrying is already in the first one. +function injectTracker(upstream, res) { + const SCAN_LIMIT = 65536; + let pending = []; + let scanned = 0; + let injected = false; + + function splice(buffer) { + const at = headInsertionPoint(buffer); + // No to splice after. Prepending would land the script above the + // doctype and drop the page into quirks mode, so leave the body alone and + // let the address bar stay where it is. + if (at === -1) { + res.write(buffer); + return; + } + res.write(buffer.subarray(0, at)); + res.write(TRACKER); + res.write(buffer.subarray(at)); + } + + upstream.on('data', (chunk) => { + if (injected) { + res.write(chunk); + return; + } + pending.push(chunk); + scanned += chunk.length; + const buffer = Buffer.concat(pending); + if (headInsertionPoint(buffer) === -1 && scanned <= SCAN_LIMIT) return; + injected = true; + pending = []; + splice(buffer); + }); + upstream.on('end', () => { + if (!injected && pending.length) splice(Buffer.concat(pending)); + res.end(); + }); + upstream.on('error', () => res.end()); +} + +const server = http.createServer((req, res) => { + if ((req.url || '').split('?')[0] === HEALTH_PATH) { + res.writeHead(200, { + 'content-type': 'text/plain', + 'x-edgeone-preview-proxy': '${PROXY_REVISION_PLACEHOLDER}', + }); + res.end('ok'); + return; + } + + const canonical = canonicalRedirect(req.url); + if (canonical) { + res.writeHead(308, { location: canonical }); + res.end(); + return; + } + + forward(req, res, rewritePath(req.url), true, true, false); +}); + +function forward(req, res, path, mayRetry, mayFollow, probing) { + const headers = forwardHeaders(req); + const proxy = http.request({ + hostname: '127.0.0.1', + port: TARGET_PORT, + path, + method: req.method, + headers, + }, (upstream) => { + // The probe's answer, which is the half of previewPrefixProbe that decides. + // Anything but a second 404 means the prefixed path is a route the upstream + // knows, so it keeps the prefix from here on. Read before the branches + // below so a probe answered with a redirect still counts as knowing it. + if (probing && upstream.statusCode !== 404) prefixAware = true; + // The one response that means the prefix should not have been stripped. + // Retried rather than passed on, because handing the browser a redirect to + // a path this proxy still strips is the same request again: it would bounce + // between the two until the browser gave up. + if ( + mayRetry + && !prefixAware + && (req.method === 'GET' || req.method === 'HEAD') + && claimsPrefix(upstream.statusCode, upstream.headers.location) + ) { + prefixAware = true; + upstream.resume(); + forward(req, res, req.url || '/', false, true, false); + return; + } + // The same claim made as a 404, which is how Astro states it. Asked rather + // than concluded: the retry's status is what tells a base-mounted app apart + // from a page that is simply not there. + if ( + mayRetry + && !prefixAware + && !prefixProbed + && (req.method === 'GET' || req.method === 'HEAD') + && prefixProbe(req.url, path, upstream.statusCode) + ) { + prefixProbed = true; + upstream.resume(); + forward(req, res, req.url, false, true, true); + return; + } + // A redirect that only normalizes a trailing slash, settled here for the + // same reason — see previewTrailingSlashFollow. Once, and never from a + // follow of its own: an upstream that keeps normalizing is a loop this + // proxy would be holding open instead of the browser. + if (mayFollow && (req.method === 'GET' || req.method === 'HEAD')) { + const follow = trailingSlashFollow( + path, + upstream.statusCode, + upstream.headers.location, + ); + if (follow) { + upstream.resume(); + forward(req, res, follow, false, false, false); + return; + } + } + const responseHeaders = { ...upstream.headers }; + if (responseHeaders.location) { + responseHeaders.location = rewriteLocation(responseHeaders.location); + } + if (Array.isArray(responseHeaders['set-cookie'])) { + responseHeaders['set-cookie'] = responseHeaders['set-cookie'].map(rewriteSetCookie); + } + const injectable = isHtmlResponse(responseHeaders) + && !responseHeaders['content-encoding']; + // The body grows by TRACKER, so the declared length no longer holds. + // Dropping it hands the response to chunked encoding. + if (injectable) delete responseHeaders['content-length']; + res.writeHead(upstream.statusCode || 502, responseHeaders); + if (injectable) injectTracker(upstream, res); + else upstream.pipe(res); + }); + proxy.on('error', () => { + if (!res.headersSent) res.writeHead(502); + res.end('preview proxy error'); + }); + // Neither a retry nor a follow has a body left to send: both are reached + // only for a GET or a HEAD, and the request stream is already consumed. + if (mayRetry) req.pipe(proxy); + else proxy.end(); +} + +server.on('upgrade', (req, socket, head) => { + const path = rewritePath(req.url); + const headers = forwardHeaders(req); + const target = net.connect(TARGET_PORT, '127.0.0.1', () => { + const headerLines = Object.entries(headers).flatMap(([key, value]) => { + if (value == null) return []; + return [key + ': ' + (Array.isArray(value) ? value.join(', ') : value)]; + }); + target.write([ + (req.method || 'GET') + ' ' + path + ' HTTP/1.1', + ...headerLines, + '', + '', + ].join('\\r\\n')); + if (head && head.length) target.write(head); + target.pipe(socket); + socket.pipe(target); + }); + target.on('error', () => socket.destroy()); + socket.on('error', () => target.destroy()); +}); + +server.listen(LISTEN_PORT, '0.0.0.0'); +`; +} diff --git a/agents/_lib/project/makers-deploy.ts b/agents/_lib/makers/project.ts similarity index 100% rename from agents/_lib/project/makers-deploy.ts rename to agents/_lib/makers/project.ts diff --git a/agents/_lib/makers/session.ts b/agents/_lib/makers/session.ts new file mode 100644 index 0000000..004042c --- /dev/null +++ b/agents/_lib/makers/session.ts @@ -0,0 +1,61 @@ +import type { ProjectState } from '../types.ts'; +import { + ensureMakersPublishProject, + resolveConversationPublishArea, + resolveMakersProjectName, + syncSandboxEnvToMakersProject, +} from './project.ts'; +import { + buildSandboxMakersEnv, + prepareSandboxGatewayEnv, + resolveMakersMasterToken, + resolveSandboxMakersToken, +} from './token.ts'; + +export type PreparedMakersSession = { + masterToken: string; + sandboxToken: string; + projectName: string; + area: string; + env: Record; + gatewayKey: string; +}; + +/** + * Token, project, and env the sandbox CLI needs before a Makers command. + * Preview, deploy, and the commands wrapper all used to do this separately. + */ +export async function prepareMakersSession( + context: any, + state: ProjectState, + options: { syncEnv?: boolean } = {}, +): Promise { + const masterToken = resolveMakersMasterToken(context); + const sandboxToken = await resolveSandboxMakersToken(state, masterToken); + const gateway = await prepareSandboxGatewayEnv(context, state); + const projectName = resolveMakersProjectName(context, state); + const area = resolveConversationPublishArea(state); + await ensureMakersPublishProject( + sandboxToken, + projectName, + area, + state.makersApiRegion, + ); + if (options.syncEnv) { + await syncSandboxEnvToMakersProject( + context, + state, + masterToken, + projectName, + state.makersApiRegion, + ); + } + return { + masterToken, + sandboxToken, + projectName, + area, + env: buildSandboxMakersEnv(sandboxToken, state.makersApiRegion), + gatewayKey: gateway.AI_GATEWAY_API_KEY || '', + }; +} diff --git a/agents/_lib/project/makers-token.ts b/agents/_lib/makers/token.ts similarity index 99% rename from agents/_lib/project/makers-token.ts rename to agents/_lib/makers/token.ts index 412f799..5b97917 100644 --- a/agents/_lib/project/makers-token.ts +++ b/agents/_lib/makers/token.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'node:crypto'; import { Makers, MakersError } from '@edgeone/makers-sdk'; import type { ProjectState } from '../types.ts'; -import { readProjectGatewayEnv } from './gateway-prompt.ts'; +import { readProjectGatewayEnv } from '../project/gateway.ts'; // Every preview start, wrapped CLI call and deploy mints its own token, so this // only has to outlive a single CLI invocation. An hour is already far more than diff --git a/shared/tool-phase.ts b/agents/_lib/makers/tool-phase.ts similarity index 100% rename from shared/tool-phase.ts rename to agents/_lib/makers/tool-phase.ts diff --git a/agents/_lib/memory.ts b/agents/_lib/memory.ts deleted file mode 100644 index 33eaf84..0000000 --- a/agents/_lib/memory.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { HISTORY_FETCH_LIMIT } from './constants.ts'; -import { createProjectState } from './project/index.ts'; -import type { - ChatTask, - ConversationMessage, - PersistedActivityTurn, - LegacyProjectSnapshot, - ProjectState, -} from './types.ts'; -import { sanitizeAssistantText } from './utils/text.ts'; -import { appendTrimmedActivityTurn, dedupeActivityTurns } from './utils/activity.ts'; - -export async function getHistory( - context: any, - conversationId: string, - options: { excludeLatestUserMessage?: string } = {}, -): Promise { - // context.store only exposes conversation-scoped message APIs, not a generic KV store. - // Read this conversation's messages and filter them into user/assistant text pairs. - try { - const messages = await context.store.getMessages({ - conversationId, - limit: HISTORY_FETCH_LIMIT, - order: 'asc', - }); - const items = Array.isArray(messages) ? messages : (messages?.items || []); - const history = items - .filter((item: any) => item.role === 'user' || item.role === 'assistant') - .map((item: any) => ({ - role: item.role as 'user' | 'assistant', - content: typeof item.content === 'string' - ? item.content - : JSON.stringify(item.content ?? ''), - })); - - // POST /session persists the submitted user message before the detached task starts. - // Remove that one record from the prompt history; the pipeline passes - // it separately as the current user turn. - const currentMessage = options.excludeLatestUserMessage; - if (currentMessage && history.at(-1)?.role === 'user' && history.at(-1)?.content === currentMessage) { - history.pop(); - } - return history; - } catch (error: any) { - if (error?.code === 'MemoryNotFoundError') { - return []; - } - throw error; - } -} - -export async function getChatTask(context: any, conversationId: string): Promise { - try { - const conversation = await context.store.getConversation({ conversationId }); - const task = conversation?.metadata?.chatTask; - return task && typeof task === 'object' && typeof task.id === 'string' - ? task as ChatTask - : null; - } catch (error: any) { - if (error?.code === 'MemoryNotFoundError') { - return null; - } - throw error; - } -} - -export async function saveChatTask(context: any, conversationId: string, task: ChatTask) { - await context.store.updateConversation({ - conversationId, - metadata: { chatTask: task }, - }); -} - -/** - * The model this conversation last ran on. Persisted so a refresh can restore - * the picker, and so a turn that arrives without an explicit choice still runs - * on the model the conversation has been using rather than silently reverting - * to the deployment default. - */ -export async function getModelPreference( - context: any, - conversationId: string, -): Promise { - try { - const conversation = await context.store.getConversation({ conversationId }); - const stored = conversation?.metadata?.modelPreference; - return typeof stored === 'string' ? stored.trim() : ''; - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - return ''; - } -} - -export async function saveModelPreference( - context: any, - conversationId: string, - model: string, -) { - try { - await context.store.updateConversation({ - conversationId, - metadata: { modelPreference: model }, - }); - } catch (error: any) { - // Same first-turn race as saveProjectState: until appendMessage creates the - // conversation, updateConversation has nothing to merge into. - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - } -} - -export async function appendTurn( - context: any, - conversationId: string, - role: 'user' | 'assistant', - content: string, -) { - // Sanitize assistant content before writing history so control sequences or raw JSON - // from new concatenation paths do not pollute the next prompt. - const safeContent = role === 'assistant' ? sanitizeAssistantText(content) : content; - await context.store.appendMessage({ - conversationId, - role, - content: safeContent, - }); -} - -export async function getProjectState(context: any, conversationId: string): Promise { - // Project state is conversation metadata, not a chat message. On first access, - // the conversation may not exist yet, so fall back to the default state. - try { - const conversation = await context.store.getConversation({ conversationId }); - const stored = conversation?.metadata?.projectState as ProjectState | undefined; - if (stored && typeof stored === 'object') { - return stored; - } - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - } - return createProjectState(conversationId); -} - -export async function saveProjectState( - context: any, - conversationId: string, - state: ProjectState, -) { - // updateConversation shallow-merges metadata; replace projectState as a whole. - try { - await context.store.updateConversation({ - conversationId, - metadata: { projectState: state }, - }); - } catch (error: any) { - // If no messages have been written, the conversation does not exist yet and - // updateConversation throws MemoryNotFoundError. appendMessage will create it - // later in this turn, and the next saveProjectState call can write normally. - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - } -} - -// Read-only compatibility for snapshots written by template versions that stored -// the archive in conversation metadata. New writes use context.sandbox.persist(). -export async function getLegacyProjectSnapshot( - context: any, - conversationId: string, -): Promise { - try { - const conversation = await context.store.getConversation({ conversationId }); - const stored = conversation?.metadata?.projectSnapshot as LegacyProjectSnapshot | undefined; - if (stored && typeof stored === 'object' && typeof stored.base64 === 'string' && stored.base64) { - return stored; - } - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - } - return null; -} - -export async function clearLegacyProjectSnapshot(context: any, conversationId: string) { - try { - await context.store.updateConversation({ - conversationId, - metadata: { projectSnapshot: null }, - }); - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') { - throw error; - } - } -} - -const ACTIVITY_TURN_LIMIT = 25; -const ACTIVITY_ITEM_LIMIT = 50; - -export async function getActivityHistory( - context: any, - conversationId: string, -): Promise { - try { - const conversation = await context.store.getConversation({ conversationId }); - const stored = conversation?.metadata?.activityHistory; - return Array.isArray(stored) - ? dedupeActivityTurns(stored.slice(-ACTIVITY_TURN_LIMIT)) - : []; - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') throw error; - return []; - } -} - -export async function saveActivityTurn( - context: any, - conversationId: string, - turn: PersistedActivityTurn, -) { - const current = await getActivityHistory(context, conversationId); - const next = appendTrimmedActivityTurn( - current, - turn, - ACTIVITY_TURN_LIMIT, - ACTIVITY_ITEM_LIMIT, - ); - try { - await context.store.updateConversation({ - conversationId, - metadata: { activityHistory: next }, - }); - } catch (error: any) { - if (error?.code !== 'MemoryNotFoundError') throw error; - } -} diff --git a/agents/_lib/pipelines/index.ts b/agents/_lib/pipelines/index.ts deleted file mode 100644 index 3a6e3df..0000000 --- a/agents/_lib/pipelines/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { runChatPipeline } from './chat.ts'; -export { DEFAULT_DEPLOY_REQUEST, runDeployPipeline } from './deploy.ts'; -export { runFileReadPipeline } from './file-read.ts'; -export { runProjectDownloadPipeline } from './download.ts'; -export { - createProjectResumeStreamResponse, - runProjectResumePreviewPipeline, -} from './resume.ts'; diff --git a/agents/_lib/pipelines/turn-lifecycle.ts b/agents/_lib/pipelines/turn-lifecycle.ts deleted file mode 100644 index 73e6a00..0000000 --- a/agents/_lib/pipelines/turn-lifecycle.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { appendTurn, saveActivityTurn, saveProjectState } from '../memory.ts'; -import type { - AgentProgressEvent, - PersistedActivity, - ProjectState, -} from '../types.ts'; -import type { ProjectCheckpointController } from './helpers.ts'; - -type TurnStatus = 'completed' | 'failed' | 'stopped'; - -type TurnLifecycleOptions = { - context: any; - conversationId: string; - message: string; - turnId: string; - userMessagePersisted: boolean; - state: ProjectState; - checkpoint: ProjectCheckpointController; -}; - -/** Owns progress aggregation and the durable commit order for one chat turn. */ -export function createTurnLifecycle(options: TurnLifecycleOptions) { - const activities: PersistedActivity[] = []; - - const recordProgress = (event: AgentProgressEvent) => { - if (event.type === 'text_segment') { - const text = event.data.text; - if (!text) return; - const last = activities.at(-1); - if (last?.kind === 'text') { - if (last.content.endsWith(text) || last.content.endsWith(text.trim())) return; - activities[activities.length - 1] = { ...last, content: `${last.content}${text}` }; - } else { - activities.push({ kind: 'text', content: text }); - } - return; - } - - if (event.type === 'tool_use') { - const existing = activities.find( - (item): item is Extract => - item.kind === 'tool' && item.toolUseId === event.data.id, - ); - if (existing) { - existing.name = event.data.name || existing.name; - existing.inputSummary = event.data.inputSummary || existing.inputSummary; - return; - } - activities.push({ - kind: 'tool', - toolUseId: event.data.id, - name: event.data.name, - status: 'running', - inputSummary: event.data.inputSummary, - startedAt: event.data.startedAt || Date.now(), - }); - return; - } - - const existing = activities.find( - (item): item is Extract => - item.kind === 'tool' && item.toolUseId === event.data.tool_use_id, - ); - if (existing) { - existing.status = event.data.status || (event.data.ok ? 'completed' : 'failed'); - existing.outputSummary = event.data.outputSummary || event.data.preview; - existing.endedAt = event.data.endedAt || Date.now(); - } - }; - - const finalize = async ( - assistant: string, - status: TurnStatus, - finalizeOptions?: { withSnapshot?: boolean; withState?: boolean }, - ) => { - if (status === 'stopped') { - for (const activity of activities) { - if (activity.kind === 'tool' && activity.status === 'running') { - activity.status = 'stopped'; - activity.endedAt = Date.now(); - } - } - } - - // Commit order matters: snapshot → project metadata → conversation. - if (finalizeOptions?.withSnapshot === true) await options.checkpoint.flush(); - if (finalizeOptions?.withState !== false) { - await saveProjectState(options.context, options.conversationId, options.state); - } - if (!options.userMessagePersisted) { - await appendTurn(options.context, options.conversationId, 'user', options.message); - } - await appendTurn(options.context, options.conversationId, 'assistant', assistant); - await saveActivityTurn(options.context, options.conversationId, { - id: options.turnId, - user: options.message, - assistant, - status, - createdAt: Date.now(), - activities, - }); - }; - - return { recordProgress, finalize }; -} diff --git a/agents/_lib/pipelines/workspace.ts b/agents/_lib/pipelines/workspace.ts deleted file mode 100644 index 795bb6f..0000000 --- a/agents/_lib/pipelines/workspace.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { clearLegacyProjectSnapshot, getProjectState } from '../memory.ts'; -import { - createProjectState, - getFileTree, - resetProjectWorkspace, - restorePersistedProject, - separateLegacyMakersDeployment, -} from '../project/index.ts'; -import type { ProjectState, StreamSend } from '../types.ts'; - -/** Restore or reset the volatile sandbox before an agent turn starts. */ -export async function prepareProjectWorkspace( - context: any, - conversationId: string, - resetProject: boolean, - send: StreamSend, -): Promise { - const state = resetProject - ? createProjectState(conversationId) - : separateLegacyMakersDeployment(await getProjectState(context, conversationId)); - - if (resetProject) { - await resetProjectWorkspace(context, state); - await clearLegacyProjectSnapshot(context, conversationId); - // The snapshot only speeds up later sessions, so a failure here (backend - // unavailable, size cap, quota) must not abort the turn the user asked for. - try { - await context.sandbox.persist({ path: state.appDir }); - } catch (error) { - send({ - type: 'log', - phase: 'scaffold', - stream: 'stderr', - message: error instanceof Error ? error.message : 'Snapshot save failed.', - }); - } - return state; - } - - try { - let hasProjectFiles = false; - try { - if (await context.sandbox.files.exists(state.appDir)) { - const tree = await getFileTree(context, state); - hasProjectFiles = tree.some((item) => item.type === 'file'); - } - } catch { - hasProjectFiles = false; - } - - if (!hasProjectFiles) { - send({ type: 'status', message: 'Restoring project from snapshot' }); - const restored = await restorePersistedProject(context, conversationId, state); - if (!restored.restored) { - if (restored.error) { - send({ - type: 'log', - phase: 'scaffold', - stream: 'stderr', - message: restored.error, - }); - } - } else { - hasProjectFiles = true; - } - } - - await ensureWorkspaceDirectories(context, state); - if (hasProjectFiles) state.created = true; - } catch (error) { - send({ - type: 'log', - phase: 'scaffold', - stream: 'stderr', - message: error instanceof Error ? error.message : 'Snapshot restore check failed.', - }); - try { - await ensureWorkspaceDirectories(context, state); - } catch { - // Scaffold reports the actionable error if directory creation still fails. - } - } - - return state; -} - -async function ensureWorkspaceDirectories(context: any, state: ProjectState) { - await context.sandbox.files.makeDir(state.sessionDir); - await context.sandbox.files.makeDir(state.appDir); -} diff --git a/agents/_lib/project/archive.ts b/agents/_lib/project/archive.ts index 44ae9c0..1a54cbc 100644 --- a/agents/_lib/project/archive.ts +++ b/agents/_lib/project/archive.ts @@ -7,7 +7,7 @@ import type { LegacyProjectSnapshot, ProjectState } from '../types.ts'; import { safeSegment } from '../utils/paths.ts'; import { runSandboxCommand } from './commands.ts'; import { assertResettableProjectPath } from './state.ts'; -import { shellQuote } from '../../../shared/shell.ts'; +import { shellQuote } from '../utils/shell.ts'; type ProjectArchiveResult = | { @@ -181,7 +181,7 @@ export async function createProjectArchive( // Inverse of createProjectArchive: restore a persisted base64 archive back into // the (empty/recycled) sandbox appDir, then reinstall dependencies. Used when the // sandbox no longer has the code but a snapshot exists in the store -// (agents/_lib/memory.ts). Binary must be produced inside the sandbox via `base64 -d` — the +// (agents/_lib/session/store.ts). Binary must be produced inside the sandbox via `base64 -d` — the // sandbox files.write API is UTF-8 only — so we write the base64 as text and // decode + extract with shell, mirroring createProjectArchive's packing path. export async function restoreProjectArchive( diff --git a/agents/_lib/project/commands.ts b/agents/_lib/project/commands.ts index 8524297..4e29df9 100644 --- a/agents/_lib/project/commands.ts +++ b/agents/_lib/project/commands.ts @@ -1,5 +1,5 @@ -import { resolveSandboxCommandOptions } from '../../../shared/sandbox-command.ts'; -import { parseEchoedExitCode, stripEchoedExit, withExitCodeEcho } from '../utils/tool-phase.ts'; +import { resolveSandboxCommandOptions } from '../project/sandbox-command.ts'; +import { parseEchoedExitCode, stripEchoedExit, withExitCodeEcho } from '../makers/tool-phase.ts'; export { resolveSandboxCommandOptions }; diff --git a/agents/_lib/pipelines/download.ts b/agents/_lib/project/download.ts similarity index 91% rename from agents/_lib/pipelines/download.ts rename to agents/_lib/project/download.ts index 16aa606..b326733 100644 --- a/agents/_lib/pipelines/download.ts +++ b/agents/_lib/project/download.ts @@ -1,6 +1,6 @@ -import { getProjectState } from '../memory.ts'; -import { createProjectArchive, restorePersistedProject } from '../project/index.ts'; -import { resolveConversationId } from '../utils/request.ts'; +import { getProjectState } from '../session/store.ts'; +import { createProjectArchive, restorePersistedProject } from './index.ts'; +import { resolveConversationId } from '../runtime/request.ts'; export async function runProjectDownloadPipeline(context: any): Promise { const { conversationId } = resolveConversationId(context, { allowQuery: true }); diff --git a/agents/_lib/project/gateway-prompt.ts b/agents/_lib/project/gateway.ts similarity index 98% rename from agents/_lib/project/gateway-prompt.ts rename to agents/_lib/project/gateway.ts index a260f1f..15a6e60 100644 --- a/agents/_lib/project/gateway-prompt.ts +++ b/agents/_lib/project/gateway.ts @@ -8,11 +8,11 @@ */ import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; -import { saveProjectState } from '../memory.ts'; +import { saveProjectState } from '../session/store.ts'; import type { ClaudeMcpTool, ProjectState, StreamSend } from '../types.ts'; import { stringifyToolResult } from '../utils/text.ts'; import { getFileTree } from './fs.ts'; -import { AGENT_GATEWAY_ENV_KEYS } from './makers-declarations.ts'; +import { AGENT_GATEWAY_ENV_KEYS } from '../makers/declarations.ts'; /** Origin the Claude Agent SDK wants. OpenAI-compatible clients need `/v1` on top. */ export const AI_GATEWAY_ORIGIN = 'https://ai-gateway.edgeone.link'; diff --git a/agents/_lib/project/index.ts b/agents/_lib/project/index.ts index 1492597..6649821 100644 --- a/agents/_lib/project/index.ts +++ b/agents/_lib/project/index.ts @@ -23,5 +23,5 @@ export { assertPreviewServerReady, } from './preview.ts'; export { createProjectArchive, restoreProjectArchive } from './archive.ts'; -export { resolveMakersProjectName } from './makers-deploy.ts'; +export { resolveMakersProjectName } from '../makers/project.ts'; export { restorePersistedProject } from './persistence.ts'; diff --git a/agents/_lib/project/persistence.ts b/agents/_lib/project/persistence.ts index 4ecc7fc..29ea3ac 100644 --- a/agents/_lib/project/persistence.ts +++ b/agents/_lib/project/persistence.ts @@ -1,4 +1,3 @@ -import { clearLegacyProjectSnapshot, getLegacyProjectSnapshot } from '../memory.ts'; import type { ProjectState } from '../types.ts'; import { restoreProjectArchive } from './archive.ts'; import { runSandboxCommand } from './commands.ts'; @@ -8,7 +7,7 @@ export async function restorePersistedProject( conversationId: string, state: ProjectState, options: { installDependencies?: boolean } = {}, -): Promise<{ restored: boolean; migratedLegacy?: boolean; error?: string }> { +): Promise<{ restored: boolean; error?: string }> { try { const restored = await context.sandbox.restore({ path: state.appDir }); if (restored?.restored) { @@ -18,19 +17,7 @@ export async function restorePersistedProject( } catch (error) { return { restored: false, error: error instanceof Error ? error.message : String(error) }; } - - const legacy = await getLegacyProjectSnapshot(context, conversationId); - if (!legacy) return { restored: false }; - const restoredLegacy = await restoreProjectArchive(context, state, legacy, options); - if (!restoredLegacy.ok) return { restored: false, error: restoredLegacy.error }; - - try { - await context.sandbox.persist({ path: state.appDir }); - await clearLegacyProjectSnapshot(context, conversationId); - } catch { - // Keep the legacy metadata until migration has durably completed. - } - return { restored: true, migratedLegacy: true }; + return { restored: false }; } async function installDependencies(context: any, state: ProjectState) { @@ -41,3 +28,5 @@ async function installDependencies(context: any, state: ProjectState) { timeout: 300, }); } + +export { restoreProjectArchive }; diff --git a/agents/_lib/project/preview.ts b/agents/_lib/project/preview.ts index e40832f..4e1c09e 100644 --- a/agents/_lib/project/preview.ts +++ b/agents/_lib/project/preview.ts @@ -17,31 +17,22 @@ import { buildMakersDevBackgroundCommand, buildMakersDevLaunchCommand, parseMakersDevExitCode, -} from '../../../shared/makers-dev.ts'; +} from '../makers/cli-dev.ts'; import { makersFileSemantic } from '../../../shared/makers-file-semantics.ts'; -import { redactSecret } from '../../../shared/makers-deploy.ts'; -import { shellQuote } from '../../../shared/shell.ts'; +import { redactSecret } from '../makers/cli-deploy.ts'; +import { shellQuote } from '../utils/shell.ts'; import { MAKERS_CLI_UNAVAILABLE_ERROR_CODE, MAKERS_CLI_UNAVAILABLE_MESSAGE, isEdgeoneCliUnavailable, -} from '../../../shared/tool-phase.ts'; -import { resolveConversationId } from '../utils/request.ts'; -import { sandboxGatewayKeyIsSet } from './gateway-prompt.ts'; +} from '../makers/tool-phase.ts'; +import { resolveConversationId } from '../runtime/request.ts'; +import { sandboxGatewayKeyIsSet } from './gateway.ts'; import { runCommandCapturingExit, runSandboxCommand } from './commands.ts'; -import { assertMakersProjectCompatible } from './makers-compat.ts'; -import { - ensureMakersPublishProject, - resolveConversationPublishArea, - resolveMakersProjectName, -} from './makers-deploy.ts'; -import { - buildSandboxMakersEnv, - describeMissingMakersRuntimeToken, - prepareSandboxGatewayEnv, - resolveMakersMasterToken, - resolveSandboxMakersToken, -} from './makers-token.ts'; +import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; +import { prepareMakersSession } from '../makers/session.ts'; +import { resolveConversationPublishArea, resolveMakersProjectName } from '../makers/project.ts'; +import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; // Where Makers mounts generated HTTP handlers; both are optional in a project. const CLOUD_FUNCTION_DIRECTORIES = ['cloud-functions', 'edge-functions']; @@ -125,7 +116,6 @@ export async function startPreviewServer( ) { const verifyRoutes = options.verifyRoutes !== false; await assertMakersProjectCompatible(context, state); - const masterToken = resolveMakersMasterToken(context); const projectName = resolveMakersProjectName(context, state); const area = resolveConversationPublishArea(state); const launchCommand = buildMakersDevLaunchCommand(MAKERS_DEV_PORT, projectName, { area }); @@ -152,16 +142,7 @@ export async function startPreviewServer( } } - // Scoped to this conversation, and redacted out of CLI output before the - // model or the UI sees it. - const sandboxToken = await resolveSandboxMakersToken(state, masterToken); - await ensureMakersPublishProject( - sandboxToken, - projectName, - area, - state.makersApiRegion, - ); - await prepareSandboxGatewayEnv(context, state); + const makers = await prepareMakersSession(context, state); const startResult = await runSandboxCommand( context, @@ -169,15 +150,15 @@ export async function startPreviewServer( makersPort: MAKERS_DEV_PORT, previewPort: PREVIEW_SERVER_PORT, previewPath: PREVIEW_PATH_PREFIX, - projectName, + projectName: makers.projectName, assetPrefixEnvName: PREVIEW_ASSET_PREFIX_ENV, forceRestart, - area, + area: makers.area, }), { cwd: state.appDir, timeout: MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, - env: buildSandboxMakersEnv(sandboxToken, state.makersApiRegion), + env: makers.env, }, ); const startOutput = [startResult.stdout, startResult.stderr].filter(Boolean).join('\n'); @@ -194,7 +175,7 @@ export async function startPreviewServer( throw new Error( redactSecret( failure, - sandboxToken, + makers.sandboxToken, ), ); } diff --git a/agents/_lib/pipelines/file-read.ts b/agents/_lib/project/read.ts similarity index 92% rename from agents/_lib/pipelines/file-read.ts rename to agents/_lib/project/read.ts index 7b34453..01fcfcf 100644 --- a/agents/_lib/pipelines/file-read.ts +++ b/agents/_lib/project/read.ts @@ -1,8 +1,8 @@ import { PREVIEW_BATCH_MAX_FILES } from '../constants.ts'; -import { getProjectState } from '../memory.ts'; -import { readFileFromSandbox, readFilesFromSandbox } from '../project/index.ts'; +import { getProjectState } from '../session/store.ts'; +import { readFileFromSandbox, readFilesFromSandbox } from './index.ts'; import { toAppRelPath } from '../utils/paths.ts'; -import { getRequestQueryParam, resolveConversationId } from '../utils/request.ts'; +import { getRequestQueryParam, resolveConversationId } from '../runtime/request.ts'; export async function runFileReadPipeline(context: any): Promise { const { conversationId } = resolveConversationId(context); diff --git a/shared/resume-file-cache.ts b/agents/_lib/project/resume-file-cache.ts similarity index 97% rename from shared/resume-file-cache.ts rename to agents/_lib/project/resume-file-cache.ts index 7aee8dd..abe90c8 100644 --- a/shared/resume-file-cache.ts +++ b/agents/_lib/project/resume-file-cache.ts @@ -1,4 +1,4 @@ -import type { FileTreeItem } from './protocol.ts'; +import type { FileTreeItem } from '../../../shared/protocol.ts'; export const RESUME_FILE_CACHE_MAX_FILES = 48; export const RESUME_FILE_CACHE_MAX_BYTES = 2 * 1024 * 1024; diff --git a/agents/_lib/pipelines/resume-files.ts b/agents/_lib/project/resume-files.ts similarity index 90% rename from agents/_lib/pipelines/resume-files.ts rename to agents/_lib/project/resume-files.ts index 7491ede..4e7bf09 100644 --- a/agents/_lib/pipelines/resume-files.ts +++ b/agents/_lib/project/resume-files.ts @@ -1,7 +1,7 @@ -import { getProjectState } from '../memory.ts'; -import { readFileFromSandbox } from '../project/index.ts'; +import { getProjectState } from '../session/store.ts'; +import { readFileFromSandbox } from './index.ts'; import type { FileTreeItem } from '../types.ts'; -import { selectResumeCacheFiles } from '../../../shared/resume-file-cache.ts'; +import { selectResumeCacheFiles } from './resume-file-cache.ts'; const RESUME_FILE_READ_BATCH_SIZE = 12; diff --git a/shared/sandbox-command.ts b/agents/_lib/project/sandbox-command.ts similarity index 100% rename from shared/sandbox-command.ts rename to agents/_lib/project/sandbox-command.ts diff --git a/agents/_lib/project/scaffold.ts b/agents/_lib/project/scaffold.ts index 35428a3..32cf410 100644 --- a/agents/_lib/project/scaffold.ts +++ b/agents/_lib/project/scaffold.ts @@ -1,11 +1,12 @@ import type { BuildResult, BuildStatus, ProjectState, ScaffoldLog } from '../types.ts'; import { detectFatalToolError } from '../utils/text.ts'; import { runCommandCapturingExit, runSandboxCommand } from './commands.ts'; -import { loadMakersFrameworkProfiles, runMakersCompatibilityCheck } from './makers-compat.ts'; -import { withFrameworkAdapter } from './makers-declarations.ts'; +import { loadMakersFrameworkProfiles } from '../makers/compat/skill-rules.ts'; +import { runMakersCompatibilityCheck } from '../makers/compat/run.ts'; +import { withFrameworkAdapter } from '../makers/declarations.ts'; import { applyProjectTemplate, listProjectTemplates, resolveProjectTemplate } from './templates.ts'; import type { AppliedTemplate } from './templates.ts'; -import { shellQuote } from '../../../shared/shell.ts'; +import { shellQuote } from '../utils/shell.ts'; // Models used to pass `${appDir}/file` into write_project_file, which joined // appDir again and created appDir/appDir/... . Lift that nested tree back to diff --git a/agents/_lib/project/state.ts b/agents/_lib/project/state.ts index 9d40ce7..61c52ee 100644 --- a/agents/_lib/project/state.ts +++ b/agents/_lib/project/state.ts @@ -1,7 +1,7 @@ import type { ProjectState } from '../types.ts'; import { safeSegment } from '../utils/paths.ts'; import { runSandboxCommand } from './commands.ts'; -import { isMakersDeployUrl } from '../../../shared/makers-deploy.ts'; +import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; export function createProjectState(conversationId: string): ProjectState { const sessionDir = `projects/${safeSegment(conversationId)}`; diff --git a/agents/_lib/project/templates.ts b/agents/_lib/project/templates.ts index 58a4fbd..e7d4100 100644 --- a/agents/_lib/project/templates.ts +++ b/agents/_lib/project/templates.ts @@ -32,7 +32,7 @@ import path from 'node:path'; import { PREVIEW_ASSET_PREFIX_ENV } from '../constants.ts'; import type { ProjectState, ScaffoldLog } from '../types.ts'; import { safeSegment } from '../utils/paths.ts'; -import { buildNpmWarmupCommand } from '../../../shared/npm-install.ts'; +import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; import { runSandboxCommand } from './commands.ts'; export type ProjectTemplate = { diff --git a/agents/_lib/project/workspace.ts b/agents/_lib/project/workspace.ts new file mode 100644 index 0000000..b538d4e --- /dev/null +++ b/agents/_lib/project/workspace.ts @@ -0,0 +1,95 @@ +import { getProjectState, saveProjectState } from '../session/store.ts'; +import { getFileTree } from './fs.ts'; +import { restorePersistedProject } from './persistence.ts'; +import { separateLegacyMakersDeployment } from './state.ts'; +import type { ProjectState, StreamSend } from '../types.ts'; +import { withTimeout } from '../turn/checkpoint.ts'; + +const SANDBOX_PROBE_MS = 15_000; +const RESTORE_BUDGET_MS = 45_000; + +async function ensureWorkspaceDirectories(context: any, state: ProjectState) { + await context.sandbox.files.makeDir(state.sessionDir); + await context.sandbox.files.makeDir(state.appDir); +} + +async function probeSandboxHasFiles(context: any, state: ProjectState) { + if (!(await context.sandbox.files.exists(state.appDir))) return false; + const tree = await getFileTree(context, state); + return tree.some((item) => item.type === 'file'); +} + +/** + * Restore the volatile sandbox from Blob-backed persist, used both before a + * prompt and when GET /session rebuilds the workspace. + */ +export async function restoreProjectWorkspace( + context: any, + conversationId: string, + options: { send?: StreamSend; mode?: 'prepare' | 'resume' } = {}, +): Promise<{ state: ProjectState; hasFiles: boolean; restoreError?: string }> { + const state = separateLegacyMakersDeployment(await getProjectState(context, conversationId)); + const send = options.send; + let hasFiles = false; + let restoreError: string | undefined; + + try { + hasFiles = await withTimeout( + probeSandboxHasFiles(context, state), + SANDBOX_PROBE_MS, + 'sandbox file probe', + ); + } catch (error) { + hasFiles = false; + restoreError = error instanceof Error ? error.message : 'Sandbox probe failed.'; + } + + if (!hasFiles) { + try { + const restored = await withTimeout( + restorePersistedProject(context, conversationId, state, { + installDependencies: options.mode !== 'resume', + }), + RESTORE_BUDGET_MS, + 'snapshot restore', + ); + hasFiles = restored.restored; + if (!restored.restored) restoreError = restored.error; + } catch (error) { + hasFiles = false; + restoreError = error instanceof Error ? error.message : 'Snapshot restore failed.'; + } + } + + try { + await ensureWorkspaceDirectories(context, state); + } catch (error) { + send?.({ + type: 'error', + error: error instanceof Error ? error.message : 'Workspace directory creation failed.', + }); + } + + if (hasFiles) state.created = true; + if (hasFiles) { + try { + await saveProjectState(context, conversationId, state); + } catch { + // The sandbox files are still the working copy for this turn. + } + } + + return { state, hasFiles, restoreError }; +} + +export async function prepareProjectWorkspace( + context: any, + conversationId: string, + send?: StreamSend, +): Promise { + const restored = await restoreProjectWorkspace(context, conversationId, { + send, + mode: 'prepare', + }); + return restored.state; +} diff --git a/agents/_lib/prompt.ts b/agents/_lib/prompt.ts index 5b48532..3829f93 100644 --- a/agents/_lib/prompt.ts +++ b/agents/_lib/prompt.ts @@ -5,8 +5,8 @@ import { PREVIEW_PUBLIC_PORT, PREVIEW_SERVER_PORT, } from './constants.ts'; -import type { ConversationMessage, ProjectState } from './types.ts'; -import { resolveConversationPublishArea } from './project/makers-deploy.ts'; +import type { ProjectState } from './types.ts'; +import { resolveConversationPublishArea } from './makers/project.ts'; // The system prompt is split into named sections so each rule has an obvious // owner. The dividing line is deliberate: platform knowledge (handler @@ -16,11 +16,10 @@ import { resolveConversationPublishArea } from './project/makers-deploy.ts'; // the product's narration and reply style. Restating platform rules here would // create a second source of truth that silently drifts when the skills update. // -// Nothing that changes between turns belongs in here. The request and the -// history travel as the turn's own message (buildTurnPrompt), which keeps this -// text identical for every turn of a conversation — a prefix that changes on -// each turn can never be cached, and the request arriving twice leaves two -// copies with no way to say which one is authoritative. +// Nothing that changes between turns belongs in here. The request travels as +// the SDK user message, and resume loads history from the transcript, which +// keeps this text identical for every turn of a conversation — a prefix that +// changes on each turn can never be cached. /** Headings, so a 40-rule prompt reads as sections rather than as a wall. */ function section(title: string, body: readonly string[], spaced = false) { @@ -338,7 +337,7 @@ const FINAL_REPLY = [ * * Everything here is either constant or fixed for the life of the conversation, * which is what lets the model provider reuse the prefix instead of re-reading - * twenty thousand characters per turn. The request itself is buildTurnPrompt's. + * twenty thousand characters per turn. The request itself is the SDK user message. */ export function buildPrompt( state: ProjectState, @@ -375,22 +374,3 @@ export function buildPrompt( ].join('\n\n'); } -/** - * The turn itself: what the user asked, and enough of the conversation to read - * it in context. - * - * This is the SDK's `prompt`, so the request reaches the model exactly once. - * Passing it here rather than in the system prompt is also what keeps the rules - * above byte-identical between turns. - */ -export function buildTurnPrompt(userMessage: string, history: ConversationMessage[]) { - const recentHistory = history - .slice(-8) - .map((item) => `${item.role === 'user' ? 'User' : 'Assistant'}: ${item.content}`) - .join('\n'); - - return [ - recentHistory ? `Recent conversation:\n${recentHistory}` : '', - `Current user request: ${userMessage}`, - ].filter(Boolean).join('\n\n'); -} diff --git a/agents/_lib/runtime/context.ts b/agents/_lib/runtime/context.ts new file mode 100644 index 0000000..9eb5355 --- /dev/null +++ b/agents/_lib/runtime/context.ts @@ -0,0 +1,57 @@ +import type { ProjectState } from '../types.ts'; + +/** The slice of the Makers agent `context` this template actually reads. */ +export type AgentContext = { + conversation_id?: string; + run_id?: string; + env?: Record; + request?: { + body?: unknown; + headers?: Headers | Record; + signal?: AbortSignal; + url?: string; + path?: string; + query?: unknown; + params?: unknown; + [key: string]: unknown; + }; + sandbox?: { + files: { + exists: (path: string) => Promise; + read: (path: string) => Promise; + write: (path: string, content: string | Uint8Array) => Promise; + makeDir: (path: string) => Promise; + remove?: (path: string) => Promise; + }; + commands: { run: (command: string, options?: Record) => Promise }; + persist: (options: { path: string }) => Promise; + restore: (options: { path: string }) => Promise<{ restored?: boolean } | undefined>; + getHost?: (port: number) => Promise; + envdAccessToken?: string; + browser?: { liveUrl?: string }; + extendTimeout?: (seconds: number) => unknown; + }; + tools?: { + toClaudeMcpServer: (name: string, options?: { alwaysLoad?: boolean }) => { + tools: unknown[]; + allowedTools: string[]; + }; + }; + utils?: { + abortActiveRun?: (conversationId: string) => Promise<{ aborted?: boolean } | undefined>; + }; + /** Test seam: an in-memory Blob stand-in. Production uses `@edgeone/pages-blob`. */ + blobStore?: BlobStoreLike; +}; + +export type BlobStoreLike = { + set: (key: string, value: string | ArrayBuffer | Blob | ReadableStream, options?: { onlyIfNew?: boolean }) => Promise; + setJSON: (key: string, value: unknown, options?: { onlyIfNew?: boolean }) => Promise; + get: (key: string, options?: { type?: 'text' | 'json' | 'arrayBuffer' | 'blob' | 'stream'; consistency?: 'strong' | 'eventual' }) => Promise; + delete: (key: string) => Promise; + list: (options?: { prefix?: string }) => Promise<{ blobs: Array<{ key: string; etag?: string }> }>; +}; + +export type WorkspaceMode = 'prepare' | 'resume'; + +export type { ProjectState }; diff --git a/agents/_lib/runtime/merge.ts b/agents/_lib/runtime/merge.ts new file mode 100644 index 0000000..fbd52d2 --- /dev/null +++ b/agents/_lib/runtime/merge.ts @@ -0,0 +1,59 @@ +const STREAM_FINISHED = Symbol('finished'); +const STREAM_ABORTED = Symbol('aborted'); + +class AsyncValueQueue { + private values: T[] = []; + private waiters: Array<(value: T) => void> = []; + + push(value: T) { + const waiter = this.waiters.shift(); + if (waiter) waiter(value); + else this.values.push(value); + } + + next() { + const value = this.values.shift(); + if (value !== undefined) return Promise.resolve(value); + return new Promise((resolve) => this.waiters.push(resolve)); + } +} + +/** Fan in several SSE generators onto one connection without waiting for the slowest. */ +export async function* mergeSseGenerators( + generators: Array>, + signal?: AbortSignal, +): AsyncGenerator { + if (generators.length === 0) return; + if (generators.length === 1) { + yield* generators[0]; + return; + } + + const queue = new AsyncValueQueue(); + let remaining = generators.length; + const abort = () => queue.push(STREAM_ABORTED); + signal?.addEventListener('abort', abort, { once: true }); + + const pumps = generators.map(async (generator) => { + try { + for await (const chunk of generator) { + if (signal?.aborted) return; + queue.push(chunk); + } + } finally { + remaining -= 1; + if (remaining === 0) queue.push(STREAM_FINISHED); + } + }); + + try { + while (!signal?.aborted) { + const item = await queue.next(); + if (item === STREAM_FINISHED || item === STREAM_ABORTED) return; + yield item; + } + } finally { + signal?.removeEventListener('abort', abort); + await Promise.allSettled(pumps); + } +} diff --git a/agents/_lib/utils/request.ts b/agents/_lib/runtime/request.ts similarity index 84% rename from agents/_lib/utils/request.ts rename to agents/_lib/runtime/request.ts index afc3102..eb0a736 100644 --- a/agents/_lib/utils/request.ts +++ b/agents/_lib/runtime/request.ts @@ -1,17 +1,11 @@ -// Request helpers for agent pipelines. Mirrors the query/header resolution the rest -// of the app relies on for the EdgeOne request shape. - export function getRequestHeader(context: any, name: string): string { const headers = context?.request?.headers; if (!headers) return ''; - // Headers / Map-like (case-insensitive get). if (typeof headers.get === 'function') { return String(headers.get(name) || ''); } - // Plain objects: try exact / lower-case keys, then a case-insensitive scan - // (some runtimes normalize header names inconsistently). const lowerName = name.toLowerCase(); const directValue = headers[name] ?? headers[lowerName]; const value = directValue @@ -99,11 +93,6 @@ export function getRequestQueryParam(context: any, name: string): { return { value: '', source: 'none' }; } -/** - * Resolve conversation id from the dual-channel routing shape used by Makers: - * context.conversation_id → makers-conversation-id → conversationId header, - * optionally falling back to query cid / conversationId (for plain navigations). - */ export function resolveConversationId( context: any, options?: { allowQuery?: boolean }, @@ -124,8 +113,6 @@ export function resolveConversationId( } if (options?.allowQuery) { - // Query-param fallback so a plain navigation can still target the right - // sandbox; the frontend prefers the headers. const cid = getRequestQueryParam(context, 'cid'); if (cid.value) { return { conversationId: cid.value, source: cid.source }; diff --git a/agents/_lib/shared.ts b/agents/_lib/runtime/sse.ts similarity index 87% rename from agents/_lib/shared.ts rename to agents/_lib/runtime/sse.ts index 7b18844..84b86e3 100644 --- a/agents/_lib/shared.ts +++ b/agents/_lib/runtime/sse.ts @@ -1,4 +1,6 @@ -export function sseEvent(data: Record): string { +import type { ChatStreamEvent, ResumeStreamEvent, SessionStreamEvent } from '../../../shared/protocol.ts'; + +export function sseEvent(data: SessionStreamEvent): string { return `data: ${JSON.stringify(data)}\n\n`; } @@ -64,3 +66,5 @@ export function createSSEResponse( }, }); } + +export type { ChatStreamEvent, ResumeStreamEvent, SessionStreamEvent }; diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts new file mode 100644 index 0000000..8075b4f --- /dev/null +++ b/agents/_lib/session/live.ts @@ -0,0 +1,724 @@ +import { + query, + type Query, + type SDKMessage, + type SDKResultMessage, + type SDKUserMessage, +} from '@anthropic-ai/claude-agent-sdk'; +import { + DEFAULT_PATH, + GATEWAY_CONVERSATION_ID_HEADER_NAME, + GATEWAY_QUOTA_BYPASS_HEADER, + GATEWAY_QUOTA_PROMPT_HEADER, + MAKERS_SKILL_NAMES, + SANDBOX_MCP_SERVER_NAME, +} from '../constants.ts'; +import { + describeModelRun, + resolveConfiguredModel, + resolveRunningModelLabel, +} from '../models.ts'; +import type { AgentContext } from '../runtime/context.ts'; +import { + assembleAgentTools, + emptyCodingResult, + type LiveSessionHandle, + type LiveTurnCallbacks, +} from '../tools/assemble.ts'; +import type { + AgentProgressEvent, + CodingAgentResult, + ProjectState, +} from '../types.ts'; +import { detectFatalToolError, truncateForStream } from '../utils/text.ts'; +import { + resolveNarrationEmit, + sanitizeAssistantText, + sanitizeNarrationText, + summarizeToolInput, + summarizeToolOutput, + type NarrationEmitState, +} from '../../../shared/timeline.ts'; +import { + isInstallCommand, + isMakersDeployCommand, + isPreviewCommand, + parseEchoedExitCode, + shortenToolName, +} from '../makers/tool-phase.ts'; +import { buildPrompt } from '../prompt.ts'; +import { resolveMakersProjectName } from '../makers/project.ts'; +import { getConversationRecord } from './store.ts'; +import { downloadTranscript, uploadTranscript } from './transcript.ts'; + +class PromptQueue implements AsyncIterable { + private messages: SDKUserMessage[] = []; + private waiters: Array<(result: IteratorResult) => void> = []; + private closed = false; + + push(message: SDKUserMessage) { + if (this.closed) return; + const waiter = this.waiters.shift(); + if (waiter) waiter({ value: message, done: false }); + else this.messages.push(message); + } + + close() { + this.closed = true; + for (const waiter of this.waiters) { + waiter({ value: undefined as unknown as SDKUserMessage, done: true }); + } + this.waiters = []; + } + + [Symbol.asyncIterator](): AsyncIterator { + return { + next: () => { + if (this.messages.length > 0) { + return Promise.resolve({ value: this.messages.shift()!, done: false as const }); + } + if (this.closed) { + return Promise.resolve({ value: undefined as unknown as SDKUserMessage, done: true as const }); + } + return new Promise>((resolve) => { + this.waiters.push(resolve); + }); + }, + }; + } +} + +type TurnWaiter = { + callbacks: LiveTurnCallbacks; + onProgress?: (event: AgentProgressEvent) => void; + resolve: (result: CodingAgentResult) => void; +}; + +type LiveQuerySession = LiveSessionHandle & { + queue: PromptQueue; + query: Query; + sessionId?: string; + transcriptPath?: string; + model: string; + turn?: TurnWaiter; + state: ProjectState; + pump: Promise; +}; + +const liveQueries = new Map(); + +export type RunCodingAgentOptions = { + context: AgentContext; + conversationId: string; + userMessage: string; + state: ProjectState; + isNewProject: boolean; + onScaffoldLog?: LiveTurnCallbacks['onScaffoldLog']; + onProgress?: (event: AgentProgressEvent) => void; + onProjectFilesChanged?: LiveTurnCallbacks['onProjectFilesChanged']; + onPreviewReady?: LiveTurnCallbacks['onPreviewReady']; + onDeploymentStatus?: LiveTurnCallbacks['onDeploymentStatus']; + abortSignal?: AbortSignal; + model?: string; + send?: LiveTurnCallbacks['send']; +}; + +function pickEnvValue(context: AgentContext, key: string) { + const value = context?.env?.[key]; + return typeof value === 'string' ? value.trim() : ''; +} + +function sanitizeHeaderValue(value: string) { + return value.replace(/[\r\n]+/g, ' ').trim(); +} + +function buildAnthropicCustomHeaders(customHeaders: string, conversationId: string) { + const safeConversationId = sanitizeHeaderValue(conversationId); + return [ + customHeaders, + GATEWAY_QUOTA_BYPASS_HEADER, + GATEWAY_QUOTA_PROMPT_HEADER, + safeConversationId + ? `${GATEWAY_CONVERSATION_ID_HEADER_NAME}: ${safeConversationId}` + : '', + ].filter(Boolean).join('\n'); +} + +function extractSandboxCommand(input: unknown) { + const record = input && typeof input === 'object' ? input as Record : {}; + const command = typeof record.command === 'string' + ? record.command + : typeof record.cmd === 'string' + ? record.cmd + : ''; + return command.trim(); +} + +function extractVisibleNarrationDelta(event: SDKMessage) { + if (event.type !== 'stream_event') return ''; + const streamEvent = (event as { event?: { type?: string; delta?: { type?: string; text?: string } } }).event; + if (streamEvent?.type !== 'content_block_delta') return ''; + const delta = streamEvent.delta; + if (delta?.type === 'text_delta' && typeof delta.text === 'string') { + return sanitizeNarrationText(delta.text); + } + return ''; +} + +type StreamingToolUseBlock = { + id: string; + name: string; + inputJson: string; + input?: unknown; +}; + +function isToolUseContentBlock(block: unknown): block is { + type: string; + id?: string; + name?: string; + input?: unknown; +} { + const record = block && typeof block === 'object' ? block as Record : {}; + return record.type === 'tool_use' || record.type === 'mcp_tool_use'; +} + +function extractVisibleTextBlock(block: unknown) { + const record = block && typeof block === 'object' ? block as Record : {}; + if (record.type !== 'text' || typeof record.text !== 'string') return ''; + return sanitizeNarrationText(record.text); +} + +function parseToolInputJson(rawJson: string, fallback: unknown) { + if (!rawJson.trim()) return fallback ?? {}; + try { + return JSON.parse(rawJson); + } catch { + return fallback ?? {}; + } +} + +type ToolProgressPhase = 'scaffold' | 'code' | 'install' | 'preview' | 'link'; + +function inferToolProgress(name: string, input: unknown): { + phaseHint?: ToolProgressPhase; + fileCount?: number; +} { + const toolName = shortenToolName(name); + if (toolName === 'ensure_project_scaffold') return { phaseHint: 'scaffold' }; + if (toolName === 'files_write' || toolName === 'write_files' || toolName === 'files_make_dir' || toolName === 'files_remove') { + return { phaseHint: 'code' }; + } + if (toolName === 'write_project_file') return { phaseHint: 'code', fileCount: 1 }; + if (toolName === 'commands') { + const cmd = extractSandboxCommand(input); + if (isInstallCommand(cmd)) return { phaseHint: 'install' }; + if (isPreviewCommand(cmd) || isMakersDeployCommand(cmd)) return { phaseHint: 'preview' }; + } + return {}; +} + +function userMessage(content: string): SDKUserMessage { + return { + type: 'user', + message: { role: 'user', content }, + parent_tool_use_id: null, + }; +} + +function flagsFrom(session: LiveQuerySession): Pick< + CodingAgentResult, + 'projectTouched' | 'filesWritten' | 'previewTouched' | 'deploymentTouched' | 'wasCreated' +> { + return { + projectTouched: session.flags.projectTouched, + filesWritten: session.flags.filesWritten, + previewTouched: session.flags.previewTouched, + deploymentTouched: session.flags.deploymentTouched, + wasCreated: session.flags.wasCreated, + }; +} + +async function persistTranscript(session: LiveQuerySession) { + if (!session.sessionId || !session.transcriptPath) return; + await uploadTranscript({ + context: session.context, + conversationId: session.conversationId, + sessionId: session.sessionId, + sourcePath: session.transcriptPath, + }); +} + +async function pumpSession(session: LiveQuerySession) { + const toolContextById = new Map(); + const toolStartedAtById = new Map(); + const pendingToolUseBlocks = new Map(); + const emittedToolUseProgress = new Map(); + let narrationState: NarrationEmitState = { currentTextBlock: '', emittedNarration: '' }; + const scaffoldToolName = `mcp__${SANDBOX_MCP_SERVER_NAME}__ensure_project_scaffold`; + let scaffoldHandled = false; + let fatalError: string | null = null; + + const emitNarration = (rawText: string, uuid: string, complete = false) => { + const resolved = resolveNarrationEmit(narrationState, rawText, complete); + narrationState = resolved.state; + if (!resolved.text) return; + session.turn?.onProgress?.({ + type: 'text_segment', + data: { uuid, text: resolved.text }, + }); + }; + + const emitToolUseProgress = (toolUse: { id?: string; name?: string; input?: unknown }) => { + const toolName = typeof toolUse.name === 'string' ? toolUse.name : ''; + const toolUseId = typeof toolUse.id === 'string' ? toolUse.id : ''; + const shortToolName = shortenToolName(toolName); + const command = shortToolName === 'commands' ? extractSandboxCommand(toolUse.input) : ''; + const progress = typeof toolUse.name === 'string' ? inferToolProgress(toolName, toolUse.input) : {}; + const inputSummary = summarizeToolInput(toolName, toolUse.input, session.getState().appDir); + const progressSignature = JSON.stringify({ + name: toolName, + command, + phaseHint: progress.phaseHint || '', + fileCount: progress.fileCount || 0, + inputSummary, + }); + if (toolUseId) { + if (emittedToolUseProgress.get(toolUseId) === progressSignature) return; + emittedToolUseProgress.set(toolUseId, progressSignature); + } + narrationState = { ...narrationState, currentTextBlock: '' }; + if (toolUseId && typeof toolUse.name === 'string') { + toolContextById.set(toolUseId, { name: toolUse.name, ...(command ? { command } : {}) }); + } + const startedAt = toolUseId ? toolStartedAtById.get(toolUseId) || Date.now() : Date.now(); + if (toolUseId) toolStartedAtById.set(toolUseId, startedAt); + session.turn?.onProgress?.({ + type: 'tool_use', + data: { + id: toolUseId, + name: toolName, + ...(command ? { command } : {}), + ...progress, + inputSummary, + startedAt, + }, + }); + }; + + const finishTurn = async (result: CodingAgentResult) => { + await persistTranscript(session).catch((error) => { + console.warn('[transcript] upload failed', error); + }); + const waiter = session.turn; + session.turn = undefined; + waiter?.resolve(result); + }; + + try { + for await (const event of session.query as AsyncIterable) { + const systemEvent = event as SDKMessage & { subtype?: string; session_id?: string }; + if (event.type === 'system' && systemEvent.subtype === 'compact_boundary') { + await persistTranscript(session).catch((error) => { + console.warn('[transcript] compaction upload failed', error); + }); + } + if (typeof systemEvent.session_id === 'string' && systemEvent.session_id) { + session.sessionId = systemEvent.session_id; + } + + if (!session.turn) continue; + + if (event.type === 'stream_event') { + emitNarration( + extractVisibleNarrationDelta(event), + typeof event.uuid === 'string' ? event.uuid : '', + false, + ); + const streamEvent = (event as { event?: Record }).event; + if (streamEvent?.type === 'content_block_start') { + const contentBlock = streamEvent.content_block; + if (contentBlock?.type === 'text') { + narrationState = { ...narrationState, currentTextBlock: '' }; + } + if (isToolUseContentBlock(contentBlock) && typeof streamEvent.index === 'number') { + pendingToolUseBlocks.set(streamEvent.index, { + id: typeof contentBlock.id === 'string' ? contentBlock.id : '', + name: typeof contentBlock.name === 'string' ? contentBlock.name : '', + inputJson: '', + input: contentBlock.input, + }); + emitToolUseProgress({ + id: contentBlock.id, + name: contentBlock.name, + input: contentBlock.input, + }); + } + } else if (streamEvent?.type === 'content_block_delta') { + const delta = streamEvent.delta; + const pendingToolUse = typeof streamEvent.index === 'number' + ? pendingToolUseBlocks.get(streamEvent.index) + : undefined; + if (pendingToolUse && delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') { + pendingToolUse.inputJson += delta.partial_json; + } + } else if (streamEvent?.type === 'content_block_stop') { + const pendingToolUse = typeof streamEvent.index === 'number' + ? pendingToolUseBlocks.get(streamEvent.index) + : undefined; + if (pendingToolUse) { + pendingToolUseBlocks.delete(streamEvent.index); + emitToolUseProgress({ + id: pendingToolUse.id, + name: pendingToolUse.name, + input: parseToolInputJson(pendingToolUse.inputJson, pendingToolUse.input), + }); + } + } + continue; + } + + if (event.type === 'assistant') { + const blocks = (event as { message?: { content?: unknown } }).message?.content; + if (Array.isArray(blocks)) { + for (const block of blocks) { + emitNarration( + extractVisibleTextBlock(block), + typeof event.uuid === 'string' ? event.uuid : '', + true, + ); + if (isToolUseContentBlock(block)) { + emitToolUseProgress({ id: block.id, name: block.name, input: block.input }); + } + } + } + continue; + } + + if (event.type === 'user') { + const blocks = (event as { message?: { content?: unknown } }).message?.content; + if (Array.isArray(blocks)) { + for (const block of blocks) { + const record = block && typeof block === 'object' ? block as Record : {}; + if (record.type !== 'tool_result') continue; + const text = Array.isArray(record.content) + ? record.content.map((item: any) => (typeof item?.text === 'string' ? item.text : '')).join(' ') + : (typeof record.content === 'string' ? record.content : ''); + const toolUseId = typeof record.tool_use_id === 'string' ? record.tool_use_id : ''; + const toolContext = toolContextById.get(toolUseId); + const toolName = toolContext?.name || ''; + const echoedExit = parseEchoedExitCode(text); + const commandFailed = typeof echoedExit === 'number' && echoedExit !== 0; + const toolFailed = record.is_error === true || commandFailed; + session.turn?.onProgress?.({ + type: 'tool_result', + data: { + id: toolUseId, + toolName, + ...(toolContext?.command ? { command: toolContext.command } : {}), + ok: !toolFailed, + preview: truncateForStream(text, 500), + outputSummary: summarizeToolOutput(text, session.getState().appDir, toolName), + status: toolFailed ? 'failed' : 'completed', + endedAt: Date.now(), + }, + }); + if (!scaffoldHandled && toolName === scaffoldToolName && record.is_error !== true) { + scaffoldHandled = true; + try { + await session.getCallbacks().onProjectFilesChanged?.(); + } catch (error) { + console.warn('[scaffold-done] onProjectFilesChanged failed', error); + } + } + if (record.is_error === true && !fatalError) { + const fatal = detectFatalToolError(text); + if (fatal) { + fatalError = `${fatal} (tool=${toolName})`; + console.warn('[fatal] aborting agent loop:', fatalError); + } + } + } + } + if (fatalError) { + await finishTurn(emptyCodingResult({ + error: fatalError, + fatal: true, + ...flagsFrom(session), + })); + fatalError = null; + } + continue; + } + + if (event.type === 'result') { + const resultMessage = event as SDKResultMessage; + const modelRun = describeModelRun(session.model, resultMessage.modelUsage); + if (modelRun.mismatch) { + console.warn('[model]', `${modelRun.line} — the gateway served a model this turn did not request`); + } else { + console.info('[model]', modelRun.line); + } + if (resultMessage.subtype !== 'success') { + await finishTurn(emptyCodingResult({ + error: Array.isArray(resultMessage.errors) && resultMessage.errors.length > 0 + ? resultMessage.errors[0] + : 'Model execution failed.', + ...flagsFrom(session), + })); + } else { + await finishTurn({ + success: true, + output: sanitizeAssistantText((resultMessage.result || '').trim()), + error: null, + ...flagsFrom(session), + }); + } + toolContextById.clear(); + toolStartedAtById.clear(); + pendingToolUseBlocks.clear(); + emittedToolUseProgress.clear(); + narrationState = { currentTextBlock: '', emittedNarration: '' }; + scaffoldHandled = false; + fatalError = null; + } + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + const fatal = detectFatalToolError(message); + if (session.turn) { + await finishTurn(emptyCodingResult({ + error: fatal || message || 'Execution failed.', + ...(fatal ? { fatal: true } : {}), + ...flagsFrom(session), + })); + } + } finally { + if (session.turn) { + await finishTurn(emptyCodingResult({ + error: 'The model stream ended without returning a result.', + ...flagsFrom(session), + })); + } + liveQueries.delete(session.conversationId); + try { + session.query.close(); + } catch (error) { + console.warn('[agent] failed to close the SDK query', error); + } + session.queue.close(); + } +} + +async function startLiveQuery(options: RunCodingAgentOptions): Promise { + const { context, conversationId } = options; + const apiKey = pickEnvValue(context, 'AI_GATEWAY_API_KEY') + || pickEnvValue(context, 'ANTHROPIC_API_KEY') + || pickEnvValue(context, 'DEEPSEEK_API_KEY'); + const authToken = pickEnvValue(context, 'ANTHROPIC_AUTH_TOKEN') + || pickEnvValue(context, 'DEEPSEEK_API_KEY'); + const model = (options.model || '').trim() || resolveConfiguredModel(context); + const baseURL = pickEnvValue(context, 'AI_GATEWAY_BASE_URL') + || pickEnvValue(context, 'ANTHROPIC_BASE_URL') + || pickEnvValue(context, 'DEEPSEEK_BASE_URL') + || ''; + const customHeaders = pickEnvValue(context, 'ANTHROPIC_CUSTOM_HEADERS'); + const executablePath = pickEnvValue(context, 'CLAUDE_CODE_EXECUTABLE_PATH'); + + if (!apiKey && !authToken) { + return emptyCodingResult({ + error: 'Missing AI_GATEWAY_API_KEY / ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / DEEPSEEK_API_KEY. The agent cannot call the model.', + }); + } + if (!baseURL) { + return emptyCodingResult({ + error: 'Missing AI_GATEWAY_BASE_URL / ANTHROPIC_BASE_URL / DEEPSEEK_BASE_URL. The agent cannot call the model.', + }); + } + + const session = { + conversationId, + context, + state: options.state, + getState: () => session.state, + getCallbacks: () => session.turn?.callbacks || {}, + flags: { + projectTouched: false, + filesWritten: false, + previewTouched: false, + deploymentTouched: false, + wasCreated: false, + }, + queue: new PromptQueue(), + query: null as unknown as Query, + model, + pump: Promise.resolve(), + } as LiveQuerySession; + + const assembled = assembleAgentTools(session); + const sdkEnv: Record = { + ANTHROPIC_BASE_URL: baseURL, + ANTHROPIC_MODEL: model, + ANTHROPIC_CUSTOM_HEADERS: buildAnthropicCustomHeaders(customHeaders, conversationId), + PATH: pickEnvValue(context, 'PATH') || DEFAULT_PATH, + HOME: pickEnvValue(context, 'HOME') || '/tmp', + CLAUDE_CONFIG_DIR: pickEnvValue(context, 'CLAUDE_CONFIG_DIR') || '/tmp/.claude', + }; + if (apiKey) sdkEnv.ANTHROPIC_API_KEY = apiKey; + if (authToken) sdkEnv.ANTHROPIC_AUTH_TOKEN = authToken; + if (!sdkEnv.ANTHROPIC_API_KEY && authToken) sdkEnv.ANTHROPIC_API_KEY = authToken; + + const record = await getConversationRecord(context, conversationId); + if (record.claudeSessionId && record.transcriptPath) { + const restored = await downloadTranscript({ + context, + conversationId, + sessionId: record.claudeSessionId, + destPath: record.transcriptPath, + }); + if (restored) { + session.sessionId = record.claudeSessionId; + session.transcriptPath = record.transcriptPath; + } + } + + const sdkOptions: Parameters[0]['options'] = { + model, + permissionMode: 'dontAsk', + maxTurns: 100, + tools: ['Skill'], + skills: [...MAKERS_SKILL_NAMES], + includePartialMessages: true, + persistSession: true, + mcpServers: { + [assembled.mcpServerName]: assembled.sandboxMcpServer, + }, + allowedTools: assembled.mcpAllowedTools, + strictMcpConfig: true, + systemPrompt: buildPrompt( + session.getState(), + options.isNewProject, + SANDBOX_MCP_SERVER_NAME, + resolveMakersProjectName(context, session.getState()), + resolveRunningModelLabel(context, model), + assembled.webSearchAvailable, + ), + env: sdkEnv, + cwd: process.cwd(), + settingSources: ['project'], + stderr: (data: string) => { + console.warn('[claude-code]', data.trimEnd()); + }, + hooks: { + SessionStart: [{ + hooks: [async (input) => { + if (input.hook_event_name === 'SessionStart') { + session.sessionId = input.session_id; + session.transcriptPath = input.transcript_path; + } + return {}; + }], + }], + PostCompact: [{ + hooks: [async () => { + await persistTranscript(session).catch((error) => { + console.warn('[transcript] post-compact upload failed', error); + }); + return {}; + }], + }], + }, + ...(session.sessionId ? { resume: session.sessionId } : {}), + }; + if (executablePath) sdkOptions.pathToClaudeCodeExecutable = executablePath; + + session.query = query({ + prompt: session.queue, + options: sdkOptions, + }); + session.pump = pumpSession(session); + liveQueries.set(conversationId, session); + return session; +} + +export function getLiveQuery(conversationId: string) { + return liveQueries.get(conversationId) || null; +} + +export async function interruptLiveQuery(conversationId: string) { + const live = liveQueries.get(conversationId); + if (!live) return false; + try { + await live.query.interrupt(); + return true; + } catch (error) { + console.warn('[agent] interrupt failed', error); + return false; + } +} + +export async function setLiveQueryModel(conversationId: string, model: string) { + const live = liveQueries.get(conversationId); + if (!live) return false; + live.model = model; + try { + await live.query.setModel(model); + return true; + } catch (error) { + console.warn('[agent] setModel failed', error); + return false; + } +} + +export async function runCodingAgent(options: RunCodingAgentOptions): Promise { + if (options.abortSignal?.aborted) { + return emptyCodingResult({ stopped: true }); + } + + let session = liveQueries.get(options.conversationId); + if (!session) { + const started = await startLiveQuery(options); + if (!('queue' in started)) return started; + session = started; + } else { + session.context = options.context; + session.state = options.state; + if ((options.model || '').trim() && options.model !== session.model) { + await setLiveQueryModel(options.conversationId, options.model!.trim()); + } + } + + session.flags.projectTouched = false; + session.flags.filesWritten = false; + session.flags.previewTouched = false; + session.flags.deploymentTouched = false; + session.flags.wasCreated = false; + + const abort = () => { + void interruptLiveQuery(options.conversationId); + }; + options.abortSignal?.addEventListener('abort', abort, { once: true }); + + try { + const result = await new Promise((resolve) => { + session!.turn = { + callbacks: { + onScaffoldLog: options.onScaffoldLog, + onProjectFilesChanged: options.onProjectFilesChanged, + onPreviewReady: options.onPreviewReady, + onDeploymentStatus: options.onDeploymentStatus, + send: options.send, + abortSignal: options.abortSignal, + }, + onProgress: options.onProgress, + resolve, + }; + session!.queue.push(userMessage(options.userMessage)); + }); + if (options.abortSignal?.aborted) { + return { ...result, success: false, stopped: true, error: null }; + } + return result; + } finally { + options.abortSignal?.removeEventListener('abort', abort); + } +} diff --git a/agents/_lib/session/projection.ts b/agents/_lib/session/projection.ts new file mode 100644 index 0000000..c135fb7 --- /dev/null +++ b/agents/_lib/session/projection.ts @@ -0,0 +1,126 @@ +import type { AssistantActivity, PersistedActivityTurn } from '../../../shared/protocol.ts'; +import { + appendNarrationChunk, + sanitizeAssistantText, + summarizeToolInput, + summarizeToolOutput, +} from '../../../shared/timeline.ts'; + +type JsonRecord = Record; + +function asRecord(value: unknown): JsonRecord { + return value && typeof value === 'object' ? value as JsonRecord : {}; +} + +function textFromContent(content: unknown): string { + if (typeof content === 'string') return sanitizeAssistantText(content); + if (!Array.isArray(content)) return ''; + return sanitizeAssistantText( + content + .map((block) => { + const record = asRecord(block); + return typeof record.text === 'string' ? record.text : ''; + }) + .join(''), + ); +} + +function toolBlocks(content: unknown): JsonRecord[] { + if (!Array.isArray(content)) return []; + return content.filter((block) => { + const record = asRecord(block); + return record.type === 'tool_use' || record.type === 'mcp_tool_use' || record.type === 'tool_result'; + }).map(asRecord); +} + +/** + * Project a Claude Code JSONL transcript into the turns the workspace UI renders. + * The file is the source of truth; this is a derived view. + */ +export function projectTranscript(jsonl: string, projectDir = ''): PersistedActivityTurn[] { + const turns: PersistedActivityTurn[] = []; + let current: PersistedActivityTurn | null = null; + + const openTurn = (user: string, createdAt: number) => { + current = { + id: `turn-${createdAt}-${turns.length}`, + user, + assistant: '', + status: 'completed', + createdAt, + activities: [], + }; + turns.push(current); + }; + + for (const rawLine of jsonl.split('\n')) { + const line = rawLine.trim(); + if (!line) continue; + let entry: JsonRecord; + try { + entry = JSON.parse(line) as JsonRecord; + } catch { + continue; + } + const createdAt = Date.parse(String(entry.timestamp || '')) || Date.now(); + const message = asRecord(entry.message); + const content = message.content; + + if (entry.type === 'user') { + const tools = toolBlocks(content); + const text = textFromContent(content); + if (tools.some((block) => block.type === 'tool_result')) { + if (!current) continue; + for (const block of tools) { + if (block.type !== 'tool_result') continue; + const id = typeof block.tool_use_id === 'string' ? block.tool_use_id : ''; + const existing = current.activities.find( + (activity): activity is Extract => + activity.kind === 'tool' && activity.toolUseId === id, + ); + const output = typeof block.content === 'string' + ? block.content + : textFromContent(block.content); + if (existing) { + existing.status = block.is_error === true ? 'failed' : 'completed'; + existing.outputSummary = summarizeToolOutput(output, projectDir, existing.name); + existing.endedAt = createdAt; + } + } + continue; + } + if (text) openTurn(text, createdAt); + continue; + } + + if (entry.type === 'assistant' && current) { + const text = textFromContent(content); + if (text) { + current.activities = appendNarrationChunk(current.activities, text); + current.assistant = text; + } + for (const block of toolBlocks(content)) { + if (block.type !== 'tool_use' && block.type !== 'mcp_tool_use') continue; + const id = typeof block.id === 'string' ? block.id : ''; + const name = typeof block.name === 'string' ? block.name : 'tool'; + current.activities.push({ + kind: 'tool', + toolUseId: id, + name, + status: 'completed', + inputSummary: summarizeToolInput(name, block.input, projectDir), + startedAt: createdAt, + }); + } + } + } + + return turns; +} + +export function turnsToMessages(turns: PersistedActivityTurn[]) { + return turns.flatMap((turn) => [ + { role: 'user' as const, content: turn.user }, + { role: 'assistant' as const, content: turn.assistant }, + ]); +} diff --git a/agents/_lib/pipelines/resume.ts b/agents/_lib/session/resume.ts similarity index 54% rename from agents/_lib/pipelines/resume.ts rename to agents/_lib/session/resume.ts index ff79798..15f564d 100644 --- a/agents/_lib/pipelines/resume.ts +++ b/agents/_lib/session/resume.ts @@ -1,30 +1,31 @@ +import { existsSync } from 'node:fs'; import { - getActivityHistory, getChatTask, - getHistory, - getLegacyProjectSnapshot, + getConversationRecord, getModelPreference, getProjectState, saveProjectState, -} from '../memory.ts'; -import { isChatTaskActive, iterateLiveChatTaskEvents } from '../chat-tasks.ts'; +} from './store.ts'; +import { hasLiveChatTask, isChatTaskActive, iterateLiveChatTaskEvents, markOrphanedTaskFailed } from './task.ts'; +import { downloadTranscript, readTranscriptText } from './transcript.ts'; +import { projectTranscript, turnsToMessages } from './projection.ts'; import { assertPreviewServerReady, getFileTree, resolvePublicLinks, - restorePersistedProject, rewritePreviewAccessToken, - runSandboxCommand, separateLegacyMakersDeployment, startPreviewServer, } from '../project/index.ts'; -import type { ChatTask, FileTreeItem, PersistedActivity, PersistedActivityTurn, ProjectState } from '../types.ts'; -import { createSSEResponse, sseEvent } from '../shared.ts'; -import { isMakersDeployUrl } from '../../../shared/makers-deploy.ts'; -import { isMakersDeployCommand, isMakersDevCommand } from '../../../shared/tool-phase.ts'; -import { resolveConversationId } from '../utils/request.ts'; -import { ensureProjectDependencies, withTimeout } from './helpers.ts'; -import { loadResumeFileContents } from './resume-files.ts'; +import { restoreProjectWorkspace } from '../project/workspace.ts'; +import { loadResumeFileContents } from '../project/resume-files.ts'; +import type { FileTreeItem, PersistedActivity, PersistedActivityTurn, ProjectState } from '../types.ts'; +import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; +import { mergeSseGenerators } from '../runtime/merge.ts'; +import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; +import { isMakersDeployCommand, isMakersDevCommand } from '../makers/tool-phase.ts'; +import { resolveConversationId } from '../runtime/request.ts'; +import { ensureProjectDependencies, withTimeout } from '../turn/checkpoint.ts'; function isMakersPreviewState(state: ProjectState) { return state.previewKind === 'makers' || isMakersDeployUrl(state.previewUrl); @@ -38,9 +39,7 @@ function toolNameImpliesProject(name: string) { } function activityIsMakersCli(activity: PersistedActivity) { - if (activity.kind !== 'tool' || !activity.name.includes('commands')) { - return false; - } + if (activity.kind !== 'tool' || !activity.name.includes('commands')) return false; const command = activity.inputSummary || ''; return isMakersDevCommand(command) || isMakersDeployCommand(command); } @@ -71,12 +70,8 @@ function projectStateImpliesPreview(state: ProjectState, activityHistory: Persis || activityHistoryImpliesPreview(activityHistory); } -// Hard ceiling for the whole workspace stage so a stuck sandbox call cannot -// leave the browser spinner pending indefinitely after stop/refresh. -// A recycled sandbox may need dependencies plus a cold Makers dev startup. const WORKSPACE_RESUME_BUDGET_MS = 600_000; const SANDBOX_PROBE_MS = 15_000; -const RESTORE_BUDGET_MS = 45_000; const PREVIEW_RESTART_BUDGET_MS = 540_000; function jsonResponse(obj: Record, status = 200) { @@ -89,36 +84,51 @@ function jsonResponse(obj: Record, status = 200) { }); } -// Fast path: store reads only. No sandbox restore / npm install / preview. -// Lets the UI paint chat history immediately after a refresh. +async function loadTranscriptJsonl(context: any, conversationId: string) { + const record = await getConversationRecord(context, conversationId); + if (record.transcriptPath && existsSync(record.transcriptPath)) { + return readTranscriptText(record.transcriptPath); + } + if (record.claudeSessionId) { + const dest = record.transcriptPath + || `/tmp/.claude/sessions/${record.claudeSessionId}.jsonl`; + const restored = await downloadTranscript({ + context, + conversationId, + sessionId: record.claudeSessionId, + destPath: dest, + }); + if (restored) return readTranscriptText(dest); + } + return ''; +} + async function loadProjectResumeHistory(context: any, conversationId: string) { - const [messages, activityHistory, snapshot, chatTask, storedState, model] = await Promise.all([ - getHistory(context, conversationId), - getActivityHistory(context, conversationId), - getLegacyProjectSnapshot(context, conversationId), - getChatTask(context, conversationId), - getProjectState(context, conversationId), + const [record, jsonl, model] = await Promise.all([ + getConversationRecord(context, conversationId), + loadTranscriptJsonl(context, conversationId), getModelPreference(context, conversationId), ]); - const state = separateLegacyMakersDeployment(storedState); - - // Prefer a durable snapshot, but also open the workspace when the turn clearly - // touched the project (stop mid-write may race the snapshot flush; sandbox may - // still hold files that workspace resume can list). - const hasProject = Boolean(snapshot?.base64) - || Boolean(state.created) - || activityHistoryImpliesProject(activityHistory); - const hasPreview = projectStateImpliesPreview(state, activityHistory); - const activeTask = isChatTaskActive(chatTask) + const state = separateLegacyMakersDeployment(record.projectState); + const activityHistory = projectTranscript(jsonl, state.appDir); + const messages = turnsToMessages(activityHistory); + const storedTask = record.chatTask || null; + let activeTask = isChatTaskActive(storedTask) + && hasLiveChatTask(conversationId, storedTask.id) ? { - id: chatTask.id, - message: chatTask.message, - status: chatTask.status, - resetProject: chatTask.resetProject === true, - createdAt: chatTask.createdAt, - startedAt: chatTask.startedAt, + id: storedTask.id, + message: storedTask.message, + status: storedTask.status, + createdAt: storedTask.createdAt, + startedAt: storedTask.startedAt, } : null; + if (isChatTaskActive(storedTask) && !activeTask) { + await markOrphanedTaskFailed(context, conversationId); + } + + const hasProject = Boolean(state.created) || activityHistoryImpliesProject(activityHistory); + const hasPreview = projectStateImpliesPreview(state, activityHistory); return { ok: true as const, @@ -131,31 +141,11 @@ async function loadProjectResumeHistory(context: any, conversationId: string) { hasPreview, needsWorkspace: hasProject, deployment: state.deployment, - // Empty until someone picks a model, which leaves the composer on whatever - // the /models route reports as this deployment's default. model, gatewayNeeded: state.gatewayPromptPending === true, }; } -export async function runProjectResumeHistoryPipeline(context: any): Promise { - const { conversationId } = resolveConversationId(context, { allowQuery: true }); - if (!conversationId) { - return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); - } - return jsonResponse(await loadProjectResumeHistory(context, conversationId)); -} - -async function probeSandboxHasFiles(context: any, state: ProjectState) { - if (!(await context.sandbox.files.exists(state.appDir))) { - return false; - } - const tree = await getFileTree(context, state); - return tree.some((item) => item.type === 'file'); -} - -// Warm sandboxes may still be serving port 8088; otherwise install + restart. -// Makers deploy URLs are durable and must not be rewritten with envdAccessToken. async function republishPreviewOnResume(context: any, state: ProjectState) { if (isMakersPreviewState(state) && state.previewUrl) { return { @@ -170,8 +160,6 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { ? context.sandbox.envdAccessToken : ''; - // Prefer rotating the token on the URL the iframe already used. This keeps - // an open preview stable even when getHost() issues a fresh sandbox host. if (state.previewUrl && accessToken) { const rewritten = rewritePreviewAccessToken(state.previewUrl, accessToken); if (rewritten) { @@ -216,81 +204,51 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { return { url: links.previewUrl, sandboxDebugUrl: links.sandboxDebugUrl, - // The dev server is a new process: whatever an open iframe shows is dead. restarted: true, }; } async function runWorkspaceRestoreBody(context: any, conversationId: string) { - const [storedState, chatTask, activityHistory] = await Promise.all([ + const [storedState, chatTask, jsonl] = await Promise.all([ getProjectState(context, conversationId), getChatTask(context, conversationId), - getActivityHistory(context, conversationId), + loadTranscriptJsonl(context, conversationId), ]); - const state = separateLegacyMakersDeployment(storedState); + const activityHistory = projectTranscript(jsonl, storedState.appDir); + const restored = await restoreProjectWorkspace(context, conversationId, { mode: 'resume' }); + const state = restored.state; const hadPreview = projectStateImpliesPreview(state, activityHistory); - const generationActive = isChatTaskActive(chatTask); + const generationActive = isChatTaskActive(chatTask) && hasLiveChatTask(conversationId, chatTask.id); - let hasFiles = false; - let restoreError: string | undefined; - try { - hasFiles = await withTimeout( - probeSandboxHasFiles(context, state), - SANDBOX_PROBE_MS, - 'sandbox file probe', - ); - } catch (error) { - hasFiles = false; - restoreError = error instanceof Error ? error.message : 'Sandbox probe failed.'; - } - - if (!hasFiles) { - try { - const restored = await withTimeout( - restorePersistedProject(context, conversationId, state, { installDependencies: false }), - RESTORE_BUDGET_MS, - 'snapshot restore', - ); - hasFiles = restored.restored; - if (!restored.restored) restoreError = restored.error; - } catch (error) { - hasFiles = false; - restoreError = error instanceof Error ? error.message : 'Snapshot restore failed.'; - } - } - - if (!hasFiles) { + if (!restored.hasFiles) { return { ok: true as const, stage: 'workspace' as const, conversation_id: conversationId, hasProject: false, - preview: restoreError ? { error: restoreError } : {}, + preview: restored.restoreError ? { error: restored.restoreError } : {}, deployment: state.deployment, files: { root: state.appDir, items: [] as FileTreeItem[] }, }; } - state.created = true; - let items: FileTreeItem[] = []; try { - items = await withTimeout( - getFileTree(context, state), - SANDBOX_PROBE_MS, - 'file tree', - ); + items = await withTimeout(getFileTree(context, state), SANDBOX_PROBE_MS, 'file tree'); } catch { items = []; } const hasFileItems = items.some((item) => item.type === 'file'); - // Only restart preview when a Makers CLI preview previously succeeded for this - // conversation. Do NOT key off package.json — a stopped mid-generation - // project often has a scaffold but is not previewable yet. const shouldRestartPreview = !generationActive && hasFileItems && hadPreview; - let preview: { url?: string; sandboxDebugUrl?: string; error?: string; restarted?: boolean; kind?: 'sandbox' | 'makers' } = {}; + let preview: { + url?: string; + sandboxDebugUrl?: string; + error?: string; + restarted?: boolean; + kind?: 'sandbox' | 'makers'; + } = {}; if (shouldRestartPreview) { try { preview = await withTimeout( @@ -302,8 +260,6 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { } catch (error) { state.previewUrl = undefined; state.sandboxDebugUrl = undefined; - // Keep previewPublished so the next refresh retries instead of sticking to Files. - // Keep the files panel usable; do not surface a hard preview error on resume. console.warn( '[resume:workspace] preview restart failed:', error instanceof Error ? error.message : error, @@ -311,7 +267,6 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { preview = {}; } } else if (!generationActive && !hadPreview) { - // Never-published / interrupted projects stay files-only. state.previewUrl = undefined; state.sandboxDebugUrl = undefined; state.previewKind = undefined; @@ -332,52 +287,17 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { deployment: state.deployment, files: { root: state.appDir, items }, gatewayNeeded: state.gatewayPromptPending === true, - ...(hasFileItems - ? { download: { url: '/download', filename: 'source.zip' } } - : {}), + ...(hasFileItems ? { download: { url: '/download', filename: 'source.zip' } } : {}), }; } -// Slow path: restore snapshot into the sandbox (when needed), then restart the -// live preview when the project was previously publishable. -export async function runProjectResumeWorkspacePipeline(context: any): Promise { - const { conversationId } = resolveConversationId(context, { allowQuery: true }); - if (!conversationId) { - return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); - } - - try { - const payload = await withTimeout( - runWorkspaceRestoreBody(context, conversationId), - WORKSPACE_RESUME_BUDGET_MS, - 'workspace resume', - ); - return jsonResponse(payload); - } catch (error) { - const message = error instanceof Error ? error.message : 'Workspace resume failed.'; - console.warn('[resume:workspace]', message); - return jsonResponse({ - ok: true, - stage: 'workspace', - conversation_id: conversationId, - hasProject: false, - preview: { error: message }, - files: { root: '', items: [] }, - }); - } -} - -// Light path: re-mint the public preview URL (fresh envdAccessToken) without -// restoring the full workspace. Used when the SPA tab stays open but the -// iframe's access_token expires — visibility return / toolbar refresh. -// Falls back to full workspace restore when the sandbox has gone cold. async function runPreviewRefreshBody(context: any, conversationId: string) { - const [storedState, activityHistory] = await Promise.all([ + const [storedState, jsonl] = await Promise.all([ getProjectState(context, conversationId), - getActivityHistory(context, conversationId), + loadTranscriptJsonl(context, conversationId), ]); const state = separateLegacyMakersDeployment(storedState); - const hadPreview = projectStateImpliesPreview(state, activityHistory); + const hadPreview = projectStateImpliesPreview(state, projectTranscript(jsonl, state.appDir)); if (!hadPreview) { return { ok: true as const, @@ -422,8 +342,6 @@ export async function runProjectResumePreviewPipeline(context: any): Promise { - private values: T[] = []; - private waiters: Array<(value: T) => void> = []; - - push(value: T) { - const waiter = this.waiters.shift(); - if (waiter) waiter(value); - else this.values.push(value); - } - - next() { - const value = this.values.shift(); - if (value !== undefined) return Promise.resolve(value); - return new Promise((resolve) => this.waiters.push(resolve)); - } -} - -async function* mergeSseGenerators( - generators: Array>, - signal?: AbortSignal, -): AsyncGenerator { - if (generators.length === 1) { - yield* generators[0]; - return; - } - - const queue = new AsyncValueQueue(); - let remaining = generators.length; - const abortPromise = signal - ? new Promise((resolve) => { - if (signal.aborted) resolve(STREAM_ABORTED); - else signal.addEventListener('abort', () => resolve(STREAM_ABORTED), { once: true }); - }) - : null; - - const pump = async (gen: AsyncGenerator) => { - try { - for await (const chunk of gen) { - if (signal?.aborted) return; - queue.push(chunk); - } - } finally { - remaining -= 1; - if (remaining === 0) queue.push(STREAM_FINISHED); - } - }; - - for (const gen of generators) void pump(gen); - - while (!signal?.aborted) { - const item = await (abortPromise - ? Promise.race([queue.next(), abortPromise]) - : queue.next()); - if (item === STREAM_FINISHED || item === STREAM_ABORTED) return; - yield item; - } -} - async function* iterateWorkspaceResumeEvents( context: any, conversationId: string, @@ -517,9 +374,6 @@ async function* iterateWorkspaceResumeEvents( if (signal?.aborted) return; yield sseEvent({ type: 'resume_workspace', data: workspace }); - // Warm the browser's source cache over this same resume connection. The - // workspace event is sent first so the UI remains progressive; each file - // then becomes immediately browseable without a /file route call. const fileItems = workspace.files?.items || []; if (!signal?.aborted && fileItems.length > 0) { const contents = await loadResumeFileContents(context, conversationId, fileItems); @@ -547,11 +401,6 @@ async function* iterateWorkspaceResumeEvents( } } -/** - * Session entry: history first, then workspace restore and/or a live task on - * the same SSE connection. Creating or opening a conversation always hits - * GET /session; POST /session is only for a new user message. - */ export async function createProjectResumeStreamResponse(context: any): Promise { const { conversationId } = resolveConversationId(context, { allowQuery: true }); if (!conversationId) { @@ -565,7 +414,9 @@ export async function createProjectResumeStreamResponse(context: any): Promise> = []; if (history.needsWorkspace) { generators.push(iterateWorkspaceResumeEvents(context, conversationId, signal)); diff --git a/agents/_lib/session/store.ts b/agents/_lib/session/store.ts new file mode 100644 index 0000000..28f0f4d --- /dev/null +++ b/agents/_lib/session/store.ts @@ -0,0 +1,166 @@ +import { getStore } from '@edgeone/pages-blob'; +import { createProjectState } from '../project/state.ts'; +import type { BlobStoreLike } from '../runtime/context.ts'; +import type { ChatTask, ProjectState } from '../types.ts'; + +const BLOB_STORE_NAME = 'vibe-sessions'; + +export type ConversationRecord = { + claudeSessionId?: string; + transcriptPath?: string; + modelPreference?: string; + projectState: ProjectState; + chatTask?: ChatTask | null; +}; + +function conversationKey(conversationId: string) { + return `conv/${conversationId}/state.json`; +} + +export function transcriptBlobKey(sessionId: string) { + return `sessions/${sessionId}.jsonl`; +} + +export function createMemoryBlobStore(): BlobStoreLike { + const data = new Map(); + return { + async set(key, value) { + if (typeof value === 'string') { + data.set(key, { kind: 'bytes', value }); + return; + } + if (value instanceof ReadableStream) { + const reader = value.getReader(); + const chunks: Uint8Array[] = []; + while (true) { + const { done, value: chunk } = await reader.read(); + if (done) break; + if (chunk) chunks.push(chunk); + } + const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0); + const bytes = new Uint8Array(total); + let offset = 0; + for (const chunk of chunks) { + bytes.set(chunk, offset); + offset += chunk.byteLength; + } + data.set(key, { kind: 'bytes', value: Buffer.from(bytes).toString('utf8') }); + return; + } + if (value instanceof ArrayBuffer) { + data.set(key, { kind: 'bytes', value: Buffer.from(value).toString('utf8') }); + return; + } + data.set(key, { kind: 'bytes', value: String(value) }); + }, + async setJSON(key, value) { + data.set(key, { kind: 'json', value }); + }, + async get(key, options) { + const entry = data.get(key); + if (!entry) return null; + if (options?.type === 'stream') { + const text = entry.kind === 'bytes' ? String(entry.value) : JSON.stringify(entry.value); + return new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(text)); + controller.close(); + }, + }); + } + if (options?.type === 'json' || entry.kind === 'json') { + return entry.kind === 'json' ? entry.value : JSON.parse(String(entry.value)); + } + return entry.kind === 'bytes' ? entry.value : JSON.stringify(entry.value); + }, + async delete(key) { + data.delete(key); + }, + async list(options) { + const prefix = options?.prefix || ''; + return { + blobs: [...data.keys()] + .filter((key) => key.startsWith(prefix)) + .map((key) => ({ key })), + }; + }, + }; +} + +export function getBlobStore(context?: { blobStore?: BlobStoreLike }): BlobStoreLike { + if (context?.blobStore) return context.blobStore; + return getStore({ name: BLOB_STORE_NAME, consistency: 'strong' }) as BlobStoreLike; +} + +export async function getConversationRecord( + context: { blobStore?: BlobStoreLike }, + conversationId: string, +): Promise { + const stored = await getBlobStore(context).get(conversationKey(conversationId), { type: 'json' }); + if (stored && typeof stored === 'object') { + const record = stored as ConversationRecord; + if (record.projectState && typeof record.projectState === 'object') { + return record; + } + } + return { projectState: createProjectState(conversationId) }; +} + +export async function saveConversationRecord( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + record: ConversationRecord, +) { + await getBlobStore(context).setJSON(conversationKey(conversationId), record); +} + +export async function patchConversationRecord( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + patch: Partial, +) { + const current = await getConversationRecord(context, conversationId); + const next: ConversationRecord = { + ...current, + ...patch, + projectState: patch.projectState || current.projectState, + }; + await saveConversationRecord(context, conversationId, next); + return next; +} + +export async function getProjectState(context: { blobStore?: BlobStoreLike }, conversationId: string) { + return (await getConversationRecord(context, conversationId)).projectState; +} + +export async function saveProjectState( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + state: ProjectState, +) { + await patchConversationRecord(context, conversationId, { projectState: state }); +} + +export async function getChatTask(context: { blobStore?: BlobStoreLike }, conversationId: string) { + return (await getConversationRecord(context, conversationId)).chatTask || null; +} + +export async function saveChatTask( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + task: ChatTask | null, +) { + await patchConversationRecord(context, conversationId, { chatTask: task }); +} + +export async function getModelPreference(context: { blobStore?: BlobStoreLike }, conversationId: string) { + return (await getConversationRecord(context, conversationId)).modelPreference?.trim() || ''; +} + +export async function saveModelPreference( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + model: string, +) { + await patchConversationRecord(context, conversationId, { modelPreference: model.trim() }); +} diff --git a/agents/_lib/chat-tasks.ts b/agents/_lib/session/task.ts similarity index 72% rename from agents/_lib/chat-tasks.ts rename to agents/_lib/session/task.ts index df8e3fc..4f963b6 100644 --- a/agents/_lib/chat-tasks.ts +++ b/agents/_lib/session/task.ts @@ -1,22 +1,21 @@ -import { runChatPipeline } from './pipelines/chat.ts'; -import { runDeployPipeline } from './pipelines/deploy.ts'; +import { runChatPipeline } from '../turn/chat.ts'; +import { runDeployPipeline } from '../turn/deploy.ts'; import { - appendTurn, getChatTask, getModelPreference, saveChatTask, saveModelPreference, -} from './memory.ts'; -import type { ChatTask, ChatTaskIntent, ChatTaskStatus, StreamSend } from './types.ts'; -import { createSSEResponse, sseEvent } from './shared.ts'; -import { resolveConversationId } from './utils/request.ts'; -import { resolveGatewayUserTurn } from '../../shared/gateway-secret.ts'; - -type TaskEvent = Record; +} from './store.ts'; +import { interruptLiveQuery } from './live.ts'; +import type { ChatTask, ChatTaskKind, ChatTaskStatus, StreamSend } from '../types.ts'; +import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; +import { resolveConversationId } from '../runtime/request.ts'; +import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; +import type { ChatStreamEvent } from '../../../shared/protocol.ts'; type SequencedEvent = { sequence: number; - event: TaskEvent; + event: ChatStreamEvent; }; type TaskListener = (event: SequencedEvent) => void; @@ -27,26 +26,21 @@ type LiveChatTask = { events: SequencedEvent[]; nextSequence: number; listeners: Set; - // Detached from the SSE HTTP request: a browser refresh/disconnect must not - // stop generation. Only /stop (via abortLiveChatTask) should abort this. abortController: AbortController; runPromise?: Promise; - /** In-memory only: written to `.env` at pipeline start, never persisted. */ gatewayApiKey?: string; gatewaySkip?: boolean; }; const liveTasks = new Map(); -/** Abort in-process chat generation for a conversation (used by /stop). */ export function abortLiveChatTask(conversationId: string) { const trimmed = conversationId.trim(); if (!trimmed) return; + void interruptLiveQuery(trimmed); for (const liveTask of liveTasks.values()) { if (liveTask.conversationId === trimmed && !liveTask.abortController.signal.aborted) { liveTask.abortController.abort(); - // Mark stopped immediately so a refresh mid-unwind does not treat this as - // an in-flight task (resume only reconnects queued/running). if (liveTask.task.status === 'queued' || liveTask.task.status === 'running') { liveTask.task = { ...liveTask.task, @@ -58,7 +52,6 @@ export function abortLiveChatTask(conversationId: string) { } } -/** Persist chatTask as stopped so resume history does not reattach activeTask. */ export async function markChatTaskStopped(context: any, conversationId: string) { const trimmed = conversationId.trim(); if (!trimmed) return; @@ -76,6 +69,20 @@ export async function markChatTaskStopped(context: any, conversationId: string) } } +export async function markOrphanedTaskFailed(context: any, conversationId: string) { + const existing = await getChatTask(context, conversationId); + if (!existing || !isChatTaskActive(existing)) return null; + if (hasLiveTask(conversationId, existing.id)) return existing; + const failed: ChatTask = { + ...existing, + status: 'failed', + finishedAt: Date.now(), + error: 'The previous generation stopped when this instance restarted.', + }; + await saveChatTask(context, conversationId, failed); + return null; +} + function taskKey(conversationId: string, taskId: string) { return `${conversationId}:${taskId}`; } @@ -91,32 +98,30 @@ export function getConversationId(context: any): string { return resolveConversationId(context).conversationId.trim(); } -function isTerminalEvent(event: TaskEvent) { +function isTerminalEvent(event: ChatStreamEvent) { return event.type === 'result' || event.type === 'error'; } -function statusFromResult(event: TaskEvent): ChatTaskStatus { - const data = event.data && typeof event.data === 'object' - ? event.data as Record - : {}; +function statusFromResult(event: ChatStreamEvent): ChatTaskStatus { + const data = event.type === 'result' && event.data ? event.data : {}; if (data.stopped === true) return 'stopped'; return data.ok === false ? 'failed' : 'completed'; } +function hasLiveTask(conversationId: string, taskId: string) { + return liveTasks.has(taskKey(conversationId, taskId)); +} + function getOrCreateLiveTask(conversationId: string, task: ChatTask): LiveChatTask { const key = taskKey(conversationId, task.id); const existing = liveTasks.get(key); - if (existing) { - return existing; - } + if (existing) return existing; const liveTask: LiveChatTask = { conversationId, task, - events: task.finalEvent - ? [{ sequence: 1, event: task.finalEvent }] - : [], - nextSequence: task.finalEvent ? 1 : 0, + events: [], + nextSequence: 0, listeners: new Set(), abortController: new AbortController(), }; @@ -124,26 +129,18 @@ function getOrCreateLiveTask(conversationId: string, task: ChatTask): LiveChatTa return liveTask; } -// file_content events carry whole files. Only the newest version of a path is -// worth replaying to a client that reconnects mid-run, so repeated writes to the -// same file must not pile up in the buffer. -function filePushPath(event: TaskEvent): string { +function filePushPath(event: ChatStreamEvent): string { if (event.type !== 'file_content') return ''; - const data = event.data && typeof event.data === 'object' - ? event.data as Record - : {}; - return typeof data.path === 'string' ? data.path : ''; + return event.data?.path || ''; } -function publish(liveTask: LiveChatTask, event: TaskEvent) { +function publish(liveTask: LiveChatTask, event: ChatStreamEvent) { const supersededPath = filePushPath(event); if (supersededPath) { const previousIndex = liveTask.events.findIndex( (record) => filePushPath(record.event) === supersededPath, ); - if (previousIndex >= 0) { - liveTask.events.splice(previousIndex, 1); - } + if (previousIndex >= 0) liveTask.events.splice(previousIndex, 1); } const record = { @@ -151,26 +148,24 @@ function publish(liveTask: LiveChatTask, event: TaskEvent) { event, }; liveTask.events.push(record); - // The event log is only a short-lived in-process replay buffer. Durable task - // state and the final result live in context.store, so a cold start does not - // turn this Map into the source of truth. if (liveTask.events.length > 2_000) { liveTask.events.splice(0, liveTask.events.length - 2_000); } - for (const listener of liveTask.listeners) { - listener(record); - } + for (const listener of liveTask.listeners) listener(record); } export function isChatTaskActive(task: ChatTask | null | undefined): task is ChatTask { return task?.status === 'queued' || task?.status === 'running'; } +export function hasLiveChatTask(conversationId: string, taskId: string) { + const live = liveTasks.get(taskKey(conversationId, taskId)); + return Boolean(live?.runPromise); +} + type ChatTaskOptions = { - resetProject?: boolean; turnId?: string; - intent?: ChatTaskIntent; - /** Already validated against this deployment's catalogue; '' means no choice. */ + kind?: ChatTaskKind; model?: string; siteDomain?: string; apiKey?: string; @@ -204,26 +199,15 @@ async function createChatTask( }; } - // Appending the user message creates a brand-new conversation, which makes - // updateConversation available for the durable task record below. The stream - // pipeline knows this message is already persisted and will not append it a - // second time. - await appendTurn(context, conversationId, 'user', message); - - // A request without a choice inherits the conversation's, so the model only - // changes when someone changes it. Recording it on the task is what makes a - // reconnect replay the run that actually happened. const requestedModel = (options.model || '').trim(); const model = requestedModel || await getModelPreference(context, conversationId); - const siteDomain = (options.siteDomain || '').trim(); const task: ChatTask = { id: taskId, message, - ...(options.intent === 'deploy' ? { intent: 'deploy' as const } : {}), + ...(options.kind === 'deploy' ? { kind: 'deploy' as const } : { kind: 'prompt' as const }), ...(siteDomain ? { siteDomain } : {}), ...(model ? { model } : {}), - resetProject: options.resetProject === true, status: 'queued', createdAt: Date.now(), }; @@ -235,8 +219,6 @@ async function createChatTask( } function withTaskAbortSignal(context: any, signal: AbortSignal) { - // Keep the same runtime context (sandbox / store / tools) but replace the HTTP - // request signal so SSE client disconnect does not cancel the agent run. const request = context?.request && typeof context.request === 'object' ? { ...context.request, signal } : { signal }; @@ -249,35 +231,28 @@ async function executeLiveTask(context: any, liveTask: LiveChatTask) { status: 'running', startedAt: liveTask.task.startedAt || Date.now(), error: undefined, - finalEvent: undefined, }; liveTask.task = runningTask; - let finalEvent: TaskEvent | undefined; + let finalEvent: ChatStreamEvent | undefined; let error: string | undefined; const send: StreamSend = (event) => { publish(liveTask, event); - if (isTerminalEvent(event)) { - finalEvent = event; - } + if (isTerminalEvent(event)) finalEvent = event; }; const taskContext = withTaskAbortSignal(context, liveTask.abortController.signal); try { await saveChatTask(taskContext, liveTask.conversationId, runningTask); - publish(liveTask, { type: 'status', message: 'Starting the chat task' }); - if (liveTask.task.intent === 'deploy') { + if (liveTask.task.kind === 'deploy') { await runDeployPipeline(taskContext, liveTask.task.message, send, { turnId: liveTask.task.id, - userMessagePersisted: true, siteDomain: liveTask.task.siteDomain, apiKey: liveTask.gatewayApiKey, gatewaySkip: liveTask.gatewaySkip, }); } else { await runChatPipeline(taskContext, liveTask.task.message, send, { - resetProject: liveTask.task.resetProject, turnId: liveTask.task.id, - userMessagePersisted: true, model: liveTask.task.model, siteDomain: liveTask.task.siteDomain, apiKey: liveTask.gatewayApiKey, @@ -304,7 +279,6 @@ async function executeLiveTask(context: any, liveTask: LiveChatTask) { ...current, status: nextStatus, finishedAt: Date.now(), - ...(finalEvent ? { finalEvent } : {}), ...(error ? { error } : {}), }; liveTask.task = nextTask; @@ -358,7 +332,6 @@ class AsyncEventQueue { const ABORTED = Symbol('aborted'); -/** Replay buffered events and subscribe to the in-process task. Used by POST /session and GET /session. */ export async function* iterateLiveChatTaskEvents( context: any, conversationId: string, @@ -367,7 +340,6 @@ export async function* iterateLiveChatTaskEvents( signal?: AbortSignal, ): AsyncGenerator { const liveTask = ensureChatTaskStarted(context, conversationId, task, extras); - yield sseEvent({ type: 'task_started', data: { @@ -385,14 +357,10 @@ export async function* iterateLiveChatTaskEvents( try { for (const record of liveTask.events) { - if (record.sequence <= afterSequence) { - yield sseEvent(record.event); - } + if (record.sequence <= afterSequence) yield sseEvent(record.event); } if (!isChatTaskActive(liveTask.task)) { - // The task may have completed after `afterSequence` was captured but - // before replay finished. Drain that race window before closing. for (const record of liveTask.events) { if (record.sequence > afterSequence) yield sseEvent(record.event); } @@ -430,7 +398,6 @@ function createLiveTaskStreamResponse( }, context?.request?.signal); } -/** Create a durable task and subscribe the same POST request to its event stream. */ export async function createChatTaskAndStreamResponse( context: any, message: string, diff --git a/agents/_lib/session/transcript.ts b/agents/_lib/session/transcript.ts new file mode 100644 index 0000000..f7690ae --- /dev/null +++ b/agents/_lib/session/transcript.ts @@ -0,0 +1,70 @@ +import { createReadStream, createWriteStream } from 'node:fs'; +import { mkdir, stat } from 'node:fs/promises'; +import path from 'node:path'; +import { Readable } from 'node:stream'; +import { pipeline } from 'node:stream/promises'; +import { getBlobStore, patchConversationRecord, transcriptBlobKey } from './store.ts'; + +const TRANSCRIPT_WARN_BYTES = 32 * 1024 * 1024; + +function toNodeReadable(value: unknown): Readable | null { + if (!value) return null; + if (value instanceof Readable) return value; + if (typeof (value as ReadableStream).getReader === 'function') { + return Readable.fromWeb(value as ReadableStream); + } + if (typeof value === 'string') { + return Readable.from([value]); + } + return null; +} + +export async function downloadTranscript(options: { + context: { blobStore?: import('../runtime/context.ts').BlobStoreLike }; + conversationId: string; + sessionId: string; + destPath: string; +}): Promise { + const store = getBlobStore(options.context); + const body = await store.get(transcriptBlobKey(options.sessionId), { type: 'stream' }); + const readable = toNodeReadable(body); + if (!readable) return false; + + await mkdir(path.dirname(options.destPath), { recursive: true }); + await pipeline(readable, createWriteStream(options.destPath)); + return true; +} + +export async function uploadTranscript(options: { + context: { blobStore?: import('../runtime/context.ts').BlobStoreLike }; + conversationId: string; + sessionId: string; + sourcePath: string; +}): Promise { + const info = await stat(options.sourcePath).catch(() => null); + if (!info) { + console.warn('[transcript] local file missing; skip upload', options.sourcePath); + return; + } + if (info.size >= TRANSCRIPT_WARN_BYTES) { + console.warn('[transcript] large session file', { + sessionId: options.sessionId, + bytes: info.size, + }); + } + + const store = getBlobStore(options.context); + await store.set( + transcriptBlobKey(options.sessionId), + Readable.toWeb(createReadStream(options.sourcePath)) as unknown as ReadableStream, + ); + await patchConversationRecord(options.context, options.conversationId, { + claudeSessionId: options.sessionId, + transcriptPath: options.sourcePath, + }); +} + +export async function readTranscriptText(filePath: string): Promise { + const { readFile } = await import('node:fs/promises'); + return readFile(filePath, 'utf8'); +} diff --git a/agents/_lib/tools/assemble.ts b/agents/_lib/tools/assemble.ts new file mode 100644 index 0000000..3c79c75 --- /dev/null +++ b/agents/_lib/tools/assemble.ts @@ -0,0 +1,178 @@ +import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; +import { + MAKERS_SKILL_NAMES, + SANDBOX_MCP_SERVER_NAME, +} from '../constants.ts'; +import { + buildRequestGatewayCredentialsTool, + REQUEST_GATEWAY_CREDENTIALS_TOOL, +} from '../project/gateway.ts'; +import type { AgentContext } from '../runtime/context.ts'; +import type { + ClaudeMcpTool, + CodingAgentResult, + DeploymentInfo, + PreviewKind, + ProjectState, + ScaffoldLog, + StreamSend, +} from '../types.ts'; +import { wrapSandboxTools } from './commands-wrap.ts'; +import { wrapWebSearchTool } from './web-search-wrap.ts'; +import { + WEB_SEARCH_API_KEY_ENV, + isWebSearchConfigured, + isWebSearchToolName, +} from '../../../shared/web-search.ts'; +import { buildLoadMakersSkillTool } from './makers-skills.ts'; +import { + buildProjectScaffoldTool, + buildWriteProjectFileTool, +} from './project-tools.ts'; + +export type LiveTurnCallbacks = { + onScaffoldLog?: (log: ScaffoldLog) => void; + onProjectFilesChanged?: (file?: { path: string; content: string }) => void | Promise; + onPreviewReady?: (preview: { url?: string; sandboxDebugUrl?: string; kind?: PreviewKind }) => void; + onDeploymentStatus?: (deployment: DeploymentInfo) => void; + send?: StreamSend; + abortSignal?: AbortSignal; +}; + +export type LiveSessionHandle = { + conversationId: string; + context: AgentContext; + getState: () => ProjectState; + getCallbacks: () => LiveTurnCallbacks; + flags: { + projectTouched: boolean; + filesWritten: boolean; + previewTouched: boolean; + deploymentTouched: boolean; + wasCreated: boolean; + }; +}; + +function isBrowserSandboxToolName(name: string) { + return name.toLowerCase().includes('browser'); +} + +function isGenericProjectWriteToolName(name: string) { + const normalized = name.toLowerCase(); + return normalized === 'files_write' + || normalized === 'write_files' + || normalized.endsWith('__files_write') + || normalized.endsWith('__write_files'); +} + +function pickEnvValue(context: AgentContext, key: string) { + const value = context?.env?.[key]; + return typeof value === 'string' ? value.trim() : ''; +} + +export function assembleAgentTools(session: LiveSessionHandle) { + const context = session.context; + if (typeof context.tools?.toClaudeMcpServer !== 'function') { + throw new Error('The current Pages Agent Runtime is missing context.tools.toClaudeMcpServer. Please upgrade to a runtime that supports the new pages-agent-toolkit Tools API.'); + } + + const mcpServerName = SANDBOX_MCP_SERVER_NAME; + const edgeoneMcp = context.tools.toClaudeMcpServer(mcpServerName, { alwaysLoad: true }); + const webSearchAvailable = isWebSearchConfigured( + pickEnvValue(context, WEB_SEARCH_API_KEY_ENV), + ); + const offerSandboxTool = (name: string) => + !isBrowserSandboxToolName(name) + && !isGenericProjectWriteToolName(name) + && (webSearchAvailable || !isWebSearchToolName(name)); + + const scaffoldTool = buildProjectScaffoldTool( + context, + session.getState(), + (log) => session.getCallbacks().onScaffoldLog?.(log), + ({ created }) => { + session.flags.projectTouched = true; + session.flags.wasCreated = created; + }, + ); + const writeProjectFileTool = buildWriteProjectFileTool( + context, + session.getState(), + async ({ written, content }) => { + session.flags.projectTouched = true; + session.flags.filesWritten = true; + await session.getCallbacks().onProjectFilesChanged?.({ path: written, content }); + }, + ); + const sandboxTools = wrapWebSearchTool(wrapSandboxTools( + edgeoneMcp.tools.filter((tool: { name: string }) => offerSandboxTool(tool.name)) as ClaudeMcpTool[], + { + context, + get state() { + return session.getState(); + }, + conversationId: session.conversationId, + get send() { + return session.getCallbacks().send; + }, + get signal() { + return session.getCallbacks().abortSignal; + }, + onPreviewReady: (preview) => { + session.flags.previewTouched = true; + if (preview.url) session.getCallbacks().onPreviewReady?.(preview); + }, + onDeploymentStatus: (deployment: DeploymentInfo) => { + session.flags.deploymentTouched = true; + session.getCallbacks().onDeploymentStatus?.(deployment); + }, + } as any, + )); + const mcpTools = [ + ...sandboxTools, + scaffoldTool, + buildLoadMakersSkillTool(), + writeProjectFileTool, + buildRequestGatewayCredentialsTool({ + context, + get state() { + return session.getState(); + }, + conversationId: session.conversationId, + get send() { + return session.getCallbacks().send; + }, + } as any), + ]; + const mcpAllowedTools = [ + ...edgeoneMcp.allowedTools.filter(offerSandboxTool), + `mcp__${mcpServerName}__ensure_project_scaffold`, + `mcp__${mcpServerName}__load_makers_skill`, + `mcp__${mcpServerName}__write_project_file`, + `mcp__${mcpServerName}__${REQUEST_GATEWAY_CREDENTIALS_TOOL}`, + 'Skill', + ]; + + return { + mcpServerName, + webSearchAvailable, + sandboxMcpServer: createSdkMcpServer({ + name: mcpServerName, + tools: mcpTools, + alwaysLoad: true, + }), + mcpAllowedTools, + }; +} + +export function emptyCodingResult(partial: Partial = {}): CodingAgentResult { + return { + success: false, + output: null, + error: null, + projectTouched: false, + filesWritten: false, + wasCreated: false, + ...partial, + }; +} diff --git a/agents/_lib/tools/commands-wrap.ts b/agents/_lib/tools/commands-wrap.ts index 7906e7e..bb696b0 100644 --- a/agents/_lib/tools/commands-wrap.ts +++ b/agents/_lib/tools/commands-wrap.ts @@ -11,44 +11,33 @@ import { PREVIEW_PATH_PREFIX, PREVIEW_SERVER_PORT, } from '../constants.ts'; -import { assertMakersProjectCompatible } from '../project/makers-compat.ts'; +import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; +import { prepareMakersSession } from '../makers/session.ts'; import { previewFailureWarrantsRestart, publishRunningPreview, startPreviewServer, } from '../project/preview.ts'; -import { - ensureMakersPublishProject, - resolveConversationPublishArea, - resolveMakersProjectName, - syncSandboxEnvToMakersProject, -} from '../project/makers-deploy.ts'; -import { pauseForGatewayCredentialsIfNeeded } from '../project/gateway-prompt.ts'; -import { - buildSandboxMakersEnv, - describeMissingMakersRuntimeToken, - prepareSandboxGatewayEnv, - resolveMakersMasterToken, - resolveSandboxMakersToken, -} from '../project/makers-token.ts'; +import { pauseForGatewayCredentialsIfNeeded } from '../project/gateway.ts'; +import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; import { MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, MAKERS_DEV_PORT_DRIFT_EXIT, buildMakersDevBackgroundCommand, buildMakersDevStopScript, parseMakersDevExitCode, -} from '../../../shared/makers-dev.ts'; +} from '../makers/cli-dev.ts'; import { buildMakersDeployCommand, describeMakersDeployment, readMakersDeployOutcome, redactSecret, -} from '../../../shared/makers-deploy.ts'; +} from '../makers/cli-deploy.ts'; import { buildNpmCacheReclaimScript, buildNpmWarmupHandoffScript, buildNpmWarmupWaitScript, -} from '../../../shared/npm-install.ts'; +} from '../makers/npm-install.ts'; import { MAKERS_CLI_UNAVAILABLE_ERROR_CODE, MAKERS_CLI_UNAVAILABLE_MESSAGE, @@ -65,7 +54,7 @@ import { shortenToolName, parseEdgeoneVersionExitCode, withExitCodeEcho, -} from '../utils/tool-phase.ts'; +} from '../makers/tool-phase.ts'; type MakersCommandLifecycle = { context: any; @@ -233,27 +222,9 @@ async function prepareMakersCommand( command: string, lifecycle: MakersCommandLifecycle, ) { - const masterToken = resolveMakersMasterToken(lifecycle.context); - const sandboxToken = await resolveSandboxMakersToken( - lifecycle.state, - masterToken, - ); - const gateway = await prepareSandboxGatewayEnv(lifecycle.context, lifecycle.state); - const env = buildSandboxMakersEnv( - sandboxToken, - lifecycle.state.makersApiRegion, - ); - // Whatever name the model typed is replaced here. It has no way to know - // which project belongs to this conversation, and a name it invents to dodge - // a collision would strand the site somewhere nobody can find again. - const projectName = resolveMakersProjectName(lifecycle.context, lifecycle.state); - const area = resolveConversationPublishArea(lifecycle.state); - await ensureMakersPublishProject( - sandboxToken, - projectName, - area, - lifecycle.state.makersApiRegion, - ); + const makers = await prepareMakersSession(lifecycle.context, lifecycle.state, { + syncEnv: isMakersDeployCommand(command), + }); if (isMakersDevCommand(command)) { return { @@ -263,42 +234,34 @@ async function prepareMakersCommand( makersPort: MAKERS_DEV_PORT, previewPort: PREVIEW_SERVER_PORT, previewPath: PREVIEW_PATH_PREFIX, - projectName, + projectName: makers.projectName, assetPrefixEnvName: PREVIEW_ASSET_PREFIX_ENV, - area, + area: makers.area, }), lifecycle.state.appDir, - env, + makers.env, MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, ), kind: 'dev' as const, - sandboxToken, - gatewayKey: gateway.AI_GATEWAY_API_KEY || '', + sandboxToken: makers.sandboxToken, + gatewayKey: makers.gatewayKey, }; } - await syncSandboxEnvToMakersProject( - lifecycle.context, - lifecycle.state, - masterToken, - projectName, - lifecycle.state.makersApiRegion, - ); - return { args: withCommandOptions( args, - buildMakersDeployCommand(projectName, command, { + buildMakersDeployCommand(makers.projectName, command, { stopDevPort: MAKERS_DEV_PORT, - area, + area: makers.area, }), lifecycle.state.appDir, - env, + makers.env, 600, ), kind: 'deploy' as const, - sandboxToken, - gatewayKey: gateway.AI_GATEWAY_API_KEY || '', + sandboxToken: makers.sandboxToken, + gatewayKey: makers.gatewayKey, }; } @@ -460,6 +423,16 @@ export function wrapSandboxTools( } if (makers.kind === 'dev') { + const missingRuntimeToken = describeMissingMakersRuntimeToken(makersOutput); + if (missingRuntimeToken) { + return { + ...appendText(result, JSON.stringify({ + status: 'error', + error: missingRuntimeToken, + })), + isError: true, + }; + } const devExitCode = parseMakersDevExitCode(makersOutput); if (devExitCode != null && devExitCode !== 0) { if (isEdgeoneCliUnavailable(makersOutput)) { diff --git a/agents/_lib/tools/project-tools.ts b/agents/_lib/tools/project-tools.ts index 9474810..689c1a7 100644 --- a/agents/_lib/tools/project-tools.ts +++ b/agents/_lib/tools/project-tools.ts @@ -1,11 +1,11 @@ import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; import { z } from 'zod'; import { ensureProjectScaffold } from '../project/index.ts'; -import { buildNpmWarmupCommand } from '../../../shared/npm-install.ts'; +import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; import { ensureMakersAgentDeclarations, ensureMakersFrameworkAdapter, -} from '../project/makers-declarations.ts'; +} from '../makers/declarations.ts'; import type { ScaffoldOutcome } from '../project/scaffold.ts'; import type { ClaudeMcpTool, ProjectState, ScaffoldLog } from '../types.ts'; import { getBlockedProjectWriteReason, toAppRelPath } from '../utils/paths.ts'; diff --git a/agents/_lib/tools/web-search-wrap.ts b/agents/_lib/tools/web-search-wrap.ts index d60f5d1..6eea99f 100644 --- a/agents/_lib/tools/web-search-wrap.ts +++ b/agents/_lib/tools/web-search-wrap.ts @@ -1,5 +1,5 @@ import type { ClaudeMcpTool } from '../types.ts'; -import { shortenToolName } from '../../../shared/tool-phase.ts'; +import { shortenToolName } from '../makers/tool-phase.ts'; import { WEB_SEARCH_API_KEY_ENV, WEB_SEARCH_TOOL_NAME, diff --git a/agents/_lib/pipelines/chat.ts b/agents/_lib/turn/chat.ts similarity index 89% rename from agents/_lib/pipelines/chat.ts rename to agents/_lib/turn/chat.ts index 70b9e41..ce961c0 100644 --- a/agents/_lib/pipelines/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -1,6 +1,6 @@ -import { runCodingAgent } from '../agent.ts'; +import { runCodingAgent } from '../session/live.ts'; import { AUTO_FIX_MAX_ATTEMPTS } from '../constants.ts'; -import { getHistory, saveProjectState } from '../memory.ts'; +import { saveProjectState } from '../session/store.ts'; import { getFileTree, runVerification } from '../project/index.ts'; import type { AgentProgressEvent, @@ -12,8 +12,8 @@ import type { } from '../types.ts'; import { buildAutoFixPrompt } from '../utils/build-errors.ts'; import { toAppRelPath } from '../utils/paths.ts'; -import { sanitizeAssistantText } from '../utils/text.ts'; -import { resolveConversationId } from '../utils/request.ts'; +import { sanitizeAssistantText } from '../../../shared/timeline.ts'; +import { resolveConversationId } from '../runtime/request.ts'; import { FILE_PUSH_MAX_BYTES, FILE_PUSH_TURN_BUDGET_BYTES, @@ -31,14 +31,14 @@ import { stripReturnedPreviewLinks, utf8ByteLength, withLiveDeploymentUrl, -} from './helpers.ts'; -import { createTurnLifecycle } from './turn-lifecycle.ts'; -import { prepareProjectWorkspace } from './workspace.ts'; -import { isMakersDeployUrl } from '../../../shared/makers-deploy.ts'; +} from './checkpoint.ts'; +import { createTurnLifecycle } from './lifecycle.ts'; +import { prepareProjectWorkspace } from '../project/workspace.ts'; +import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; import { applyUserGatewayDecision, isRequestGatewayCredentialsTool, -} from '../project/gateway-prompt.ts'; +} from '../project/gateway.ts'; import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; export async function runChatPipeline( @@ -46,9 +46,7 @@ export async function runChatPipeline( message: string, send: StreamSend, options: { - resetProject?: boolean; turnId?: string; - userMessagePersisted?: boolean; /** Validated model for this turn; '' or absent runs the configured default. */ model?: string; siteDomain?: string; @@ -94,16 +92,9 @@ export async function runChatPipeline( await extendExistingSandboxTimeout(context); - send({ - type: 'status', - message: 'Running the agent workflow', - }); - - const shouldResetProject = options.resetProject === true; const state = await prepareProjectWorkspace( context, conversationId, - shouldResetProject, send, ); const siteDomain = String(options.siteDomain || '').trim(); @@ -125,11 +116,6 @@ export async function runChatPipeline( send, ); } - const history = shouldResetProject - ? [] - : await getHistory(context, conversationId, { - excludeLatestUserMessage: options.userMessagePersisted ? message : undefined, - }); const isInitialProjectTurn = !state.created; const hiddenScaffoldToolUseIds = new Set(); const activityTurnId = options.turnId @@ -138,36 +124,20 @@ export async function runChatPipeline( // Mid-turn debounced snapshots + exit-path flush so a recycled sandbox still // has a restorable workspace in project Blob storage. const checkpoint = createProjectCheckpointController(context, conversationId, state, (persistenceError) => { - send({ - type: 'log', - phase: 'agent', - stream: 'stderr', - message: persistenceError, - }); + console.warn('[checkpoint]', persistenceError); }); const turn = createTurnLifecycle({ context, conversationId, message, turnId: activityTurnId, - userMessagePersisted: options.userMessagePersisted === true, state, checkpoint, }); const recordProgress = turn.recordProgress; const finalizeTurn = turn.finalize; - const handleScaffoldLog = (log: ScaffoldLog) => { - if (!isInitialProjectTurn) { - return; - } - send({ - type: 'log', - phase: 'scaffold', - stream: log.stream, - message: log.content, - }); - }; + const handleScaffoldLog = (_log: ScaffoldLog) => {}; const forwardProgress = (event: AgentProgressEvent) => { // Forward structured progress events directly; the frontend renders by type. if (event.type === 'tool_use') { @@ -179,7 +149,7 @@ export async function runChatPipeline( return; } } - if (event.type === 'tool_result' && hiddenScaffoldToolUseIds.has(event.data.tool_use_id)) { + if (event.type === 'tool_result' && hiddenScaffoldToolUseIds.has(event.data.id)) { return; } if (event.type === 'text_segment') { @@ -193,11 +163,11 @@ export async function runChatPipeline( } const narration = { ...event, data: { ...event.data, text } }; recordProgress(narration); - send(narration as unknown as Record); + send(narration); return; } recordProgress(event); - send(event as unknown as Record); + send(event); }; const fileTreePush = createFileTreePushController(context, state, send); // The model already handed us the full text of every file it wrote, so stream it @@ -268,21 +238,21 @@ export async function runChatPipeline( }; // The model handles creative code work; build and service steps remain deterministic. - const modelResult = await runCodingAgent( + const modelResult = await runCodingAgent({ context, conversationId, - message, - history, + userMessage: message, state, - !state.created, - handleScaffoldLog, - forwardProgress, - handleProjectFilesChanged, - handlePreviewReady, - handleDeploymentStatus, + isNewProject: !state.created, + onScaffoldLog: handleScaffoldLog, + onProgress: forwardProgress, + onProjectFilesChanged: handleProjectFilesChanged, + onPreviewReady: handlePreviewReady, + onDeploymentStatus: handleDeploymentStatus, abortSignal, - { model: options.model, send }, - ); + model: options.model, + send, + }); if (modelResult.stopped || abortSignal?.aborted) { const stoppedReply = STOPPED_TURN_REPLY[replyLocale]; @@ -525,11 +495,6 @@ export async function runChatPipeline( if (build.status === 'failed' && modelResult.success) { autoFixAttempts = AUTO_FIX_MAX_ATTEMPTS; autoFixApplied = true; - send({ - type: 'status', - message: `Verification failed. Running auto-fix 1/${AUTO_FIX_MAX_ATTEMPTS}`, - }); - const autoFixPrompt = buildAutoFixPrompt( message, assistantReply, @@ -537,27 +502,21 @@ export async function runChatPipeline( 1, AUTO_FIX_MAX_ATTEMPTS, ); - const autoFixResult = await runCodingAgent( + const autoFixResult = await runCodingAgent({ context, conversationId, - autoFixPrompt, - [ - ...history, - { role: 'user', content: message }, - { role: 'assistant', content: assistantReply }, - ], + userMessage: autoFixPrompt, state, - false, - handleScaffoldLog, - forwardProgress, - handleProjectFilesChanged, - handlePreviewReady, - handleDeploymentStatus, + isNewProject: false, + onScaffoldLog: handleScaffoldLog, + onProgress: forwardProgress, + onProjectFilesChanged: handleProjectFilesChanged, + onPreviewReady: handlePreviewReady, + onDeploymentStatus: handleDeploymentStatus, abortSignal, - // Repairing on a different model than the one that wrote the code would - // make a failed build hard to attribute to either. - { model: options.model, send }, - ); + model: options.model, + send, + }); if (autoFixResult.stopped || abortSignal?.aborted) { const stoppedReply = STOPPED_TURN_REPLY[replyLocale]; await finalizeTurn(stoppedReply, 'stopped', { withSnapshot: true }); diff --git a/agents/_lib/pipelines/helpers.ts b/agents/_lib/turn/checkpoint.ts similarity index 98% rename from agents/_lib/pipelines/helpers.ts rename to agents/_lib/turn/checkpoint.ts index 08ab2a4..9f856ee 100644 --- a/agents/_lib/pipelines/helpers.ts +++ b/agents/_lib/turn/checkpoint.ts @@ -276,12 +276,7 @@ export function createFileTreePushController( return items; } catch (error) { // Non-fatal: the turn pushes the final tree again when it completes. - send({ - type: 'log', - phase: 'agent', - stream: 'stderr', - message: error instanceof Error ? error.message : fallbackMessage, - }); + console.warn('[file-tree]', error instanceof Error ? error.message : fallbackMessage); return []; } }; diff --git a/agents/_lib/pipelines/deploy.ts b/agents/_lib/turn/deploy.ts similarity index 88% rename from agents/_lib/pipelines/deploy.ts rename to agents/_lib/turn/deploy.ts index ac30fad..008d82b 100644 --- a/agents/_lib/pipelines/deploy.ts +++ b/agents/_lib/turn/deploy.ts @@ -1,27 +1,20 @@ import { MAKERS_DEV_PORT } from '../constants.ts'; -import { saveProjectState } from '../memory.ts'; +import { saveProjectState } from '../session/store.ts'; import { getFileTree, runSandboxCommand } from '../project/index.ts'; -import { assertMakersProjectCompatible } from '../project/makers-compat.ts'; +import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; import { - ensureMakersPublishProject, resolveConversationPublishArea, resolveMakersProjectName, - syncSandboxEnvToMakersProject, -} from '../project/makers-deploy.ts'; +} from '../makers/project.ts'; import { startPreviewServer } from '../project/preview.ts'; import { applyUserGatewayDecision, askUserForGatewayCredentials, shouldPauseForGatewayCredentials, -} from '../project/gateway-prompt.ts'; +} from '../project/gateway.ts'; import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; -import { - buildSandboxMakersEnv, - describeMissingMakersRuntimeToken, - prepareSandboxGatewayEnv, - resolveMakersMasterToken, - resolveSandboxMakersToken, -} from '../project/makers-token.ts'; +import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; +import { prepareMakersSession } from '../makers/session.ts'; import type { AgentProgressEvent, DeploymentInfo, @@ -37,21 +30,19 @@ import { parseMakersDeployProgress, readMakersDeployOutcome, redactSecret, -} from '../../../shared/makers-deploy.ts'; -import { resolveConversationId } from '../utils/request.ts'; +} from '../makers/cli-deploy.ts'; +import { resolveConversationId } from '../runtime/request.ts'; import { createProjectCheckpointController, ensureProjectDependencies, extendExistingSandboxTimeout, previewLinkFromState, withLiveDeploymentUrl, -} from './helpers.ts'; -import { createTurnLifecycle } from './turn-lifecycle.ts'; -import { prepareProjectWorkspace } from './workspace.ts'; +} from './checkpoint.ts'; +import { createTurnLifecycle } from './lifecycle.ts'; +import { prepareProjectWorkspace } from '../project/workspace.ts'; /** Used when an API caller asks to publish without wording the request itself. */ -export const DEFAULT_DEPLOY_REQUEST = 'Deploy this project'; - const DEPLOY_TIMEOUT_SECONDS = 600; /** Only has to start the background script, so it never needs the publish budget. */ @@ -182,14 +173,13 @@ export async function runDeployPipeline( send: StreamSend, options: { turnId?: string; - userMessagePersisted?: boolean; siteDomain?: string; apiKey?: string; gatewaySkip?: boolean; } = {}, ) { const { conversationId } = resolveConversationId(context); - const request = message.trim() || DEFAULT_DEPLOY_REQUEST; + const request = message.trim() || 'Deploy this project'; const copy = /[\u3400-\u9fff]/.test(request) ? COPY.zh : COPY.en; if (!conversationId) { @@ -206,9 +196,8 @@ export async function runDeployPipeline( } await extendExistingSandboxTimeout(context); - send({ type: 'status', message: 'Publishing the project to Makers' }); - const state = await prepareProjectWorkspace(context, conversationId, false, send); + const state = await prepareProjectWorkspace(context, conversationId, send); const siteDomain = String(options.siteDomain || '').trim(); if (siteDomain && state.siteDomain !== siteDomain) { state.siteDomain = siteDomain; @@ -220,7 +209,6 @@ export async function runDeployPipeline( message: request, turnId: options.turnId || String(context?.run_id || `${Date.now()}-${Math.random().toString(36).slice(2)}`), - userMessagePersisted: options.userMessagePersisted === true, state, // Publishing writes no project files, so nothing here ever needs a snapshot. checkpoint: createProjectCheckpointController(context, conversationId, state), @@ -273,7 +261,7 @@ export async function runDeployPipeline( const toolUseId = `deploy-${startedAt}`; const emit = (event: AgentProgressEvent) => { turn.recordProgress(event); - send(event as unknown as Record); + send(event); }; const publish = (deployment: DeploymentInfo) => { state.deployment = deployment; @@ -296,7 +284,7 @@ export async function runDeployPipeline( emit({ type: 'tool_result', data: { - tool_use_id: toolUseId, + id: toolUseId, toolName: 'commands', ok: false, preview: '', @@ -326,30 +314,10 @@ export async function runDeployPipeline( try { await assertMakersProjectCompatible(context, state); await ensureProjectDependencies(context, state); - const masterToken = resolveMakersMasterToken(context); - sandboxToken = await resolveSandboxMakersToken( - state, - masterToken, - ); - await ensureMakersPublishProject( - sandboxToken, - resolveMakersProjectName(context, state), - resolveConversationPublishArea(state), - state.makersApiRegion, - ); - await syncSandboxEnvToMakersProject( - context, - state, - masterToken, - resolveMakersProjectName(context, state), - state.makersApiRegion, - ); - const gateway = await prepareSandboxGatewayEnv(context, state); - sandboxEnv = buildSandboxMakersEnv( - sandboxToken, - state.makersApiRegion, - ); - gatewayKey = gateway.AI_GATEWAY_API_KEY || ''; + const makers = await prepareMakersSession(context, state, { syncEnv: true }); + sandboxToken = makers.sandboxToken; + sandboxEnv = makers.env; + gatewayKey = makers.gatewayKey; } catch (error) { await fail(error instanceof Error ? error.message : String(error)); return; @@ -441,7 +409,7 @@ export async function runDeployPipeline( emit({ type: 'tool_result', data: { - tool_use_id: toolUseId, + id: toolUseId, toolName: 'commands', ok: true, preview: '', diff --git a/agents/_lib/turn/lifecycle.ts b/agents/_lib/turn/lifecycle.ts new file mode 100644 index 0000000..a2c7d8f --- /dev/null +++ b/agents/_lib/turn/lifecycle.ts @@ -0,0 +1,57 @@ +import { saveProjectState } from '../session/store.ts'; +import type { AgentProgressEvent, ProjectState } from '../types.ts'; +import type { ProjectCheckpointController } from './checkpoint.ts'; +import { applyStreamEvent } from '../../../shared/timeline.ts'; +import type { PersistedActivityTurn } from '../../../shared/protocol.ts'; + +type TurnStatus = 'completed' | 'failed' | 'stopped'; + +type TurnLifecycleOptions = { + context: any; + conversationId: string; + message: string; + turnId: string; + state: ProjectState; + checkpoint: ProjectCheckpointController; +}; + +/** Owns in-memory progress folding and the durable commit order for one turn. */ +export function createTurnLifecycle(options: TurnLifecycleOptions) { + let turn: PersistedActivityTurn = { + id: options.turnId, + user: options.message, + assistant: '', + status: 'completed', + createdAt: Date.now(), + activities: [], + }; + + const recordProgress = (event: AgentProgressEvent) => { + turn = applyStreamEvent(turn, event); + }; + + const finalize = async ( + assistant: string, + status: TurnStatus, + finalizeOptions?: { withSnapshot?: boolean; withState?: boolean }, + ) => { + turn = { ...turn, assistant, status }; + if (status === 'stopped') { + turn = { + ...turn, + activities: turn.activities.map((activity) => ( + activity.kind === 'tool' && activity.status === 'running' + ? { ...activity, status: 'stopped' as const, endedAt: Date.now() } + : activity + )), + }; + } + + if (finalizeOptions?.withSnapshot === true) await options.checkpoint.flush(); + if (finalizeOptions?.withState !== false) { + await saveProjectState(options.context, options.conversationId, options.state); + } + }; + + return { recordProgress, finalize }; +} diff --git a/agents/_lib/types.ts b/agents/_lib/types.ts index c55b877..5966aef 100644 --- a/agents/_lib/types.ts +++ b/agents/_lib/types.ts @@ -2,6 +2,7 @@ import type { SdkMcpToolDefinition } from '@anthropic-ai/claude-agent-sdk'; import type { ActivityStatus, BuildStatus, + ChatStreamEvent, DeploymentInfo, PreviewKind, } from '../../shared/protocol.ts'; @@ -37,22 +38,6 @@ export type ProjectState = { gatewaySkipped?: boolean; }; -// A base64 archive of the whole project, persisted outside the volatile sandbox so -// the code survives sandbox recycling (see agents/_lib/memory.ts snapshot helpers). The -// fields mirror createProjectArchive's success result plus a write timestamp. -export type LegacyProjectSnapshot = { - base64: string; - filename: string; - contentType: string; - size: number; - updatedAt: number; -}; - -export type ConversationMessage = { - role: 'user' | 'assistant'; - content: string; -}; - export type ChatTaskStatus = 'queued' | 'running' | 'completed' | 'failed' | 'stopped'; /** @@ -60,23 +45,20 @@ export type ChatTaskStatus = 'queued' | 'running' | 'completed' | 'failed' | 'st * command, so 'deploy' skips the model entirely — but it still occupies the * same slot, so it cannot race a generation over the same sandbox. */ -export type ChatTaskIntent = 'chat' | 'deploy'; +export type ChatTaskKind = 'prompt' | 'deploy'; export type ChatTask = { id: string; message: string; - /** Absent on tasks persisted before deploy became a task of its own. */ - intent?: ChatTaskIntent; + kind?: ChatTaskKind; /** Public site root from the browser; picks overseas vs global acceleration. */ siteDomain?: string; /** Model this turn runs on. Absent means the deployment's configured default. */ model?: string; - resetProject: boolean; status: ChatTaskStatus; createdAt: number; startedAt?: number; finishedAt?: number; - finalEvent?: Record; error?: string; }; @@ -105,7 +87,7 @@ export type PersistedActivityTurn = { activities: PersistedActivity[]; }; -export type StreamSend = (event: Record) => void; +export type StreamSend = (event: ChatStreamEvent) => void; export type ScaffoldLog = { stream: 'status' | 'stdout' | 'stderr'; @@ -140,42 +122,9 @@ export type BuildResult = { fatal?: boolean; }; -// Progress events streamed to the frontend. tool_use is the model's tool request, -// and tool_result is the tool response. The assistant message renders these live. -export type AgentProgressEvent = - | { - type: 'tool_use'; - data: { - id: string; - name: string; - command?: string; - phaseHint?: 'scaffold' | 'code' | 'install' | 'preview' | 'link'; - fileCount?: number; - inputSummary?: string; - /** Output from a call still in flight; see the note in protocol.ts. */ - outputSummary?: string; - startedAt?: number; - }; - } - | { - type: 'tool_result'; - data: { - tool_use_id: string; - toolName?: string; - command?: string; - ok: boolean; - preview: string; - outputSummary?: string; - status?: ActivityStatus; - endedAt?: number; - }; - } - | { - type: 'text_segment'; - data: { - uuid: string; - text: string; - }; - }; +export type AgentProgressEvent = Extract< + ChatStreamEvent, + { type: 'tool_use' | 'tool_result' | 'text_segment' } +>; export type ClaudeMcpTool = SdkMcpToolDefinition; diff --git a/agents/_lib/utils/activity.ts b/agents/_lib/utils/activity.ts deleted file mode 100644 index f8fff20..0000000 --- a/agents/_lib/utils/activity.ts +++ /dev/null @@ -1,142 +0,0 @@ -import type { PersistedActivityTurn } from '../types.ts'; - -const SUMMARY_LIMIT = 2_000; -const SENSITIVE_KEY = /(authorization|cookie|password|passwd|secret|token|api[_-]?key|private[_-]?key|credential)/i; - -function truncate(value: string, limit = SUMMARY_LIMIT) { - const normalized = value.replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, '').trim(); - return normalized.length > limit ? `${normalized.slice(0, limit)}\n... truncated` : normalized; -} - -function redactInlineSecrets(value: string) { - return value - .replace(/(authorization\s*:\s*)(?:bearer\s+)?[^"'\s]+(?:\s+[^"'\s]+)?/gi, '$1[REDACTED]') - .replace(/((?:authorization|cookie|password|passwd|secret|token|api[_-]?key|private[_-]?key)\s*[:=]\s*)([^\s,;]+)/gi, '$1[REDACTED]') - .replace(/(bearer\s+)[A-Za-z0-9._~+\/-]+/gi, '$1[REDACTED]'); -} - -function safeValue(value: unknown, projectDir: string, depth = 0): unknown { - if (depth > 4) return '[nested value omitted]'; - if (typeof value === 'string') { - const withoutProjectPath = projectDir ? value.split(projectDir).join('') : value; - return truncate(redactInlineSecrets(withoutProjectPath), 600); - } - if (typeof value === 'number' || typeof value === 'boolean' || value == null) return value; - if (Array.isArray(value)) return value.slice(0, 20).map((item) => safeValue(item, projectDir, depth + 1)); - if (typeof value === 'object') { - return Object.fromEntries( - Object.entries(value as Record) - .slice(0, 30) - .map(([key, child]) => [ - key, - SENSITIVE_KEY.test(key) ? '[REDACTED]' : safeValue(child, projectDir, depth + 1), - ]), - ); - } - return String(value); -} - -function summarizeFileWrites(input: Record) { - const files = Array.isArray(input.files) ? input.files : []; - if (files.length === 0) return ''; - return files.slice(0, 30).map((file) => { - const record = file && typeof file === 'object' ? file as Record : {}; - const path = typeof record.path === 'string' ? record.path : ''; - const length = typeof record.content === 'string' ? record.content.length : 0; - return `${path} (${length.toLocaleString('en-US')} chars)`; - }).join('\n'); -} - -export function summarizeToolInput(name: string, input: unknown, projectDir = '') { - const record = input && typeof input === 'object' ? input as Record : {}; - const shortName = name.replace(/^mcp__[^_]+__/, ''); - - if (shortName === 'Skill' || shortName === 'load_makers_skill') { - const skill = typeof record.skill === 'string' ? record.skill : ''; - const ref = typeof record.ref === 'string' ? record.ref.trim() : ''; - // A deeper document is a second load of a reference already on screen, so - // without the ref the two rows are indistinguishable and the timeline looks - // like it is repeating itself. Kept plain when there is no ref, which is - // every row persisted before this and every load of an overview. - return truncate(ref ? JSON.stringify({ skill, ref }) : skill, 200); - } - if (shortName === 'write_project_files') { - return truncate(summarizeFileWrites(record) || 'Project files'); - } - if (shortName === 'write_project_file' || shortName === 'files_write' || shortName === 'write_files') { - if (typeof record.path !== 'string' && typeof record.content !== 'string') return ''; - const path = typeof record.path === 'string' ? record.path : ''; - const length = typeof record.content === 'string' ? record.content.length : 0; - return `${path} (${length.toLocaleString('en-US')} chars)`; - } - if (shortName === 'commands') { - const command = typeof record.command === 'string' - ? record.command - : typeof record.cmd === 'string' - ? record.cmd - : ''; - return truncate(redactInlineSecrets(projectDir ? command.split(projectDir).join('') : command)); - } - if ( - shortName === 'files_make_dir' - || shortName === 'files_remove' - || shortName === 'files_exists' - || shortName === 'files_read' - || shortName === 'files_list' - ) { - const path = typeof record.path === 'string' - ? record.path - : typeof record.file_path === 'string' - ? record.file_path - : ''; - return path ? truncate(projectDir ? path.split(projectDir).join('') : path) : ''; - } - - return truncate(JSON.stringify(safeValue(record, projectDir), null, 2)); -} - -export function summarizeToolOutput(value: string, projectDir = '', name = '') { - // The SDK answers a successful Skill call with "Launching skill: ", - // which only repeats the row header. Keep real failures. - if (name.replace(/^mcp__[^_]+__/, '') === 'Skill' && /^launching skill:/i.test(value.trim())) { - return ''; - } - if ( - name.replace(/^mcp__[^_]+__/, '') === 'load_makers_skill' - && /^---\s*\nname:/i.test(value.trim()) - ) { - return ''; - } - const withoutProjectPath = projectDir ? value.split(projectDir).join('') : value; - return truncate(redactInlineSecrets(withoutProjectPath)); -} - -export function appendTrimmedActivityTurn( - current: PersistedActivityTurn[], - turn: PersistedActivityTurn, - turnLimit = 25, - itemLimit = 50, -) { - const nextTurn = { ...turn, activities: turn.activities.slice(-itemLimit) }; - return [...current.filter((item) => item.id !== turn.id), nextTurn].slice(-turnLimit); -} - -export function dedupeActivityTurns(turns: PersistedActivityTurn[]) { - const result: PersistedActivityTurn[] = []; - for (const turn of turns) { - const previous = result.at(-1); - const isRetryDuplicate = previous - && previous.user === turn.user - && previous.assistant === turn.assistant - && previous.status === turn.status - && Math.abs(previous.createdAt - turn.createdAt) < 30_000; - if (!isRetryDuplicate) { - result.push(turn); - continue; - } - if (turn.activities.length >= previous.activities.length) { - result[result.length - 1] = turn; - } - } - return result; -} diff --git a/agents/_lib/utils/narration.ts b/agents/_lib/utils/narration.ts deleted file mode 100644 index d741d9a..0000000 --- a/agents/_lib/utils/narration.ts +++ /dev/null @@ -1,119 +0,0 @@ -export function sanitizeNarrationText(input: string) { - if (!input) return ''; - return input - .replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, '') - .replace(/\[20[01]~/g, '') - .replace(/\x1b\][^\x07]*\x07/g, '') - .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, '') - .replace(/]*>/gi, '') - .replace(/<\/think>/gi, '') - .replace(/\n{4,}/g, '\n\n\n'); -} - -/** - * Shortest accumulated block that a repeated delta can be measured against. - * Below it, "the delta repeats everything so far" is a coincidence between two - * short fragments rather than evidence of a re-send. - */ -const MIN_RESEND_PREFIX = 8; - -export type NarrationEmitState = { - /** Text already streamed for the current assistant text block. */ - currentTextBlock: string; - /** All narration emitted for the whole agent turn. */ - emittedNarration: string; -}; - -/** - * Resolve the next narration chunk to emit. - * - * Stream deltas are incremental; complete assistant snapshots may repeat the - * already-streamed prefix. Only the missing suffix should be forwarded, and - * dedupe is scoped to the current text block so earlier phrases like - * "简洁好用的 Todolist" cannot swallow a later "用的 Todolist". - */ -export function resolveNarrationEmit( - state: NarrationEmitState, - rawText: string, - complete = false, -): { state: NarrationEmitState; text: string | null } { - const text = sanitizeNarrationText(rawText); - if (!text) { - return { state, text: null }; - } - - if (complete) { - const trimmed = text.trim(); - if (!trimmed) { - return { state, text: null }; - } - - const streamed = state.currentTextBlock; - const streamedTrimmed = streamed.trimEnd(); - - if (streamed.includes(trimmed) || streamedTrimmed === trimmed) { - return { state, text: null }; - } - - let nextChunk = trimmed; - if (streamed && trimmed.startsWith(streamed)) { - nextChunk = trimmed.slice(streamed.length); - } else if (streamedTrimmed && trimmed.startsWith(streamedTrimmed)) { - nextChunk = trimmed.slice(streamedTrimmed.length); - } else if (streamed) { - // Stream and snapshot diverged — keep the streamed text as source of truth. - return { state, text: null }; - } else { - // Empty block window (e.g. after a tool call cleared it). Skip only when this - // exact snapshot was already emitted as the trailing narration — use endsWith - // so earlier phrases like "简洁好用的 …" cannot swallow "用的 …". - const emittedTrimmed = state.emittedNarration.trimEnd(); - if (emittedTrimmed.endsWith(trimmed)) { - return { state, text: null }; - } - nextChunk = trimmed; - } - - nextChunk = sanitizeNarrationText(nextChunk); - if (!nextChunk.trim()) { - return { state, text: null }; - } - - const currentTextBlock = sanitizeNarrationText(`${streamed}${nextChunk}`); - const emittedNarration = sanitizeNarrationText(`${state.emittedNarration}${nextChunk}`); - return { - state: { currentTextBlock, emittedNarration }, - text: nextChunk, - }; - } - - // Incremental delta. Some providers re-send the whole block in place of the - // new fragment, which is only safely recognisable as an exact prefix of a - // block long enough that a genuine fragment could not repeat it by accident. - // Nothing here may compare against the tail: deltas are token-sized, so a - // chunk like "a" landing after an "a" is ordinary text, and dropping it - // quietly corrupts whatever it belonged to — a URL loses a character and - // still looks like a URL. - if ( - state.currentTextBlock.length >= MIN_RESEND_PREFIX - && text.startsWith(state.currentTextBlock) - ) { - const remainder = text.slice(state.currentTextBlock.length); - if (!remainder) { - return { state, text: null }; - } - const currentTextBlock = sanitizeNarrationText(`${state.currentTextBlock}${remainder}`); - const emittedNarration = sanitizeNarrationText(`${state.emittedNarration}${remainder}`); - return { - state: { currentTextBlock, emittedNarration }, - text: remainder, - }; - } - - const currentTextBlock = sanitizeNarrationText(`${state.currentTextBlock}${text}`); - const emittedNarration = sanitizeNarrationText(`${state.emittedNarration}${text}`); - return { - state: { currentTextBlock, emittedNarration }, - text, - }; -} diff --git a/shared/shell.ts b/agents/_lib/utils/shell.ts similarity index 100% rename from shared/shell.ts rename to agents/_lib/utils/shell.ts diff --git a/agents/_lib/utils/text.ts b/agents/_lib/utils/text.ts index 83cf520..88de2b0 100644 --- a/agents/_lib/utils/text.ts +++ b/agents/_lib/utils/text.ts @@ -1,4 +1,4 @@ -export { sanitizeAssistantText } from '../../../shared/sanitize-assistant-text.ts'; +export { sanitizeAssistantText } from '../../../shared/timeline.ts'; export function stringifyToolResult(result: unknown) { if (typeof result === 'string') { diff --git a/agents/_lib/utils/tool-phase.ts b/agents/_lib/utils/tool-phase.ts deleted file mode 100644 index b974d08..0000000 --- a/agents/_lib/utils/tool-phase.ts +++ /dev/null @@ -1,20 +0,0 @@ -export { - MAKERS_CLI_UNAVAILABLE_ERROR_CODE, - MAKERS_CLI_UNAVAILABLE_MESSAGE, - buildEdgeoneVersionCheckCommand, - forbiddenSandboxCommandReason, - isEdgeoneVersionCommand, - isEdgeoneCliUnavailable, - isBareInstallCommand, - isInstallCommand, - isScaffolderCommand, - isMakersDeployCommand, - isMakersDevCommand, - isPreviewCommand, - isVerificationCommand, - parseEchoedExitCode, - parseEdgeoneVersionExitCode, - shortenToolName, - stripEchoedExit, - withExitCodeEcho, -} from '../../../shared/tool-phase.ts'; diff --git a/agents/deploy.ts b/agents/deploy.ts new file mode 100644 index 0000000..3127a32 --- /dev/null +++ b/agents/deploy.ts @@ -0,0 +1,24 @@ +import { createChatTaskAndStreamResponse } from './_lib/session/task.ts'; + +/** Publish the current project. Deterministic — does not call the model. */ +export async function onRequestPost(context: any) { + const body = context?.request?.body || {}; + try { + const apiKey = String(body?.apiKey || '').trim(); + return await createChatTaskAndStreamResponse(context, String(body?.message || '').trim(), { + kind: 'deploy', + turnId: String(body?.turnId || '').trim() || undefined, + siteDomain: String(body?.siteDomain || '').trim() || undefined, + ...(apiKey ? { apiKey } : {}), + ...(body?.gatewaySkip === true ? { gatewaySkip: true } : {}), + }); + } catch (error) { + return new Response(JSON.stringify({ + ok: false, + error: error instanceof Error ? error.message : 'Failed to start the deploy task.', + }), { + status: 500, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } +} diff --git a/agents/download.ts b/agents/download.ts index d2ff360..c728b78 100644 --- a/agents/download.ts +++ b/agents/download.ts @@ -1,4 +1,4 @@ -import { runProjectDownloadPipeline } from './_lib/pipelines/index.ts'; +import { runProjectDownloadPipeline } from './_lib/project/download.ts'; export async function onRequest(context: any) { return runProjectDownloadPipeline(context); diff --git a/agents/file.ts b/agents/file.ts index 0a2731e..8f89eb9 100644 --- a/agents/file.ts +++ b/agents/file.ts @@ -1,4 +1,4 @@ -import { runFileReadPipeline } from './_lib/pipelines/index.ts'; +import { runFileReadPipeline } from './_lib/project/read.ts'; export async function onRequest(context: any) { return runFileReadPipeline(context); diff --git a/agents/preview.ts b/agents/preview.ts index def209e..dda7540 100644 --- a/agents/preview.ts +++ b/agents/preview.ts @@ -1,4 +1,4 @@ -import { runProjectResumePreviewPipeline } from './_lib/pipelines/index.ts'; +import { runProjectResumePreviewPipeline } from './_lib/session/resume.ts'; /** Re-mint the public preview URL without restoring the full workspace. */ export async function onRequestPost(context: any) { diff --git a/agents/prompt.ts b/agents/prompt.ts new file mode 100644 index 0000000..99e5796 --- /dev/null +++ b/agents/prompt.ts @@ -0,0 +1,37 @@ +import { createChatTaskAndStreamResponse } from './_lib/session/task.ts'; +import { resolveRequestedModel } from './_lib/models.ts'; + +/** Submit a user message. Generation streams back as SSE. */ +export async function onRequestPost(context: any) { + const body = context?.request?.body || {}; + const message = String(body?.message || '').trim(); + if (!message) { + return new Response(JSON.stringify({ + ok: false, + error: 'Please describe the page or feature you want to build first.', + }), { + status: 400, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } + + try { + const apiKey = String(body?.apiKey || '').trim(); + return await createChatTaskAndStreamResponse(context, message, { + kind: 'prompt', + turnId: String(body?.turnId || '').trim() || undefined, + model: resolveRequestedModel(context, body?.model), + siteDomain: String(body?.siteDomain || '').trim() || undefined, + ...(apiKey ? { apiKey } : {}), + ...(body?.gatewaySkip === true ? { gatewaySkip: true } : {}), + }); + } catch (error) { + return new Response(JSON.stringify({ + ok: false, + error: error instanceof Error ? error.message : 'Failed to start the chat task.', + }), { + status: 500, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } +} diff --git a/agents/session-model.ts b/agents/session-model.ts new file mode 100644 index 0000000..e6965e6 --- /dev/null +++ b/agents/session-model.ts @@ -0,0 +1,41 @@ +import { saveModelPreference } from './_lib/session/store.ts'; +import { setLiveQueryModel } from './_lib/session/live.ts'; +import { resolveRequestedModel } from './_lib/models.ts'; +import { resolveConversationId } from './_lib/runtime/request.ts'; + +/** Persist the conversation's model preference, and hot-swap a live Query when one exists. */ +export async function onRequestPost(context: any) { + const { conversationId } = resolveConversationId(context); + if (!conversationId) { + return new Response(JSON.stringify({ ok: false, error: 'missing conversation_id' }), { + status: 400, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } + + const body = context?.request?.body || {}; + const model = resolveRequestedModel(context, body?.model); + try { + await saveModelPreference(context, conversationId, model); + let applied = false; + if (model) { + applied = await setLiveQueryModel(conversationId, model); + } + return new Response(JSON.stringify({ + ok: true, + conversation_id: conversationId, + model, + applied, + }), { + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } catch (error) { + return new Response(JSON.stringify({ + ok: false, + error: error instanceof Error ? error.message : 'Failed to update the session model.', + }), { + status: 500, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); + } +} diff --git a/agents/session.ts b/agents/session.ts index 3f78eff..c0a580a 100644 --- a/agents/session.ts +++ b/agents/session.ts @@ -1,50 +1,6 @@ -import { createChatTaskAndStreamResponse } from './_lib/chat-tasks.ts'; -import { createProjectResumeStreamResponse, DEFAULT_DEPLOY_REQUEST } from './_lib/pipelines/index.ts'; -import { resolveRequestedModel } from './_lib/models.ts'; +import { createProjectResumeStreamResponse } from './_lib/session/resume.ts'; /** Session entry: history, workspace, and an in-flight task's SSE on one GET. */ export async function onRequestGet(context: any) { return createProjectResumeStreamResponse(context); } - -/** Submit a turn. Only called when the user sends text (or publish). */ -export async function onRequestPost(context: any) { - const body = context?.request?.body || {}; - // Publishing occupies the same task slot as a generation. Reconnect after - // refresh goes through GET /session, not this method. - const intent = body?.intent === 'deploy' ? 'deploy' as const : 'chat' as const; - const message = String(body?.message || '').trim() - || (intent === 'deploy' ? DEFAULT_DEPLOY_REQUEST : ''); - if (!message) { - return new Response(JSON.stringify({ - ok: false, - error: 'Please describe the page or feature you want to build first.', - }), { - status: 400, - headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, - }); - } - - try { - const apiKey = String(body?.apiKey || '').trim(); - return await createChatTaskAndStreamResponse(context, message, { - intent, - resetProject: body?.resetProject === true, - turnId: String(body?.turnId || '').trim() || undefined, - // Anything this deployment does not offer resolves to '', so a client - // cannot name an arbitrary model and have it billed through the gateway. - model: resolveRequestedModel(context, body?.model), - siteDomain: String(body?.siteDomain || '').trim() || undefined, - ...(apiKey ? { apiKey } : {}), - ...(body?.gatewaySkip === true ? { gatewaySkip: true } : {}), - }); - } catch (error) { - return new Response(JSON.stringify({ - ok: false, - error: error instanceof Error ? error.message : 'Failed to start the chat task.', - }), { - status: 500, - headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, - }); - } -} diff --git a/agents/stop.ts b/agents/stop.ts index 129c0bc..feb55ce 100644 --- a/agents/stop.ts +++ b/agents/stop.ts @@ -1,8 +1,6 @@ -import { abortLiveChatTask, markChatTaskStopped } from './_lib/chat-tasks.ts'; -import { getProjectState, saveActivityTurn, saveProjectState } from './_lib/memory.ts'; -import { persistProjectSnapshot } from './_lib/pipelines/helpers.ts'; -import type { PersistedActivity } from './_lib/types.ts'; -import { replyLocaleFor, STOPPED_TURN_REPLY } from '../shared/user-facing-reply.ts'; +import { abortLiveChatTask, markChatTaskStopped } from './_lib/session/task.ts'; +import { getProjectState, saveProjectState } from './_lib/session/store.ts'; +import { persistProjectSnapshot } from './_lib/turn/checkpoint.ts'; export async function onRequest(context: any) { const conversationId = String(context?.request?.body?.conversation_id || '').trim(); @@ -15,18 +13,9 @@ export async function onRequest(context: any) { try { const discardProject = context?.request?.body?.discardProject === true; - // Stop the detached in-process run first (SSE disconnect no longer aborts it). abortLiveChatTask(conversationId); - // Persist stopped before unwind finishes so refresh/resume does not see an - // activeTask and duplicate the activityHistory user/assistant rows. await markChatTaskStopped(context, conversationId); - // Cancel the platform run before touching the sandbox. A long-running install - // or build can otherwise make the snapshot command queue behind the very work - // this endpoint is trying to stop. const result = await context.utils?.abortActiveRun?.(conversationId); - // "Stop and start new" intentionally abandons this conversation, so avoid a - // full zip -> base64 -> store round trip that the new workspace will never use. - // A normal Stop still snapshots immediately for same-conversation resume. let persisted: boolean | undefined; if (!discardProject) { try { @@ -41,28 +30,6 @@ export async function onRequest(context: any) { console.warn('[stop] project snapshot failed', error); } } - const rawTurn = context?.request?.body?.turn; - if (rawTurn && typeof rawTurn === 'object') { - const turn = rawTurn as Record; - const user = String(turn.user || '').slice(0, 20_000); - const assistant = STOPPED_TURN_REPLY[replyLocaleFor(user)]; - const activities = (Array.isArray(turn.activities) ? turn.activities : []) - .slice(-50) - .filter((activity): activity is PersistedActivity => Boolean(activity) && typeof activity === 'object') - .map((activity) => activity.kind === 'tool' && activity.status === 'running' - ? { ...activity, status: 'stopped' as const, endedAt: Date.now() } - : activity); - if (user) { - await saveActivityTurn(context, conversationId, { - id: String(turn.id || context.run_id || Date.now()), - user, - assistant, - status: 'stopped', - createdAt: Number(turn.createdAt) || Date.now(), - activities, - }); - } - } return new Response(JSON.stringify({ ok: true, conversation_id: conversationId, @@ -77,7 +44,7 @@ export async function onRequest(context: any) { error: error instanceof Error ? error.message : 'Failed to stop the active run.', }), { status: 500, - headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + headers: { 'content-type': 'application/json; charset=utf-8' }, }); } } diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts new file mode 100644 index 0000000..18b07b9 --- /dev/null +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -0,0 +1,598 @@ +'use client'; + +import { useEffect, useRef, useState, type MutableRefObject } from 'react'; +import { + appendNarrationChunk, + dropTrailingSummaryEcho, + sanitizeThinkingContent, +} from '../../../../shared/timeline'; +import { extractApiKeyFromUserText } from '../../../../shared/gateway-secret'; +import { STOPPED_TURN_REPLY } from '../../../../shared/user-facing-reply'; +import type { Locale } from '@/app/i18n'; +import { + cacheConversationId, + createConversationId, + createMessageId, + extractProjectName, + getOrCreateCachedConversationId, + markLastTurnStopped, +} from '@/app/lib/conversation'; +import type { FileContentCache } from '@/app/hooks/use-file-content-cache'; +import type { + AssistantActivity, + AssistantStatus, + ChatMessage, + ChatResponse, + ChatStreamEvent, +} from '@/app/types/workspace'; +import { consumeEventStream } from '../sse'; +import { + openSessionStream, + startDeployTurn, + startPromptTurn, + stopChatTask, +} from '../workspace-api'; +import type { PreviewSurfaceApi } from './use-preview-surface'; +import type { WorkspaceStateApi } from './use-workspace-state'; + +type LiveCopy = { + noDisplay: string; + processingFailed: string; + requestFailedPrefix: string; + unknownError: string; + agentFlowEnded: string; +}; + +export function useLiveTurn(options: { + language: Locale; + model: string; + t: { response: LiveCopy; workspace: { deployRequest: string; gatewayPromptApiKey: string; gatewayPromptSkip: string } }; + fileCache: FileContentCache; + workspace: WorkspaceStateApi; + preview: PreviewSurfaceApi; + conversationId: string | null; + setConversationId: (id: string | null) => void; + conversationIdRef: MutableRefObject; + workspaceEpochRef: MutableRefObject; + loadingRef: MutableRefObject; +}) { + const { + language, + model, + t, + fileCache, + workspace, + preview, + conversationId, + setConversationId, + conversationIdRef, + workspaceEpochRef, + loadingRef, + } = options; + + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [loading, setLoading] = useState(false); + const messagesRef = useRef([]); + const chatAbortControllerRef = useRef(null); + const activeTurnIdRef = useRef(''); + const stoppingRef = useRef(false); + + useEffect(() => { + loadingRef.current = loading; + }, [loading]); + + useEffect(() => { + messagesRef.current = messages; + }, [messages]); + + useEffect(() => () => { + chatAbortControllerRef.current?.abort(); + chatAbortControllerRef.current = null; + }, []); + + const startLiveChatSessionRef = useRef<(opts: { + requestConversationId: string; + assistantMessageId: string; + abortController: AbortController; + }) => { + handleStreamEvent: (event: ChatStreamEvent) => void; + finish: () => void; + }>(() => ({ + handleStreamEvent: () => {}, + finish: () => {}, + })); + + function startLiveChatSession(sessionOptions: { + requestConversationId: string; + assistantMessageId: string; + abortController: AbortController; + }) { + const { assistantMessageId } = sessionOptions; + const workspaceEpoch = workspaceEpochRef.current; + const requestAbortController = sessionOptions.abortController; + const activatedPreviewRevisions = new Map(); + let sawProjectActivity = false; + let openedFirstFile = false; + let pendingFirstFilePath: string | null = null; + + const revealFirstFile = (path: string) => { + if (openedFirstFile || !path) return; + openedFirstFile = true; + pendingFirstFilePath = null; + workspace.setFilesFocusPath(path); + workspace.setSandboxTab('files'); + workspace.setResultPanelOpen(true); + }; + + const patchAssistant = (patch: Partial) => { + setMessages((current) => + current.map((item) => + item.id === assistantMessageId ? { ...item, ...patch } : item, + ), + ); + }; + + const appendTextActivity = (text: string) => { + setMessages((current) => + current.map((item) => { + if (item.id !== assistantMessageId) return item; + const nextText = sanitizeThinkingContent(text); + if (!nextText) return item; + return { + ...item, + activities: appendNarrationChunk(item.activities ?? [], nextText), + }; + }), + ); + }; + + const upsertToolActivity = ( + toolUseId: string, + patch: Partial>, + ) => { + setMessages((current) => current.map((item) => { + if (item.id !== assistantMessageId) return item; + const activities = [...(item.activities ?? [])]; + const index = activities.findIndex( + (activity) => activity.kind === 'tool' && activity.toolUseId === toolUseId, + ); + if (index >= 0) { + activities[index] = { ...activities[index], ...patch } as AssistantActivity; + } else { + activities.push({ + kind: 'tool', + toolUseId, + name: patch.name || '', + status: patch.status || 'running', + inputSummary: patch.inputSummary, + outputSummary: patch.outputSummary, + startedAt: patch.startedAt || Date.now(), + endedAt: patch.endedAt, + }); + } + return { ...item, activities }; + })); + }; + + const finalizeAssistant = ( + finalContent: string, + finalStatus: AssistantStatus, + ) => { + workspace.setGatewayBusy(false); + setMessages((current) => + current.map((item) => + item.id === assistantMessageId + ? { + ...item, + content: finalContent, + activities: dropTrailingSummaryEcho( + item.activities ?? [], + finalContent, + ).map((activity) => + activity.kind === 'tool' && activity.status === 'running' + ? { + ...activity, + status: finalStatus === 'stopped' + ? 'stopped' as const + : finalStatus === 'error' + ? 'failed' as const + : 'completed' as const, + endedAt: Date.now(), + } + : activity, + ), + status: finalStatus, + } + : item, + ), + ); + }; + + const applyResponse = (data: ChatResponse) => { + if (data.conversation_id) { + cacheConversationId(data.conversation_id); + setConversationId(data.conversation_id); + } + if (data.preview) { + preview.activatePreview(data.preview, activatedPreviewRevisions); + workspace.setSandboxTab('preview'); + workspace.setResultPanelOpen(true); + } + if (data.deployment) { + workspace.setDeployment(data.deployment); + workspace.setResultPanelOpen(true); + } + if (data.download) { + workspace.setDownload(data.download); + } + if (data.build) { + workspace.setBuild(data.build); + } + if (data.files) { + workspace.setFileTree(data.files); + if (data.files.items.some((item) => item.type === 'file')) { + workspace.setResultPanelOpen(true); + } + } + if (data.gatewayNeeded) { + workspace.setGatewayNeeded(true); + } + workspace.setFilesRefreshing(false); + + const finalText = data.reply || data.error || t.response.noDisplay; + const finalStatus: AssistantStatus = data.stopped ? 'stopped' : data.ok === false ? 'error' : 'done'; + finalizeAssistant(finalText, finalStatus); + }; + + const handleStreamEvent = (event: ChatStreamEvent) => { + if (workspaceEpoch !== workspaceEpochRef.current) return; + if (event.type === 'task_started') { + if (event.data?.conversation_id) { + cacheConversationId(event.data.conversation_id); + setConversationId(event.data.conversation_id); + } + return; + } + if (event.type === 'ping') return; + if (event.type === 'gateway_credentials') { + if (event.data?.status === 'needed') { + workspace.setGatewayNeeded(true); + workspace.setGatewayBusy(false); + } + if (event.data?.status === 'resolved') { + workspace.setGatewayNeeded(false); + workspace.setGatewayBusy(false); + } + return; + } + if (event.type === 'result' && event.data) { + applyResponse(event.data); + setLoading(false); + return; + } + if (event.type === 'agent' && event.data) { + const agentData = event.data; + const text = agentData.reply || agentData.error || t.response.noDisplay; + if (!sawProjectActivity) { + finalizeAssistant(text, agentData.ok === false ? 'error' : 'done'); + return; + } + patchAssistant({ content: text }); + return; + } + if (event.type === 'text_segment' && event.data?.text) { + appendTextActivity(event.data.text); + return; + } + if (event.type === 'tool_use' && event.data) { + sawProjectActivity = true; + upsertToolActivity(event.data.id || '', { + name: event.data.name || '', + status: 'running', + inputSummary: event.data.inputSummary || event.data.command, + ...(event.data.outputSummary ? { outputSummary: event.data.outputSummary } : {}), + startedAt: event.data.startedAt, + }); + return; + } + if (event.type === 'tool_result' && event.data) { + sawProjectActivity = true; + upsertToolActivity(event.data.id || '', { + name: event.data.toolName || '', + status: event.data.status || (event.data.ok === false ? 'failed' : 'completed'), + outputSummary: event.data.outputSummary || event.data.preview, + endedAt: event.data.endedAt || Date.now(), + }); + return; + } + if (event.type === 'file_content' && event.data?.path) { + const content = event.data.content || ''; + fileCache.write(event.data.path, { + content, + size: typeof event.data.size === 'number' ? event.data.size : content.length, + truncated: false, + }); + if (!openedFirstFile) { + pendingFirstFilePath = event.data.path; + } + return; + } + if (event.type === 'file_tree' && event.data) { + sawProjectActivity = true; + workspace.setFileTree(event.data); + workspace.setFilesRefreshing(false); + if (pendingFirstFilePath) { + revealFirstFile(pendingFirstFilePath); + } + return; + } + if (event.type === 'deployment_status' && event.data) { + sawProjectActivity = true; + workspace.setDeployment(event.data); + workspace.setResultPanelOpen(true); + return; + } + if (event.type === 'preview_ready' && event.data) { + sawProjectActivity = true; + if (event.data.preview) { + preview.activatePreview(event.data.preview, activatedPreviewRevisions); + workspace.setSandboxTab('preview'); + workspace.setResultPanelOpen(true); + } + if (event.data.download) { + workspace.setDownload(event.data.download); + } + return; + } + if (event.type === 'error') { + finalizeAssistant(event.error || t.response.processingFailed, 'error'); + setLoading(false); + } + }; + + const finish = () => { + const ownsActiveWorkspace = workspaceEpoch === workspaceEpochRef.current + && chatAbortControllerRef.current === requestAbortController; + if (ownsActiveWorkspace) { + if (!stoppingRef.current) { + setMessages((current) => + current.map((item) => + item.id === assistantMessageId && item.status === 'running' + ? { + ...item, + status: 'done', + content: item.content || t.response.agentFlowEnded, + } + : item, + ), + ); + } + setLoading(false); + workspace.setFilesRefreshing(false); + chatAbortControllerRef.current = null; + if (!stoppingRef.current) { + activeTurnIdRef.current = ''; + } + stoppingRef.current = false; + } + }; + + return { handleStreamEvent, finish, applyResponse, finalizeAssistant }; + } + + startLiveChatSessionRef.current = startLiveChatSession; + + async function attachChatStream(attachOptions: { + requestConversationId: string; + assistantMessageId: string; + response: Response; + abortController: AbortController; + }) { + const session = startLiveChatSession(attachOptions); + try { + chatAbortControllerRef.current = attachOptions.abortController; + stoppingRef.current = false; + + const contentType = attachOptions.response.headers.get('content-type') || ''; + if (!attachOptions.response.body || !contentType.includes('text/event-stream')) { + session.applyResponse((await attachOptions.response.json().catch(() => ({ + ok: false, + error: `${attachOptions.response.status}`, + }))) as ChatResponse); + return; + } + + await consumeEventStream(attachOptions.response, session.handleStreamEvent); + } catch (error) { + if ((error instanceof Error && error.name === 'AbortError') || stoppingRef.current) { + return; + } + const msg = `${t.response.requestFailedPrefix}${error instanceof Error ? error.message : t.response.unknownError}`; + session.finalizeAssistant(msg, 'error'); + } finally { + session.finish(); + } + } + + async function sendMessage(message: string, sendOptions: { + deploy?: boolean; + apiKey?: string; + gatewaySkip?: boolean; + } = {}) { + const trimmed = message.trim(); + if (!trimmed || loading) return; + + const extractedKey = sendOptions.apiKey + ? undefined + : extractApiKeyFromUserText(trimmed); + const inboundApiKey = sendOptions.apiKey || extractedKey?.apiKey; + const displayMessage = extractedKey?.maskedText || trimmed; + + const isDeploy = sendOptions.deploy === true; + const isGatewayCard = Boolean(sendOptions.apiKey || sendOptions.gatewaySkip); + const isGatewayContinue = Boolean(inboundApiKey || sendOptions.gatewaySkip); + const isStartingFromHome = !isDeploy && !isGatewayCard + && messages.length === 0 + && !preview.preview + && !workspace.deployment + && !workspace.build + && !workspace.fileTree; + const requestConversationId = isStartingFromHome + ? createConversationId() + : conversationId || getOrCreateCachedConversationId(); + if (isStartingFromHome) { + cacheConversationId(requestConversationId); + setConversationId(requestConversationId); + preview.resetPreview(); + workspace.resetWorkspace(); + } else if (!conversationId) { + setConversationId(requestConversationId); + } + + const userMessageId = createMessageId('user'); + const assistantMessageId = createMessageId('assistant'); + activeTurnIdRef.current = assistantMessageId; + + setMessages((current) => [ + ...current, + { id: userMessageId, role: 'user', content: displayMessage }, + { + id: assistantMessageId, + role: 'assistant', + content: '', + activities: [], + status: 'running', + }, + ]); + if (!isDeploy) { + workspace.setFilesRefreshing(true); + if (!isGatewayCard) setInput(''); + } + if (isGatewayContinue) { + workspace.setGatewayNeeded(false); + workspace.setGatewayBusy(false); + } + setLoading(true); + + try { + const requestAbortController = new AbortController(); + chatAbortControllerRef.current = requestAbortController; + stoppingRef.current = false; + if (isStartingFromHome) { + try { + const resumeResponse = await openSessionStream( + requestConversationId, + requestAbortController.signal, + ); + const resumeType = resumeResponse.headers.get('content-type') || ''; + if ( + resumeResponse.ok + && resumeResponse.body + && resumeType.includes('text/event-stream') + ) { + await consumeEventStream(resumeResponse, () => {}); + } + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') throw error; + } + } + const response = isDeploy + ? await startDeployTurn({ + conversationId: requestConversationId, + turnId: assistantMessageId, + ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), + ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), + siteDomain: extractProjectName().domain, + signal: requestAbortController.signal, + }) + : await startPromptTurn({ + conversationId: requestConversationId, + message: displayMessage, + turnId: assistantMessageId, + ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), + ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), + ...(model ? { model } : {}), + siteDomain: extractProjectName().domain, + signal: requestAbortController.signal, + }); + await attachChatStream({ + requestConversationId, + assistantMessageId, + response, + abortController: requestAbortController, + }); + } catch (error) { + if ((error instanceof Error && error.name === 'AbortError') || stoppingRef.current) { + setLoading(false); + workspace.setFilesRefreshing(false); + chatAbortControllerRef.current = null; + activeTurnIdRef.current = ''; + stoppingRef.current = false; + return; + } + const msg = `${t.response.requestFailedPrefix}${error instanceof Error ? error.message : t.response.unknownError}`; + setMessages((current) => + current.map((item) => + item.id === assistantMessageId + ? { + ...item, + content: msg, + status: 'error' as AssistantStatus, + } + : item, + ), + ); + setLoading(false); + workspace.setFilesRefreshing(false); + chatAbortControllerRef.current = null; + activeTurnIdRef.current = ''; + stoppingRef.current = false; + } + } + + function stopCurrentTask(stopOptions: { discardProject?: boolean } = {}) { + const cid = conversationIdRef.current || conversationId; + if (!loadingRef.current || !cid || stoppingRef.current) return null; + stoppingRef.current = true; + const stoppedText = STOPPED_TURN_REPLY[language]; + const stopped = markLastTurnStopped(messagesRef.current, stoppedText); + setMessages(stopped.messages); + setLoading(false); + workspace.setFilesRefreshing(false); + workspace.setGatewayNeeded(false); + workspace.setGatewayBusy(false); + + const stoppedTurn = { + id: activeTurnIdRef.current, + user: stopped.userContent, + assistant: stoppedText, + status: 'stopped' as const, + createdAt: Date.now(), + activities: stopped.activities, + }; + + const stopRequest = stopChatTask(cid, stoppedTurn, stopOptions).catch(() => null); + chatAbortControllerRef.current?.abort(); + return stopRequest; + } + + return { + messages, + setMessages, + input, + setInput, + loading, + setLoading, + loadingRef, + messagesRef, + chatAbortControllerRef, + activeTurnIdRef, + stoppingRef, + startLiveChatSessionRef, + sendMessage, + stopCurrentTask, + }; +} + +export type LiveTurnApi = ReturnType; diff --git a/app/features/workspace/hooks/use-preview-surface.ts b/app/features/workspace/hooks/use-preview-surface.ts new file mode 100644 index 0000000..624fa49 --- /dev/null +++ b/app/features/workspace/hooks/use-preview-surface.ts @@ -0,0 +1,415 @@ +'use client'; + +import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react'; +import { isMakersDeployUrl } from '../../../../shared/makers-url'; +import { previewDeepLink } from '../../../../shared/preview-link'; +import type { FileTree, LinkInfo } from '@/app/types/workspace'; +import { fetchPreviewRefresh } from '../workspace-api'; + +const PREVIEW_CREDENTIAL_REFRESH_MS = 8 * 60_000; +const PREVIEW_REFRESH_POLL_MS = 60_000; + +export function isSamePreviewTarget(a: string, b: string) { + try { + const left = new URL(a); + const right = new URL(b); + return left.origin === right.origin && left.pathname === right.pathname; + } catch { + return false; + } +} + +export function isPreviewMessageOrigin(origin: string, previewUrls: readonly string[]) { + if (!origin || origin === 'null') return false; + return previewUrls.some((url) => { + if (!url) return false; + try { + return new URL(url).origin === origin; + } catch { + return false; + } + }); +} + +export function usePreviewSurface(options: { + conversationIdRef: MutableRefObject; + loadingRef: MutableRefObject; + workspaceRestoringRef: MutableRefObject; + setFileTree: (tree: FileTree) => void; + setDownload: (download: LinkInfo) => void; +}) { + const [preview, setPreview] = useState(null); + const [previewViewport, setPreviewViewport] = useState<'desktop' | 'mobile'>('desktop'); + const [activePreviewUrl, setActivePreviewUrl] = useState(''); + const [activePreviewRevision, setActivePreviewRevision] = useState(0); + const [activePreviewLoaded, setActivePreviewLoaded] = useState(false); + const [previewRefreshing, setPreviewRefreshing] = useState(false); + const [previewRefreshFailed, setPreviewRefreshFailed] = useState(false); + const [previewCopied, setPreviewCopied] = useState(false); + const [previewPath, setPreviewPath] = useState(''); + const previewPathRef = useRef(''); + const [pendingPreviewUrl, setPendingPreviewUrl] = useState(''); + const pendingPreviewUrlRef = useRef(''); + const [pendingPreviewRevision, setPendingPreviewRevision] = useState(0); + const activePreviewUrlRef = useRef(''); + const activePreviewRevisionRef = useRef(0); + const previewRevisionRef = useRef(0); + const previewRefreshInFlightRef = useRef(false); + const previewHiddenAtRef = useRef(0); + const previewRefreshedAtRef = useRef(0); + const hasLivePreviewRef = useRef(false); + const isMakersPreviewRef = useRef(false); + const refreshPreviewLinkRef = useRef<(options?: { + showLoading?: boolean; + remountIframe?: boolean; + }) => Promise>(async () => false); + + const shareablePreviewUrl = preview?.url || activePreviewUrl; + + useEffect(() => { + pendingPreviewUrlRef.current = pendingPreviewUrl; + }, [pendingPreviewUrl]); + + useEffect(() => { + hasLivePreviewRef.current = Boolean(preview?.url); + isMakersPreviewRef.current = preview?.kind === 'makers' || isMakersDeployUrl(preview?.url); + }, [preview?.url, preview?.kind]); + + useEffect(() => { + const applyFreshPreviewUrl = ( + url: string, + sandboxDebugUrl?: string, + applyOptions?: { remountIframe?: boolean }, + ): boolean => { + setPreview({ url, sandboxDebugUrl }); + setPreviewRefreshFailed(false); + previewRefreshedAtRef.current = Date.now(); + + if ( + applyOptions?.remountIframe === false + && activePreviewUrlRef.current + && isSamePreviewTarget(activePreviewUrlRef.current, url) + ) { + return false; + } + + const revision = previewRevisionRef.current + 1; + previewRevisionRef.current = revision; + activePreviewUrlRef.current = url; + activePreviewRevisionRef.current = revision; + setActivePreviewUrl(url); + setActivePreviewRevision(revision); + setActivePreviewLoaded(false); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + return true; + }; + + const refreshPreviewLink = async (refreshOptions?: { + showLoading?: boolean; + remountIframe?: boolean; + }) => { + const id = options.conversationIdRef.current; + if ( + !id + || !hasLivePreviewRef.current + || isMakersPreviewRef.current + || options.loadingRef.current + || options.workspaceRestoringRef.current + || previewRefreshInFlightRef.current + ) { + return false; + } + + previewRefreshInFlightRef.current = true; + const willRemount = refreshOptions?.remountIframe !== false; + const previousActiveUrl = activePreviewUrlRef.current; + + if (refreshOptions?.showLoading) { + setPreviewRefreshing(true); + setPreviewRefreshFailed(false); + setActivePreviewLoaded(false); + if (willRemount && previousActiveUrl) { + activePreviewUrlRef.current = ''; + setActivePreviewUrl(''); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + } + } + + try { + const data = await fetchPreviewRefresh(id); + if (data?.ok && data.preview?.url) { + applyFreshPreviewUrl(data.preview.url, data.preview.sandboxDebugUrl, { + remountIframe: willRemount || data.preview.restarted === true, + }); + if (data.files?.items?.length) { + options.setFileTree(data.files); + } + if (data.download?.url) { + options.setDownload(data.download); + } + return true; + } + if (refreshOptions?.showLoading) { + setPreviewRefreshFailed(true); + } + return false; + } finally { + previewRefreshInFlightRef.current = false; + if (refreshOptions?.showLoading) { + setPreviewRefreshing(false); + } + } + }; + + refreshPreviewLinkRef.current = refreshPreviewLink; + + const onVisibility = () => { + if (document.visibilityState !== 'visible') { + previewHiddenAtRef.current = Date.now(); + return; + } + + const hiddenFor = previewHiddenAtRef.current + ? Date.now() - previewHiddenAtRef.current + : 0; + previewHiddenAtRef.current = 0; + const credentialAge = Date.now() - previewRefreshedAtRef.current; + const wentStale = hiddenFor >= PREVIEW_CREDENTIAL_REFRESH_MS + || credentialAge >= PREVIEW_CREDENTIAL_REFRESH_MS; + if (!wentStale || isMakersPreviewRef.current) return; + + void refreshPreviewLink({ + remountIframe: true, + showLoading: true, + }); + }; + + const refreshTimer = window.setInterval(() => { + if ( + document.visibilityState === 'visible' + && hasLivePreviewRef.current + && !isMakersPreviewRef.current + && Date.now() - previewRefreshedAtRef.current >= PREVIEW_CREDENTIAL_REFRESH_MS + ) { + void refreshPreviewLink({ + remountIframe: true, + showLoading: true, + }); + } + }, PREVIEW_REFRESH_POLL_MS); + + document.addEventListener('visibilitychange', onVisibility); + return () => { + window.clearInterval(refreshTimer); + document.removeEventListener('visibilitychange', onVisibility); + refreshPreviewLinkRef.current = async () => false; + }; + // Mount-only: conversation, loading, and restore flags are read from refs. + }, []); + + const promotePendingPreview = () => { + if (!pendingPreviewUrl) return; + activePreviewUrlRef.current = pendingPreviewUrl; + activePreviewRevisionRef.current = pendingPreviewRevision; + setActivePreviewUrl(pendingPreviewUrl); + setActivePreviewRevision(pendingPreviewRevision); + setActivePreviewLoaded(true); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + }; + + useEffect(() => { + if (!activePreviewUrl || activePreviewLoaded || previewRefreshing) return; + const timer = window.setTimeout(() => setActivePreviewLoaded(true), 3000); + return () => window.clearTimeout(timer); + }, [activePreviewUrl, activePreviewLoaded, activePreviewRevision, previewRefreshing]); + + useEffect(() => { + if (!pendingPreviewUrl) return; + const timer = window.setTimeout(() => { + activePreviewUrlRef.current = pendingPreviewUrl; + activePreviewRevisionRef.current = pendingPreviewRevision; + setActivePreviewUrl(pendingPreviewUrl); + setActivePreviewRevision(pendingPreviewRevision); + setActivePreviewLoaded(true); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + }, 3000); + return () => window.clearTimeout(timer); + }, [pendingPreviewUrl, pendingPreviewRevision]); + + useEffect(() => { + const onMessage = (event: MessageEvent) => { + const payload = event.data; + if (!payload || typeof payload !== 'object') return; + const path = (payload as { __edgeonePreviewPath?: unknown }).__edgeonePreviewPath; + if (typeof path !== 'string' || !path) return; + if (!isPreviewMessageOrigin(event.origin, [ + activePreviewUrlRef.current, + pendingPreviewUrlRef.current, + ])) return; + if (path === previewPathRef.current) return; + previewPathRef.current = path; + setPreviewPath(path); + }; + window.addEventListener('message', onMessage); + return () => window.removeEventListener('message', onMessage); + }, []); + + const handleActivePreviewLoad = useCallback(() => { + if (!previewRefreshInFlightRef.current) { + setActivePreviewLoaded(true); + } + }, []); + + function handleRefreshPreview() { + if (!shareablePreviewUrl) return; + void refreshPreviewLinkRef.current({ showLoading: true }); + } + + function handleOpenPreview() { + if (shareablePreviewUrl) { + window.open( + previewDeepLink(shareablePreviewUrl, previewPath), + '_blank', + 'noopener,noreferrer', + ); + } + } + + async function handleCopyPreviewUrl() { + if (!shareablePreviewUrl || !navigator.clipboard) return; + const urlToCopy = previewDeepLink(shareablePreviewUrl, previewPath); + try { + await navigator.clipboard.writeText(urlToCopy); + setPreviewCopied(true); + window.setTimeout(() => setPreviewCopied(false), 1600); + } catch { + setPreviewCopied(false); + } + } + + function activatePreview(nextPreview: LinkInfo, activatedPreviewRevisions: Map) { + if (!nextPreview.url) { + if (nextPreview.error) { + setPreview((current) => + current?.url + ? { + ...nextPreview, + url: current.url, + sandboxDebugUrl: nextPreview.sandboxDebugUrl ?? current.sandboxDebugUrl, + } + : nextPreview, + ); + } + return; + } + + setPreview(nextPreview); + setPreviewRefreshFailed(false); + previewRefreshedAtRef.current = Date.now(); + let revision = activatedPreviewRevisions.get(nextPreview.url); + if (revision === undefined) { + revision = previewRevisionRef.current + 1; + previewRevisionRef.current = revision; + activatedPreviewRevisions.set(nextPreview.url, revision); + } + + if (!activePreviewUrlRef.current) { + activePreviewUrlRef.current = nextPreview.url; + activePreviewRevisionRef.current = revision; + setActivePreviewUrl(nextPreview.url); + setActivePreviewRevision(revision); + setActivePreviewLoaded(false); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + return; + } + + if ( + activePreviewUrlRef.current === nextPreview.url + && activePreviewRevisionRef.current === revision + ) { + return; + } + + setPendingPreviewUrl(nextPreview.url); + setPendingPreviewRevision(revision); + } + + function applyResumedPreview(nextPreview: LinkInfo | undefined) { + if (nextPreview?.url) { + setPreview(nextPreview); + setPreviewRefreshFailed(false); + previewRefreshedAtRef.current = Date.now(); + const revision = previewRevisionRef.current + 1; + previewRevisionRef.current = revision; + activePreviewUrlRef.current = nextPreview.url; + activePreviewRevisionRef.current = revision; + setActivePreviewUrl(nextPreview.url); + setActivePreviewRevision(revision); + setActivePreviewLoaded(false); + return; + } + setPreview(null); + setPreviewRefreshFailed(false); + activePreviewUrlRef.current = ''; + activePreviewRevisionRef.current = 0; + setActivePreviewUrl(''); + setActivePreviewRevision(0); + setActivePreviewLoaded(false); + previewPathRef.current = ''; + setPreviewPath(''); + } + + const resetPreview = useCallback(() => { + setPreview(null); + setPreviewViewport('desktop'); + activePreviewUrlRef.current = ''; + activePreviewRevisionRef.current = 0; + previewRevisionRef.current = 0; + setActivePreviewUrl(''); + setActivePreviewRevision(0); + setActivePreviewLoaded(false); + setPreviewRefreshFailed(false); + setPendingPreviewUrl(''); + setPendingPreviewRevision(0); + setPreviewCopied(false); + previewPathRef.current = ''; + setPreviewPath(''); + }, []); + + return { + preview, + setPreview, + previewViewport, + setPreviewViewport, + activePreviewUrl, + activePreviewRevision, + activePreviewLoaded, + previewRefreshing, + previewRefreshFailed, + previewCopied, + previewPath, + pendingPreviewUrl, + pendingPreviewRevision, + shareablePreviewUrl, + previewRevisionRef, + activePreviewUrlRef, + activePreviewRevisionRef, + previewPathRef, + previewRefreshedAtRef, + promotePendingPreview, + handleActivePreviewLoad, + handleRefreshPreview, + handleOpenPreview, + handleCopyPreviewUrl, + activatePreview, + applyResumedPreview, + resetPreview, + }; +} + +export type PreviewSurfaceApi = ReturnType; diff --git a/app/features/workspace/hooks/use-session-resume.ts b/app/features/workspace/hooks/use-session-resume.ts new file mode 100644 index 0000000..a82a538 --- /dev/null +++ b/app/features/workspace/hooks/use-session-resume.ts @@ -0,0 +1,302 @@ +'use client'; + +import { useEffect, useRef, useState, type MutableRefObject } from 'react'; +import { dropTrailingSummaryEcho } from '../../../../shared/timeline'; +import type { FileContentCache } from '@/app/hooks/use-file-content-cache'; +import { + clearCachedConversationId, + createMessageId, + getStoredConversationId, +} from '@/app/lib/conversation'; +import type { + AssistantStatus, + ChatMessage, + ChatStreamEvent, + ResumeData, + SessionStreamEvent, +} from '@/app/types/workspace'; +import { consumeEventStream } from '../sse'; +import { openSessionStream } from '../workspace-api'; +import type { LiveTurnApi } from './use-live-turn'; +import type { PreviewSurfaceApi } from './use-preview-surface'; +import type { WorkspaceStateApi } from './use-workspace-state'; + +export function useSessionResume(options: { + workspace: WorkspaceStateApi; + preview: PreviewSurfaceApi; + live: LiveTurnApi; + fileCache: FileContentCache; + setConversationId: (id: string | null) => void; + setModel: (model: string) => void; + conversationIdRef: MutableRefObject; + workspaceEpochRef: MutableRefObject; + workspaceRestoringRef: MutableRefObject; +}) { + const { + workspace, + preview, + live, + fileCache, + setConversationId, + setModel, + conversationIdRef, + workspaceEpochRef, + workspaceRestoringRef, + } = options; + + const [resumeChecked, setResumeChecked] = useState(true); + const [workspaceRestoring, setWorkspaceRestoring] = useState(false); + const resumeAbortControllerRef = useRef(null); + + useEffect(() => { + workspaceRestoringRef.current = workspaceRestoring; + }, [workspaceRestoring, workspaceRestoringRef]); + + useEffect(() => { + let cancelled = false; + const workspaceEpoch = workspaceEpochRef.current; + const existing = getStoredConversationId(); + if (!existing) { + return; + } + + setResumeChecked(false); + setConversationId(existing); + + const applyHistory = (data: ResumeData): { restored: boolean; liveTaskId: string | null } => { + const history = Array.isArray(data.messages) ? data.messages : []; + const activeTask = data.activeTask; + if (!data.hasProject && history.length === 0 && !activeTask && !data.deployment) { + return { restored: false, liveTaskId: null }; + } + if (data.conversation_id) { + setConversationId(data.conversation_id); + } + if (data.model) { + setModel(data.model); + } + const activityHistory = Array.isArray(data.activityHistory) ? data.activityHistory : []; + let nextMessages: ChatMessage[] = activityHistory.length > 0 + ? activityHistory.flatMap((turn) => [ + { + id: `${turn.id}-user`, + role: 'user' as const, + content: turn.user, + status: 'done' as AssistantStatus, + }, + { + id: `${turn.id}-assistant`, + role: 'assistant' as const, + content: turn.assistant, + activities: dropTrailingSummaryEcho(turn.activities ?? [], turn.assistant), + status: turn.status === 'completed' ? 'done' as const : turn.status === 'failed' ? 'error' as const : 'stopped' as const, + }, + ]) + : history.map((item) => ({ + id: createMessageId(item.role), + role: item.role, + content: item.content, + status: 'done' as AssistantStatus, + })); + + if (activeTask?.id && activeTask.message) { + const persistedTurn = activityHistory.find((turn) => turn.id === activeTask.id); + const turnAlreadyFinished = persistedTurn + && (persistedTurn.status === 'stopped' + || persistedTurn.status === 'completed' + || persistedTurn.status === 'failed'); + + if (!turnAlreadyFinished) { + const assistantId = activeTask.id; + const userId = `${activeTask.id}-user`; + const last = nextMessages.at(-1); + const hasRunningAssistant = nextMessages.some( + (item) => item.role === 'assistant' && item.id === assistantId && item.status === 'running', + ); + const hasUserForTurn = nextMessages.some((item) => item.id === userId); + + if (!hasRunningAssistant) { + if (last?.role === 'user' && last.content === activeTask.message) { + nextMessages = [ + ...nextMessages.slice(0, -1), + { ...last, id: userId }, + { + id: assistantId, + role: 'assistant', + content: '', + activities: [], + status: 'running', + }, + ]; + } else if (!hasUserForTurn && !(last?.role === 'assistant' && last.id === assistantId)) { + nextMessages = [ + ...nextMessages, + { + id: userId, + role: 'user', + content: activeTask.message, + status: 'done', + }, + { + id: assistantId, + role: 'assistant', + content: '', + activities: [], + status: 'running', + }, + ]; + } + } + live.activeTurnIdRef.current = assistantId; + live.setLoading(true); + workspace.setFilesRefreshing(true); + } + } + + const seenIds = new Set(); + nextMessages = nextMessages.filter((item) => { + if (seenIds.has(item.id)) return false; + seenIds.add(item.id); + return true; + }); + + live.setMessages(nextMessages); + workspace.setGatewayNeeded(Boolean(data.gatewayNeeded)); + workspace.setDeployment(data.deployment ?? null); + if (data.deployment) { + workspace.setResultPanelOpen(true); + } + if (data.hasProject || data.needsWorkspace || activeTask) { + if (data.hasProject || data.needsWorkspace) { + workspace.setSandboxTab(data.hasPreview ? 'preview' : 'files'); + setWorkspaceRestoring(true); + workspace.setFilesRefreshing(true); + workspace.setResultPanelOpen(true); + } + } + const liveTaskId = activeTask?.id + && nextMessages.some((item) => item.id === activeTask.id && item.status === 'running') + ? activeTask.id + : null; + return { restored: true, liveTaskId }; + }; + + const applyWorkspace = (data: ResumeData) => { + if (data.gatewayNeeded) workspace.setGatewayNeeded(true); + const hasFiles = Boolean(data.files?.items.some((item) => item.type === 'file')); + if (data.files) { + workspace.setFileTree(data.files); + } + if (hasFiles || data.preview?.url) { + workspace.setResultPanelOpen(true); + } + if (data.download?.url) { + workspace.setDownload(data.download); + } + if (data.deployment) { + workspace.setDeployment(data.deployment); + workspace.setResultPanelOpen(true); + } + preview.applyResumedPreview(data.preview); + if (data.preview?.url) { + workspace.setSandboxTab('preview'); + } else if (hasFiles) { + workspace.setSandboxTab('files'); + } + }; + + const resumeController = new AbortController(); + resumeAbortControllerRef.current = resumeController; + (async () => { + const liveAttach = { + session: null as { + handleStreamEvent: (event: ChatStreamEvent) => void; + finish: () => void; + } | null, + }; + try { + const response = await openSessionStream(existing, resumeController.signal); + const contentType = response.headers.get('content-type') || ''; + if (!response.ok || !response.body || !contentType.includes('text/event-stream')) { + return; + } + + await consumeEventStream(response, (event) => { + if (cancelled || workspaceEpoch !== workspaceEpochRef.current || event.type === 'ping') return; + + if (event.type === 'resume_history' && event.data?.ok) { + const historyData = event.data; + const { restored, liveTaskId } = applyHistory(historyData); + if (!restored) { + clearCachedConversationId(); + conversationIdRef.current = null; + setConversationId(null); + } + setResumeChecked(true); + + if (liveTaskId) { + const conversationForRun = historyData.conversation_id || existing; + liveAttach.session = live.startLiveChatSessionRef.current({ + requestConversationId: conversationForRun, + assistantMessageId: liveTaskId, + abortController: resumeController, + }); + live.chatAbortControllerRef.current = resumeController; + } + return; + } + + if (event.type === 'resume_workspace' && event.data?.ok) { + applyWorkspace(event.data); + return; + } + + if (event.type === 'resume_file_content' && event.data?.path && typeof event.data.content === 'string') { + fileCache.write(event.data.path, { + content: event.data.content, + size: typeof event.data.size === 'number' + ? event.data.size + : new TextEncoder().encode(event.data.content).byteLength, + truncated: Boolean(event.data.truncated), + mtime: event.data.mtime, + }); + return; + } + + liveAttach.session?.handleStreamEvent(event as ChatStreamEvent); + }); + } catch (error) { + if (!(error instanceof Error && error.name === 'AbortError')) { + // Resume is best-effort. + } + } finally { + if (!cancelled) { + setResumeChecked(true); + if (workspaceEpoch === workspaceEpochRef.current) { + setWorkspaceRestoring(false); + liveAttach.session?.finish(); + if (!liveAttach.session) workspace.setFilesRefreshing(false); + } + } + } + })(); + + return () => { + cancelled = true; + resumeController.abort(); + if (resumeAbortControllerRef.current === resumeController) { + resumeAbortControllerRef.current = null; + } + }; + }, []); + + return { + resumeChecked, + setResumeChecked, + workspaceRestoring, + setWorkspaceRestoring, + resumeAbortControllerRef, + }; +} + +export type SessionResumeApi = ReturnType; diff --git a/app/features/workspace/hooks/use-workspace-state.ts b/app/features/workspace/hooks/use-workspace-state.ts new file mode 100644 index 0000000..4bcfdc6 --- /dev/null +++ b/app/features/workspace/hooks/use-workspace-state.ts @@ -0,0 +1,106 @@ +'use client'; + +import { useCallback, useState } from 'react'; +import { + base64ToBlob, + getOrCreateCachedConversationId, +} from '@/app/lib/conversation'; +import type { + BuildInfo, + DeploymentInfo, + FileTree, + LinkInfo, +} from '@/app/types/workspace'; +import { fetchProjectArchive } from '../workspace-api'; + +export function useWorkspaceState() { + const [deployment, setDeployment] = useState(null); + const [download, setDownload] = useState(null); + const [downloadBusy, setDownloadBusy] = useState(false); + const [build, setBuild] = useState(null); + const [sandboxTab, setSandboxTab] = useState<'preview' | 'files'>('preview'); + const [fileTree, setFileTree] = useState(null); + const [filesRefreshing, setFilesRefreshing] = useState(false); + const [filesFocusPath, setFilesFocusPath] = useState(null); + const [resultPanelOpen, setResultPanelOpen] = useState(false); + const [dismissedDeployTurnId, setDismissedDeployTurnId] = useState(''); + const [gatewayNeeded, setGatewayNeeded] = useState(false); + const [gatewayBusy, setGatewayBusy] = useState(false); + + const resetWorkspace = useCallback(() => { + setDeployment(null); + setDownload(null); + setBuild(null); + setFileTree(null); + setFilesRefreshing(false); + setFilesFocusPath(null); + setResultPanelOpen(false); + setDismissedDeployTurnId(''); + setGatewayNeeded(false); + setGatewayBusy(false); + setSandboxTab('preview'); + }, []); + + async function handleDownload(conversationId: string | null, failedMessage: string) { + if (!download?.url || downloadBusy) return; + setDownloadBusy(true); + setDownload((current) => (current ? { ...current, error: undefined } : current)); + try { + const cid = conversationId || getOrCreateCachedConversationId(); + const resp = await fetchProjectArchive(download.url, cid); + const data = (await resp.json().catch(() => null)) as + | { ok?: boolean; base64?: string; filename?: string; contentType?: string; error?: string } + | null; + if (!resp.ok || !data?.ok || !data.base64) { + const message = data?.error || `${resp.status}`; + setDownload((current) => (current ? { ...current, error: message } : current)); + return; + } + const blob = base64ToBlob(data.base64, data.contentType || 'application/zip'); + const filename = data.filename || download.filename || 'source.zip'; + const objectUrl = URL.createObjectURL(blob); + const anchor = document.createElement('a'); + anchor.href = objectUrl; + anchor.download = filename; + document.body.appendChild(anchor); + anchor.click(); + anchor.remove(); + window.setTimeout(() => URL.revokeObjectURL(objectUrl), 1000); + } catch (error) { + const message = error instanceof Error ? error.message : failedMessage; + setDownload((current) => (current ? { ...current, error: message } : current)); + } finally { + setDownloadBusy(false); + } + } + + return { + deployment, + setDeployment, + download, + setDownload, + downloadBusy, + build, + setBuild, + sandboxTab, + setSandboxTab, + fileTree, + setFileTree, + filesRefreshing, + setFilesRefreshing, + filesFocusPath, + setFilesFocusPath, + resultPanelOpen, + setResultPanelOpen, + dismissedDeployTurnId, + setDismissedDeployTurnId, + gatewayNeeded, + setGatewayNeeded, + gatewayBusy, + setGatewayBusy, + resetWorkspace, + handleDownload, + }; +} + +export type WorkspaceStateApi = ReturnType; diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index 2151e5b..e7010c1 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -24,8 +24,6 @@ export function openSessionStream(conversationId: string, signal?: AbortSignal) }); } -// Cold session restore can reinstall the EdgeOne CLI (420s ceiling) and project -// dependencies before makers-dev starts. const PREVIEW_CLIENT_TIMEOUT_MS = 620_000; export function fetchPreviewRefresh(conversationId: string) { @@ -42,13 +40,6 @@ export function fetchPreviewRefresh(conversationId: string) { .finally(() => clearTimeout(timer)); } -/** - * The models this deployment offers. Fetched rather than bundled: the list is - * assembled from server environment the browser cannot read, and the server - * validates against the same list, so building one here could only drift. - * - * An edge function, so it does not need a conversation the way agent routes do. - */ export function fetchModelCatalog(signal?: AbortSignal) { return fetch('/models', { method: 'GET', @@ -62,29 +53,22 @@ export function fetchModelCatalog(signal?: AbortSignal) { .catch(() => null); } -export function startSessionTurn(options: { +export function startPromptTurn(options: { conversationId: string; message: string; turnId: string; - resetProject: boolean; - /** 'deploy' publishes the current project instead of running the model. */ - intent?: 'deploy'; - /** Omitted runs the deployment default; the server drops anything it does not offer. */ model?: string; siteDomain?: string; - /** Real key from the input card; the visible message stays masked. */ apiKey?: string; gatewaySkip?: boolean; signal?: AbortSignal; }) { - return fetch('/session', { + return fetch('/prompt', { method: 'POST', headers: conversationHeaders(options.conversationId), body: JSON.stringify({ message: options.message, turnId: options.turnId, - ...(options.resetProject ? { resetProject: true } : {}), - ...(options.intent ? { intent: options.intent } : {}), ...(options.model ? { model: options.model } : {}), ...(options.siteDomain ? { siteDomain: options.siteDomain } : {}), ...(options.apiKey ? { apiKey: options.apiKey } : {}), @@ -94,15 +78,40 @@ export function startSessionTurn(options: { }); } +export function startDeployTurn(options: { + conversationId: string; + turnId: string; + siteDomain?: string; + apiKey?: string; + gatewaySkip?: boolean; + signal?: AbortSignal; +}) { + return fetch('/deploy', { + method: 'POST', + headers: conversationHeaders(options.conversationId), + body: JSON.stringify({ + turnId: options.turnId, + ...(options.siteDomain ? { siteDomain: options.siteDomain } : {}), + ...(options.apiKey ? { apiKey: options.apiKey } : {}), + ...(options.gatewaySkip ? { gatewaySkip: true } : {}), + }), + signal: options.signal, + }); +} + +export function setSessionModel(conversationId: string, model: string) { + return fetch('/session-model', { + method: 'POST', + headers: conversationHeaders(conversationId), + body: JSON.stringify({ model }), + }).then((response) => readJson<{ ok?: boolean; model?: string }>(response)); +} + export async function stopChatTask( conversationId: string, turn: PersistedActivityTurn, options: { discardProject?: boolean } = {}, ) { - // Agent routes reject a missing makers-conversation-id before the handler - // runs. /stop still puts conversation_id in the body so abortActiveRun can - // target the live chat; the header is what gets the request accepted and - // sticky-routed to the instance that holds abortLiveChatTask. return fetch('/stop', { method: 'POST', headers: conversationHeaders(conversationId), diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 1c0b43b..a0f6866 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -1,18 +1,14 @@ 'use client'; -import { FormEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { FormEvent, useEffect, useMemo, useRef, useState } from 'react'; import dynamic from 'next/dynamic'; import { Check, Code2, Copy, Download, - ExternalLink, Eye, - Laptop, - RefreshCw, Rocket, - Smartphone, } from 'lucide-react'; import { Button } from '@/app/components/ui/button'; import { @@ -26,68 +22,35 @@ import { } from '@/app/components/ui/dialog'; import { Tabs, TabsList, TabsTrigger } from '@/app/components/ui/tabs'; import { - appendNarrationChunk, - dropTrailingSummaryEcho, lastFinishedAssistant, resolveDeployOffer, -} from '@/app/lib/tool-activity'; +} from '../../../shared/timeline'; import { useFileContentCache } from '@/app/hooks/use-file-content-cache'; import { useTypewriterPlaceholder } from '@/app/hooks/use-typewriter-placeholder'; import { TEMPLATE_SOURCE_URL, TENCENT_CLOUD_CONTACT_URL, - base64ToBlob, - cacheConversationId, clearCachedConversationId, - createConversationId, - createMessageId, extractProjectName, getContactUrl, getMakersModelsDocsUrl, - getOrCreateCachedConversationId, - getStoredConversationId, getTemplateDeployUrl, - markLastTurnStopped, - sanitizeThinkingContent, } from '@/app/lib/conversation'; import { LANGUAGE_STORAGE_KEY, TRANSLATIONS, type Locale } from '@/app/i18n'; -import { extractApiKeyFromUserText, maskApiKey } from '../../../shared/gateway-secret'; -import { isMakersDeployUrl } from '../../../shared/makers-deploy'; +import { maskApiKey } from '../../../shared/gateway-secret'; import { previewDisplayPathFromPath } from '../../../shared/preview-display-path'; -import { previewDeepLink } from '../../../shared/preview-link'; -import { STOPPED_TURN_REPLY } from '../../../shared/user-facing-reply'; import type { ModelOption } from '../../../shared/models'; -import type { - AssistantActivity, - AssistantStatus, - BuildInfo, - ChatMessage, - ChatResponse, - ChatStreamEvent, - DeploymentInfo, - FileTree, - LinkInfo, - ResumeData, - SessionStreamEvent, -} from '@/app/types/workspace'; import { HomeStage } from './components/home-stage'; import { PreviewControls } from './components/preview-controls'; import { PreviewFrame } from './components/preview-frame'; import { SiteHeader } from './components/site-header'; import { WorkspaceErrorBar } from './components/workspace-error-bar'; -import { consumeEventStream } from './sse'; -import { - fetchModelCatalog, - fetchProjectArchive, - fetchPreviewRefresh, - openSessionStream, - startSessionTurn, - stopChatTask, -} from './workspace-api'; +import { fetchModelCatalog, setSessionModel } from './workspace-api'; +import { useLiveTurn } from './hooks/use-live-turn'; +import { usePreviewSurface } from './hooks/use-preview-surface'; +import { useSessionResume } from './hooks/use-session-resume'; +import { useWorkspaceState } from './hooks/use-workspace-state'; -// Covers a panel while its chunk arrives. Only reachable when the warm-up below -// has not finished in time, which is why it is a bare spinner rather than a -// skeleton of a panel the user may never look at. function PanelLoading() { return (
@@ -99,15 +62,6 @@ function PanelLoading() { ); } -// Both panels are kept out of the landing page's bundle. The home screen is -// server-rendered, so its example chips and composer are painted — and look -// clickable — before React has attached a single handler, and every module this -// file imports has to download and evaluate before that first click does -// anything. These two are the heaviest here (react-markdown + remark-gfm, -// prism-react-renderer) and the home screen renders neither. -// -// `ssr: false` gives up nothing: `hasWorkspace` is false in the initial state, so -// this subtree was never part of the server render either way. const importAgentConversation = () => import('@/app/components/agent-conversation').then((mod) => mod.AgentConversation); const AgentConversation = dynamic(importAgentConversation, { @@ -119,178 +73,77 @@ const FilesPanel = dynamic( { ssr: false, loading: PanelLoading }, ); -// Refresh before the sandbox credential is likely to expire. A gateway auth -// response is a JSON document, so it must never be allowed to replace the user -// preview inside the iframe. -const PREVIEW_CREDENTIAL_REFRESH_MS = 8 * 60_000; -const PREVIEW_REFRESH_POLL_MS = 60_000; - -function isSamePreviewTarget(a: string, b: string) { - try { - const left = new URL(a); - const right = new URL(b); - return left.origin === right.origin && left.pathname === right.pathname; - } catch { - return false; - } -} - -// Whether a postMessage came from a preview this workspace is actually showing. -// The address chip is driven by the iframe, so without this check any page -// holding a handle on this window could write an arbitrary route into it. -function isPreviewMessageOrigin(origin: string, previewUrls: readonly string[]) { - if (!origin || origin === 'null') return false; - return previewUrls.some((url) => { - if (!url) return false; - try { - return new URL(url).origin === origin; - } catch { - return false; - } - }); -} - export function WorkspaceScreen() { const [language, setLanguage] = useState('zh'); const [contactUrl, setContactUrl] = useState(TENCENT_CLOUD_CONTACT_URL); const [templateDeployUrl, setTemplateDeployUrl] = useState(() => getTemplateDeployUrl('')); const [makersModelsDocsUrl, setMakersModelsDocsUrl] = useState(() => getMakersModelsDocsUrl('')); - const [messages, setMessages] = useState([]); - const [input, setInput] = useState(''); const [conversationId, setConversationId] = useState(null); - // Empty until /models answers, which keeps the picker hidden rather than - // briefly showing a menu the server has not confirmed it accepts. const [models, setModels] = useState([]); const [model, setModel] = useState(''); - // Resume-on-load: rehydrate the last conversation's workspace after a refresh. - // `resumeChecked` gates the first render. It MUST init to a constant (not from - // localStorage): SSR has no localStorage, so deriving it there would mismatch the - // client's first paint and trigger a hydration error. Start `true` (render home), - // matching SSR — a first-time visitor then stays on home and never flashes the - // "restoring…" screen. A returning visitor is switched to `false` inside the - // client-only effect below (after hydration), which shows the restore screen - // while GET /session runs. - const [resumeChecked, setResumeChecked] = useState(true); - const [preview, setPreview] = useState(null); - const [deployment, setDeployment] = useState(null); - const [download, setDownload] = useState(null); - const [downloadBusy, setDownloadBusy] = useState(false); - const [build, setBuild] = useState(null); - const [loading, setLoading] = useState(false); - const [sandboxTab, setSandboxTab] = useState<'preview' | 'files'>('preview'); - const [previewViewport, setPreviewViewport] = useState<'desktop' | 'mobile'>('desktop'); - const [fileTree, setFileTree] = useState(null); - const [filesRefreshing, setFilesRefreshing] = useState(false); - // Path the Files panel should open (first generated file). Cleared on new project. - const [filesFocusPath, setFilesFocusPath] = useState(null); - // Right preview/code panel stays closed until the first real file arrives (or resume). - const [resultPanelOpen, setResultPanelOpen] = useState(false); - // Slow resume stage: snapshot restore + npm install + preview restart. - const [workspaceRestoring, setWorkspaceRestoring] = useState(false); const [newProjectConfirmOpen, setNewProjectConfirmOpen] = useState(false); - const [dismissedDeployTurnId, setDismissedDeployTurnId] = useState(''); - const [gatewayNeeded, setGatewayNeeded] = useState(false); - const [gatewayBusy, setGatewayBusy] = useState(false); - const fileCache = useFileContentCache(); - const [activePreviewUrl, setActivePreviewUrl] = useState(''); - const [activePreviewRevision, setActivePreviewRevision] = useState(0); - const [activePreviewLoaded, setActivePreviewLoaded] = useState(false); - // Covers the remint window before the iframe remounts. Independent of - // activePreviewLoaded so the 3s onLoad fallback cannot uncover an expired - // token's AUTHENTICATION_FAILED response mid-refresh. - const [previewRefreshing, setPreviewRefreshing] = useState(false); - // When credential renewal fails, keep the iframe detached and show our own - // retry state instead of restoring an expired URL that can render the sandbox - // gateway's AUTHENTICATION_FAILED JSON. - const [previewRefreshFailed, setPreviewRefreshFailed] = useState(false); - const [previewCopied, setPreviewCopied] = useState(false); - // Mirror of the preview iframe's current route (pathname + search + hash), - // posted back by an injected script. Empty until the first message arrives. - const [previewPath, setPreviewPath] = useState(''); - const previewPathRef = useRef(''); - const [pendingPreviewUrl, setPendingPreviewUrl] = useState(''); - const pendingPreviewUrlRef = useRef(''); - const [pendingPreviewRevision, setPendingPreviewRevision] = useState(0); - const activePreviewUrlRef = useRef(''); - const activePreviewRevisionRef = useRef(0); - const previewRevisionRef = useRef(0); - const previewRefreshInFlightRef = useRef(false); - const previewHiddenAtRef = useRef(0); - const previewRefreshedAtRef = useRef(0); + const conversationIdRef = useRef(null); - const messagesRef = useRef([]); const loadingRef = useRef(false); const workspaceRestoringRef = useRef(false); - const hasLivePreviewRef = useRef(false); - const isMakersPreviewRef = useRef(false); - const chatAbortControllerRef = useRef(null); - // Resume runs from a mount-only effect, so unmount was the one thing that could - // stop it. Starting a new project has to reach it too, or its late events - // restore the previous conversation on top of the fresh one. - const resumeAbortControllerRef = useRef(null); - const activeTurnIdRef = useRef(''); - const stoppingRef = useRef(false); - // Invalidates callbacks from an aborted workspace after "Stop and start new" - // has already painted the fresh home screen. const workspaceEpochRef = useRef(0); - // Resume-on-load attaches an in-flight run after history paints. The - // effect closes over this ref so it always uses the latest session factory. - const startLiveChatSessionRef = useRef<(options: { - requestConversationId: string; - assistantMessageId: string; - abortController: AbortController; - }) => { - handleStreamEvent: (event: ChatStreamEvent) => void; - finish: () => void; - }>(() => ({ - handleStreamEvent: () => {}, - finish: () => {}, - })); - // Visibility / toolbar preview refresh — kept on a ref so the listener effect - // can stay mount-only while still calling the latest implementation. - const refreshPreviewLinkRef = useRef<(options?: { - showLoading?: boolean; - remountIframe?: boolean; - }) => Promise>(async () => false); + const fileCache = useFileContentCache(); + + const workspace = useWorkspaceState(); + const preview = usePreviewSurface({ + conversationIdRef, + loadingRef, + workspaceRestoringRef, + setFileTree: workspace.setFileTree, + setDownload: workspace.setDownload, + }); const t = TRANSLATIONS[language]; - const canSend = input.trim().length > 0 && !loading; - // Do not leave a streaming /session socket attached to makers-dev after this - // workspace unmounts (navigation, HMR, or closing the app shell). - useEffect(() => () => { - chatAbortControllerRef.current?.abort(); - chatAbortControllerRef.current = null; - }, []); - // `preview.url` always carries the freshest access_token, while `activePreviewUrl` - // is only what the iframe happens to be showing (deliberately left stale so a - // token rotation does not reload the running app). - const shareablePreviewUrl = preview?.url || activePreviewUrl; - const hasWorkspace = messages.length > 0 - || Boolean(preview) - || Boolean(deployment) - || Boolean(build) - || workspaceRestoring; - // Publishing needs a finished project and an idle sandbox. The download link - // is what says the project exists as files rather than as a half-written - // turn, and it survives a refresh the same way the Files panel does. - const hasDeployableProject = Boolean(download?.url); - // Specifically the publish, not any running turn: publishing stops the preview - // server so the build does not share its output directory, so for that stretch - // the preview is showing a page whose server is gone. A generation leaves the - // server up and the preview usable. - const publishing = deployment?.status === 'running'; - const deployRunning = loading || publishing; - const canDeployProject = hasDeployableProject && !deployRunning && !workspaceRestoring; + const live = useLiveTurn({ + language, + model, + t, + fileCache, + workspace, + preview, + conversationId, + setConversationId, + conversationIdRef, + workspaceEpochRef, + loadingRef, + }); + const resume = useSessionResume({ + workspace, + preview, + live, + fileCache, + setConversationId, + setModel, + conversationIdRef, + workspaceEpochRef, + workspaceRestoringRef, + }); + + const canSend = live.input.trim().length > 0 && !live.loading; + const hasWorkspace = live.messages.length > 0 + || Boolean(preview.preview) + || Boolean(workspace.deployment) + || Boolean(workspace.build) + || resume.workspaceRestoring; + const hasDeployableProject = Boolean(workspace.download?.url); + const publishing = workspace.deployment?.status === 'running'; + const deployRunning = live.loading || publishing; + const canDeployProject = hasDeployableProject && !deployRunning && !resume.workspaceRestoring; const deployHint = hasDeployableProject ? (canDeployProject ? t.deployLabel : t.workspace.deployNeedsIdle) : t.workspace.deployNeedsProject; - const deployOfferKind = resolveDeployOffer(messages, { + const deployOfferKind = resolveDeployOffer(live.messages, { canDownload: hasDeployableProject, - loading: deployRunning || workspaceRestoring, - hasLiveDeployment: deployment?.status === 'success', + loading: deployRunning || resume.workspaceRestoring, + hasLiveDeployment: workspace.deployment?.status === 'success', }); - const deployOfferTurnId = lastFinishedAssistant(messages)?.id || ''; - const deployOffer = deployOfferKind && deployOfferTurnId && deployOfferTurnId !== dismissedDeployTurnId + const deployOfferTurnId = lastFinishedAssistant(live.messages)?.id || ''; + const deployOffer = deployOfferKind && deployOfferTurnId && deployOfferTurnId !== workspace.dismissedDeployTurnId ? { prompt: deployOfferKind === 'again' ? t.workspace.deployOfferAgain @@ -299,16 +152,8 @@ export function WorkspaceScreen() { dismiss: t.workspace.deployOfferDismiss, } : null; - // The panel actions are icons, so the tooltip is the only thing that names - // them, and it has to explain a refusal as well as the action. - const downloadHint = downloadBusy ? t.workspace.downloading : t.workspace.downloadSource; - // Address bar shows the preview's current route once the injected tracker - // reports it; before that it falls back to a bare root path so the sandbox - // host is never shown. - const previewDisplayPath = previewDisplayPathFromPath(previewPath); - // The memoized panels below only skip a render if their props hold their - // identity, so the copy they read is assembled once per language rather than - // rebuilt inline on every streamed token. + const downloadHint = workspace.downloadBusy ? t.workspace.downloading : t.workspace.downloadSource; + const previewDisplayPath = previewDisplayPathFromPath(preview.previewPath); const previewFrameCopy = useMemo(() => ({ unavailable: t.workspace.previewUnavailable, loading: t.workspace.loadingPreview, @@ -339,17 +184,15 @@ export function WorkspaceScreen() { copyLink: t.workspace.copyLink, linkCopied: t.workspace.linkCopied, }), [t]); - // Cycling typewriter placeholder for the landing prompt; pauses while the - // field has text. Label and prompt are the same sentence by design, so this - // demonstrates exactly what a chip would send. const placeholderPhrases = useMemo( () => t.home.examples.map((example) => `${example.label}…`), [t], ); const typedPlaceholder = useTypewriterPlaceholder( placeholderPhrases, - !hasWorkspace && input.length === 0, + !hasWorkspace && live.input.length === 0, ); + const clearFileCache = fileCache.clear; useEffect(() => { clearFileCache(); @@ -359,41 +202,10 @@ export function WorkspaceScreen() { conversationIdRef.current = conversationId; }, [conversationId]); - useEffect(() => { - loadingRef.current = loading; - }, [loading]); - - // Stopping a turn reads the committed list rather than the render closure it - // was scheduled from: during streaming those differ, and the difference is - // exactly the activities the user was watching when they hit the button. - useEffect(() => { - messagesRef.current = messages; - }, [messages]); - - // The preview message listener is mount-only, so it reads the allowed origins - // off refs rather than re-subscribing every time a preview swaps. - useEffect(() => { - pendingPreviewUrlRef.current = pendingPreviewUrl; - }, [pendingPreviewUrl]); - - useEffect(() => { - workspaceRestoringRef.current = workspaceRestoring; - }, [workspaceRestoring]); - - useEffect(() => { - hasLivePreviewRef.current = Boolean(preview?.url); - isMakersPreviewRef.current = preview?.kind === 'makers' || isMakersDeployUrl(preview?.url); - }, [preview?.url, preview?.kind]); - - // Every new file listing is the authoritative view of what is on disk, so use it - // to stamp or drop cached file contents. Covers all three sources of a tree - // (streamed file_tree, the final result, and GET /session). Deliberately keyed on the - // tree alone: reconciling on a cache write would stamp freshly streamed content - // with the previous listing's mtime. const reconcileFileCache = fileCache.reconcile; useEffect(() => { - reconcileFileCache(fileTree); - }, [fileTree, reconcileFileCache]); + reconcileFileCache(workspace.fileTree); + }, [workspace.fileTree, reconcileFileCache]); useEffect(() => { const { domain } = extractProjectName(); @@ -409,11 +221,6 @@ export function WorkspaceScreen() { } }, []); - // Warm the conversation chunk once the landing page is already interactive. - // Hydration no longer waits on it, but the first submit would, and that click - // is the one moment the user is watching. Deliberately delayed rather than - // fired on mount: /models and GET /session decide what the first paint can do, so - // they get the connection first. useEffect(() => { if (hasWorkspace) return; const timer = window.setTimeout(() => void importAgentConversation(), 1200); @@ -425,1263 +232,55 @@ export function WorkspaceScreen() { void fetchModelCatalog(controller.signal).then((catalog) => { if (controller.signal.aborted || !catalog?.ok || !Array.isArray(catalog.models)) return; setModels(catalog.models); - // Only seeds the selection. A conversation being resumed overwrites this - // with its own choice, and that runs after /models either way because it - // waits on the network too. setModel((current) => current || catalog.defaultModel || ''); }); return () => controller.abort(); }, []); - useEffect(() => { - // Progressive resume: paint chat history as soon as store data returns, then - // bootstrap the sandbox (restore + npm install + preview) in the background. - let cancelled = false; - const workspaceEpoch = workspaceEpochRef.current; - const existing = getStoredConversationId(); - if (!existing) { - return; - } - - setResumeChecked(false); - setConversationId(existing); - - const applyHistory = (data: ResumeData): { restored: boolean; liveTaskId: string | null } => { - const history = Array.isArray(data.messages) ? data.messages : []; - const activeTask = data.activeTask; - if (!data.hasProject && history.length === 0 && !activeTask && !data.deployment) { - return { restored: false, liveTaskId: null }; - } - if (data.conversation_id) { - setConversationId(data.conversation_id); - } - // Absent until someone picks one, in which case the deployment default - // seeded from /models is already the right answer. - if (data.model) { - setModel(data.model); - } - const activityHistory = Array.isArray(data.activityHistory) ? data.activityHistory : []; - let nextMessages: ChatMessage[] = activityHistory.length > 0 - ? activityHistory.flatMap((turn) => [ - { - id: `${turn.id}-user`, - role: 'user' as const, - content: turn.user, - status: 'done' as AssistantStatus, - }, - { - id: `${turn.id}-assistant`, - role: 'assistant' as const, - content: turn.assistant, - activities: dropTrailingSummaryEcho(turn.activities ?? [], turn.assistant), - status: turn.status === 'completed' ? 'done' as const : turn.status === 'failed' ? 'error' as const : 'stopped' as const, - }, - ]) - : history.map((item) => ({ - id: createMessageId(item.role), - role: item.role, - content: item.content, - status: 'done' as AssistantStatus, - })); - - // In-flight turn is not in activityHistory yet. Merge it so refresh keeps - // the user prompt visible and a running assistant slot ready for SSE replay. - // Exception: /stop may already have persisted the turn while the chat task is - // still marked running during unwind — merging again duplicates `${turnId}-user`. - if (activeTask?.id && activeTask.message) { - const persistedTurn = activityHistory.find((turn) => turn.id === activeTask.id); - const turnAlreadyFinished = persistedTurn - && (persistedTurn.status === 'stopped' - || persistedTurn.status === 'completed' - || persistedTurn.status === 'failed'); - - if (!turnAlreadyFinished) { - const assistantId = activeTask.id; - const userId = `${activeTask.id}-user`; - const last = nextMessages.at(-1); - const hasRunningAssistant = nextMessages.some( - (item) => item.role === 'assistant' && item.id === assistantId && item.status === 'running', - ); - const hasUserForTurn = nextMessages.some((item) => item.id === userId); - - if (!hasRunningAssistant) { - if (last?.role === 'user' && last.content === activeTask.message) { - nextMessages = [ - ...nextMessages.slice(0, -1), - { ...last, id: userId }, - { - id: assistantId, - role: 'assistant', - content: '', - activities: [], - status: 'running', - }, - ]; - } else if (!hasUserForTurn && !(last?.role === 'assistant' && last.id === assistantId)) { - nextMessages = [ - ...nextMessages, - { - id: userId, - role: 'user', - content: activeTask.message, - status: 'done', - }, - { - id: assistantId, - role: 'assistant', - content: '', - activities: [], - status: 'running', - }, - ]; - } - } - activeTurnIdRef.current = assistantId; - setLoading(true); - setFilesRefreshing(true); - } - } - - // Last line of defense against duplicate React keys after stop/resume races. - const seenIds = new Set(); - nextMessages = nextMessages.filter((item) => { - if (seenIds.has(item.id)) return false; - seenIds.add(item.id); - return true; - }); - - setMessages(nextMessages); - setGatewayNeeded(Boolean(data.gatewayNeeded)); - // Authoritative: this payload always carries the conversation's stored - // deployment, so an absent one means there is none to show. Setting only on - // presence left a card from an earlier session on screen indefinitely — - // startNewProject was the one place that ever cleared it. - setDeployment(data.deployment ?? null); - if (data.deployment) { - setResultPanelOpen(true); - } - if (data.hasProject || data.needsWorkspace || activeTask) { - if (data.hasProject || data.needsWorkspace) { - // If a preview was published before, stay on the preview pane and show - // the restoring spinner while workspace resume restarts the server. - // Otherwise show source first (interrupted / never-published projects). - setSandboxTab(data.hasPreview ? 'preview' : 'files'); - setWorkspaceRestoring(true); - setFilesRefreshing(true); - setResultPanelOpen(true); - } - } - const liveTaskId = activeTask?.id - && nextMessages.some((item) => item.id === activeTask.id && item.status === 'running') - ? activeTask.id - : null; - return { restored: true, liveTaskId }; - }; - - const applyWorkspace = (data: ResumeData) => { - if (data.gatewayNeeded) setGatewayNeeded(true); - const hasFiles = Boolean(data.files?.items.some((item) => item.type === 'file')); - if (data.files) { - setFileTree(data.files); - } - if (hasFiles || data.preview?.url) { - setResultPanelOpen(true); - } - if (data.download?.url) { - setDownload(data.download); - } - // Set-only on purpose, unlike applyHistory: the workspace phase has an error - // fallback that reports a restore failure without the deployment field, and - // clearing on that would drop the card history just restored. - if (data.deployment) { - setDeployment(data.deployment); - setResultPanelOpen(true); - } - if (data.preview?.url) { - setPreview(data.preview); - setPreviewRefreshFailed(false); - setSandboxTab('preview'); - previewRefreshedAtRef.current = Date.now(); - const revision = previewRevisionRef.current + 1; - previewRevisionRef.current = revision; - activePreviewUrlRef.current = data.preview.url; - activePreviewRevisionRef.current = revision; - setActivePreviewUrl(data.preview.url); - setActivePreviewRevision(revision); - setActivePreviewLoaded(false); - } else { - // No live preview on this resume — clear stale iframe state. Only then - // fall back to the Files tab (do not steal the tab when preview is ready). - setPreview(null); - setPreviewRefreshFailed(false); - activePreviewUrlRef.current = ''; - activePreviewRevisionRef.current = 0; - setActivePreviewUrl(''); - setActivePreviewRevision(0); - setActivePreviewLoaded(false); - previewPathRef.current = ''; - setPreviewPath(''); - if (hasFiles) { - setSandboxTab('files'); - } - } - }; - - const resumeController = new AbortController(); - resumeAbortControllerRef.current = resumeController; - (async () => { - const live = { - session: null as { - handleStreamEvent: (event: ChatStreamEvent) => void; - finish: () => void; - } | null, - }; - try { - const response = await openSessionStream(existing, resumeController.signal); - const contentType = response.headers.get('content-type') || ''; - if (!response.ok || !response.body || !contentType.includes('text/event-stream')) { - return; - } - - await consumeEventStream(response, (event) => { - if (cancelled || workspaceEpoch !== workspaceEpochRef.current || event.type === 'ping') return; - - if (event.type === 'resume_history' && event.data?.ok) { - const historyData = event.data; - const { restored, liveTaskId } = applyHistory(historyData); - if (!restored) { - // A cached ID alone does not mean a conversation exists. Remove - // stale/empty IDs so later refreshes stay on the home screen. - clearCachedConversationId(); - conversationIdRef.current = null; - setConversationId(null); - } - // History arrives first, so the UI paints while workspace restore - // and a live task continue over this same HTTP connection. - setResumeChecked(true); - - if (liveTaskId) { - const conversationForRun = historyData.conversation_id || existing; - live.session = startLiveChatSessionRef.current({ - requestConversationId: conversationForRun, - assistantMessageId: liveTaskId, - abortController: resumeController, - }); - chatAbortControllerRef.current = resumeController; - } - return; - } - - if (event.type === 'resume_workspace' && event.data?.ok) { - applyWorkspace(event.data); - return; - } - - if (event.type === 'resume_file_content' && event.data?.path && typeof event.data.content === 'string') { - fileCache.write(event.data.path, { - content: event.data.content, - size: typeof event.data.size === 'number' - ? event.data.size - : new TextEncoder().encode(event.data.content).byteLength, - truncated: Boolean(event.data.truncated), - mtime: event.data.mtime, - }); - return; - } - - live.session?.handleStreamEvent(event as ChatStreamEvent); - }); - } catch (error) { - if (!(error instanceof Error && error.name === 'AbortError')) { - // Resume is best-effort; on failure the user sees the restored history - // if that phase already arrived, otherwise the home screen. - } - } finally { - // The probe is over either way, but a superseded workspace no longer owns - // the panels: clearing them here would undo what the new project's first - // turn has already set. - if (!cancelled) { - setResumeChecked(true); - if (workspaceEpoch === workspaceEpochRef.current) { - setWorkspaceRestoring(false); - live.session?.finish(); - if (!live.session) setFilesRefreshing(false); - } - } - } - })(); - - return () => { - cancelled = true; - resumeController.abort(); - if (resumeAbortControllerRef.current === resumeController) { - resumeAbortControllerRef.current = null; - } - }; - }, []); - - // Re-mint the iframe access_token when the tab becomes visible again, or when - // the user hits refresh. The SPA keeps the old preview URL in memory; the - // sandbox gateway rejects expired envdAccessToken with AUTHENTICATION_FAILED. - useEffect(() => { - const applyFreshPreviewUrl = ( - url: string, - sandboxDebugUrl?: string, - options?: { remountIframe?: boolean }, - ): boolean => { - setPreview({ url, sandboxDebugUrl }); - setPreviewRefreshFailed(false); - previewRefreshedAtRef.current = Date.now(); - - // Same host and path means only the token rotated. Reloading would throw - // away the running app (route, scroll, form state), so keep the frame and - // let copy / open / the next reload pick up the fresh URL from `preview`. - if ( - options?.remountIframe === false - && activePreviewUrlRef.current - && isSamePreviewTarget(activePreviewUrlRef.current, url) - ) { - return false; - } - - const revision = previewRevisionRef.current + 1; - previewRevisionRef.current = revision; - activePreviewUrlRef.current = url; - activePreviewRevisionRef.current = revision; - setActivePreviewUrl(url); - setActivePreviewRevision(revision); - setActivePreviewLoaded(false); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - return true; - }; - - const refreshPreviewLink = async (options?: { - showLoading?: boolean; - remountIframe?: boolean; - }) => { - const id = conversationIdRef.current; - if ( - !id - || !hasLivePreviewRef.current - || isMakersPreviewRef.current - || loadingRef.current - || workspaceRestoringRef.current - || previewRefreshInFlightRef.current - ) { - return false; - } - - previewRefreshInFlightRef.current = true; - - const willRemount = options?.remountIframe !== false; - const previousActiveUrl = activePreviewUrlRef.current; - - if (options?.showLoading) { - setPreviewRefreshing(true); - setPreviewRefreshFailed(false); - setActivePreviewLoaded(false); - // Drop the live frame immediately so an expired envdAccessToken cannot - // paint AUTHENTICATION_FAILED under (or ahead of) the loading overlay - // while POST /preview is in flight. - if (willRemount && previousActiveUrl) { - activePreviewUrlRef.current = ''; - setActivePreviewUrl(''); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - } - } - - try { - // Backend stage=preview remints the token on the existing host, and - // escalates to full workspace restore when the sandbox has gone cold. - const data = await fetchPreviewRefresh(id); - if (data?.ok && data.preview?.url) { - applyFreshPreviewUrl(data.preview.url, data.preview.sandboxDebugUrl, { - // A restarted dev server invalidates whatever the frame is showing, - // so that case always reloads even when the caller asked not to. - remountIframe: willRemount || data.preview.restarted === true, - }); - if (data.files?.items?.length) { - setFileTree(data.files); - } - if (data.download?.url) { - setDownload(data.download); - } - return true; - } - // The preview stage already escalates to full workspace restore on the - // backend, so a second frontend fallback request would only duplicate work. - // Never restore `previousActiveUrl` here: it may contain the expired token - // that caused the refresh, and displaying it leaks the gateway JSON into - // the product UI. - if (options?.showLoading) { - setPreviewRefreshFailed(true); - } - return false; - } finally { - previewRefreshInFlightRef.current = false; - if (options?.showLoading) { - setPreviewRefreshing(false); - } - } - }; - - refreshPreviewLinkRef.current = refreshPreviewLink; - - const onVisibility = () => { - if (document.visibilityState !== 'visible') { - previewHiddenAtRef.current = Date.now(); - return; - } - - const hiddenFor = previewHiddenAtRef.current - ? Date.now() - previewHiddenAtRef.current - : 0; - previewHiddenAtRef.current = 0; - const credentialAge = Date.now() - previewRefreshedAtRef.current; - const wentStale = hiddenFor >= PREVIEW_CREDENTIAL_REFRESH_MS - || credentialAge >= PREVIEW_CREDENTIAL_REFRESH_MS; - // Short tab switches keep the current iframe and token. Refresh only after - // a genuinely stale interval or an explicit toolbar action. - // Makers deploy URLs do not use sandbox envdAccessToken — skip remint. - if (!wentStale || isMakersPreviewRef.current) return; - - void refreshPreviewLink({ - remountIframe: true, - showLoading: true, - }); - }; - - const refreshTimer = window.setInterval(() => { - if ( - document.visibilityState === 'visible' - && hasLivePreviewRef.current - && !isMakersPreviewRef.current - && Date.now() - previewRefreshedAtRef.current >= PREVIEW_CREDENTIAL_REFRESH_MS - ) { - void refreshPreviewLink({ - remountIframe: true, - showLoading: true, - }); - } - }, PREVIEW_REFRESH_POLL_MS); - - document.addEventListener('visibilitychange', onVisibility); - return () => { - window.clearInterval(refreshTimer); - document.removeEventListener('visibilitychange', onVisibility); - refreshPreviewLinkRef.current = async () => false; - }; - }, []); - useEffect(() => { document.documentElement.lang = language === 'zh' ? 'zh-CN' : 'en'; window.localStorage.setItem(LANGUAGE_STORAGE_KEY, language); }, [language]); - const promotePendingPreview = () => { - if (!pendingPreviewUrl) { - return; - } - activePreviewUrlRef.current = pendingPreviewUrl; - activePreviewRevisionRef.current = pendingPreviewRevision; - setActivePreviewUrl(pendingPreviewUrl); - setActivePreviewRevision(pendingPreviewRevision); - setActivePreviewLoaded(true); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - }; - - // Cross-origin iframe onLoad may not fire in some environments. Hide the - // overlay after 3 seconds as a fallback to avoid a permanently blank preview. - // Skip while a token remint is in flight — uncovering early would flash - // AUTHENTICATION_FAILED from the expired frame. - useEffect(() => { - if (!activePreviewUrl || activePreviewLoaded || previewRefreshing) { - return; - } - const timer = window.setTimeout(() => setActivePreviewLoaded(true), 3000); - return () => window.clearTimeout(timer); - }, [activePreviewUrl, activePreviewLoaded, activePreviewRevision, previewRefreshing]); - - // Keep the same fallback for the background iframe so the old preview is not - // kept forever when onLoad does not fire. - useEffect(() => { - if (!pendingPreviewUrl) { - return; - } - const timer = window.setTimeout(() => { - activePreviewUrlRef.current = pendingPreviewUrl; - activePreviewRevisionRef.current = pendingPreviewRevision; - setActivePreviewUrl(pendingPreviewUrl); - setActivePreviewRevision(pendingPreviewRevision); - setActivePreviewLoaded(true); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - }, 3000); - return () => window.clearTimeout(timer); - }, [pendingPreviewUrl, pendingPreviewRevision]); - - // Track the preview iframe's current route. The sandbox app injects a small - // script (Vite transformIndexHtml) that posts `location.pathname + search + - // hash` back to the parent so the address bar can mirror it instead of the - // raw sandbox host. The listener is mount-only; previewPathRef keeps the - // latest value without re-subscribing on every path change. - useEffect(() => { - const onMessage = (event: MessageEvent) => { - const payload = event.data; - if (!payload || typeof payload !== 'object') return; - const path = (payload as { __edgeonePreviewPath?: unknown }).__edgeonePreviewPath; - if (typeof path !== 'string' || !path) return; - // Both iframes are eligible: the pending one is already navigating in the - // background when a refresh swaps previews. - if (!isPreviewMessageOrigin(event.origin, [ - activePreviewUrlRef.current, - pendingPreviewUrlRef.current, - ])) return; - if (path === previewPathRef.current) return; - previewPathRef.current = path; - setPreviewPath(path); - }; - window.addEventListener('message', onMessage); - return () => window.removeEventListener('message', onMessage); - }, []); - - function startLiveChatSession(options: { - requestConversationId: string; - assistantMessageId: string; - abortController: AbortController; - }) { - const { - assistantMessageId, - } = options; - const workspaceEpoch = workspaceEpochRef.current; - const requestAbortController = options.abortController; - const activatedPreviewRevisions = new Map(); - let sawProjectActivity = false; - // Expand the right panel and open a file only after the first real file arrives. - // file_content seeds the path; the following file_tree mounts the panel so the - // Files list is not empty. Do not open on tool_use — that fires before any bytes. - let openedFirstFile = false; - let pendingFirstFilePath: string | null = null; - - const revealFirstFile = (path: string) => { - if (openedFirstFile || !path) return; - openedFirstFile = true; - pendingFirstFilePath = null; - setFilesFocusPath(path); - setSandboxTab('files'); - setResultPanelOpen(true); - }; - - const patchAssistant = (patch: Partial) => { - setMessages((current) => - current.map((item) => - item.id === assistantMessageId ? { ...item, ...patch } : item, - ), - ); - }; - - const appendTextActivity = (text: string) => { - setMessages((current) => - current.map((item) => { - if (item.id !== assistantMessageId) { - return item; - } - const nextText = sanitizeThinkingContent(text); - if (!nextText) { - return item; - } - return { - ...item, - activities: appendNarrationChunk(item.activities ?? [], nextText), - }; - }), - ); - }; - - const upsertToolActivity = ( - toolUseId: string, - patch: Partial>, - ) => { - setMessages((current) => current.map((item) => { - if (item.id !== assistantMessageId) return item; - const activities = [...(item.activities ?? [])]; - const index = activities.findIndex( - (activity) => activity.kind === 'tool' && activity.toolUseId === toolUseId, - ); - if (index >= 0) { - activities[index] = { ...activities[index], ...patch } as AssistantActivity; - } else { - activities.push({ - kind: 'tool', - toolUseId, - name: patch.name || '', - status: patch.status || 'running', - inputSummary: patch.inputSummary, - outputSummary: patch.outputSummary, - startedAt: patch.startedAt || Date.now(), - endedAt: patch.endedAt, - }); - } - return { ...item, activities }; - })); - }; - - const finalizeAssistant = ( - finalContent: string, - finalStatus: AssistantStatus, - ) => { - // The API key card outlives this turn: the user fills it after the - // assistant stops. Clearing it here made the input flash and vanish. - setGatewayBusy(false); - setMessages((current) => - current.map((item) => - item.id === assistantMessageId - ? { - ...item, - content: finalContent, - activities: dropTrailingSummaryEcho( - item.activities ?? [], - finalContent, - ).map((activity) => - activity.kind === 'tool' && activity.status === 'running' - ? { - ...activity, - status: finalStatus === 'stopped' - ? 'stopped' as const - : finalStatus === 'error' - ? 'failed' as const - : 'completed' as const, - endedAt: Date.now(), - } - : activity, - ), - status: finalStatus, - } - : item, - ), - ); - }; - - const activatePreview = (nextPreview: LinkInfo) => { - if (!nextPreview.url) { - if (nextPreview.error) { - setPreview((current) => - current?.url - ? { - ...nextPreview, - url: current.url, - sandboxDebugUrl: nextPreview.sandboxDebugUrl ?? current.sandboxDebugUrl, - } - : nextPreview, - ); - } - return; - } - - setPreview(nextPreview); - setPreviewRefreshFailed(false); - setSandboxTab('preview'); - setResultPanelOpen(true); - previewRefreshedAtRef.current = Date.now(); - let revision = activatedPreviewRevisions.get(nextPreview.url); - if (revision === undefined) { - revision = previewRevisionRef.current + 1; - previewRevisionRef.current = revision; - activatedPreviewRevisions.set(nextPreview.url, revision); - } - - if (!activePreviewUrlRef.current) { - activePreviewUrlRef.current = nextPreview.url; - activePreviewRevisionRef.current = revision; - setActivePreviewUrl(nextPreview.url); - setActivePreviewRevision(revision); - setActivePreviewLoaded(false); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - return; - } - - if ( - activePreviewUrlRef.current === nextPreview.url - && activePreviewRevisionRef.current === revision - ) { - return; - } - - setPendingPreviewUrl(nextPreview.url); - setPendingPreviewRevision(revision); - }; - - const applyResponse = (data: ChatResponse) => { - if (data.conversation_id) { - cacheConversationId(data.conversation_id); - setConversationId(data.conversation_id); - } - if (data.preview) { - activatePreview(data.preview); - } - if (data.deployment) { - setDeployment(data.deployment); - setResultPanelOpen(true); - } - if (data.download) { - setDownload(data.download); - } - if (data.build) { - setBuild(data.build); - } - if (data.files) { - setFileTree(data.files); - if (data.files.items.some((item) => item.type === 'file')) { - setResultPanelOpen(true); - } - } - if (data.gatewayNeeded) { - setGatewayNeeded(true); - } - setFilesRefreshing(false); - - const finalText = data.reply || data.error || t.response.noDisplay; - const finalStatus: AssistantStatus = data.stopped ? 'stopped' : data.ok === false ? 'error' : 'done'; - finalizeAssistant(finalText, finalStatus); - }; - - const handleStreamEvent = (event: ChatStreamEvent) => { - if (workspaceEpoch !== workspaceEpochRef.current) { - return; - } - if (event.type === 'task_started') { - if (event.data?.conversation_id) { - cacheConversationId(event.data.conversation_id); - setConversationId(event.data.conversation_id); - } - return; - } - if (event.type === 'status' && event.message) { - return; - } - if (event.type === 'ping') return; - if (event.type === 'gateway_credentials') { - if (event.data?.status === 'needed') { - setGatewayNeeded(true); - setGatewayBusy(false); - } - if (event.data?.status === 'resolved') { - setGatewayNeeded(false); - setGatewayBusy(false); - } - return; - } - if (event.type === 'result' && event.data) { - applyResponse(event.data); - setLoading(false); - return; - } - if (event.type === 'agent' && event.data) { - const agentData = event.data; - const text = agentData.reply || agentData.error || t.response.noDisplay; - // agent events can arrive before the final aggregate result with build - // and preview data. For plain Q&A without project tool activity, the - // agent event is already complete and can finish the frontend wait state. - // If project tools ran, keep the message running until result finalizes it. - if (!sawProjectActivity) { - finalizeAssistant(text, agentData.ok === false ? 'error' : 'done'); - return; - } - patchAssistant({ content: text }); - return; - } - if (event.type === 'text_segment' && event.data?.text) { - appendTextActivity(event.data.text); - return; - } - if (event.type === 'tool_use' && event.data) { - sawProjectActivity = true; - const toolUseId = event.data.id || ''; - const toolName = event.data.name || ''; - upsertToolActivity(toolUseId, { - name: toolName, - status: 'running', - inputSummary: event.data.inputSummary || event.data.command, - // Present when the call is long enough to report on itself before it - // ends. Left out of the patch when absent rather than written as - // undefined, so an ordinary tool_use cannot blank a running tail. - ...(event.data.outputSummary ? { outputSummary: event.data.outputSummary } : {}), - startedAt: event.data.startedAt, - }); - return; - } - if (event.type === 'tool_result' && event.data) { - sawProjectActivity = true; - upsertToolActivity(event.data.tool_use_id || '', { - name: event.data.toolName || '', - status: event.data.status || (event.data.ok === false ? 'failed' : 'completed'), - outputSummary: event.data.outputSummary || event.data.preview, - endedAt: event.data.endedAt || Date.now(), - }); - return; - } - if (event.type === 'file_content' && event.data?.path) { - // The agent just wrote this file and handed us the text, so seed the cache - // now; the file_tree event that follows stamps it with the sandbox mtime - // and is what actually expands the right panel. - const content = event.data.content || ''; - fileCache.write(event.data.path, { - content, - size: typeof event.data.size === 'number' ? event.data.size : content.length, - truncated: false, - }); - if (!openedFirstFile) { - pendingFirstFilePath = event.data.path; - } - return; - } - if (event.type === 'file_tree' && event.data) { - sawProjectActivity = true; - setFileTree(event.data); - setFilesRefreshing(false); - if (pendingFirstFilePath) { - revealFirstFile(pendingFirstFilePath); - } - return; - } - if (event.type === 'deployment_status' && event.data) { - sawProjectActivity = true; - setDeployment(event.data); - setResultPanelOpen(true); - return; - } - if (event.type === 'preview_ready' && event.data) { - sawProjectActivity = true; - if (event.data.preview) { - activatePreview(event.data.preview); - } - if (event.data.download) { - setDownload(event.data.download); - } - return; - } - if (event.type === 'error') { - finalizeAssistant(event.error || t.response.processingFailed, 'error'); - setLoading(false); - return; - } - if (event.type === 'log' && event.message) { - sawProjectActivity = true; - } - }; - - const finish = () => { - const ownsActiveWorkspace = workspaceEpoch === workspaceEpochRef.current - && chatAbortControllerRef.current === requestAbortController; - // An old aborted stream may unwind after the user has already submitted the - // first prompt in a new project. Never let that stale finally block clear the - // new request's loading state, controller, or turn id. - if (ownsActiveWorkspace) { - // Fallback only when the stream died unexpectedly. Stop/abort already set a - // terminal status; overwriting it would hide an in-flight reconnect. - if (!stoppingRef.current) { - setMessages((current) => - current.map((item) => - item.id === assistantMessageId && item.status === 'running' - ? { - ...item, - status: 'done', - content: item.content || t.response.agentFlowEnded, - } - : item, - ), - ); - } - setLoading(false); - setFilesRefreshing(false); - chatAbortControllerRef.current = null; - if (!stoppingRef.current) { - activeTurnIdRef.current = ''; - } - stoppingRef.current = false; - } - }; - - return { handleStreamEvent, finish, applyResponse, finalizeAssistant }; - } - - startLiveChatSessionRef.current = startLiveChatSession; - - async function attachChatStream(options: { - requestConversationId: string; - assistantMessageId: string; - response: Response; - abortController: AbortController; - }) { - const session = startLiveChatSession(options); - try { - chatAbortControllerRef.current = options.abortController; - stoppingRef.current = false; - - const contentType = options.response.headers.get('content-type') || ''; - if (!options.response.body || !contentType.includes('text/event-stream')) { - session.applyResponse((await options.response.json().catch(() => ({ - ok: false, - error: `${options.response.status}`, - }))) as ChatResponse); - return; - } - - await consumeEventStream(options.response, session.handleStreamEvent); - } catch (error) { - if ((error instanceof Error && error.name === 'AbortError') || stoppingRef.current) { - return; - } - const msg = `${t.response.requestFailedPrefix}${error instanceof Error ? error.message : t.response.unknownError}`; - session.finalizeAssistant(msg, 'error'); - } finally { - session.finish(); - } - } - - async function sendMessage(message: string, options: { - intent?: 'deploy'; - apiKey?: string; - gatewaySkip?: boolean; - } = {}) { - const trimmed = message.trim(); - if (!trimmed || loading) { - return; - } - - const extractedKey = options.apiKey - ? undefined - : extractApiKeyFromUserText(trimmed); - const inboundApiKey = options.apiKey || extractedKey?.apiKey; - const displayMessage = extractedKey?.maskedText || trimmed; - - // Publishing and the API key card act on the project that is already here: - // they never start a workspace and never clear what the user is typing. - // A key typed in the composer is not the card: it can still start a project. - const isDeploy = options.intent === 'deploy'; - const isGatewayCard = Boolean(options.apiKey || options.gatewaySkip); - const isGatewayContinue = Boolean(inboundApiKey || options.gatewaySkip); - const isStartingFromHome = !isDeploy && !isGatewayCard && !hasWorkspace; - const requestConversationId = isStartingFromHome - ? createConversationId() - : conversationId || getOrCreateCachedConversationId(); - if (isStartingFromHome) { - cacheConversationId(requestConversationId); - setConversationId(requestConversationId); - setPreview(null); - setDeployment(null); - setDownload(null); - setBuild(null); - setFileTree(null); - setFilesRefreshing(false); - setFilesFocusPath(null); - setResultPanelOpen(false); - setWorkspaceRestoring(false); - setSandboxTab('preview'); - activePreviewUrlRef.current = ''; - activePreviewRevisionRef.current = 0; - previewRevisionRef.current = 0; - setActivePreviewUrl(''); - setActivePreviewRevision(0); - setActivePreviewLoaded(false); - setPreviewRefreshFailed(false); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - previewPathRef.current = ''; - setPreviewPath(''); - } else if (!conversationId) { - setConversationId(requestConversationId); - } - - const userMessageId = createMessageId('user'); - const assistantMessageId = createMessageId('assistant'); - activeTurnIdRef.current = assistantMessageId; - - setMessages((current) => [ - ...current, - { id: userMessageId, role: 'user', content: displayMessage }, - { - id: assistantMessageId, - role: 'assistant', - content: '', - activities: [], - status: 'running', - }, - ]); - if (!isDeploy) { - setFilesRefreshing(true); - if (!isGatewayCard) setInput(''); - } - if (isGatewayContinue) { - setGatewayNeeded(false); - setGatewayBusy(false); - } - setLoading(true); - - try { - const requestAbortController = new AbortController(); - chatAbortControllerRef.current = requestAbortController; - stoppingRef.current = false; - // Creating a conversation always hits GET /session first. A brand-new id - // is empty and the stream closes immediately; then POST /session sends the text. - if (isStartingFromHome) { - try { - const resumeResponse = await openSessionStream( - requestConversationId, - requestAbortController.signal, - ); - const resumeType = resumeResponse.headers.get('content-type') || ''; - if ( - resumeResponse.ok - && resumeResponse.body - && resumeType.includes('text/event-stream') - ) { - await consumeEventStream(resumeResponse, () => {}); - } - } catch (error) { - if (error instanceof Error && error.name === 'AbortError') throw error; - } - } - const response = await startSessionTurn({ - conversationId: requestConversationId, - message: displayMessage, - turnId: assistantMessageId, - resetProject: isStartingFromHome, - ...(options.intent ? { intent: options.intent } : {}), - ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), - ...(options.gatewaySkip ? { gatewaySkip: true } : {}), - ...(model ? { model } : {}), - siteDomain: extractProjectName().domain, - signal: requestAbortController.signal, - }); - await attachChatStream({ - requestConversationId, - assistantMessageId, - response, - abortController: requestAbortController, - }); - } catch (error) { - if ((error instanceof Error && error.name === 'AbortError') || stoppingRef.current) { - setLoading(false); - setFilesRefreshing(false); - chatAbortControllerRef.current = null; - activeTurnIdRef.current = ''; - stoppingRef.current = false; - return; - } - const msg = `${t.response.requestFailedPrefix}${error instanceof Error ? error.message : t.response.unknownError}`; - setMessages((current) => - current.map((item) => - item.id === assistantMessageId - ? { - ...item, - content: msg, - status: 'error' as AssistantStatus, - } - : item, - ), - ); - setLoading(false); - setFilesRefreshing(false); - chatAbortControllerRef.current = null; - activeTurnIdRef.current = ''; - stoppingRef.current = false; - } - } - async function handleSubmit(event: FormEvent) { event.preventDefault(); - await sendMessage(input); - } - - function stopCurrentTask(options: { discardProject?: boolean } = {}) { - const cid = conversationIdRef.current || conversationId; - if (!loadingRef.current || !cid || stoppingRef.current) return null; - stoppingRef.current = true; - const stoppedText = STOPPED_TURN_REPLY[language]; - // The screen and the /stop payload are the same fact, so they come from one - // pass over one snapshot. Deriving them separately let them disagree about - // which turn was interrupted and which of its tools were still running. - const stopped = markLastTurnStopped(messagesRef.current, stoppedText); - setMessages(stopped.messages); - setLoading(false); - setFilesRefreshing(false); - setGatewayNeeded(false); - setGatewayBusy(false); - - const stoppedTurn = { - id: activeTurnIdRef.current, - user: stopped.userContent, - assistant: stoppedText, - status: 'stopped' as const, - createdAt: Date.now(), - activities: stopped.activities, - }; - - const stopRequest = stopChatTask(cid, stoppedTurn, options).catch(() => null); - chatAbortControllerRef.current?.abort(); - return stopRequest; + await live.sendMessage(live.input); } - async function handleStop() { - await stopCurrentTask(); - } - - function handleConversationSubmit() { - void sendMessage(input); - } - - function handleConversationStop() { - void handleStop(); - } - - // The iframe reports every load, including the one that lands mid-remint while - // the overlay is deliberately still up. - const handleActivePreviewLoad = useCallback(() => { - if (!previewRefreshInFlightRef.current) { - setActivePreviewLoaded(true); - } - }, []); - - async function handleDownload() { - if (!download?.url || downloadBusy) { - return; - } - setDownloadBusy(true); - setDownload((current) => (current ? { ...current, error: undefined } : current)); - try { - // /download must hit the same sandbox the project lives in; sticky routing - // keys off the conversation id header, so send it like /file and /session do - // (a plain could not set this header). - const cid = conversationId || getOrCreateCachedConversationId(); - const resp = await fetchProjectArchive(download.url, cid); - const data = (await resp.json().catch(() => null)) as - | { ok?: boolean; base64?: string; filename?: string; contentType?: string; error?: string } - | null; - if (!resp.ok || !data?.ok || !data.base64) { - const message = data?.error || `${resp.status}`; - setDownload((current) => (current ? { ...current, error: message } : current)); - return; - } - const blob = base64ToBlob(data.base64, data.contentType || 'application/zip'); - const filename = data.filename || download.filename || 'source.zip'; - const objectUrl = URL.createObjectURL(blob); - const anchor = document.createElement('a'); - anchor.href = objectUrl; - anchor.download = filename; - document.body.appendChild(anchor); - anchor.click(); - anchor.remove(); - window.setTimeout(() => URL.revokeObjectURL(objectUrl), 1000); - } catch (error) { - const message = error instanceof Error ? error.message : t.workspace.downloadFailed; - setDownload((current) => (current ? { ...current, error: message } : current)); - } finally { - setDownloadBusy(false); - } + function handleModelChange(next: string) { + setModel(next); + if (conversationId) void setSessionModel(conversationId, next); } function handleDeployProject() { - if (!canDeployProject) { - return; - } + if (!canDeployProject) return; if (deployOfferTurnId) { - setDismissedDeployTurnId(deployOfferTurnId); - } - void sendMessage(t.workspace.deployRequest, { intent: 'deploy' }); - } - - function handleRefreshPreview() { - if (!shareablePreviewUrl) { - return; - } - // A failed remint deliberately leaves the iframe detached. Reloading the - // existing URL as a fallback would expose the sandbox gateway's raw auth - // response to the user. - void refreshPreviewLinkRef.current({ - showLoading: true, - }); - } - - function handleOpenPreview() { - if (shareablePreviewUrl) { - // Same address the copy control hands out: two buttons for "this page" - // that disagree is how one of them ends up untested. - window.open( - previewDeepLink(shareablePreviewUrl, previewPath), - '_blank', - 'noopener,noreferrer', - ); - } - } - - async function handleCopyPreviewUrl() { - if (!shareablePreviewUrl || !navigator.clipboard) { - return; - } - // Deep-links the page the user is actually looking at, off the freshest - // shareable URL so the copied link still carries a live access token. - const urlToCopy = previewDeepLink(shareablePreviewUrl, previewPath); - try { - await navigator.clipboard.writeText(urlToCopy); - setPreviewCopied(true); - window.setTimeout(() => setPreviewCopied(false), 1600); - } catch { - setPreviewCopied(false); + workspace.setDismissedDeployTurnId(deployOfferTurnId); } + void live.sendMessage(t.workspace.deployRequest, { deploy: true }); } - // Return to an uncommitted home state. A conversation ID is created and cached - // only when the user sends the first message, so refreshing an untouched home - // screen does not trigger an empty GET /session request. function startNewProject() { workspaceEpochRef.current += 1; - chatAbortControllerRef.current = null; - resumeAbortControllerRef.current?.abort(); - resumeAbortControllerRef.current = null; - activeTurnIdRef.current = ''; - stoppingRef.current = false; + live.chatAbortControllerRef.current = null; + resume.resumeAbortControllerRef.current?.abort(); + resume.resumeAbortControllerRef.current = null; + live.activeTurnIdRef.current = ''; + live.stoppingRef.current = false; loadingRef.current = false; conversationIdRef.current = null; clearCachedConversationId(); setConversationId(null); - setMessages([]); - setDismissedDeployTurnId(''); - setGatewayNeeded(false); - setGatewayBusy(false); - setLoading(false); - setPreview(null); - setDeployment(null); - setDownload(null); - setBuild(null); - setFileTree(null); - setFilesRefreshing(false); - setFilesFocusPath(null); - setResultPanelOpen(false); - setWorkspaceRestoring(false); - setSandboxTab('preview'); - setPreviewViewport('desktop'); - activePreviewUrlRef.current = ''; - activePreviewRevisionRef.current = 0; - previewRevisionRef.current = 0; - setActivePreviewUrl(''); - setActivePreviewRevision(0); - setActivePreviewLoaded(false); - setPreviewRefreshFailed(false); - setPendingPreviewUrl(''); - setPendingPreviewRevision(0); - setPreviewCopied(false); - previewPathRef.current = ''; - setPreviewPath(''); - setInput(''); + live.setMessages([]); + live.setLoading(false); + live.setInput(''); + workspace.resetWorkspace(); + preview.resetPreview(); + resume.setWorkspaceRestoring(false); } function handleNewProject() { - if (loadingRef.current) { + if (live.loadingRef.current) { setNewProjectConfirmOpen(true); return; } @@ -1690,17 +289,13 @@ export function WorkspaceScreen() { function confirmNewProject() { setNewProjectConfirmOpen(false); - if (loadingRef.current) { - // Fire cancellation against the old conversation, but do not make the new - // workspace wait for snapshot persistence or the /stop response. - void stopCurrentTask({ discardProject: true }); + if (live.loadingRef.current) { + void live.stopCurrentTask({ discardProject: true }); } startNewProject(); } - // Hold the first paint until the resume check resolves, so a returning user does - // not see the home screen flash before their project is restored. - if (!resumeChecked) { + if (!resume.resumeChecked) { return (
void sendMessage(input)} + onSend={() => void live.sendMessage(live.input)} /> )}
- {/* Gated, not just hidden by the class above: mounting it is what fetches - its chunk, and on the home screen there are no messages to keep alive - in it anyway. */} {hasWorkspace && void live.sendMessage(live.input)} + onStop={() => void live.stopCurrentTask()} + deployOffer={workspace.gatewayNeeded ? null : deployOffer} onDeployOffer={handleDeployProject} onDismissDeployOffer={() => { - if (deployOfferTurnId) setDismissedDeployTurnId(deployOfferTurnId); + if (deployOfferTurnId) workspace.setDismissedDeployTurnId(deployOfferTurnId); }} - gatewayPrompt={gatewayNeeded && !loading ? { + gatewayPrompt={workspace.gatewayNeeded && !live.loading ? { title: t.workspace.gatewayPromptTitle, docs: t.workspace.gatewayPromptDocs, docsUrl: makersModelsDocsUrl, @@ -1797,24 +389,23 @@ export function WorkspaceScreen() { continue: t.workspace.gatewayPromptContinue, skip: t.workspace.gatewayPromptSkip, } : null} - gatewayBusy={gatewayBusy} + gatewayBusy={workspace.gatewayBusy} onGatewaySubmit={(values) => { const apiKey = values.apiKey.trim(); - if (!apiKey || loading || gatewayBusy) return; - void sendMessage(`${t.workspace.gatewayPromptApiKey}: ${maskApiKey(apiKey)}`, { apiKey }); + if (!apiKey || live.loading || workspace.gatewayBusy) return; + void live.sendMessage(`${t.workspace.gatewayPromptApiKey}: ${maskApiKey(apiKey)}`, { apiKey }); }} onGatewaySkip={() => { - if (loading || gatewayBusy) return; - void sendMessage(t.workspace.gatewayPromptSkip, { gatewaySkip: true }); + if (live.loading || workspace.gatewayBusy) return; + void live.sendMessage(t.workspace.gatewayPromptSkip, { gatewaySkip: true }); }} />} - {/* ===== RIGHT: preview / files — mounts after the first written file ===== */} - {resultPanelOpen &&
+ {workspace.resultPanelOpen &&
setSandboxTab(value as 'preview' | 'files')} + value={workspace.sandboxTab} + onValueChange={(value) => workspace.setSandboxTab(value as 'preview' | 'files')} className="workspace-topbar-tabs" > @@ -1825,36 +416,27 @@ export function WorkspaceScreen() { {t.workspace.code} - {filesRefreshing && {t.files.refreshing}} + {workspace.filesRefreshing && {t.files.refreshing}}
- {sandboxTab === 'preview' && shareablePreviewUrl && !previewRefreshing && !previewRefreshFailed && ( + {workspace.sandboxTab === 'preview' && preview.shareablePreviewUrl && !preview.previewRefreshing && !preview.previewRefreshFailed && ( )}
- {/* Taking the project somewhere else: out to the edge, or out as - source. These belong to the project rather than to the preview, - so they stay put across both tabs and through a refresh that - has not produced a URL yet. */}
- {/* First and the only one carrying colour. A publish in flight - is the same refusal as having no project: the button stays - the rocket and greys out. Swapping it for a spinner read as - a second progress indicator beside the deploy step the - conversation is already narrating. */}
- {sandboxTab === 'preview' && shareablePreviewUrl && !previewRefreshing && !previewRefreshFailed && ( + {workspace.sandboxTab === 'preview' && preview.shareablePreviewUrl && !preview.previewRefreshing && !preview.previewRefreshFailed && ( )}
- {/* A publish reports itself in the conversation — the tool row while it - runs, then the live address in the reply — so the panel does not - repeat it above the preview. */}
- {/* The preview pane stays mounted and is only hidden behind the Code tab: - unmounting the iframe would reload the sandbox app (and lose its route, - scroll position and form state) on every tab switch. */} -
- {preview?.url ? ( +
+ {preview.preview?.url ? ( ) : (
- {workspaceRestoring ? ( + {resume.workspaceRestoring ? ( <> - {sandboxTab === 'files' && ( + {workspace.sandboxTab === 'files' && (
)}
}
diff --git a/app/lib/assistant-timeline.ts b/app/lib/assistant-timeline.ts index 2ba303e..f069f5a 100644 --- a/app/lib/assistant-timeline.ts +++ b/app/lib/assistant-timeline.ts @@ -1,116 +1,9 @@ -import type { AssistantActivity } from '../../shared/protocol'; -import { presentToolActivity } from './tool-activity.ts'; - -type ToolActivity = Extract; - -export type AssistantTimelineTextBlock = { - kind: 'text'; - index: number; - content: string; -}; - -export type AssistantTimelineToolItem = { - index: number; - activity: ToolActivity; - /** - * Later calls that would have printed this row's label a second time. They - * stay on the row — it holds their status and their detail — so only the - * duplicate line is gone, not the work it stood for. - */ - repeats: ToolActivity[]; -}; - -export type AssistantTimelineToolBlock = { - kind: 'tools'; - items: AssistantTimelineToolItem[]; -}; - -export type AssistantTimelineBlock = AssistantTimelineTextBlock | AssistantTimelineToolBlock; - -export function normalizeTimelineText(value: string) { - return value.replace(/\s+/g, ' ').trim(); -} - -/** - * The row a reference load belongs to, or nothing for a tool whose label names - * what it touched and so cannot repeat by itself. - * - * A topic and the documents beneath it are separate loads that print the same - * label, and the agent takes several in a row, so the label is the row's - * identity: the second load joins the row already saying it. Reading up on a - * subject is one step of the run however many files it took. - */ -function referenceRowKey(activity: ToolActivity) { - const { topic, detailed } = presentToolActivity(activity); - return topic ? `${topic}:${detailed ? 'detail' : 'overview'}` : ''; -} - -/** - * Collapse consecutive tool calls into one chain-log block so the stream is - * text → tools → text → tools, matching the persisted activity order. - */ -export function buildAssistantTimeline(activities: AssistantActivity[]): AssistantTimelineBlock[] { - const blocks: AssistantTimelineBlock[] = []; - // Reference rows of the open chain only. Narration between two loads is the - // agent saying what it turns to next, so a load after it opens a row of its - // own — folding across the sentence would hide the step it announced. - const referenceRows = new Map(); - - for (let index = 0; index < activities.length; index += 1) { - const activity = activities[index]; - if (activity.kind === 'text') { - if (!activity.content.trim()) continue; - blocks.push({ kind: 'text', index, content: activity.content }); - continue; - } - - let chain = blocks.at(-1); - if (chain?.kind !== 'tools') { - const opened: AssistantTimelineToolBlock = { kind: 'tools', items: [] }; - blocks.push(opened); - referenceRows.clear(); - chain = opened; - } - - const key = referenceRowKey(activity); - const open = key ? referenceRows.get(key) : undefined; - if (open) { - open.repeats.push(activity); - continue; - } - - const item: AssistantTimelineToolItem = { index, activity, repeats: [] }; - if (key) referenceRows.set(key, item); - chain.items.push(item); - } - return blocks; -} - -export function lastTimelineText(blocks: AssistantTimelineBlock[]) { - for (let index = blocks.length - 1; index >= 0; index -= 1) { - const block = blocks[index]; - if (block.kind === 'text') return block; - } - return undefined; -} - -/** - * Text that still needs to render after the activity timeline. If the finalized - * reply only extends the last streamed narration, keep the narration in place - * and return just the leftover so it stays after later tool calls. - */ -export function trailingTimelineContent( - lastText: string | undefined, - finalContent: string, - status?: 'running' | 'done' | 'error' | 'stopped', -) { - const trailing = finalContent.trim(); - if (!trailing || status === 'running') return ''; - if (status === 'error' || !lastText?.trim()) return trailing; - - const left = normalizeTimelineText(lastText); - const right = normalizeTimelineText(trailing); - if (left === right) return ''; - if (right.startsWith(left)) return right.slice(left.length).trimStart(); - return trailing; -} +export { + buildAssistantTimeline, + lastTimelineText, + trailingTimelineContent, + type AssistantTimelineBlock, + type AssistantTimelineTextBlock, + type AssistantTimelineToolBlock, + type AssistantTimelineToolItem, +} from '../../shared/timeline.ts'; diff --git a/app/lib/conversation.ts b/app/lib/conversation.ts index 25ef828..20f21cb 100644 --- a/app/lib/conversation.ts +++ b/app/lib/conversation.ts @@ -1,4 +1,5 @@ import type { AssistantActivity, ChatMessage } from '../types/workspace'; +import { sanitizeThinkingContent as timelineSanitizeThinkingContent } from '../../shared/timeline.ts'; const CONVERSATION_STORAGE_KEY = 'vibe-coding-platform-conversation-id'; @@ -114,15 +115,7 @@ export function markLastTurnStopped( } export function sanitizeThinkingContent(value: string) { - return value - .replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, '') - .replace(/\[20[01]~/g, '') - .replace(/\x1b\][^\x07]*\x07/g, '') - .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, '') - .replace(/]*>/gi, '') - .replace(/<\/think>/gi, '') - .replace(/\n{4,}/g, '\n\n\n') - .replace(/]*)?)?)?)?)?$/i, ''); + return timelineSanitizeThinkingContent(value); } export function extractProjectName() { diff --git a/app/lib/tool-activity.ts b/app/lib/tool-activity.ts index 5d78201..481be6f 100644 --- a/app/lib/tool-activity.ts +++ b/app/lib/tool-activity.ts @@ -1,350 +1,12 @@ -import type { AssistantActivity } from '../../shared/protocol'; -import { WEB_SEARCH_TOOL_NAME } from '../../shared/web-search.ts'; - -export type ToolAction = - | 'Environment Preparing' - | 'Glob' - | 'Read file' - | 'Write file' - | 'Edit file' - | 'Create folder' - | 'Delete file' - | 'Create preview' - | 'Deploy project' - | 'Load skill' - | 'Search web' - | 'Run command'; - -/** - * What a reference load is actually about, in the user's terms. The tool takes a - * document id such as `makers-storage`, which is internal naming on two counts — - * it carries the platform tier and it names a file nobody outside the agent can - * open — so the id is resolved to one of these before it reaches a label, and the - * locale table supplies the words. - */ -export type ReferenceTopic = - | 'platform' - | 'structure' - | 'serverApi' - | 'edgeApi' - | 'aiEndpoint' - | 'storage' - | 'middleware' - | 'migration' - | 'cli' - | 'deployment' - | 'environment' - | 'framework'; - -/** Keyed by the ids `load_makers_skill` accepts, plus the router skill. */ -export const REFERENCE_TOPICS: Readonly> = { - 'edgeone-makers-tools': 'platform', - 'makers-recipes': 'structure', - 'makers-cloud-functions': 'serverApi', - 'makers-edge-functions': 'edgeApi', - 'makers-agents': 'aiEndpoint', - 'makers-storage': 'storage', - 'makers-middleware': 'middleware', - 'makers-migration': 'migration', - 'makers-cli': 'cli', - 'makers-deploy': 'deployment', - 'makers-env-adaption': 'environment', - 'makers-frameworks': 'framework', -}; - -export type ToolPresentation = { - action: ToolAction; - target?: string; - /** - * Set instead of `target` for reference loads. An unrecognised id still - * resolves to a topic, because falling back to the id would put the one string - * this indirection exists to hide back on screen. - */ - topic?: ReferenceTopic; - /** - * A deeper document rather than the topic overview. The agent reaches for one - * right after the overview it belongs to, so without this the second row is a - * word-for-word copy of the first and the timeline looks stuck. - */ - detailed?: boolean; -}; - -/** - * Actions that only exist because the project runs on Makers. They are the one - * tier of the activity stream that carries the platform accent, so plain file - * work stays visually quiet. - */ -const PLATFORM_ACTIONS = new Set([ - 'Load skill', - 'Create preview', - 'Deploy project', -]); - -export function toolActionTier(action: ToolAction): 'platform' | 'file' { - return PLATFORM_ACTIONS.has(action) ? 'platform' : 'file'; -} - -/** - * Shortest chunk that may be skipped as an already-rendered replay. Narration - * streams in token-sized pieces, so "the text already ends with this chunk" is - * the normal case for a repeated character and says nothing about a replay. - * Skipping one silently corrupts what it belonged to: a URL that loses a - * character is still shaped like a URL, and the reader has no way to tell. - */ -const MIN_REPLAY_CHUNK = 24; - -/** - * Append a streamed narration chunk to an assistant message's activities. - * - * A resumed turn replays what the browser already rendered, so a chunk big - * enough to be unmistakable is dropped when it is already present. - */ -export function appendNarrationChunk( - activities: readonly AssistantActivity[], - text: string, -): AssistantActivity[] { - const list = [...activities]; - const last = list.at(-1); - if (last?.kind !== 'text') { - list.push({ kind: 'text', content: text }); - return list; - } - - const trimmed = text.trim(); - if (trimmed.length >= MIN_REPLAY_CHUNK && last.content.includes(trimmed)) { - return list; - } - list[list.length - 1] = { ...last, content: `${last.content}${text}` }; - return list; -} - -function withoutUrls(text: string) { - return text.replace(/https?:\/\/\S+/g, '').replace(/\s+/g, ''); -} - -/** - * The model's closing narration and the turn summary are the same sentence - * emitted twice: once as streamed progress, once as the answer. Only the - * summary is compacted and carries the live deployment URL, so the trailing - * narration is the copy to drop. - * - * Neither whitespace nor links can be compared literally. Streamed chunks and - * the final text break lines differently, and the summary moves the deployment - * URL onto a line of its own, so the prose is what has to match. - */ -export function dropTrailingSummaryEcho( - activities: readonly T[], - finalContent: string, -): T[] { - const list = [...activities]; - const last = list.at(-1); - if (!last || last.kind !== 'text') { - return list; - } - - const echoes = (narration: string, summary: string) => Boolean(narration) - && Boolean(summary) - && (summary.includes(narration) || narration.includes(summary)); - const content = last.content || ''; - if ( - echoes(content.replace(/\s+/g, ''), finalContent.replace(/\s+/g, '')) - || echoes(withoutUrls(content), withoutUrls(finalContent)) - ) { - list.pop(); - } - return list; -} - -function shortToolName(name: string) { - return name.replace(/^mcp__[^_]+__/, '').replaceAll('_', ' '); -} - -function cleanSummaryTarget(summary = '') { - const firstLine = summary.trim().split('\n')[0] || ''; - return firstLine - .replace(/^\/?/, '') - .replace(/\s+\([\d,.]+ chars\)$/, '') - .trim(); -} - -function readStructuredTarget(summary = '') { - const trimmed = summary.trim(); - if (!trimmed.startsWith('{')) return ''; - try { - const input = JSON.parse(trimmed) as Record; - for (const key of ['path', 'file_path', 'pattern', 'glob', 'query', 'command', 'cmd', 'skill']) { - if (typeof input[key] === 'string') return cleanSummaryTarget(input[key]); - } - } catch { - return ''; - } - return ''; -} - -/** Only a reference load carries a ref, so a bare id stays a bare id. */ -function readReferenceRequest(summary = '') { - const trimmed = summary.trim(); - if (!trimmed.startsWith('{')) { - return { skill: cleanSummaryTarget(trimmed), ref: '' }; - } - try { - const input = JSON.parse(trimmed) as Record; - return { - skill: typeof input.skill === 'string' ? input.skill : '', - ref: typeof input.ref === 'string' ? input.ref.trim() : '', - }; - } catch { - return { skill: '', ref: '' }; - } -} - -export function presentToolActivity( - activity: { name: string; inputSummary?: string }, - previouslyReadPaths: ReadonlySet = new Set(), -): ToolPresentation { - const name = shortToolName(activity.name).toLowerCase(); - const structuredTarget = readStructuredTarget(activity.inputSummary); - const target = structuredTarget || cleanSummaryTarget(activity.inputSummary); - - if (name.includes('ensure project scaffold') || name.includes('environment')) { - return { action: 'Environment Preparing' }; - } - if (name === 'skill' || name === 'load makers skill') { - const request = readReferenceRequest(activity.inputSummary); - return { - action: 'Load skill', - topic: REFERENCE_TOPICS[request.skill] || 'platform', - detailed: Boolean(request.ref), - }; - } - // Ahead of the command branches: this tool takes a `query`, which is the same - // field a shell call's target is read from, so the fallback would otherwise - // label a search as a command whose text happens to be the search terms. - if (name === shortToolName(WEB_SEARCH_TOOL_NAME)) { - return { action: 'Search web', target }; - } - if (name.includes('glob') || name.includes('files list') || name.includes('folder search')) { - return { action: 'Glob', target: target || '**/*' }; - } - if (name.includes('make dir') || name.includes('mkdir')) { - return { action: 'Create folder', target }; - } - if (name.includes('files remove') || name.includes('files delete')) { - return { action: 'Delete file', target }; - } - if (name.includes('read') || name.includes('files exists')) { - return { action: 'Read file', target }; - } - if (name.includes('write project file') || name.includes('files write') || name.includes('write files')) { - return { action: previouslyReadPaths.has(target) ? 'Edit file' : 'Write file', target }; - } - if (name === 'commands' || name.includes('command')) { - if (/\bedgeone\s+makers\s+deploy\b/i.test(target)) { - return { action: 'Deploy project' }; - } - if (/\bedgeone\s+makers\s+dev\b/i.test(target)) { - return { action: 'Create preview' }; - } - return { action: 'Run command', target }; - } - return { action: 'Run command', target: target || shortToolName(activity.name) }; -} - -/** - * When the composer should offer a production deploy. - * - * The topbar rocket can start a deploy at any idle moment. A short prompt also - * appears above the input after a finished project turn — not while the agent - * is busy, not after a successful or failed deploy of that same turn, and not - * after a pure Q&A. A failed deploy already has its own row in the stream. - */ - -export type DeployOfferKind = 'first' | 'again'; - -export type DeployOfferActivity = { - kind?: string; - status?: string; - name?: string; - inputSummary?: string; -}; - -export type DeployOfferMessage = { - id?: string; - role: string; - status?: string; - activities?: DeployOfferActivity[]; -}; - -export function isDeployProjectActivity(activity: DeployOfferActivity) { - if (activity.kind !== 'tool' || !activity.name) return false; - return presentToolActivity({ - name: activity.name, - inputSummary: activity.inputSummary, - }).action === 'Deploy project'; -} - -export function lastFinishedAssistant(messages: readonly T[]) { - for (let index = messages.length - 1; index >= 0; index -= 1) { - const item = messages[index]; - if (item.role === 'assistant' && item.status && item.status !== 'running') { - return item; - } - } - return undefined; -} - -function activitiesOf(message?: DeployOfferMessage) { - return message?.activities ?? []; -} - -function hasSuccessfulDeploy(activities: readonly DeployOfferActivity[]) { - return activities.some((activity) => ( - isDeployProjectActivity(activity) && activity.status === 'completed' - )); -} - -function hasFailedDeploy(activities: readonly DeployOfferActivity[]) { - return activities.some((activity) => ( - isDeployProjectActivity(activity) - && (activity.status === 'failed' || activity.status === 'stopped') - )); -} - -function usedDeployTool(activities: readonly DeployOfferActivity[]) { - return activities.some((activity) => isDeployProjectActivity(activity)); -} - -function touchedProject(activities: readonly DeployOfferActivity[]) { - return activities.some((activity) => ( - activity.kind === 'tool' && !isDeployProjectActivity(activity) - )); -} - -export function resolveDeployOffer( - messages: readonly DeployOfferMessage[], - options: { - canDownload: boolean; - loading: boolean; - hasLiveDeployment?: boolean; - }, -): DeployOfferKind | null { - if (options.loading || !options.canDownload) return null; - - const last = lastFinishedAssistant(messages); - if (!last || last.status !== 'done') return null; - - const lastActivities = activitiesOf(last); - if (hasSuccessfulDeploy(lastActivities)) return null; - if (hasFailedDeploy(lastActivities)) return null; - if (usedDeployTool(lastActivities)) return null; - - const everPublished = Boolean(options.hasLiveDeployment) - || messages.some((message) => hasSuccessfulDeploy(activitiesOf(message))); - if (touchedProject(lastActivities)) return everPublished ? 'again' : 'first'; - - const anyTools = messages.some((message) => ( - activitiesOf(message).some((activity) => activity.kind === 'tool') - )); - if (!everPublished && !anyTools) return 'first'; - return null; -} +export { + REFERENCE_TOPICS, + appendNarrationChunk, + dropTrailingSummaryEcho, + isDeployProjectActivity, + lastFinishedAssistant, + presentToolActivity, + resolveDeployOffer, + toolActionTier, + type ReferenceTopic, + type ToolAction, +} from '../../shared/timeline.ts'; diff --git a/package-lock.json b/package-lock.json index f2353cc..e95e8ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@anthropic-ai/claude-agent-sdk": "^0.3.143", "@anthropic-ai/sdk": "^0.96.0", "@edgeone/makers-sdk": "0.1.0", + "@edgeone/pages-blob": "^0.0.14", "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-tabs": "^1.1.17", "class-variance-authority": "^0.7.1", @@ -222,6 +223,15 @@ "node": ">=20" } }, + "node_modules/@edgeone/pages-blob": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@edgeone/pages-blob/-/pages-blob-0.0.14.tgz", + "integrity": "sha512-u4zSac1JKhhrWiYSbqWm4zw8Uo3b+nruz2rBcgxEdWaCIvyLQzcSJVBN3OVVe2QeqS4olXqrifoYsYRD9sXOEA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", diff --git a/package.json b/package.json index 1baa312..c008880 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@anthropic-ai/claude-agent-sdk": "^0.3.143", "@anthropic-ai/sdk": "^0.96.0", "@edgeone/makers-sdk": "0.1.0", + "@edgeone/pages-blob": "^0.0.14", "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-tabs": "^1.1.17", "class-variance-authority": "^0.7.1", diff --git a/shared/makers-url.ts b/shared/makers-url.ts new file mode 100644 index 0000000..0ff62e8 --- /dev/null +++ b/shared/makers-url.ts @@ -0,0 +1,19 @@ +/** + * Heuristic for a Makers production URL versus a sandbox preview URL. + * Shared by the browser (share/copy) and the agent runtime (resume / state). + */ + +export function isMakersDeployUrl(url?: string | null): boolean { + if (!url) return false; + try { + const parsed = new URL(url); + if (parsed.pathname === '/preview/' || parsed.pathname.startsWith('/preview/')) { + return false; + } + return /(?:^|\.)edgeone\.(?:cool|ai|link)$/i.test(parsed.hostname) + || /(?:^|\.)pages\.edgeone\./i.test(parsed.hostname) + || /(?:^|\.)edgeone\.page$/i.test(parsed.hostname); + } catch { + return false; + } +} diff --git a/shared/protocol.ts b/shared/protocol.ts index b57e745..c4fd05e 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -85,7 +85,6 @@ type ActiveChatTask = { id: string; message: string; status: 'queued' | 'running'; - resetProject?: boolean; createdAt?: number; startedAt?: number; }; @@ -137,7 +136,6 @@ export type ChatStreamEvent = status?: 'queued' | 'running' | 'completed' | 'failed' | 'stopped'; }; } - | { type: 'status'; message?: string } | { type: 'result'; data?: ChatResponse } | { type: 'agent'; data?: Pick } | { type: 'file_tree'; data?: FileTree } @@ -174,7 +172,7 @@ export type ChatStreamEvent = | { type: 'tool_result'; data?: { - tool_use_id?: string; + id?: string; toolName?: string; command?: string; ok?: boolean; @@ -194,12 +192,6 @@ export type ChatStreamEvent = }; } | { type: 'error'; error?: string } - | { - type: 'log'; - phase?: 'scaffold' | 'agent'; - stream?: 'status' | 'stdout' | 'stderr'; - message?: string; - } | { type: 'ping'; ts?: number }; export type ResumeStreamEvent = diff --git a/shared/sanitize-assistant-text.ts b/shared/sanitize-assistant-text.ts deleted file mode 100644 index b17e55f..0000000 --- a/shared/sanitize-assistant-text.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** Remove terminal controls, leaked reasoning, and raw tool blocks from model text. */ -export function sanitizeAssistantText(input: string): string { - if (!input) return ''; - let text = input; - - text = text.replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, ''); - text = text.replace(/\[20[01]~/g, ''); - text = text.replace(/\x1b\][^\x07]*\x07/g, ''); - text = text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, ''); - text = stripThinkBlocks(text); - text = stripJsonBlocksMatching(text, /\{\s*"type"\s*:\s*"(?:tool_use|tool_result)"/); - return text.replace(/\n{3,}/g, '\n\n').trim(); -} - -function stripThinkBlocks(text: string): string { - return text - .replace(/]*>[\s\S]*?<\/think>/gi, '') - .replace(/]*>[\s\S]*$/i, ''); -} - -function stripJsonBlocksMatching(text: string, startPattern: RegExp): string { - let out = ''; - let index = 0; - while (index < text.length) { - const rest = text.slice(index); - const match = rest.match(startPattern); - if (!match || match.index === undefined) { - out += rest; - break; - } - out += rest.slice(0, match.index); - const start = index + match.index; - const end = findJsonObjectEnd(text, start); - if (end < 0) { - // The block never closes, which means the stream was cut inside it. Keeping - // the tail would print `{"type":"tool_use","input":{"path":` into the chat, - // and nothing after an unclosed brace is readable prose anyway — the same - // call stripThinkBlocks makes for an unterminated . - break; - } - index = end + 1; - } - return out; -} - -function findJsonObjectEnd(text: string, start: number): number { - if (text[start] !== '{') return -1; - let depth = 0; - let inString = false; - let escaped = false; - for (let index = start; index < text.length; index += 1) { - const character = text[index]; - if (inString) { - if (escaped) { - escaped = false; - continue; - } - if (character === '\\') { - escaped = true; - continue; - } - if (character === '"') inString = false; - continue; - } - if (character === '"') { - inString = true; - continue; - } - if (character === '{') depth += 1; - if (character === '}' && --depth === 0) return index; - } - return -1; -} diff --git a/shared/timeline.ts b/shared/timeline.ts new file mode 100644 index 0000000..8d25c3c --- /dev/null +++ b/shared/timeline.ts @@ -0,0 +1,643 @@ +/** + * Conversation timeline: sanitizing, tool presentation, live-event folding. + * One module for the agent runtime and the browser so resume and live SSE + * cannot disagree about a turn. + */ + +import type { AssistantActivity, ChatStreamEvent, PersistedActivityTurn } from './protocol.ts'; +import { WEB_SEARCH_TOOL_NAME } from './web-search.ts'; + +export function sanitizeAssistantText(input: string): string { + if (!input) return ''; + let text = input; + text = stripControls(text); + text = stripThinkBlocks(text); + text = stripJsonBlocksMatching(text, /\{\s*"type"\s*:\s*"(?:tool_use|tool_result)"/); + return text.replace(/\n{3,}/g, '\n\n').trim(); +} + +export function sanitizeNarrationText(input: string) { + if (!input) return ''; + return stripControls(input) + .replace(/]*>/gi, '') + .replace(/<\/think>/gi, '') + .replace(/\n{4,}/g, '\n\n\n'); +} + +export function sanitizeThinkingContent(value: string) { + return sanitizeNarrationText(value) + .replace(/]*)?)?)?)?)?$/i, ''); +} + +function stripControls(text: string) { + return text + .replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, '') + .replace(/\[20[01]~/g, '') + .replace(/\x1b\][^\x07]*\x07/g, '') + .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, ''); +} + +function stripThinkBlocks(text: string): string { + return text + .replace(/]*>[\s\S]*?<\/think>/gi, '') + .replace(/]*>[\s\S]*$/i, ''); +} + +function stripJsonBlocksMatching(text: string, startPattern: RegExp): string { + let out = ''; + let index = 0; + while (index < text.length) { + const rest = text.slice(index); + const match = rest.match(startPattern); + if (!match || match.index === undefined) { + out += rest; + break; + } + out += rest.slice(0, match.index); + const start = index + match.index; + const end = findJsonObjectEnd(text, start); + if (end < 0) break; + index = end + 1; + } + return out; +} + +function findJsonObjectEnd(text: string, start: number): number { + if (text[start] !== '{') return -1; + let depth = 0; + let inString = false; + let escaped = false; + for (let index = start; index < text.length; index += 1) { + const character = text[index]; + if (inString) { + if (escaped) { + escaped = false; + continue; + } + if (character === '\\') { + escaped = true; + continue; + } + if (character === '"') inString = false; + continue; + } + if (character === '"') { + inString = true; + continue; + } + if (character === '{') depth += 1; + if (character === '}' && --depth === 0) return index; + } + return -1; +} + +const MIN_RESEND_PREFIX = 8; + +export type NarrationEmitState = { + currentTextBlock: string; + emittedNarration: string; +}; + +export function resolveNarrationEmit( + state: NarrationEmitState, + rawText: string, + complete = false, +): { state: NarrationEmitState; text: string | null } { + const text = sanitizeNarrationText(rawText); + if (!text) return { state, text: null }; + + if (complete) { + const trimmed = text.trim(); + if (!trimmed) return { state, text: null }; + const streamed = state.currentTextBlock; + const streamedTrimmed = streamed.trimEnd(); + if (streamed.includes(trimmed) || streamedTrimmed === trimmed) { + return { state, text: null }; + } + let nextChunk = trimmed; + if (streamed && trimmed.startsWith(streamed)) { + nextChunk = trimmed.slice(streamed.length); + } else if (streamedTrimmed && trimmed.startsWith(streamedTrimmed)) { + nextChunk = trimmed.slice(streamedTrimmed.length); + } else if (streamed) { + return { state, text: null }; + } else { + if (state.emittedNarration.trimEnd().endsWith(trimmed)) { + return { state, text: null }; + } + nextChunk = trimmed; + } + nextChunk = sanitizeNarrationText(nextChunk); + if (!nextChunk.trim()) return { state, text: null }; + return { + state: { + currentTextBlock: sanitizeNarrationText(`${streamed}${nextChunk}`), + emittedNarration: sanitizeNarrationText(`${state.emittedNarration}${nextChunk}`), + }, + text: nextChunk, + }; + } + + if (state.currentTextBlock.length >= MIN_RESEND_PREFIX && text.startsWith(state.currentTextBlock)) { + const remainder = text.slice(state.currentTextBlock.length); + if (!remainder) return { state, text: null }; + return { + state: { + currentTextBlock: sanitizeNarrationText(`${state.currentTextBlock}${remainder}`), + emittedNarration: sanitizeNarrationText(`${state.emittedNarration}${remainder}`), + }, + text: remainder, + }; + } + + return { + state: { + currentTextBlock: sanitizeNarrationText(`${state.currentTextBlock}${text}`), + emittedNarration: sanitizeNarrationText(`${state.emittedNarration}${text}`), + }, + text, + }; +} + +const SUMMARY_LIMIT = 2_000; +const SENSITIVE_KEY = /(authorization|cookie|password|passwd|secret|token|api[_-]?key|private[_-]?key|credential)/i; + +function truncate(value: string, limit = SUMMARY_LIMIT) { + const normalized = value.replace(/\x1b\[[0-9;?]*[~A-Za-z]/g, '').trim(); + return normalized.length > limit ? `${normalized.slice(0, limit)}\n... truncated` : normalized; +} + +function redactInlineSecrets(value: string) { + return value + .replace(/(authorization\s*:\s*)(?:bearer\s+)?[^"'\s]+(?:\s+[^"'\s]+)?/gi, '$1[REDACTED]') + .replace(/((?:authorization|cookie|password|passwd|secret|token|api[_-]?key|private[_-]?key)\s*[:=]\s*)([^\s,;]+)/gi, '$1[REDACTED]') + .replace(/(bearer\s+)[A-Za-z0-9._~+\/-]+/gi, '$1[REDACTED]'); +} + +function safeValue(value: unknown, projectDir: string, depth = 0): unknown { + if (depth > 4) return '[nested value omitted]'; + if (typeof value === 'string') { + const withoutProjectPath = projectDir ? value.split(projectDir).join('') : value; + return truncate(redactInlineSecrets(withoutProjectPath), 600); + } + if (typeof value === 'number' || typeof value === 'boolean' || value == null) return value; + if (Array.isArray(value)) return value.slice(0, 20).map((item) => safeValue(item, projectDir, depth + 1)); + if (typeof value === 'object') { + return Object.fromEntries( + Object.entries(value as Record) + .slice(0, 30) + .map(([key, child]) => [ + key, + SENSITIVE_KEY.test(key) ? '[REDACTED]' : safeValue(child, projectDir, depth + 1), + ]), + ); + } + return String(value); +} + +export function summarizeToolInput(name: string, input: unknown, projectDir = '') { + const record = input && typeof input === 'object' ? input as Record : {}; + const shortName = name.replace(/^mcp__[^_]+__/, ''); + + if (shortName === 'Skill' || shortName === 'load_makers_skill') { + const skill = typeof record.skill === 'string' ? record.skill : ''; + const ref = typeof record.ref === 'string' ? record.ref.trim() : ''; + return truncate(ref ? JSON.stringify({ skill, ref }) : skill, 200); + } + if (shortName === 'write_project_file' || shortName === 'files_write' || shortName === 'write_files') { + if (typeof record.path !== 'string' && typeof record.content !== 'string') return ''; + const path = typeof record.path === 'string' ? record.path : ''; + const length = typeof record.content === 'string' ? record.content.length : 0; + return `${path} (${length.toLocaleString('en-US')} chars)`; + } + if (shortName === 'commands') { + const command = typeof record.command === 'string' + ? record.command + : typeof record.cmd === 'string' + ? record.cmd + : ''; + return truncate(redactInlineSecrets(projectDir ? command.split(projectDir).join('') : command)); + } + if ( + shortName === 'files_make_dir' + || shortName === 'files_remove' + || shortName === 'files_exists' + || shortName === 'files_read' + || shortName === 'files_list' + ) { + const path = typeof record.path === 'string' + ? record.path + : typeof record.file_path === 'string' + ? record.file_path + : ''; + return path ? truncate(projectDir ? path.split(projectDir).join('') : path) : ''; + } + + return truncate(JSON.stringify(safeValue(record, projectDir), null, 2)); +} + +export function summarizeToolOutput(value: string, projectDir = '', name = '') { + if (name.replace(/^mcp__[^_]+__/, '') === 'Skill' && /^launching skill:/i.test(value.trim())) { + return ''; + } + if (name.replace(/^mcp__[^_]+__/, '') === 'load_makers_skill' && /^---\s*\nname:/i.test(value.trim())) { + return ''; + } + const withoutProjectPath = projectDir ? value.split(projectDir).join('') : value; + return truncate(redactInlineSecrets(withoutProjectPath)); +} + +export type ToolAction = + | 'Environment Preparing' + | 'Glob' + | 'Read file' + | 'Write file' + | 'Edit file' + | 'Create folder' + | 'Delete file' + | 'Create preview' + | 'Deploy project' + | 'Load skill' + | 'Search web' + | 'Run command'; + +export type ReferenceTopic = + | 'platform' + | 'structure' + | 'serverApi' + | 'edgeApi' + | 'aiEndpoint' + | 'storage' + | 'middleware' + | 'migration' + | 'cli' + | 'deployment' + | 'environment' + | 'framework'; + +export const REFERENCE_TOPICS: Readonly> = { + 'edgeone-makers-tools': 'platform', + 'makers-recipes': 'structure', + 'makers-cloud-functions': 'serverApi', + 'makers-edge-functions': 'edgeApi', + 'makers-agents': 'aiEndpoint', + 'makers-storage': 'storage', + 'makers-middleware': 'middleware', + 'makers-migration': 'migration', + 'makers-cli': 'cli', + 'makers-deploy': 'deployment', + 'makers-env-adaption': 'environment', + 'makers-frameworks': 'framework', +}; + +export type ToolPresentation = { + action: ToolAction; + target?: string; + topic?: ReferenceTopic; + detailed?: boolean; +}; + +const PLATFORM_ACTIONS = new Set(['Load skill', 'Create preview', 'Deploy project']); + +export function toolActionTier(action: ToolAction): 'platform' | 'file' { + return PLATFORM_ACTIONS.has(action) ? 'platform' : 'file'; +} + +const MIN_REPLAY_CHUNK = 24; + +export function appendNarrationChunk( + activities: readonly AssistantActivity[], + text: string, +): AssistantActivity[] { + const list = [...activities]; + const last = list.at(-1); + if (last?.kind !== 'text') { + list.push({ kind: 'text', content: text }); + return list; + } + const trimmed = text.trim(); + if (trimmed.length >= MIN_REPLAY_CHUNK && last.content.includes(trimmed)) { + return list; + } + list[list.length - 1] = { ...last, content: `${last.content}${text}` }; + return list; +} + +function withoutUrls(text: string) { + return text.replace(/https?:\/\/\S+/g, '').replace(/\s+/g, ''); +} + +export function dropTrailingSummaryEcho( + activities: readonly T[], + finalContent: string, +): T[] { + const list = [...activities]; + const last = list.at(-1); + if (!last || last.kind !== 'text') return list; + const echoes = (narration: string, summary: string) => Boolean(narration) + && Boolean(summary) + && (summary.includes(narration) || narration.includes(summary)); + const content = last.content || ''; + if ( + echoes(content.replace(/\s+/g, ''), finalContent.replace(/\s+/g, '')) + || echoes(withoutUrls(content), withoutUrls(finalContent)) + ) { + list.pop(); + } + return list; +} + +function shortToolName(name: string) { + return name.replace(/^mcp__[^_]+__/, '').replaceAll('_', ' '); +} + +function cleanSummaryTarget(summary = '') { + const firstLine = summary.trim().split('\n')[0] || ''; + return firstLine + .replace(/^\/?/, '') + .replace(/\s+\([\d,.]+ chars\)$/, '') + .trim(); +} + +function readStructuredTarget(summary = '') { + const trimmed = summary.trim(); + if (!trimmed.startsWith('{')) return ''; + try { + const input = JSON.parse(trimmed) as Record; + for (const key of ['path', 'file_path', 'pattern', 'glob', 'query', 'command', 'cmd', 'skill']) { + if (typeof input[key] === 'string') return cleanSummaryTarget(input[key]); + } + } catch { + return ''; + } + return ''; +} + +function readReferenceRequest(summary = '') { + const trimmed = summary.trim(); + if (!trimmed.startsWith('{')) { + return { skill: cleanSummaryTarget(trimmed), ref: '' }; + } + try { + const input = JSON.parse(trimmed) as Record; + return { + skill: typeof input.skill === 'string' ? input.skill : '', + ref: typeof input.ref === 'string' ? input.ref.trim() : '', + }; + } catch { + return { skill: '', ref: '' }; + } +} + +export function presentToolActivity( + activity: { name: string; inputSummary?: string }, + previouslyReadPaths: ReadonlySet = new Set(), +): ToolPresentation { + const name = shortToolName(activity.name).toLowerCase(); + const structuredTarget = readStructuredTarget(activity.inputSummary); + const target = structuredTarget || cleanSummaryTarget(activity.inputSummary); + + if (name.includes('ensure project scaffold') || name.includes('environment')) { + return { action: 'Environment Preparing' }; + } + if (name === 'skill' || name === 'load makers skill') { + const request = readReferenceRequest(activity.inputSummary); + return { + action: 'Load skill', + topic: REFERENCE_TOPICS[request.skill] || 'platform', + detailed: Boolean(request.ref), + }; + } + if (name === shortToolName(WEB_SEARCH_TOOL_NAME)) { + return { action: 'Search web', target }; + } + if (name.includes('glob') || name.includes('files list') || name.includes('folder search')) { + return { action: 'Glob', target: target || '**/*' }; + } + if (name.includes('make dir') || name.includes('mkdir')) { + return { action: 'Create folder', target }; + } + if (name.includes('files remove') || name.includes('files delete')) { + return { action: 'Delete file', target }; + } + if (name.includes('read') || name.includes('files exists')) { + return { action: 'Read file', target }; + } + if (name.includes('write project file') || name.includes('files write') || name.includes('write files')) { + return { action: previouslyReadPaths.has(target) ? 'Edit file' : 'Write file', target }; + } + if (name === 'commands' || name.includes('command')) { + if (/\bedgeone\s+makers\s+deploy\b/i.test(target)) return { action: 'Deploy project' }; + if (/\bedgeone\s+makers\s+dev\b/i.test(target)) return { action: 'Create preview' }; + return { action: 'Run command', target }; + } + return { action: 'Run command', target: target || shortToolName(activity.name) }; +} + +export type DeployOfferKind = 'first' | 'again'; + +export type DeployOfferActivity = { + kind?: string; + status?: string; + name?: string; + inputSummary?: string; +}; + +export type DeployOfferMessage = { + id?: string; + role: string; + status?: string; + activities?: DeployOfferActivity[]; +}; + +export function isDeployProjectActivity(activity: DeployOfferActivity) { + if (activity.kind !== 'tool' || !activity.name) return false; + return presentToolActivity({ + name: activity.name, + inputSummary: activity.inputSummary, + }).action === 'Deploy project'; +} + +export function lastFinishedAssistant(messages: readonly T[]) { + for (let index = messages.length - 1; index >= 0; index -= 1) { + const item = messages[index]; + if (item.role === 'assistant' && item.status && item.status !== 'running') { + return item; + } + } + return undefined; +} + +export function resolveDeployOffer( + messages: readonly DeployOfferMessage[], + options: { + canDownload: boolean; + loading: boolean; + hasLiveDeployment?: boolean; + }, +): DeployOfferKind | null { + if (options.loading || !options.canDownload) return null; + const last = lastFinishedAssistant(messages); + if (!last || last.status !== 'done') return null; + const lastActivities = last.activities ?? []; + const hasSuccessfulDeploy = (activities: readonly DeployOfferActivity[]) => + activities.some((activity) => isDeployProjectActivity(activity) && activity.status === 'completed'); + const hasFailedDeploy = lastActivities.some((activity) => ( + isDeployProjectActivity(activity) + && (activity.status === 'failed' || activity.status === 'stopped') + )); + if (hasSuccessfulDeploy(lastActivities) || hasFailedDeploy) return null; + if (lastActivities.some((activity) => isDeployProjectActivity(activity))) return null; + const everPublished = Boolean(options.hasLiveDeployment) + || messages.some((message) => hasSuccessfulDeploy(message.activities ?? [])); + if (lastActivities.some((activity) => activity.kind === 'tool' && !isDeployProjectActivity(activity))) { + return everPublished ? 'again' : 'first'; + } + const anyTools = messages.some((message) => ( + (message.activities ?? []).some((activity) => activity.kind === 'tool') + )); + if (!everPublished && !anyTools) return 'first'; + return null; +} + +type ToolActivity = Extract; + +export type AssistantTimelineTextBlock = { + kind: 'text'; + index: number; + content: string; +}; + +export type AssistantTimelineToolItem = { + index: number; + activity: ToolActivity; + repeats: ToolActivity[]; +}; + +export type AssistantTimelineToolBlock = { + kind: 'tools'; + items: AssistantTimelineToolItem[]; +}; + +export type AssistantTimelineBlock = AssistantTimelineTextBlock | AssistantTimelineToolBlock; + +export function normalizeTimelineText(value: string) { + return value.replace(/\s+/g, ' ').trim(); +} + +function referenceRowKey(activity: ToolActivity) { + const { topic, detailed } = presentToolActivity(activity); + return topic ? `${topic}:${detailed ? 'detail' : 'overview'}` : ''; +} + +export function buildAssistantTimeline(activities: AssistantActivity[]): AssistantTimelineBlock[] { + const blocks: AssistantTimelineBlock[] = []; + const referenceRows = new Map(); + + for (let index = 0; index < activities.length; index += 1) { + const activity = activities[index]; + if (activity.kind === 'text') { + if (!activity.content.trim()) continue; + blocks.push({ kind: 'text', index, content: activity.content }); + continue; + } + + let chain = blocks.at(-1); + if (chain?.kind !== 'tools') { + const opened: AssistantTimelineToolBlock = { kind: 'tools', items: [] }; + blocks.push(opened); + referenceRows.clear(); + chain = opened; + } + + const key = referenceRowKey(activity); + const open = key ? referenceRows.get(key) : undefined; + if (open) { + open.repeats.push(activity); + continue; + } + + const item: AssistantTimelineToolItem = { index, activity, repeats: [] }; + if (key) referenceRows.set(key, item); + chain.items.push(item); + } + return blocks; +} + +export function lastTimelineText(blocks: AssistantTimelineBlock[]) { + for (let index = blocks.length - 1; index >= 0; index -= 1) { + const block = blocks[index]; + if (block.kind === 'text') return block; + } + return undefined; +} + +export function trailingTimelineContent( + lastText: string | undefined, + finalContent: string, + status?: 'running' | 'done' | 'error' | 'stopped', +) { + const trailing = finalContent.trim(); + if (!trailing || status === 'running') return ''; + if (status === 'error' || !lastText?.trim()) return trailing; + const left = normalizeTimelineText(lastText); + const right = normalizeTimelineText(trailing); + if (left === right) return ''; + if (right.startsWith(left)) return right.slice(left.length).trimStart(); + return trailing; +} + +export function applyStreamEvent( + turn: PersistedActivityTurn, + event: ChatStreamEvent, +): PersistedActivityTurn { + if (event.type === 'text_segment' && event.data?.text) { + return { + ...turn, + activities: appendNarrationChunk(turn.activities, event.data.text), + }; + } + if (event.type === 'tool_use' && event.data?.id) { + const existing = turn.activities.find( + (item): item is Extract => + item.kind === 'tool' && item.toolUseId === event.data?.id, + ); + if (existing) { + existing.name = event.data.name || existing.name; + existing.inputSummary = event.data.inputSummary || existing.inputSummary; + existing.outputSummary = event.data.outputSummary || existing.outputSummary; + return { ...turn, activities: [...turn.activities] }; + } + return { + ...turn, + activities: [ + ...turn.activities, + { + kind: 'tool', + toolUseId: event.data.id, + name: event.data.name || 'tool', + status: 'running', + inputSummary: event.data.inputSummary, + outputSummary: event.data.outputSummary, + startedAt: event.data.startedAt || Date.now(), + }, + ], + }; + } + if (event.type === 'tool_result' && event.data?.id) { + return { + ...turn, + activities: turn.activities.map((activity) => ( + activity.kind === 'tool' && activity.toolUseId === event.data?.id + ? { + ...activity, + status: event.data.status || (event.data.ok ? 'completed' : 'failed'), + outputSummary: event.data.outputSummary || event.data.preview || activity.outputSummary, + endedAt: event.data.endedAt || Date.now(), + } + : activity + )), + }; + } + return turn; +} diff --git a/tests/activity.test.ts b/tests/activity.test.ts index 5e3eccb..ab4772a 100644 --- a/tests/activity.test.ts +++ b/tests/activity.test.ts @@ -1,12 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { - appendTrimmedActivityTurn, - dedupeActivityTurns, - summarizeToolInput, - summarizeToolOutput, -} from '../agents/_lib/utils/activity.ts'; -import type { PersistedActivityTurn } from '../agents/_lib/types.ts'; +import { summarizeToolInput, summarizeToolOutput } from '../shared/timeline.ts'; test('tool summaries redact secrets and project paths', () => { const summary = summarizeToolInput('mcp__edgeone__commands', { @@ -19,23 +13,6 @@ test('tool summaries redact secrets and project paths', () => { assert.match(summary, //); }); -test('activity history collapses immediate retry duplicates', () => { - const base: PersistedActivityTurn = { - id: 'first', - user: 'stop this', - assistant: 'stopped', - status: 'stopped', - createdAt: 100, - activities: [], - }; - const deduped = dedupeActivityTurns([ - base, - { ...base, id: 'retry', createdAt: 200, activities: [{ kind: 'text', content: 'partial' }] }, - ]); - assert.equal(deduped.length, 1); - assert.equal(deduped[0].id, 'retry'); -}); - test('file writes expose paths and sizes without source contents', () => { const summary = summarizeToolInput('write_project_file', { path: 'src/app.tsx', @@ -73,23 +50,3 @@ test('tool output is capped at two kilobytes', () => { assert.ok(summary.length < 2_100); assert.match(summary, /truncated$/); }); - -test('activity history replaces duplicate turns and applies both caps', () => { - const makeTurn = (id: string, count = 1): PersistedActivityTurn => ({ - id, - user: id, - assistant: id, - status: 'completed', - createdAt: 1, - activities: Array.from({ length: count }, (_, index) => ({ - kind: 'text' as const, - content: `${id}-${index}`, - })), - }); - const current = [makeTurn('one'), makeTurn('two')]; - const next = appendTrimmedActivityTurn(current, makeTurn('two', 4), 2, 3); - - assert.deepEqual(next.map((turn) => turn.id), ['one', 'two']); - assert.equal(next[1].activities.length, 3); - assert.equal(next[1].activities[0].kind === 'text' && next[1].activities[0].content, 'two-1'); -}); diff --git a/tests/app-shell.test.ts b/tests/app-shell.test.ts index f83f180..326cbe8 100644 --- a/tests/app-shell.test.ts +++ b/tests/app-shell.test.ts @@ -76,7 +76,7 @@ test('workspace actions stay in place and go quiet instead of disappearing', asy screen.indexOf("sandboxTab === 'preview'", actionsStart), ); assert.ok(projectActions.length > 0); - assert.match(projectActions, /disabled=\{downloadBusy \|\| !download\?\.url\}/); + assert.match(projectActions, /disabled=\{workspace\.downloadBusy \|\| !workspace\.download\?\.url\}/); assert.match(projectActions, /disabled=\{!canDeployProject\}/); assert.doesNotMatch(projectActions, /\{download\?\.url && /); // A native title is dropped on a disabled control, so the tooltip is CSS on an diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index bb1cdba..129b400 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import { readdir, readFile } from 'node:fs/promises'; +import { access, readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; import test from 'node:test'; @@ -53,7 +53,7 @@ test('shared modules remain runtime agnostic', async () => { const source = await readFile(file, 'utf8'); assert.doesNotMatch( source, - /(?:from\s+|import\s*)['"](?:react|next|@anthropic-ai|\.\.\/app|\.\.\/agents)/, + /(?:from\s+|import\s*)['"](?:react|next|@anthropic-ai|\.\.\/app|\.\.\/agents|node:)/, `${file} contains a framework or runtime dependency`, ); } @@ -61,6 +61,9 @@ test('shared modules remain runtime agnostic', async () => { const AGENT_ROUTE_FILES = new Set([ 'agents/session.ts', + 'agents/prompt.ts', + 'agents/deploy.ts', + 'agents/session-model.ts', 'agents/preview.ts', 'agents/stop.ts', 'agents/file.ts', @@ -87,3 +90,51 @@ test('agent routes stay at agents/ and implementation lives in agents/_lib/', as ); } }); + +test('workspace screen is assembled from session, live, preview, and workspace hooks', async () => { + const hooks = await readdir(path.join('app', 'features', 'workspace', 'hooks')); + for (const name of [ + 'use-session-resume.ts', + 'use-live-turn.ts', + 'use-preview-surface.ts', + 'use-workspace-state.ts', + ]) { + assert.ok(hooks.includes(name), `missing workspace hook ${name}`); + } +}); + +test('retired session-truth modules stay gone', async () => { + for (const target of [ + 'agents/_lib/memory.ts', + 'agents/_lib/agent.ts', + 'agents/_lib/chat-tasks.ts', + 'agents/_lib/shared.ts', + 'agents/_lib/pipelines', + 'shared/makers-dev.ts', + 'shared/makers-deploy.ts', + 'shared/npm-install.ts', + 'shared/tool-phase.ts', + 'shared/sanitize-assistant-text.ts', + ]) { + await assert.rejects(access(target), `${target} should have been removed`); + } +}); + +test('session kernel and makers CLI live under agents/_lib', async () => { + for (const target of [ + 'agents/_lib/session/store.ts', + 'agents/_lib/session/transcript.ts', + 'agents/_lib/session/live.ts', + 'agents/_lib/session/projection.ts', + 'agents/_lib/makers/session.ts', + 'agents/_lib/makers/cli-dev.ts', + 'agents/_lib/makers/preview-proxy-source.ts', + 'shared/timeline.ts', + 'shared/protocol.ts', + 'agents/prompt.ts', + 'agents/deploy.ts', + 'agents/session-model.ts', + ]) { + await access(target); + } +}); diff --git a/tests/chat-stream.test.ts b/tests/chat-stream.test.ts index 2cb21da..5976f4a 100644 --- a/tests/chat-stream.test.ts +++ b/tests/chat-stream.test.ts @@ -1,15 +1,15 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { createSSEResponse, sseEvent } from '../agents/_lib/shared.ts'; +import { createSSEResponse, sseEvent } from '../agents/_lib/runtime/sse.ts'; test('SSE responses frame events and terminate with DONE', async () => { const response = createSSEResponse(async function* () { - yield sseEvent({ type: 'status', message: 'ready' }); + yield sseEvent({ type: 'ping', ts: 1 }); }); const body = await response.text(); assert.equal(response.headers.get('content-type'), 'text/event-stream; charset=utf-8'); assert.equal(response.headers.get('cache-control'), 'no-cache, no-transform'); - assert.match(body, /^data: \{"type":"status","message":"ready"\}\n\n/); + assert.match(body, /^data: \{"type":"ping","ts":1\}\n\n/); assert.match(body, /data: \[DONE\]\n\n$/); }); diff --git a/tests/commands-wrap.test.ts b/tests/commands-wrap.test.ts index 2d99c9e..7369a8b 100644 --- a/tests/commands-wrap.test.ts +++ b/tests/commands-wrap.test.ts @@ -1,14 +1,14 @@ import assert from 'node:assert/strict'; import test from 'node:test'; import { wrapSandboxTools } from '../agents/_lib/tools/commands-wrap.ts'; -import { resolveMakersProjectName } from '../agents/_lib/project/makers-deploy.ts'; +import { resolveMakersProjectName } from '../agents/_lib/makers/project.ts'; import { MAKERS_DEV_PORT, PREVIEW_PATH_PREFIX, PREVIEW_PUBLIC_PORT, PREVIEW_SERVER_PORT, } from '../agents/_lib/constants.ts'; -import { MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS } from '../shared/makers-dev.ts'; +import { MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS } from '../agents/_lib/makers/cli-dev.ts'; import type { ClaudeMcpTool, DeploymentInfo, diff --git a/tests/deploy-task.test.ts b/tests/deploy-task.test.ts index 07ebf5a..92522a3 100644 --- a/tests/deploy-task.test.ts +++ b/tests/deploy-task.test.ts @@ -8,16 +8,16 @@ import { presentToolActivity } from '../app/lib/tool-activity.ts'; // mid-publish reconnects through the stream the frontend already knows. test('publishing occupies the chat task slot instead of a route of its own', async () => { const [tasks, route, resume, client] = await Promise.all([ - readFile('agents/_lib/chat-tasks.ts', 'utf8'), - readFile('agents/session.ts', 'utf8'), - readFile('agents/_lib/pipelines/resume.ts', 'utf8'), + readFile('agents/_lib/session/task.ts', 'utf8'), + readFile('agents/deploy.ts', 'utf8'), + readFile('agents/_lib/session/resume.ts', 'utf8'), readFile('app/features/workspace/workspace-api.ts', 'utf8'), ]); - assert.match(tasks, /intent === 'deploy'[\s\S]*?runDeployPipeline/); - assert.match(route, /body\?\.intent === 'deploy'/); + assert.match(tasks, /kind === 'deploy'[\s\S]*?runDeployPipeline/); + assert.match(route, /kind: 'deploy'/); assert.match(route, /siteDomain: String\(body\?\.siteDomain/); - assert.match(client, /\.\.\.\(options\.intent \? \{ intent: options\.intent \} : \{\}\)/); + assert.match(client, /fetch\('\/deploy'/); assert.match(client, /siteDomain: options\.siteDomain/); assert.doesNotMatch(resume, /streamUrl: `\/chat\?runId=/); assert.match(resume, /iterateLiveChatTaskEvents/); @@ -26,29 +26,30 @@ test('publishing occupies the chat task slot instead of a route of its own', asy // The project, the credential and the target project are all decided before // the button is even enabled, so there is nothing here for a model to choose. test('the deploy pipeline publishes without the model in the loop', async () => { - const pipeline = await readFile('agents/_lib/pipelines/deploy.ts', 'utf8'); + const [pipeline, session] = await Promise.all([ + readFile('agents/_lib/turn/deploy.ts', 'utf8'), + readFile('agents/_lib/makers/session.ts', 'utf8'), + ]); assert.doesNotMatch(pipeline, /runCodingAgent|from '\.\.\/(?:_agent|agent)'/); + assert.match(pipeline, /prepareMakersSession\(context, state, \{ syncEnv: true \}\)/); assert.match(pipeline, /projectName: resolveMakersProjectName\(context, state\),/); assert.match(pipeline, /buildMakersDeployLaunchCommand\(target\.projectName,/); assert.match(pipeline, /resolveConversationPublishArea\(state\)/); - assert.match(pipeline, /ensureMakersPublishProject/); + assert.match(session, /ensureMakersPublishProject/); assert.match( - pipeline, + session, /syncSandboxEnvToMakersProject\(\s*context,\s*state,\s*masterToken,/, ); assert.match(pipeline, /readMakersDeployOutcome\(stdout, '', sandboxToken\)/); - // Same short-lived tenant credential as every other sandbox CLI call. - assert.match(pipeline, /resolveSandboxMakersToken\(/); - assert.match(pipeline, /prepareSandboxGatewayEnv\(context, state\)/); + assert.match(session, /resolveSandboxMakersToken\(/); + assert.match(session, /prepareSandboxGatewayEnv\(context, state\)/); assert.match(pipeline, /shouldPauseForGatewayCredentials/); assert.doesNotMatch(pipeline, /waitForGatewayDecision/); - assert.match(pipeline, /buildSandboxMakersEnv\(/); - assert.doesNotMatch(pipeline, /sandboxEnv\.AI_GATEWAY/); - assert.doesNotMatch(pipeline, /buildSandboxMakersEnv\([^)]*gateway/); - // Nothing to publish is answered before the CLI is ever started. + assert.match(session, /buildSandboxMakersEnv\(/); + assert.doesNotMatch(session, /sandboxEnv\.AI_GATEWAY/); + assert.doesNotMatch(session, /buildSandboxMakersEnv\([^)]*gateway/); assert.match(pipeline, /if \(!files\.some\(\(item\) => item\.type === 'file'\)\)/); - // The live URL is the deliverable, so the reply carries it in full. assert.match(pipeline, /withLiveDeploymentUrl\(copy\.success, outcome\.url\)/); }); @@ -59,9 +60,9 @@ test('the deploy pipeline publishes without the model in the loop', async () => // do it separately. test('publishing stops the preview dev server before the build starts', async () => { const [deploy, dev, pipeline, wrapper] = await Promise.all([ - readFile('shared/makers-deploy.ts', 'utf8'), - readFile('shared/makers-dev.ts', 'utf8'), - readFile('agents/_lib/pipelines/deploy.ts', 'utf8'), + readFile('agents/_lib/makers/cli-deploy.ts', 'utf8'), + readFile('agents/_lib/makers/cli-dev.ts', 'utf8'), + readFile('agents/_lib/turn/deploy.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), ]); @@ -93,7 +94,7 @@ test('publishing stops the preview dev server before the build starts', async () test('publishing restarts the preview without paying for the smoke gates again', async () => { const [preview, pipeline, wrapper] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('agents/_lib/pipelines/deploy.ts', 'utf8'), + readFile('agents/_lib/turn/deploy.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), ]); @@ -156,7 +157,7 @@ test('a publish closes the routes out of the preview without covering it', async // if the pipeline forwards it. Discarding it here is what left a failed deploy // showing one sentence that named no cause. test('a failed publish shows the CLI output on the card and one line in the chat', async () => { - const pipeline = await readFile('agents/_lib/pipelines/deploy.ts', 'utf8'); + const pipeline = await readFile('agents/_lib/turn/deploy.ts', 'utf8'); assert.match(pipeline, /await fail\(error, outcome\.status === 'error' \? outcome\.detail \?\? '' : ''\)/); // The CLI's own diagnosis is what gets reported. A watch that ran out only @@ -193,7 +194,7 @@ test('publish is offered above the composer after a finished project turn', asyn ]); assert.match(screen, /resolveDeployOffer/); - assert.match(screen, /deployOffer=\{gatewayNeeded \? null : deployOffer\}/); + assert.match(screen, /deployOffer=\{workspace\.gatewayNeeded \? null : deployOffer\}/); assert.match(screen, /onDeployOffer=\{handleDeployProject\}/); assert.match(conversation, /className="deploy-offer"/); assert.match(conversation, /className="conversation-composer-dock"/); @@ -203,20 +204,21 @@ test('publish is offered above the composer after a finished project turn', asyn }); test('the deploy button is disabled until a project exists and nothing is running', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); + const [screen, live] = await Promise.all([ + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + ]); - assert.match(screen, /const hasDeployableProject = Boolean\(download\?\.url\)/); - assert.match(screen, /const publishing = deployment\?\.status === 'running'/); - assert.match(screen, /const deployRunning = loading \|\| publishing/); + assert.match(screen, /const hasDeployableProject = Boolean\(workspace\.download\?\.url\)/); + assert.match(screen, /const publishing = workspace\.deployment\?\.status === 'running'/); + assert.match(screen, /const deployRunning = live\.loading \|\| publishing/); assert.match( screen, - /const canDeployProject = hasDeployableProject && !deployRunning && !workspaceRestoring/, + /const canDeployProject = hasDeployableProject && !deployRunning && !resume\.workspaceRestoring/, ); - assert.match(screen, /sendMessage\(t\.workspace\.deployRequest, \{ intent: 'deploy' \}\)/); - assert.match(screen, /siteDomain: extractProjectName\(\)\.domain/); + assert.match(screen, /sendMessage\(t\.workspace\.deployRequest, \{ deploy: true \}\)/); + assert.match(live, /siteDomain: extractProjectName\(\)\.domain/); assert.match(screen, /disabled=\{!canDeployProject\}/); - // The rocket stays put while a publish runs. A spinner here was a second - // progress indicator next to the preview overlay that already says so. assert.match(screen, /className="workspace-icon-button is-publish"/); assert.doesNotMatch(screen, /is-running/); assert.match(screen, //); @@ -224,8 +226,6 @@ test('the deploy button is disabled until a project exists and nothing is runnin screen.slice(screen.indexOf('handleDeployProject'), screen.indexOf('handleDownload')), /workspace-icon-spinner/, ); - // An icon says nothing on its own, and a disabled one says even less about - // why, so the same tooltip names the action and explains a refusal. assert.match(screen, /data-tooltip=\{deployHint\}/); assert.match( screen, @@ -247,7 +247,7 @@ test('the header ships the template, the panel ships the project', async () => { assert.match(header, /href=\{templateSourceUrl\}/); assert.doesNotMatch(header, /canDeploy|onDeploy|onDownload/); assert.match(screen, /onClick=\{handleDeployProject\}/); - assert.match(screen, /onClick=\{\(\) => void handleDownload\(\)\}/); + assert.match(screen, /onClick=\{\(\) => void workspace\.handleDownload\(conversationId, t\.workspace\.downloadFailed\)\}/); }); // Resume hands back whatever deployment the stored conversation carries, so the @@ -255,12 +255,12 @@ test('the header ships the template, the panel ships the project', async () => { // it on presence, a URL published in an earlier session stayed on screen through a // session that never published anything. test('resumed history decides the deployment card, including when there is none', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); - const start = screen.indexOf('const applyHistory = (data: ResumeData)'); - const body = screen.slice(start, screen.indexOf('const applyWorkspace = (data: ResumeData) => {', start)); + const resume = await readFile('app/features/workspace/hooks/use-session-resume.ts', 'utf8'); + const start = resume.indexOf('const applyHistory = (data: ResumeData)'); + const body = resume.slice(start, resume.indexOf('const applyWorkspace = (data: ResumeData) => {', start)); assert.ok(start >= 0 && body.length > 0); - assert.match(body, /setDeployment\(data\.deployment \?\? null\)/); + assert.match(body, /workspace\.setDeployment\(data\.deployment \?\? null\)/); assert.doesNotMatch(body, /if \(data\.deployment\) \{\s*setDeployment/); }); @@ -268,7 +268,10 @@ test('resumed history decides the deployment card, including when there is none' // the user starts a new project used to keep applying its events, restoring the // previous conversation — id, history and deployment — over the fresh one. test('starting a new project stops the resume that was already in flight', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); + const [screen, resume] = await Promise.all([ + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/hooks/use-session-resume.ts', 'utf8'), + ]); const reset = screen.slice( screen.indexOf('function startNewProject() {'), screen.indexOf('function handleNewProject() {'), @@ -276,9 +279,8 @@ test('starting a new project stops the resume that was already in flight', async assert.ok(reset.length > 0); assert.match(reset, /resumeAbortControllerRef\.current\?\.abort\(\)/); - // Aborting only stops the fetch; events already in hand still need the epoch. assert.match( - screen, + resume, /if \(cancelled \|\| workspaceEpoch !== workspaceEpochRef\.current \|\| event\.type === 'ping'\) return;/, ); }); @@ -286,14 +288,15 @@ test('starting a new project stops the resume that was already in flight', async // The composer is a text field the user may be mid-sentence in, and the Files // panel is not waiting on anything a publish does. test('publishing leaves the composer and the files panel alone', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); - const start = screen.indexOf('async function sendMessage('); - const body = screen.slice(start, screen.indexOf('async function handleSubmit(', start)); + const live = await readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'); + const start = live.indexOf('async function sendMessage('); + const body = live.slice(start, live.indexOf('function stopCurrentTask(', start)); assert.ok(start >= 0 && body.length > 0); - assert.match(body, /const isStartingFromHome = !isDeploy && !isGatewayCard && !hasWorkspace/); + assert.match(body, /const isStartingFromHome = !isDeploy && !isGatewayCard/); assert.match(body, /if \(isStartingFromHome\) \{[\s\S]*?openSessionStream/); - assert.match(body, /startSessionTurn\(/); - assert.match(body, /if \(!isDeploy\) \{\s*setFilesRefreshing\(true\);/); + assert.match(body, /startPromptTurn\(/); + assert.match(body, /startDeployTurn\(/); + assert.match(body, /if \(!isDeploy\) \{\s*workspace\.setFilesRefreshing\(true\);/); assert.match(body, /if \(!isGatewayCard\) setInput\(''\)/); }); diff --git a/tests/gateway-prompt.test.ts b/tests/gateway-prompt.test.ts index ba03803..3a9566a 100644 --- a/tests/gateway-prompt.test.ts +++ b/tests/gateway-prompt.test.ts @@ -22,7 +22,7 @@ import { readProjectGatewayEnv, sandboxGatewayKeyIsSet, shouldPauseForGatewayCredentials, -} from '../agents/_lib/project/gateway-prompt.ts'; +} from '../agents/_lib/project/gateway.ts'; import { projectState } from './helpers/fixtures.ts'; function sandboxFiles(initial: Array<[string, string]>) { @@ -232,9 +232,10 @@ test('request_gateway_credentials asks once and does not wait', async () => { }); test('the conversation card asks for API Key and submits a masked chat turn', async () => { - const [conversation, screen, api] = await Promise.all([ + const [conversation, screen, live, api] = await Promise.all([ readFile('app/components/agent-conversation.tsx', 'utf8'), readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), readFile('app/features/workspace/workspace-api.ts', 'utf8'), ]); const card = conversation.slice( @@ -266,20 +267,18 @@ test('the conversation card asks for API Key and submits a masked chat turn', as assert.equal(DEFAULT_AI_GATEWAY_BASE_URL, 'https://ai-gateway.edgeone.link/v1'); assert.equal(AI_GATEWAY_ORIGIN, 'https://ai-gateway.edgeone.link'); assert.match(screen, /maskApiKey\(apiKey\)/); - assert.match(screen, /extractApiKeyFromUserText\(trimmed\)/); - assert.match(screen, /inboundApiKey \? \{ apiKey: inboundApiKey \}/); + assert.match(live, /extractApiKeyFromUserText\(trimmed\)/); + assert.match(live, /inboundApiKey \? \{ apiKey: inboundApiKey \}/); assert.match(screen, /sendMessage\(`\$\{t\.workspace\.gatewayPromptApiKey\}: \$\{maskApiKey\(apiKey\)\}`, \{ apiKey \}\)/); assert.match(screen, /sendMessage\(t\.workspace\.gatewayPromptSkip, \{ gatewaySkip: true \}\)/); assert.doesNotMatch(api, /gateway-credentials/); assert.match(api, /options\.apiKey \? \{ apiKey: options\.apiKey \}/); - // The card stays after the assistant turn ends; wiping it in finalize made - // the input appear and then vanish. - const finalize = screen.slice( - screen.indexOf('const finalizeAssistant'), - screen.indexOf('const activatePreview'), + const finalize = live.slice( + live.indexOf('const finalizeAssistant'), + live.indexOf('const applyResponse'), ); assert.doesNotMatch(finalize, /setGatewayNeeded\(false\)/); - assert.match(screen, /if \(data\.gatewayNeeded\) \{\s*setGatewayNeeded\(true\);/); + assert.match(live, /if \(data\.gatewayNeeded\) \{\s*workspace\.setGatewayNeeded\(true\);/); }); test('the API key card waits until the assistant turn has finished', async () => { @@ -291,15 +290,15 @@ test('the API key card waits until the assistant turn has finished', async () => // The tool asks mid-stream, but showing the card then greys it out for the // last few seconds of copy. Hold it until loading is false so it appears // ready to type into. - assert.match(screen, /gatewayPrompt=\{gatewayNeeded && !loading \? \{/); + assert.match(screen, /gatewayPrompt=\{workspace\.gatewayNeeded && !live\.loading \? \{/); assert.match(conversation, /autoFocus/); assert.match(conversation, /disabled=\{gatewayBusy\}/); }); test('a turn waiting for the API key is completed, not a red error', async () => { const [chat, helpers, prompt] = await Promise.all([ - readFile('agents/_lib/pipelines/chat.ts', 'utf8'), - readFile('agents/_lib/pipelines/helpers.ts', 'utf8'), + readFile('agents/_lib/turn/chat.ts', 'utf8'), + readFile('agents/_lib/turn/checkpoint.ts', 'utf8'), readFile('agents/_lib/prompt.ts', 'utf8'), ]); const pause = chat.slice( @@ -326,8 +325,8 @@ test('a turn waiting for the API key is completed, not a red error', async () => test('the host writes .env from a chat sentence, not only from the card', async () => { const [chat, tasks, prompt] = await Promise.all([ - readFile('agents/_lib/pipelines/chat.ts', 'utf8'), - readFile('agents/_lib/chat-tasks.ts', 'utf8'), + readFile('agents/_lib/turn/chat.ts', 'utf8'), + readFile('agents/_lib/session/task.ts', 'utf8'), readFile('agents/_lib/prompt.ts', 'utf8'), ]); assert.match(chat, /resolveGatewayUserTurn\(message, options\.apiKey\)/); diff --git a/tests/makers-compat.test.ts b/tests/makers-compat.test.ts index 03d1082..57ac2e6 100644 --- a/tests/makers-compat.test.ts +++ b/tests/makers-compat.test.ts @@ -51,10 +51,10 @@ test('official Makers router and progressive references are vendored unchanged i // What the prompt actually says is asserted behaviourally in // prompt-single-source.test.ts; this covers the SDK session wiring around it. test('the SDK session is wired to the vendored skills and the extracted prompt', async () => { - const source = await readFile('agents/_lib/agent.ts', 'utf8'); + const source = await readFile('agents/_lib/session/live.ts', 'utf8'); assert.match(source, /skills: \[\.\.\.MAKERS_SKILL_NAMES\]/); assert.match(source, /tools: \['Skill'\]/); - assert.match(source, /buildPrompt\([\s\S]*?makersProjectName,[\s\S]*?\)/); + assert.match(source, /buildPrompt\(/); assert.doesNotMatch( source, /Vite projects must support sandbox preview under/, @@ -75,10 +75,10 @@ test('package.json without scripts.build is not a thrown verification failure', test('direct sandbox CLI replaces custom tools while retaining relevant compatibility checks', async () => { const [agent, projectTools, commandTools, compatibility] = await Promise.all([ - readFile('agents/_lib/agent.ts', 'utf8'), + readFile('agents/_lib/session/live.ts', 'utf8'), readFile('agents/_lib/tools/project-tools.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), - readFile('agents/_lib/project/makers-compat.ts', 'utf8'), + readFile('agents/_lib/makers/compat/lint-script.ts', 'utf8'), ]); const paths = await readFile('agents/_lib/utils/paths.ts', 'utf8'); assert.doesNotMatch(agent, /buildPublishPreviewTool|buildDeployToMakersTool/); @@ -97,7 +97,7 @@ test('direct sandbox CLI replaces custom tools while retaining relevant compatib test('specific Makers skill loader reads official references without changing them', async () => { const source = await readFile('agents/_lib/tools/makers-skills.ts', 'utf8'); - const agent = await readFile('agents/_lib/agent.ts', 'utf8'); + const agent = await readFile('agents/_lib/tools/assemble.ts', 'utf8'); assert.match(source, /'load_makers_skill'/); assert.match(agent, /buildLoadMakersSkillTool/); assert.match(agent, /__load_makers_skill/); @@ -114,7 +114,7 @@ test('specific Makers skill loader reads official references without changing th }); test('cold resume restores project dependencies without managing the sandbox CLI', async () => { - const resume = await readFile('agents/_lib/pipelines/resume.ts', 'utf8'); + const resume = await readFile('agents/_lib/session/resume.ts', 'utf8'); const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); assert.match(resume, /const depsReady = await ensureProjectDependencies\(context, state\)/); assert.doesNotMatch(resume, /prewarmEdgeoneCli|npm install -g edgeone/); diff --git a/tests/makers-declarations.test.ts b/tests/makers-declarations.test.ts index 0f996fc..79f292c 100644 --- a/tests/makers-declarations.test.ts +++ b/tests/makers-declarations.test.ts @@ -5,10 +5,8 @@ import os from 'node:os'; import path from 'node:path'; import { promisify } from 'node:util'; import test from 'node:test'; -import { - buildMakersCompatibilityScript, - loadMakersValidationRules, -} from '../agents/_lib/project/makers-compat.ts'; +import { buildMakersCompatibilityScript } from '../agents/_lib/makers/compat/lint-script.ts'; +import { loadMakersFrameworkProfiles, loadMakersValidationRules } from '../agents/_lib/makers/compat/skill-rules.ts'; import { ensureMakersAgentDeclarations, inferMakersAgentFramework, @@ -16,8 +14,7 @@ import { withAgentFramework, withFrameworkAdapter, type ProjectFileRead, -} from '../agents/_lib/project/makers-declarations.ts'; -import { loadMakersFrameworkProfiles } from '../agents/_lib/project/makers-compat.ts'; +} from '../agents/_lib/makers/declarations.ts'; import { buildWriteProjectFileTool } from '../agents/_lib/tools/project-tools.ts'; import { projectState } from './helpers/fixtures.ts'; diff --git a/tests/makers-deploy.test.ts b/tests/makers-deploy.test.ts index 24c81a3..05ce3f9 100644 --- a/tests/makers-deploy.test.ts +++ b/tests/makers-deploy.test.ts @@ -20,15 +20,15 @@ import { parseMakersDeployProgress, readMakersDeployOutcome, redactSecret, -} from '../shared/makers-deploy.ts'; -import { shellQuote } from '../shared/shell.ts'; +} from '../agents/_lib/makers/cli-deploy.ts'; +import { shellQuote } from '../agents/_lib/utils/shell.ts'; import { ensureMakersPublishProject, parsePublishableDotEnv, resolveConversationPublishArea, resolveMakersProjectName, syncSandboxEnvToMakersProject, -} from '../agents/_lib/project/makers-deploy.ts'; +} from '../agents/_lib/makers/project.ts'; import { projectState } from './helpers/fixtures.ts'; test('builds a non-interactive direct CLI deploy command', () => { @@ -591,18 +591,19 @@ test('each conversation owns one project, for preview and deploy alike', () => { // about the name, whichever ran first would pin the link file and the other // would either be silently ignored or repoint it mid-conversation. test('preview and deploy resolve the project through the same function', async () => { - const [previewSource, commandSource] = await Promise.all([ + const [previewSource, commandSource, sessionSource] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readFile('agents/_lib/makers/session.ts', 'utf8'), ]); - assert.match(previewSource, /resolveMakersProjectName\(context, state\)/); - assert.match(commandSource, /resolveMakersProjectName\(lifecycle\.context, lifecycle\.state\)/); - // One resolution per command, reused by both branches. - assert.equal(commandSource.match(/resolveMakersProjectName\(/g)?.length, 1); - for (const source of [previewSource, commandSource]) { + assert.match(previewSource, /prepareMakersSession\(context, state\)/); + assert.match(commandSource, /prepareMakersSession\(lifecycle\.context, lifecycle\.state/); + assert.match(sessionSource, /resolveMakersProjectName\(context, state\)/); + assert.match(sessionSource, /ensureMakersPublishProject\(/); + assert.equal(sessionSource.match(/resolveMakersProjectName\(/g)?.length, 1); + for (const source of [previewSource, commandSource, sessionSource]) { assert.doesNotMatch(source, /vibe-coding-playground/); - assert.match(source, /ensureMakersPublishProject/); } }); @@ -789,42 +790,34 @@ test('deploy does not copy .env when there is no master token', async () => { }); test('deploy copies .env with the runtime master token, not the sandbox tenant token', async () => { - const [deploy, wrap, helper] = await Promise.all([ - readFile('agents/_lib/pipelines/deploy.ts', 'utf8'), + const [session, wrap, helper] = await Promise.all([ + readFile('agents/_lib/makers/session.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), - readFile('agents/_lib/project/makers-deploy.ts', 'utf8'), + readFile('agents/_lib/makers/project.ts', 'utf8'), ]); assert.match(helper, /masterToken: string/); assert.match( - deploy, + session, /syncSandboxEnvToMakersProject\(\s*context,\s*state,\s*masterToken,/, ); - assert.match( - wrap, - /syncSandboxEnvToMakersProject\(\s*lifecycle\.context,\s*lifecycle\.state,\s*masterToken,/, - ); + assert.match(wrap, /prepareMakersSession\(/); assert.doesNotMatch( - deploy, + session, /syncSandboxEnvToMakersProject\(\s*context,\s*state,\s*sandboxToken,/, ); - assert.doesNotMatch( - wrap, - /syncSandboxEnvToMakersProject\(\s*lifecycle\.context,\s*lifecycle\.state,\s*sandboxToken,/, - ); - assert.match(deploy, /ensureMakersPublishProject\(\s*sandboxToken,/); - assert.match(wrap, /ensureMakersPublishProject\(\s*sandboxToken,/); + assert.match(session, /ensureMakersPublishProject\(\s*sandboxToken,/); }); test('uses the sandbox-provided CLI without installing or prewarming it', async () => { const paths = [ - 'shared/makers-deploy.ts', + 'agents/_lib/makers/cli-deploy.ts', 'agents/_lib/tools/commands-wrap.ts', - 'agents/_lib/project/makers-deploy.ts', + 'agents/_lib/makers/project.ts', 'agents/_lib/project/preview.ts', 'agents/_lib/project/scaffold.ts', - 'agents/_lib/pipelines/chat.ts', - 'agents/_lib/pipelines/resume.ts', + 'agents/_lib/turn/chat.ts', + 'agents/_lib/session/resume.ts', ]; const source = (await Promise.all(paths.map((path) => readFile(path, 'utf8')))).join('\n'); diff --git a/tests/makers-dev.test.ts b/tests/makers-dev.test.ts index 21ba3e6..49f743b 100644 --- a/tests/makers-dev.test.ts +++ b/tests/makers-dev.test.ts @@ -25,7 +25,7 @@ import { previewTrailingSlashFollow, previewUpstreamClaimsPrefix, rewritePreviewProxyPath, -} from '../shared/makers-dev.ts'; +} from '../agents/_lib/makers/cli-dev.ts'; import { MAKERS_DEV_PORT, PREVIEW_ASSET_PREFIX_ENV, @@ -1044,12 +1044,16 @@ test('makers-dev captured exit markers preserve CLI failures', () => { }); test('sandbox preview publishes the fixed gateway path through a local adapter', async () => { - const preview = await readFile('agents/_lib/project/preview.ts', 'utf8'); + const [preview, session] = await Promise.all([ + readFile('agents/_lib/project/preview.ts', 'utf8'), + readFile('agents/_lib/makers/session.ts', 'utf8'), + ]); assert.doesNotMatch(preview, /ensureEdgeoneCli|npm install -g edgeone/); assert.match(preview, /buildMakersDevLaunchCommand/); assert.match(preview, /buildMakersDevBackgroundCommand/); assert.match(preview, /resolveConversationPublishArea\(state\)/); - assert.match(preview, /ensureMakersPublishProject/); + assert.match(preview, /prepareMakersSession/); + assert.match(session, /ensureMakersPublishProject/); assert.doesNotMatch(preview, /syncSandboxEnvToMakersProject/); assert.match(preview, /getHost\(PREVIEW_PUBLIC_PORT\)/); assert.match( diff --git a/tests/makers-file-semantics.test.ts b/tests/makers-file-semantics.test.ts index 7ed4aa2..cf66287 100644 --- a/tests/makers-file-semantics.test.ts +++ b/tests/makers-file-semantics.test.ts @@ -48,8 +48,8 @@ test('maps Makers function and agent files to public routes', () => { test('does not mislabel helpers, configs, or ordinary frontend files as routes', () => { assert.equal(file('agents/_shared.ts'), null); assert.equal(file('agents/chat/_tools.ts'), null); - assert.equal(file('agents/_lib/agent.ts'), null); - assert.equal(file('agents/_lib/pipelines/index.ts'), null); + assert.equal(file('agents/_lib/session/live.ts'), null); + assert.equal(file('agents/_lib/turn/chat.ts'), null); assert.equal(file('agents/_lib/project/index.ts'), null); assert.equal(file('cloud-functions/requirements.txt'), null); assert.equal(file('edge-functions/api/README.md'), null); diff --git a/tests/makers-lint.test.ts b/tests/makers-lint.test.ts index ee57d27..3331273 100644 --- a/tests/makers-lint.test.ts +++ b/tests/makers-lint.test.ts @@ -5,13 +5,9 @@ import os from 'node:os'; import path from 'node:path'; import { promisify } from 'node:util'; import test from 'node:test'; -import { - assertMakersProjectCompatible, - buildMakersCompatibilityScript, - loadMakersFrameworkProfiles, - loadMakersValidationRules, - runMakersCompatibilityCheck, -} from '../agents/_lib/project/makers-compat.ts'; +import { assertMakersProjectCompatible, runMakersCompatibilityCheck } from '../agents/_lib/makers/compat/run.ts'; +import { buildMakersCompatibilityScript } from '../agents/_lib/makers/compat/lint-script.ts'; +import { loadMakersFrameworkProfiles, loadMakersValidationRules } from '../agents/_lib/makers/compat/skill-rules.ts'; import { projectState } from './helpers/fixtures.ts'; const execFileAsync = promisify(execFile); diff --git a/tests/makers-sub-token.test.ts b/tests/makers-sub-token.test.ts index d494441..c7c2cfe 100644 --- a/tests/makers-sub-token.test.ts +++ b/tests/makers-sub-token.test.ts @@ -9,7 +9,7 @@ import { resolveMakersMasterToken, resolveSandboxGatewayEnv, resolveSandboxMakersToken, -} from '../agents/_lib/project/makers-token.ts'; +} from '../agents/_lib/makers/token.ts'; import { projectState } from './helpers/fixtures.ts'; test('sandbox Makers tenant IDs are generated server-side and remain stable', () => { @@ -99,7 +99,7 @@ test('the runtime credential is read from API_TOKEN', () => { // valid" with nothing pointing at the split. Both ends default to production, // so the way to keep them together is to leave nothing to configure. test('no environment switch is left for a deployment to get wrong', async () => { - const tokenSource = await readFile('agents/_lib/project/makers-token.ts', 'utf8'); + const tokenSource = await readFile('agents/_lib/makers/token.ts', 'utf8'); for (const name of [ 'MAKERS_API_ENV', @@ -122,7 +122,7 @@ test('no environment switch is left for a deployment to get wrong', async () => // scrubber, which blanks whatever follows a "token:" label and leaves the // operator staring at "[REDACTED] region detection failed." test('token issue failures survive the activity scrubber', async () => { - const tokenSource = await readFile('agents/_lib/project/makers-token.ts', 'utf8'); + const tokenSource = await readFile('agents/_lib/makers/token.ts', 'utf8'); const messages = tokenSource.match(/Failed to issue a temporary Makers[^`]*/g) || []; assert.equal(messages.length, 2); @@ -145,7 +145,7 @@ test('a sandbox CLI login failure names the runtime key, not a browser login', ( // The master credential is the one thing the sandbox must never hold: it is // account-wide and long-lived, while a CLI process is neither. test('the master credential is exchanged, never handed to the sandbox', async () => { - const tokenSource = await readFile('agents/_lib/project/makers-token.ts', 'utf8'); + const tokenSource = await readFile('agents/_lib/makers/token.ts', 'utf8'); const resolver = tokenSource.match( /export async function resolveSandboxMakersToken[\s\S]*?\n}/, )?.[0] || ''; @@ -174,16 +174,17 @@ test('the tenant token is redacted out of CLI output', async () => { readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), ]); - assert.match(previewSource, /redactSecret\(\s*failure,\s*sandboxToken,?\s*\)/); + assert.match(previewSource, /redactSecret\(\s*failure,\s*makers\.sandboxToken,?\s*\)/); assert.match(commandSource, /redactToolResult\(result, makers\.sandboxToken\)/); assert.match(commandSource, /redactSecret\(/); }); test('direct CLI calls route the runtime credential through one resolver', async () => { - const [tokenSource, previewSource, commandSource, packageSource] = await Promise.all([ - readFile('agents/_lib/project/makers-token.ts', 'utf8'), + const [tokenSource, previewSource, commandSource, sessionSource, packageSource] = await Promise.all([ + readFile('agents/_lib/makers/token.ts', 'utf8'), readFile('agents/_lib/project/preview.ts', 'utf8'), readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readFile('agents/_lib/makers/session.ts', 'utf8'), readFile('package.json', 'utf8'), ]); @@ -193,18 +194,14 @@ test('direct CLI calls route the runtime credential through one resolver', async // Resume starts Makers dev without an LLM command, while normal preview and // deploy calls are intercepted on the generic sandbox commands tool. Neither // may reach past the resolver for a credential of its own. - assert.match(previewSource, /resolveSandboxMakersToken\(state, masterToken\)/); - assert.match(previewSource, /prepareSandboxGatewayEnv\(context, state\)/); - assert.match(previewSource, /buildSandboxMakersEnv\(sandboxToken, state\.makersApiRegion\)/); - assert.doesNotMatch(previewSource, /buildSandboxMakersEnv\([^)]*gateway/); - assert.match(commandSource, /resolveSandboxMakersToken\(/); - assert.match(commandSource, /prepareSandboxGatewayEnv\(lifecycle\.context, lifecycle\.state\)/); - assert.match( - commandSource, - /syncSandboxEnvToMakersProject\(\s*lifecycle\.context,\s*lifecycle\.state,\s*masterToken,/, - ); + assert.match(sessionSource, /resolveSandboxMakersToken\(state, masterToken\)/); + assert.match(sessionSource, /prepareSandboxGatewayEnv\(context, state\)/); + assert.match(sessionSource, /buildSandboxMakersEnv\(sandboxToken, state\.makersApiRegion\)/); + assert.doesNotMatch(sessionSource, /buildSandboxMakersEnv\([^)]*gateway/); + assert.match(previewSource, /prepareMakersSession\(context, state\)/); + assert.match(commandSource, /prepareMakersSession\(lifecycle\.context, lifecycle\.state/); assert.match(commandSource, /pauseForGatewayCredentialsIfNeeded/); - assert.match(commandSource, /buildSandboxMakersEnv\(/); + assert.doesNotMatch(previewSource, /buildSandboxMakersEnv\([^)]*gateway/); assert.doesNotMatch(commandSource, /buildSandboxMakersEnv\([^)]*gateway/); assert.doesNotMatch(tokenSource, /\.\.\.gateway/); for (const source of [previewSource, commandSource]) { diff --git a/tests/narration.test.ts b/tests/narration.test.ts index 85cebac..65c060b 100644 --- a/tests/narration.test.ts +++ b/tests/narration.test.ts @@ -3,7 +3,7 @@ import test from 'node:test'; import { resolveNarrationEmit, type NarrationEmitState, -} from '../agents/_lib/utils/narration.ts'; +} from '../shared/timeline.ts'; function emptyState(): NarrationEmitState { return { currentTextBlock: '', emittedNarration: '' }; diff --git a/tests/npm-install.test.ts b/tests/npm-install.test.ts index dc9ea13..2a3c194 100644 --- a/tests/npm-install.test.ts +++ b/tests/npm-install.test.ts @@ -11,12 +11,12 @@ import { buildNpmWarmupCommand, buildNpmWarmupHandoffScript, buildNpmWarmupWaitScript, -} from '../shared/npm-install.ts'; +} from '../agents/_lib/makers/npm-install.ts'; import { isBareInstallCommand, isScaffolderCommand, withExitCodeEcho, -} from '../shared/tool-phase.ts'; +} from '../agents/_lib/makers/tool-phase.ts'; const run = promisify(execFile); diff --git a/tests/preview-path.test.ts b/tests/preview-path.test.ts index c14e9ea..b1c0cb0 100644 --- a/tests/preview-path.test.ts +++ b/tests/preview-path.test.ts @@ -10,7 +10,7 @@ import { buildGeneratedApiSmokeScript, buildGeneratedChatSmokeScript, buildPreviewProxyScript, -} from '../shared/makers-dev.ts'; +} from '../agents/_lib/makers/cli-dev.ts'; import { agentRoutesFromListing, generatedRoutesFromListing } from '../agents/_lib/project/preview.ts'; import { previewDisplayPathFromPath } from '../shared/preview-display-path.ts'; @@ -51,9 +51,9 @@ test('preview address chip hides the gateway prefix and access_token', () => { // the route the preview opened with for as long as the tests stayed green. So // run the real proxy against a stub upstream and read what reaches the browser. test('the preview proxy feeds the route mirror the parent listens for', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); - assert.match(screen, /__edgeonePreviewPath/); - assert.match(screen, /addEventListener\('message'/); + const preview = await readFile('app/features/workspace/hooks/use-preview-surface.ts', 'utf8'); + assert.match(preview, /__edgeonePreviewPath/); + assert.match(preview, /addEventListener\('message'/); const upstream = http.createServer((req, res) => { if ((req.url || '').startsWith('/asset.js')) { @@ -160,7 +160,8 @@ async function waitForServer(url: string) { test('sandbox preview strips the public prefix before forwarding to makers-dev', async () => { const preview = await readFile('agents/_lib/project/preview.ts', 'utf8'); - const makersDev = await readFile('shared/makers-dev.ts', 'utf8'); + const makersDev = await readFile('agents/_lib/makers/cli-dev.ts', 'utf8'); + const proxySource = await readFile('agents/_lib/makers/preview-proxy-source.ts', 'utf8'); assert.match(preview, /makers-dev/); assert.match(preview, /buildMakersDevLaunchCommand/); assert.match(preview, /assertMakersProjectCompatible/); @@ -169,7 +170,7 @@ test('sandbox preview strips the public prefix before forwarding to makers-dev', assert.match(makersDev, /skip-env-sync/); assert.match(makersDev, /skip-ai-gateway-sync/); assert.match(makersDev, /buildPreviewProxyScript/); - assert.match(makersDev, /server\.on\('upgrade'/); + assert.match(proxySource, /server\.on\('upgrade'/); assert.match(preview, /PREVIEW_PATH_PREFIX/); assert.doesNotMatch(preview, /python3 -m http\.server/); }); @@ -177,7 +178,7 @@ test('sandbox preview strips the public prefix before forwarding to makers-dev', test('agent chat previews are smoke-tested before being published', async () => { const [preview, makersDev] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('shared/makers-dev.ts', 'utf8'), + readFile('agents/_lib/makers/cli-dev.ts', 'utf8'), ]); assert.match(preview, /assertGeneratedAgentChatReady/); assert.match(preview, /buildGeneratedChatSmokeScript/); @@ -472,19 +473,22 @@ test('cold preview probes do not throw on curl connection refused', async () => }); test('expired preview credentials never fall back to the stale iframe URL', async () => { - const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); + const [screen, preview] = await Promise.all([ + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/hooks/use-preview-surface.ts', 'utf8'), + ]); - assert.match(screen, /PREVIEW_CREDENTIAL_REFRESH_MS/); - assert.match(screen, /isMakersPreviewRef/); - assert.match(screen, /setPreviewRefreshFailed\(true\)/); + assert.match(preview, /PREVIEW_CREDENTIAL_REFRESH_MS/); + assert.match(preview, /isMakersPreviewRef/); + assert.match(preview, /setPreviewRefreshFailed\(true\)/); assert.match(screen, /previewUnavailable/); assert.doesNotMatch( - screen, + preview, /setActivePreviewUrl\(previousActiveUrl\)/, 'a failed credential remint must not reveal the gateway auth response', ); assert.doesNotMatch( - screen, + preview, /reload the current iframe src \(same token\)/, 'manual refresh must not retry an expired access token', ); diff --git a/tests/project-templates.test.ts b/tests/project-templates.test.ts index 45ec703..703d90f 100644 --- a/tests/project-templates.test.ts +++ b/tests/project-templates.test.ts @@ -14,7 +14,7 @@ import { } from '../agents/_lib/project/templates.ts'; import { describeScaffold } from '../agents/_lib/tools/project-tools.ts'; import { PREVIEW_ASSET_PREFIX_ENV } from '../agents/_lib/constants.ts'; -import { NPM_WARMUP_BASE } from '../shared/npm-install.ts'; +import { NPM_WARMUP_BASE } from '../agents/_lib/makers/npm-install.ts'; import { projectState } from './helpers/fixtures.ts'; const execFileAsync = promisify(execFile); diff --git a/tests/prompt-single-source.test.ts b/tests/prompt-single-source.test.ts index b0b259d..5f76871 100644 --- a/tests/prompt-single-source.test.ts +++ b/tests/prompt-single-source.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict'; import { readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; import test from 'node:test'; -import { buildPrompt, buildTurnPrompt } from '../agents/_lib/prompt.ts'; +import { buildPrompt } from '../agents/_lib/prompt.ts'; import { MAKERS_DEV_PORT, PREVIEW_ASSET_PREFIX_ENV, @@ -229,21 +229,19 @@ test('the prompt reflects whether the workspace already exists', () => { assert.match(renderPrompt(false), /already prepared a project workspace/); }); -test('recent conversation history is included when present', () => { - const withHistory = buildTurnPrompt('再加一个深色模式', [ - { role: 'user', content: '做一个待办列表' }, - { role: 'assistant', content: '已完成,右侧可以预览。' }, - ]); - assert.match(withHistory, /Recent conversation:/); - assert.match(withHistory, /User: 做一个待办列表/); - assert.match(withHistory, /Current user request: 再加一个深色模式/); - assert.doesNotMatch(buildTurnPrompt('再加一个深色模式', []), /Recent conversation:/); +test('the system prompt is the same on every turn of a conversation', () => { + const request = '做一个带留言板的网站'; + const prompt = renderPrompt(); + + assert.equal(prompt, renderPrompt(), 'the rules must not vary between two identical calls'); + assert.ok( + !prompt.includes(request), + 'the request belongs to the SDK user message; a copy here changes the cached prefix every turn', + ); + assert.doesNotMatch(prompt, /Recent conversation:/); + assert.doesNotMatch(prompt, /Current user request:/); }); -// The rules are a ~20k-character prefix. Anything turn-specific in here makes -// that prefix new on every turn, so the provider re-reads all of it instead of -// reusing it, and the request would arrive twice with no way to say which copy -// is authoritative. test('an international site tells the model to preview onto the overseas area', () => { const prompt = buildPrompt( projectState('projects/demo', { siteDomain: 'edgeone.dev' }), @@ -256,19 +254,6 @@ test('an international site tells the model to preview onto the overseas area', assert.doesNotMatch(prompt, /--area global/); }); -test('the system prompt is the same on every turn of a conversation', () => { - const request = '做一个带留言板的网站'; - const prompt = renderPrompt(); - - assert.equal(prompt, renderPrompt(), 'the rules must not vary between two identical calls'); - assert.ok( - !prompt.includes(request), - 'the request belongs to buildTurnPrompt; a copy here changes the cached prefix every turn', - ); - assert.doesNotMatch(prompt, /Recent conversation:/); - assert.doesNotMatch(prompt, /Current user request:/); -}); - test('the prompt reads as sections rather than one wall of rules', () => { const prompt = renderPrompt(); const headings = prompt.match(/^## .+$/gm) ?? []; diff --git a/tests/resume-file-cache.test.ts b/tests/resume-file-cache.test.ts index 21a83b7..7c53f1d 100644 --- a/tests/resume-file-cache.test.ts +++ b/tests/resume-file-cache.test.ts @@ -4,7 +4,7 @@ import { RESUME_FILE_CACHE_MAX_BYTES, RESUME_FILE_CACHE_MAX_FILES, selectResumeCacheFiles, -} from '../shared/resume-file-cache.ts'; +} from '../agents/_lib/project/resume-file-cache.ts'; import type { FileTreeItem } from '../shared/protocol.ts'; function file(path: string, size: number): FileTreeItem { diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index 637e0e5..d0323bd 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -16,22 +16,34 @@ test('the model menu is an edge function, not an agent route', async () => { await assert.rejects(access('agents/models.ts')); }); -test('session is GET restore plus POST turn; preview remint is its own route', async () => { +test('session is GET restore; turns go through /prompt and /deploy', async () => { const session = await readFile('agents/session.ts', 'utf8'); + const prompt = await readFile('agents/prompt.ts', 'utf8'); + const deploy = await readFile('agents/deploy.ts', 'utf8'); + const model = await readFile('agents/session-model.ts', 'utf8'); const preview = await readFile('agents/preview.ts', 'utf8'); - const tasks = await readFile('agents/_lib/chat-tasks.ts', 'utf8'); + const tasks = await readFile('agents/_lib/session/task.ts', 'utf8'); const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); assert.match(session, /onRequestGet/); assert.match(session, /createProjectResumeStreamResponse/); - assert.match(session, /onRequestPost/); - assert.match(session, /createChatTaskAndStreamResponse/); + assert.doesNotMatch(session, /onRequestPost/); + assert.match(prompt, /onRequestPost/); + assert.match(prompt, /kind: 'prompt'/); + assert.match(deploy, /onRequestPost/); + assert.match(deploy, /kind: 'deploy'/); + assert.match(model, /onRequestPost/); + assert.match(model, /saveModelPreference/); + assert.match(model, /setLiveQueryModel/); assert.match(tasks, /export async function\* iterateLiveChatTaskEvents/); assert.match(client, /fetch\('\/session',[\s\S]*?method: 'GET'/); - assert.match(client, /fetch\('\/session',[\s\S]*?method: 'POST'/); + assert.match(client, /fetch\('\/prompt',[\s\S]*?method: 'POST'/); + assert.match(client, /fetch\('\/deploy',[\s\S]*?method: 'POST'/); + assert.match(client, /fetch\('\/session-model',[\s\S]*?method: 'POST'/); assert.doesNotMatch(client, /fetch\('\/chat'/); assert.doesNotMatch(client, /fetch\('\/resume'/); - assert.doesNotMatch(client, /\/chat\?runId/); + assert.doesNotMatch(client, /intent:/); + assert.doesNotMatch(client, /resetProject/); assert.match(preview, /onRequestPost/); assert.match(preview, /runProjectResumePreviewPipeline/); assert.doesNotMatch(preview, /onRequestGet/); @@ -43,7 +55,7 @@ test('session is GET restore plus POST turn; preview remint is its own route', a test('initial session restore is one progressive SSE request that can attach a live task', async () => { const route = await readFile('agents/session.ts', 'utf8'); - const pipeline = await readFile('agents/_lib/pipelines/resume.ts', 'utf8'); + const pipeline = await readFile('agents/_lib/session/resume.ts', 'utf8'); const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); assert.match(route, /onRequestGet/); @@ -82,9 +94,6 @@ test('stop sends makers-conversation-id like every other agent route', async () const stopFn = client.slice(start, end); assert.ok(start >= 0 && end > start); - // The platform 400s agent routes that omit the header - // (`Invalid makers-conversation-id: header is missing`). A body-only first - // request never reaches abortLiveChatTask. assert.match(stopFn, /headers: conversationHeaders\(conversationId\)/); assert.match(stopFn, /conversation_id: conversationId/); assert.doesNotMatch(stopFn, /AGENT_CONVERSATION_ID_REQUIRED/); @@ -95,13 +104,13 @@ test('starting a new project does not wait for the old stop request', async () = const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); const stopRoute = await readFile('agents/stop.ts', 'utf8'); const start = screen.indexOf('function confirmNewProject()'); - const end = screen.indexOf('// Hold the first paint', start); + const end = screen.indexOf('if (!resume.resumeChecked)', start); const confirmBlock = screen.slice(start, end); const abortIndex = stopRoute.indexOf('abortActiveRun'); const snapshotIndex = stopRoute.indexOf('if (!discardProject)'); assert.ok(start >= 0 && end > start); - assert.match(confirmBlock, /void stopCurrentTask\(\{ discardProject: true \}\)/); + assert.match(confirmBlock, /void live\.stopCurrentTask\(\{ discardProject: true \}\)/); assert.match(confirmBlock, /startNewProject\(\)/); assert.doesNotMatch(confirmBlock, /await/); assert.match(client, /options\.discardProject \? \{ discardProject: true \} : \{\}/); @@ -109,16 +118,18 @@ test('starting a new project does not wait for the old stop request', async () = assert.match(stopRoute, /if \(!discardProject\) \{[\s\S]*?persistProjectSnapshot/); }); -test('workspace persistence uses the sandbox SDK and metadata snapshots are read-only migration data', async () => { - const helpers = await readFile('agents/_lib/pipelines/helpers.ts', 'utf8'); +test('workspace persistence uses the sandbox SDK and Blob state.json, not context.store', async () => { + const helpers = await readFile('agents/_lib/turn/checkpoint.ts', 'utf8'); const persistence = await readFile('agents/_lib/project/persistence.ts', 'utf8'); - const memory = await readFile('agents/_lib/memory.ts', 'utf8'); + const store = await readFile('agents/_lib/session/store.ts', 'utf8'); assert.match(helpers, /context\.sandbox\.persist\(\{ path: state\.appDir \}\)/); assert.match(persistence, /context\.sandbox\.restore\(\{ path: state\.appDir \}\)/); - assert.match(persistence, /getLegacyProjectSnapshot/); - assert.match(persistence, /clearLegacyProjectSnapshot/); - assert.doesNotMatch(memory, /saveProjectSnapshot/); - assert.doesNotMatch(memory, /listConversations/); - assert.doesNotMatch(memory, /deleteConversation/); + assert.doesNotMatch(persistence, /getLegacyProjectSnapshot/); + assert.doesNotMatch(persistence, /clearLegacyProjectSnapshot/); + assert.match(store, /getStore\(\{ name: BLOB_STORE_NAME, consistency: 'strong' \}\)/); + assert.doesNotMatch(store, /context\.store/); + assert.doesNotMatch(store, /saveProjectSnapshot/); + assert.doesNotMatch(store, /listConversations/); + assert.doesNotMatch(store, /deleteConversation/); }); diff --git a/tests/sandbox-timeout.test.ts b/tests/sandbox-timeout.test.ts index 5b0d109..21a4a98 100644 --- a/tests/sandbox-timeout.test.ts +++ b/tests/sandbox-timeout.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; import { readFile } from 'node:fs/promises'; -import { resolveSandboxCommandOptions } from '../shared/sandbox-command.ts'; +import { resolveSandboxCommandOptions } from '../agents/_lib/project/sandbox-command.ts'; test('timeout in seconds is also sent as timeoutMs', () => { assert.deepEqual(resolveSandboxCommandOptions({ cwd: '/app', timeout: 420 }), { diff --git a/tests/sanitize-assistant-text.test.ts b/tests/sanitize-assistant-text.test.ts index c2027fb..04f2443 100644 --- a/tests/sanitize-assistant-text.test.ts +++ b/tests/sanitize-assistant-text.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { sanitizeAssistantText } from '../shared/sanitize-assistant-text.ts'; +import { sanitizeAssistantText } from '../shared/timeline.ts'; // Every assistant reply is persisted and streamed through this function, so a // gap here reaches the user as terminal garbage, leaked reasoning, or raw tool diff --git a/tests/sse-parser.test.ts b/tests/sse-parser.test.ts index 4205790..fa6cbbf 100644 --- a/tests/sse-parser.test.ts +++ b/tests/sse-parser.test.ts @@ -16,14 +16,14 @@ function responseFromChunks(chunks: string[]) { test('SSE parser handles split frames and stops at DONE', async () => { const events: ChatStreamEvent[] = []; const response = responseFromChunks([ - 'data: {"type":"status","message":"run', - 'ning"}\n\ndata: {"type":"ping","ts":1}\n\n', + 'data: {"type":"ping","ts":', + '2}\n\ndata: {"type":"ping","ts":1}\n\n', 'data: [DONE]\n\ndata: {"type":"error","error":"ignored"}\n\n', ]); await consumeEventStream(response, (event) => events.push(event)); assert.deepEqual(events, [ - { type: 'status', message: 'running' }, + { type: 'ping', ts: 2 }, { type: 'ping', ts: 1 }, ]); }); diff --git a/tests/tool-activity.test.ts b/tests/tool-activity.test.ts index 7832641..f616d99 100644 --- a/tests/tool-activity.test.ts +++ b/tests/tool-activity.test.ts @@ -7,7 +7,7 @@ import { dropTrailingSummaryEcho, presentToolActivity, } from '../app/lib/tool-activity.ts'; -import { summarizeToolInput } from '../agents/_lib/utils/activity.ts'; +import { summarizeToolInput } from '../shared/timeline.ts'; import { MAKERS_REFERENCE_SKILL_NAMES } from '../agents/_lib/tools/makers-skills.ts'; test('direct Makers CLI dev and deploy commands have distinct actions', () => { diff --git a/tests/tool-phase.test.ts b/tests/tool-phase.test.ts index 6449d7c..c7276f5 100644 --- a/tests/tool-phase.test.ts +++ b/tests/tool-phase.test.ts @@ -15,7 +15,7 @@ import { shortenToolName, stripEchoedExit, withExitCodeEcho, -} from '../agents/_lib/utils/tool-phase.ts'; +} from '../agents/_lib/makers/tool-phase.ts'; test('shortens MCP tool names', () => { assert.equal(shortenToolName('mcp__edgeone-sandbox__files_write'), 'files_write'); diff --git a/tests/transcript.test.ts b/tests/transcript.test.ts new file mode 100644 index 0000000..9f6ad10 --- /dev/null +++ b/tests/transcript.test.ts @@ -0,0 +1,148 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, readFile as readDisk, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import test from 'node:test'; +import { createMemoryBlobStore, transcriptBlobKey } from '../agents/_lib/session/store.ts'; +import { downloadTranscript, uploadTranscript } from '../agents/_lib/session/transcript.ts'; +import { projectTranscript } from '../agents/_lib/session/projection.ts'; +import { applyStreamEvent } from '../shared/timeline.ts'; +import type { PersistedActivityTurn } from '../shared/protocol.ts'; + +test('transcript upload and download stay a single JSONL file', async () => { + const directory = await mkdtemp(path.join(tmpdir(), 'transcript-')); + const source = path.join(directory, 'session.jsonl'); + const dest = path.join(directory, 'restored.jsonl'); + const jsonl = [ + JSON.stringify({ + type: 'user', + timestamp: '2026-01-01T00:00:00.000Z', + message: { role: 'user', content: 'Make a todo list' }, + }), + JSON.stringify({ + type: 'assistant', + timestamp: '2026-01-01T00:00:01.000Z', + message: { + role: 'assistant', + content: [ + { type: 'text', text: 'Working on it.' }, + { type: 'tool_use', id: 'tool-1', name: 'write_project_file', input: { path: 'src/app.tsx', content: 'x' } }, + ], + }, + }), + JSON.stringify({ + type: 'user', + timestamp: '2026-01-01T00:00:02.000Z', + message: { + role: 'user', + content: [ + { type: 'tool_result', tool_use_id: 'tool-1', content: 'wrote src/app.tsx' }, + ], + }, + }), + ].join('\n'); + await writeFile(source, jsonl); + + const blobStore = createMemoryBlobStore(); + const context = { blobStore }; + await uploadTranscript({ + context, + conversationId: 'conv-1', + sessionId: 'sess-1', + sourcePath: source, + }); + const stored = await blobStore.get(transcriptBlobKey('sess-1')); + assert.equal(stored, jsonl); + + const restored = await downloadTranscript({ + context, + conversationId: 'conv-1', + sessionId: 'sess-1', + destPath: dest, + }); + assert.equal(restored, true); + assert.equal(await readDisk(dest, 'utf8'), jsonl); + await rm(directory, { recursive: true, force: true }); +}); + +test('JSONL projects into conversation turns with tool results folded in', () => { + const jsonl = [ + JSON.stringify({ + type: 'user', + timestamp: '2026-01-01T00:00:00.000Z', + message: { role: 'user', content: 'Build a page' }, + }), + JSON.stringify({ + type: 'assistant', + timestamp: '2026-01-01T00:00:01.000Z', + message: { + role: 'assistant', + content: [ + { type: 'text', text: 'Writing files.' }, + { type: 'tool_use', id: 'call-1', name: 'write_project_file', input: { path: 'index.html', content: '

Hi

' } }, + ], + }, + }), + JSON.stringify({ + type: 'user', + timestamp: '2026-01-01T00:00:02.000Z', + message: { + role: 'user', + content: [ + { type: 'tool_result', tool_use_id: 'call-1', content: 'ok' }, + ], + }, + }), + ].join('\n'); + + const turns = projectTranscript(jsonl, '/tmp/project'); + assert.equal(turns.length, 1); + assert.equal(turns[0].user, 'Build a page'); + assert.equal(turns[0].assistant, 'Writing files.'); + assert.equal(turns[0].activities.length, 2); + assert.equal(turns[0].activities[0].kind, 'text'); + const tool = turns[0].activities[1]; + assert.equal(tool.kind, 'tool'); + if (tool.kind === 'tool') { + assert.equal(tool.toolUseId, 'call-1'); + assert.equal(tool.status, 'completed'); + assert.match(tool.inputSummary || '', /index\.html/); + assert.equal(tool.outputSummary, 'ok'); + } +}); + +test('live SSE events fold into the same turn model as a JSONL projection', () => { + let turn: PersistedActivityTurn = { + id: 'turn-1', + user: 'Build a page', + assistant: '', + status: 'completed', + createdAt: 1, + activities: [], + }; + turn = applyStreamEvent(turn, { type: 'text_segment', data: { text: 'Writing files.' } }); + turn = applyStreamEvent(turn, { + type: 'tool_use', + data: { id: 'call-1', name: 'write_project_file', inputSummary: 'index.html (8 chars)' }, + }); + turn = applyStreamEvent(turn, { + type: 'tool_result', + data: { id: 'call-1', ok: true, outputSummary: 'ok', status: 'completed' }, + }); + assert.equal(turn.activities.length, 2); + const tool = turn.activities[1]; + assert.equal(tool.kind, 'tool'); + if (tool.kind === 'tool') { + assert.equal(tool.toolUseId, 'call-1'); + assert.equal(tool.status, 'completed'); + assert.equal(tool.outputSummary, 'ok'); + } +}); + +test('compaction re-uploads the local transcript file', async () => { + const { readFile } = await import('node:fs/promises'); + const live = await readFile('agents/_lib/session/live.ts', 'utf8'); + assert.match(live, /subtype === 'compact_boundary'/); + assert.match(live, /PostCompact:/); + assert.match(live, /persistTranscript\(session\)/); +}); diff --git a/tests/user-facing-reply.test.ts b/tests/user-facing-reply.test.ts index 64e2cab..4ec1cb1 100644 --- a/tests/user-facing-reply.test.ts +++ b/tests/user-facing-reply.test.ts @@ -11,7 +11,7 @@ import { const LIVE_URL = 'https://vibe-coding-playground.edgeone.app/?eo_token=abc123def456&eo_time=1787882262'; test('Chinese fallback stays concise and localized', async () => { - const source = await readFile('agents/_lib/pipelines/helpers.ts', 'utf8'); + const source = await readFile('agents/_lib/turn/checkpoint.ts', 'utf8'); assert.match(source, /已按你的需求完成/); assert.match(source, /右侧预览已就绪/); }); @@ -31,11 +31,11 @@ test('successful replies keep only the user-facing outcome paragraph', () => { }); test('step narration is streamed to the user while the summary stays compact', async () => { - const chat = await readFile('agents/_lib/pipelines/chat.ts', 'utf8'); + const chat = await readFile('agents/_lib/turn/chat.ts', 'utf8'); const prompt = await readFile('agents/_lib/prompt.ts', 'utf8'); assert.match(chat, /if \(event\.type === 'text_segment'\)/); assert.match(chat, /recordProgress\(narration\)/); - assert.match(chat, /send\(narration as unknown as Record\)/); + assert.match(chat, /send\(narration\)/); assert.match(prompt, /Keep narrating as you work/); assert.match(prompt, /always write it in the user language/); }); @@ -197,7 +197,7 @@ test('the live URL is guaranteed in the reply, in the reply language', () => { // state.deployment survives the turn that created it, so an unrelated later // reply must not pick up a stale address. test('only the deployment from the current turn reaches the reply', async () => { - const chat = await readFile('agents/_lib/pipelines/chat.ts', 'utf8'); + const chat = await readFile('agents/_lib/turn/chat.ts', 'utf8'); assert.match(chat, /modelResult\.deploymentTouched\s*\n?\s*&& state\.deployment\?\.status === 'success'/); assert.match(chat, /withLiveDeploymentUrl\(/); }); From 289e9b8ee798e1f3dd4f6eb4b7ce0c8373670142 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 10:59:12 +0800 Subject: [PATCH 05/26] refactor(session): carry model choice on /prompt --- agents/_lib/session/live.ts | 5 ++- agents/_lib/session/task.ts | 4 +- agents/session-model.ts | 41 ------------------- app/features/workspace/hooks/use-live-turn.ts | 5 ++- app/features/workspace/workspace-api.ts | 8 ---- app/features/workspace/workspace-screen.tsx | 11 ++--- tests/architecture.test.ts | 3 +- tests/models.test.ts | 37 +++++++++++++++++ tests/route-consolidation.test.ts | 7 +--- 9 files changed, 51 insertions(+), 70 deletions(-) delete mode 100644 agents/session-model.ts diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts index 8075b4f..32926ad 100644 --- a/agents/_lib/session/live.ts +++ b/agents/_lib/session/live.ts @@ -674,6 +674,7 @@ export async function runCodingAgent(options: RunCodingAgentOptions): Promise([]); + const modelRef = useRef(model); const chatAbortControllerRef = useRef(null); const activeTurnIdRef = useRef(''); const stoppingRef = useRef(false); + modelRef.current = model; + useEffect(() => { loadingRef.current = loading; }, [loading]); @@ -510,9 +513,9 @@ export function useLiveTurn(options: { conversationId: requestConversationId, message: displayMessage, turnId: assistantMessageId, + model: modelRef.current, ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), - ...(model ? { model } : {}), siteDomain: extractProjectName().domain, signal: requestAbortController.signal, }); diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index e7010c1..af7c6fb 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -99,14 +99,6 @@ export function startDeployTurn(options: { }); } -export function setSessionModel(conversationId: string, model: string) { - return fetch('/session-model', { - method: 'POST', - headers: conversationHeaders(conversationId), - body: JSON.stringify({ model }), - }).then((response) => readJson<{ ok?: boolean; model?: string }>(response)); -} - export async function stopChatTask( conversationId: string, turn: PersistedActivityTurn, diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index a0f6866..0023471 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -45,7 +45,7 @@ import { PreviewControls } from './components/preview-controls'; import { PreviewFrame } from './components/preview-frame'; import { SiteHeader } from './components/site-header'; import { WorkspaceErrorBar } from './components/workspace-error-bar'; -import { fetchModelCatalog, setSessionModel } from './workspace-api'; +import { fetchModelCatalog } from './workspace-api'; import { useLiveTurn } from './hooks/use-live-turn'; import { usePreviewSurface } from './hooks/use-preview-surface'; import { useSessionResume } from './hooks/use-session-resume'; @@ -247,11 +247,6 @@ export function WorkspaceScreen() { await live.sendMessage(live.input); } - function handleModelChange(next: string) { - setModel(next); - if (conversationId) void setSessionModel(conversationId, next); - } - function handleDeployProject() { if (!canDeployProject) return; if (deployOfferTurnId) { @@ -349,7 +344,7 @@ export function WorkspaceScreen() { loading={live.loading} models={models} model={model} - onModelChange={handleModelChange} + onModelChange={setModel} onInputChange={live.setInput} onSubmit={handleSubmit} onSend={() => void live.sendMessage(live.input)} @@ -371,7 +366,7 @@ export function WorkspaceScreen() { compact models={models} model={model} - onModelChange={handleModelChange} + onModelChange={setModel} copy={conversationCopy} onInputChange={live.setInput} onSubmit={() => void live.sendMessage(live.input)} diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index 129b400..5872bdc 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -63,7 +63,6 @@ const AGENT_ROUTE_FILES = new Set([ 'agents/session.ts', 'agents/prompt.ts', 'agents/deploy.ts', - 'agents/session-model.ts', 'agents/preview.ts', 'agents/stop.ts', 'agents/file.ts', @@ -110,6 +109,7 @@ test('retired session-truth modules stay gone', async () => { 'agents/_lib/chat-tasks.ts', 'agents/_lib/shared.ts', 'agents/_lib/pipelines', + 'agents/session-model.ts', 'shared/makers-dev.ts', 'shared/makers-deploy.ts', 'shared/npm-install.ts', @@ -133,7 +133,6 @@ test('session kernel and makers CLI live under agents/_lib', async () => { 'shared/protocol.ts', 'agents/prompt.ts', 'agents/deploy.ts', - 'agents/session-model.ts', ]) { await access(target); } diff --git a/tests/models.test.ts b/tests/models.test.ts index eb09164..0e22675 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -213,3 +213,40 @@ test('the model picker keeps what the native select gave it for free', async () assert.ok(guard > 0 && picker.lastIndexOf('useEffect(', guard) < guard); assert.equal(picker.slice(guard).includes('useEffect('), false); }); + +// The picker is the user's choice for the next turn. Persisting it through a +// dedicated /session-model route would write a preference before they asked +// for anything; /prompt is when a model is actually needed, so that is where +// the choice travels. Omitting it is valid: the runtime then uses the +// deployment default rather than a previously stored preference. +test('the composer model travels on /prompt, not a session-model route', async () => { + const [ + screen, + client, + live, + prompt, + task, + agent, + ] = await Promise.all([ + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/workspace-api.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + readFile('agents/prompt.ts', 'utf8'), + readFile('agents/_lib/session/task.ts', 'utf8'), + readFile('agents/_lib/session/live.ts', 'utf8'), + ]); + + assert.match(screen, /onModelChange=\{setModel\}/); + assert.doesNotMatch(screen, /setSessionModel/); + assert.doesNotMatch(client, /\/session-model/); + assert.match(client, /\.\.\.\(options\.model \? \{ model: options\.model \} : \{\}\)/); + assert.match(live, /model: modelRef\.current/); + assert.match(prompt, /resolveRequestedModel\(context, body\?\.model\)/); + assert.doesNotMatch(task, /getModelPreference/); + assert.match(task, /requestedModel \? \{ model: requestedModel \}/); + assert.match(task, /saveModelPreference\(context, conversationId, requestedModel\)/); + assert.match( + agent, + /\(options\.model \|\| ''\)\.trim\(\) \|\| resolveConfiguredModel\(options\.context\)/, + ); +}); diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index d0323bd..f6a6432 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -20,7 +20,6 @@ test('session is GET restore; turns go through /prompt and /deploy', async () => const session = await readFile('agents/session.ts', 'utf8'); const prompt = await readFile('agents/prompt.ts', 'utf8'); const deploy = await readFile('agents/deploy.ts', 'utf8'); - const model = await readFile('agents/session-model.ts', 'utf8'); const preview = await readFile('agents/preview.ts', 'utf8'); const tasks = await readFile('agents/_lib/session/task.ts', 'utf8'); const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); @@ -32,14 +31,11 @@ test('session is GET restore; turns go through /prompt and /deploy', async () => assert.match(prompt, /kind: 'prompt'/); assert.match(deploy, /onRequestPost/); assert.match(deploy, /kind: 'deploy'/); - assert.match(model, /onRequestPost/); - assert.match(model, /saveModelPreference/); - assert.match(model, /setLiveQueryModel/); assert.match(tasks, /export async function\* iterateLiveChatTaskEvents/); assert.match(client, /fetch\('\/session',[\s\S]*?method: 'GET'/); assert.match(client, /fetch\('\/prompt',[\s\S]*?method: 'POST'/); assert.match(client, /fetch\('\/deploy',[\s\S]*?method: 'POST'/); - assert.match(client, /fetch\('\/session-model',[\s\S]*?method: 'POST'/); + assert.doesNotMatch(client, /fetch\('\/session-model'/); assert.doesNotMatch(client, /fetch\('\/chat'/); assert.doesNotMatch(client, /fetch\('\/resume'/); assert.doesNotMatch(client, /intent:/); @@ -50,6 +46,7 @@ test('session is GET restore; turns go through /prompt and /deploy', async () => assert.match(client, /fetch\('\/preview',[\s\S]*?method: 'POST'/); await assert.rejects(access('agents/chat.ts')); await assert.rejects(access('agents/resume.ts')); + await assert.rejects(access('agents/session-model.ts')); await assert.rejects(access('agents/session/index.ts')); }); From 0bafc20266eb2e47317333764f0f434bd646ec06 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 12:09:56 +0800 Subject: [PATCH 06/26] feat(workspace): keep the result panel shut until the user opens it The stream used to reveal preview/code and pick a tab. Leave that column behind a resident toggle so opening it is always a user action. --- app/features/workspace/hooks/use-live-turn.ts | 11 --- .../workspace/hooks/use-session-resume.ts | 15 --- .../workspace/hooks/use-workspace-state.ts | 6 +- app/features/workspace/workspace-screen.tsx | 91 +++++++++++++++---- app/i18n.ts | 6 ++ app/styles/workspace.css | 21 ++++- tests/app-shell.test.ts | 21 +++++ 7 files changed, 122 insertions(+), 49 deletions(-) diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index a2bfd50..48991ea 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -124,8 +124,6 @@ export function useLiveTurn(options: { openedFirstFile = true; pendingFirstFilePath = null; workspace.setFilesFocusPath(path); - workspace.setSandboxTab('files'); - workspace.setResultPanelOpen(true); }; const patchAssistant = (patch: Partial) => { @@ -219,12 +217,9 @@ export function useLiveTurn(options: { } if (data.preview) { preview.activatePreview(data.preview, activatedPreviewRevisions); - workspace.setSandboxTab('preview'); - workspace.setResultPanelOpen(true); } if (data.deployment) { workspace.setDeployment(data.deployment); - workspace.setResultPanelOpen(true); } if (data.download) { workspace.setDownload(data.download); @@ -234,9 +229,6 @@ export function useLiveTurn(options: { } if (data.files) { workspace.setFileTree(data.files); - if (data.files.items.some((item) => item.type === 'file')) { - workspace.setResultPanelOpen(true); - } } if (data.gatewayNeeded) { workspace.setGatewayNeeded(true); @@ -333,15 +325,12 @@ export function useLiveTurn(options: { if (event.type === 'deployment_status' && event.data) { sawProjectActivity = true; workspace.setDeployment(event.data); - workspace.setResultPanelOpen(true); return; } if (event.type === 'preview_ready' && event.data) { sawProjectActivity = true; if (event.data.preview) { preview.activatePreview(event.data.preview, activatedPreviewRevisions); - workspace.setSandboxTab('preview'); - workspace.setResultPanelOpen(true); } if (event.data.download) { workspace.setDownload(event.data.download); diff --git a/app/features/workspace/hooks/use-session-resume.ts b/app/features/workspace/hooks/use-session-resume.ts index a82a538..8e1abd3 100644 --- a/app/features/workspace/hooks/use-session-resume.ts +++ b/app/features/workspace/hooks/use-session-resume.ts @@ -163,15 +163,10 @@ export function useSessionResume(options: { live.setMessages(nextMessages); workspace.setGatewayNeeded(Boolean(data.gatewayNeeded)); workspace.setDeployment(data.deployment ?? null); - if (data.deployment) { - workspace.setResultPanelOpen(true); - } if (data.hasProject || data.needsWorkspace || activeTask) { if (data.hasProject || data.needsWorkspace) { - workspace.setSandboxTab(data.hasPreview ? 'preview' : 'files'); setWorkspaceRestoring(true); workspace.setFilesRefreshing(true); - workspace.setResultPanelOpen(true); } } const liveTaskId = activeTask?.id @@ -183,26 +178,16 @@ export function useSessionResume(options: { const applyWorkspace = (data: ResumeData) => { if (data.gatewayNeeded) workspace.setGatewayNeeded(true); - const hasFiles = Boolean(data.files?.items.some((item) => item.type === 'file')); if (data.files) { workspace.setFileTree(data.files); } - if (hasFiles || data.preview?.url) { - workspace.setResultPanelOpen(true); - } if (data.download?.url) { workspace.setDownload(data.download); } if (data.deployment) { workspace.setDeployment(data.deployment); - workspace.setResultPanelOpen(true); } preview.applyResumedPreview(data.preview); - if (data.preview?.url) { - workspace.setSandboxTab('preview'); - } else if (hasFiles) { - workspace.setSandboxTab('files'); - } }; const resumeController = new AbortController(); diff --git a/app/features/workspace/hooks/use-workspace-state.ts b/app/features/workspace/hooks/use-workspace-state.ts index 4bcfdc6..05139df 100644 --- a/app/features/workspace/hooks/use-workspace-state.ts +++ b/app/features/workspace/hooks/use-workspace-state.ts @@ -13,12 +13,14 @@ import type { } from '@/app/types/workspace'; import { fetchProjectArchive } from '../workspace-api'; +export type SandboxTab = 'preview' | 'files'; + export function useWorkspaceState() { const [deployment, setDeployment] = useState(null); const [download, setDownload] = useState(null); const [downloadBusy, setDownloadBusy] = useState(false); const [build, setBuild] = useState(null); - const [sandboxTab, setSandboxTab] = useState<'preview' | 'files'>('preview'); + const [sandboxTab, setSandboxTab] = useState(null); const [fileTree, setFileTree] = useState(null); const [filesRefreshing, setFilesRefreshing] = useState(false); const [filesFocusPath, setFilesFocusPath] = useState(null); @@ -38,7 +40,7 @@ export function useWorkspaceState() { setDismissedDeployTurnId(''); setGatewayNeeded(false); setGatewayBusy(false); - setSandboxTab('preview'); + setSandboxTab(null); }, []); async function handleDownload(conversationId: string | null, failedMessage: string) { diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 0023471..6dc0f61 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -8,6 +8,8 @@ import { Copy, Download, Eye, + PanelRight, + PanelRightClose, Rocket, } from 'lucide-react'; import { Button } from '@/app/components/ui/button'; @@ -49,7 +51,34 @@ import { fetchModelCatalog } from './workspace-api'; import { useLiveTurn } from './hooks/use-live-turn'; import { usePreviewSurface } from './hooks/use-preview-surface'; import { useSessionResume } from './hooks/use-session-resume'; -import { useWorkspaceState } from './hooks/use-workspace-state'; +import { useWorkspaceState, type SandboxTab } from './hooks/use-workspace-state'; + +function ResultPanelToggle({ + open, + showLabel, + hideLabel, + onToggle, +}: { + open: boolean; + showLabel: string; + hideLabel: string; + onToggle: () => void; +}) { + const label = open ? hideLabel : showLabel; + return ( + + ); +} function PanelLoading() { return ( @@ -396,25 +425,44 @@ export function WorkspaceScreen() { }} />} - {workspace.resultPanelOpen &&
+ {hasWorkspace && !workspace.resultPanelOpen && ( +
+ workspace.setResultPanelOpen(true)} + /> +
+ )} + + {workspace.resultPanelOpen &&
- workspace.setSandboxTab(value as 'preview' | 'files')} - className="workspace-topbar-tabs" - > - - - - {t.workspace.preview} - - - - {t.workspace.code} - {workspace.filesRefreshing && {t.files.refreshing}} - - - +
+ workspace.setResultPanelOpen(false)} + /> + workspace.setSandboxTab(value as SandboxTab)} + className="workspace-topbar-tablist" + > + + + + {t.workspace.preview} + + + + {t.workspace.code} + {workspace.filesRefreshing && {t.files.refreshing}} + + + +
{workspace.sandboxTab === 'preview' && preview.shareablePreviewUrl && !preview.previewRefreshing && !preview.previewRefreshFailed && ( @@ -469,6 +517,11 @@ export function WorkspaceScreen() {
+ {!workspace.sandboxTab && ( +
+

{t.workspace.choosePanel}

+
+ )}
{preview.preview?.url ? ( { + const [screen, live, resume, state] = await Promise.all([ + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-session-resume.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-workspace-state.ts', 'utf8'), + ]); + + assert.match(screen, /function ResultPanelToggle\(/); + assert.match(screen, /workspace\.setResultPanelOpen\(true\)/); + assert.match(screen, /workspace\.setResultPanelOpen\(false\)/); + assert.match(screen, /onValueChange=\{\(value\) => workspace\.setSandboxTab\(value as SandboxTab\)\}/); + assert.match(state, /useState\(null\)/); + assert.doesNotMatch(live, /setResultPanelOpen\(/); + assert.doesNotMatch(live, /setSandboxTab\(/); + assert.doesNotMatch(resume, /setResultPanelOpen\(/); + assert.doesNotMatch(resume, /setSandboxTab\(/); +}); From 6e9250545b699354fc52fdb55694cb16a8146a65 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 12:14:48 +0800 Subject: [PATCH 07/26] feat(workspace): stream the raw SDK transcript beside preview and code The chat column is a projection; this tab reads the Claude JSONL file itself, including while a turn is still writing it. --- agents/_lib/session/live.ts | 19 ++- agents/_lib/session/resume.ts | 22 +-- agents/_lib/session/transcript.ts | 159 +++++++++++++++++- agents/transcript.ts | 17 ++ app/components/session-panel.tsx | 145 ++++++++++++++++ .../workspace/hooks/use-workspace-state.ts | 2 +- app/features/workspace/workspace-api.ts | 8 + app/features/workspace/workspace-screen.tsx | 19 +++ app/i18n.ts | 25 ++- shared/protocol.ts | 19 ++- tests/architecture.test.ts | 2 + tests/route-consolidation.test.ts | 23 +++ tests/transcript.test.ts | 109 +++++++++++- 13 files changed, 535 insertions(+), 34 deletions(-) create mode 100644 agents/transcript.ts create mode 100644 app/components/session-panel.tsx diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts index 32926ad..ea1c8ff 100644 --- a/agents/_lib/session/live.ts +++ b/agents/_lib/session/live.ts @@ -48,8 +48,8 @@ import { } from '../makers/tool-phase.ts'; import { buildPrompt } from '../prompt.ts'; import { resolveMakersProjectName } from '../makers/project.ts'; -import { getConversationRecord } from './store.ts'; -import { downloadTranscript, uploadTranscript } from './transcript.ts'; +import { getConversationRecord, patchConversationRecord } from './store.ts'; +import { downloadTranscript, resolveClaudeTranscriptPath, uploadTranscript } from './transcript.ts'; class PromptQueue implements AsyncIterable { private messages: SDKUserMessage[] = []; @@ -324,6 +324,9 @@ async function pumpSession(session: LiveQuerySession) { } if (typeof systemEvent.session_id === 'string' && systemEvent.session_id) { session.sessionId = systemEvent.session_id; + if (!session.transcriptPath) { + session.transcriptPath = resolveClaudeTranscriptPath(systemEvent.session_id); + } } if (!session.turn) continue; @@ -613,7 +616,17 @@ async function startLiveQuery(options: RunCodingAgentOptions): Promise { if (input.hook_event_name === 'SessionStart') { session.sessionId = input.session_id; - session.transcriptPath = input.transcript_path; + session.transcriptPath = resolveClaudeTranscriptPath(input.session_id, { + explicitPath: input.transcript_path, + }); + // Remember the path now. Upload still waits for the turn to end; + // the Session tab reads this local file while the agent is running. + await patchConversationRecord(session.context, session.conversationId, { + claudeSessionId: input.session_id, + transcriptPath: session.transcriptPath, + }).catch((error) => { + console.warn('[transcript] session path persist failed', error); + }); } return {}; }], diff --git a/agents/_lib/session/resume.ts b/agents/_lib/session/resume.ts index 15f564d..f60bec2 100644 --- a/agents/_lib/session/resume.ts +++ b/agents/_lib/session/resume.ts @@ -1,4 +1,3 @@ -import { existsSync } from 'node:fs'; import { getChatTask, getConversationRecord, @@ -7,7 +6,7 @@ import { saveProjectState, } from './store.ts'; import { hasLiveChatTask, isChatTaskActive, iterateLiveChatTaskEvents, markOrphanedTaskFailed } from './task.ts'; -import { downloadTranscript, readTranscriptText } from './transcript.ts'; +import { loadTranscriptJsonl } from './transcript.ts'; import { projectTranscript, turnsToMessages } from './projection.ts'; import { assertPreviewServerReady, @@ -84,25 +83,6 @@ function jsonResponse(obj: Record, status = 200) { }); } -async function loadTranscriptJsonl(context: any, conversationId: string) { - const record = await getConversationRecord(context, conversationId); - if (record.transcriptPath && existsSync(record.transcriptPath)) { - return readTranscriptText(record.transcriptPath); - } - if (record.claudeSessionId) { - const dest = record.transcriptPath - || `/tmp/.claude/sessions/${record.claudeSessionId}.jsonl`; - const restored = await downloadTranscript({ - context, - conversationId, - sessionId: record.claudeSessionId, - destPath: dest, - }); - if (restored) return readTranscriptText(dest); - } - return ''; -} - async function loadProjectResumeHistory(context: any, conversationId: string) { const [record, jsonl, model] = await Promise.all([ getConversationRecord(context, conversationId), diff --git a/agents/_lib/session/transcript.ts b/agents/_lib/session/transcript.ts index f7690ae..089b5b2 100644 --- a/agents/_lib/session/transcript.ts +++ b/agents/_lib/session/transcript.ts @@ -1,9 +1,13 @@ -import { createReadStream, createWriteStream } from 'node:fs'; +import { createReadStream, createWriteStream, existsSync, readdirSync } from 'node:fs'; import { mkdir, stat } from 'node:fs/promises'; import path from 'node:path'; import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; -import { getBlobStore, patchConversationRecord, transcriptBlobKey } from './store.ts'; +import { resolveConversationId } from '../runtime/request.ts'; +import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; +import { getBlobStore, getConversationRecord, patchConversationRecord, transcriptBlobKey } from './store.ts'; + +const TRANSCRIPT_WATCH_MS = 250; const TRANSCRIPT_WARN_BYTES = 32 * 1024 * 1024; @@ -68,3 +72,154 @@ export async function readTranscriptText(filePath: string): Promise { const { readFile } = await import('node:fs/promises'); return readFile(filePath, 'utf8'); } + +export type TranscriptLocateOptions = { + configDir?: string; + cwd?: string; +}; + +/** Claude persists JSONL at `$CLAUDE_CONFIG_DIR/projects//.jsonl`. */ +export function claudeProjectDirName(cwd: string): string { + return cwd.replace(/[/\\]/g, '-'); +} + +export function resolveClaudeTranscriptPath( + sessionId: string, + options?: TranscriptLocateOptions & { explicitPath?: string }, +): string { + const explicit = (options?.explicitPath || '').trim(); + if (explicit && existsSync(explicit)) return explicit; + if (!sessionId) return explicit; + + const configDir = (options?.configDir || '').trim() || '/tmp/.claude'; + const cwd = (options?.cwd || '').trim() || process.cwd(); + const conventional = path.join( + configDir, + 'projects', + claudeProjectDirName(cwd), + `${sessionId}.jsonl`, + ); + if (existsSync(conventional)) return conventional; + + const legacy = path.join(configDir, 'sessions', `${sessionId}.jsonl`); + if (existsSync(legacy)) return legacy; + + const projects = path.join(configDir, 'projects'); + if (existsSync(projects)) { + for (const entry of readdirSync(projects, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const candidate = path.join(projects, entry.name, `${sessionId}.jsonl`); + if (existsSync(candidate)) return candidate; + } + } + + return explicit || conventional; +} + +/** + * The Claude JSONL file is the only history this product keeps. Resume and the + * Session tab both read it here so they cannot drift onto a second copy. + */ +export async function loadTranscriptJsonl( + context: { blobStore?: import('../runtime/context.ts').BlobStoreLike }, + conversationId: string, + livePath = '', + locate?: TranscriptLocateOptions & { sessionId?: string }, +): Promise { + const record = await getConversationRecord(context, conversationId); + const sessionId = locate?.sessionId || record.claudeSessionId || ''; + const resolved = resolveClaudeTranscriptPath(sessionId, { + explicitPath: livePath || record.transcriptPath, + configDir: locate?.configDir, + cwd: locate?.cwd, + }); + if (resolved && existsSync(resolved)) { + return readTranscriptText(resolved); + } + if (sessionId) { + const dest = resolved || path.join('/tmp/.claude/sessions', `${sessionId}.jsonl`); + const restored = await downloadTranscript({ + context, + conversationId, + sessionId, + destPath: dest, + }); + if (restored) return readTranscriptText(dest); + } + return ''; +} + +function jsonResponse(obj: Record, status = 200) { + return new Response(JSON.stringify(obj), { + status, + headers: { + 'content-type': 'application/json; charset=utf-8', + 'cache-control': 'no-store', + }, + }); +} + +export type LiveTranscriptRef = { + path?: string; + sessionId?: string; + active?: boolean; +}; + +function sleep(ms: number, signal?: AbortSignal) { + return new Promise((resolve) => { + if (signal?.aborted) { + resolve(); + return; + } + const timer = setTimeout(resolve, ms); + const onAbort = () => { + clearTimeout(timer); + resolve(); + }; + signal?.addEventListener('abort', onAbort, { once: true }); + }); +} + +/** GET /transcript — JSONL snapshots while the live file is being written. */ +export async function createTranscriptStreamResponse( + context: any, + resolveLive: (conversationId: string) => LiveTranscriptRef | null, +): Promise { + const { conversationId } = resolveConversationId(context); + if (!conversationId) { + return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); + } + + return createSSEResponse(async function* (signal) { + let lastSignature = ''; + let watching = true; + + while (!signal?.aborted && watching) { + const live = resolveLive(conversationId); + const record = !live?.sessionId || !live?.path + ? await getConversationRecord(context, conversationId) + : null; + const sessionId = live?.sessionId || record?.claudeSessionId || ''; + const transcriptPath = resolveClaudeTranscriptPath(sessionId, { + explicitPath: live?.path || record?.transcriptPath, + }); + const jsonl = await loadTranscriptJsonl(context, conversationId, transcriptPath, { sessionId }); + const data = { + ok: true as const, + conversation_id: conversationId, + sessionId, + transcriptPath, + jsonl, + live: Boolean(live?.active), + }; + const signature = `${data.sessionId}\0${data.transcriptPath}\0${data.live}\0${data.jsonl}`; + if (signature !== lastSignature) { + lastSignature = signature; + yield sseEvent({ type: 'transcript', data }); + } + watching = Boolean(live?.active); + if (!watching) break; + await sleep(TRANSCRIPT_WATCH_MS, signal); + } + }, context?.request?.signal); +} diff --git a/agents/transcript.ts b/agents/transcript.ts new file mode 100644 index 0000000..3c4fbf1 --- /dev/null +++ b/agents/transcript.ts @@ -0,0 +1,17 @@ +import { getLiveQuery } from './_lib/session/live.ts'; +import { createTranscriptStreamResponse, resolveClaudeTranscriptPath } from './_lib/session/transcript.ts'; + +/** Session source of truth: stream the Claude JSONL file, unprojected. */ +export async function onRequestGet(context: any) { + return createTranscriptStreamResponse(context, (conversationId) => { + const live = getLiveQuery(conversationId); + if (!live) return null; + return { + path: resolveClaudeTranscriptPath(live.sessionId || '', { + explicitPath: live.transcriptPath, + }), + sessionId: live.sessionId, + active: Boolean(live.turn), + }; + }); +} diff --git a/app/components/session-panel.tsx b/app/components/session-panel.tsx new file mode 100644 index 0000000..796dc88 --- /dev/null +++ b/app/components/session-panel.tsx @@ -0,0 +1,145 @@ +'use client'; + +import { memo, useEffect, useState } from 'react'; +import type { SessionCopy } from '../i18n'; +import { consumeEventStream } from '../features/workspace/sse'; +import { openTranscriptStream } from '../features/workspace/workspace-api'; +import type { TranscriptData, TranscriptStreamEvent } from '../../../shared/protocol'; +import { Spinner } from './spinner'; + +type SessionState = + | { status: 'loading' } + | { status: 'empty' } + | { + status: 'ready'; + jsonl: string; + sessionId: string; + transcriptPath: string; + } + | { status: 'error'; error: string }; + +export const SessionPanel = memo(function SessionPanel({ + conversationId, + live, + copy, +}: { + conversationId: string | null; + live: boolean; + copy: SessionCopy; +}) { + const [state, setState] = useState({ status: 'loading' }); + + useEffect(() => { + if (!conversationId) { + setState({ status: 'empty' }); + return; + } + + const controller = new AbortController(); + let cancelled = false; + + const apply = (data: TranscriptData) => { + if (data.ok === false) { + setState({ status: 'error', error: data.error || copy.failed }); + return; + } + const jsonl = typeof data.jsonl === 'string' ? data.jsonl : ''; + if (!jsonl) { + setState({ status: 'empty' }); + return; + } + const sessionId = data.sessionId || ''; + const transcriptPath = data.transcriptPath || ''; + setState((current) => ( + current.status === 'ready' + && current.jsonl === jsonl + && current.sessionId === sessionId + && current.transcriptPath === transcriptPath + ? current + : { status: 'ready', jsonl, sessionId, transcriptPath } + )); + }; + + (async () => { + try { + const response = await openTranscriptStream(conversationId, controller.signal); + const contentType = response.headers.get('content-type') || ''; + if (cancelled) return; + if (!response.ok || !response.body || !contentType.includes('text/event-stream')) { + setState({ status: 'error', error: copy.failed }); + return; + } + await consumeEventStream(response, (event) => { + if (cancelled || event.type === 'ping') return; + if (event.type === 'error') { + setState({ status: 'error', error: event.error || copy.failed }); + return; + } + if (event.type === 'transcript' && event.data) apply(event.data); + }); + } catch (error) { + if (cancelled || (error instanceof DOMException && error.name === 'AbortError')) return; + if (error instanceof Error && error.name === 'AbortError') return; + setState({ status: 'error', error: copy.failed }); + } + })(); + + return () => { + cancelled = true; + controller.abort(); + }; + }, [conversationId, copy.failed, live]); + + if (state.status === 'loading') { + return ( +
+ +

{copy.loading}

+
+ ); + } + + if (state.status === 'empty') { + return ( +
+ {live ? ( + <> + +

{copy.writing}

+ + ) : ( +

{copy.empty}

+ )} +
+ ); + } + + if (state.status === 'error') { + return ( +
+

{state.error}

+
+ ); + } + + const lines = state.jsonl.split('\n'); + const lineCount = state.jsonl.endsWith('\n') ? lines.length - 1 : lines.length; + const sourceLabel = state.transcriptPath || copy.source; + + return ( +
+
+

+ {sourceLabel} +

+
+ {state.sessionId ? {state.sessionId} : null} + {copy.lines(lineCount)} +
+
+
+        {state.jsonl}
+      
+
+ ); +}); diff --git a/app/features/workspace/hooks/use-workspace-state.ts b/app/features/workspace/hooks/use-workspace-state.ts index 05139df..fca45b1 100644 --- a/app/features/workspace/hooks/use-workspace-state.ts +++ b/app/features/workspace/hooks/use-workspace-state.ts @@ -13,7 +13,7 @@ import type { } from '@/app/types/workspace'; import { fetchProjectArchive } from '../workspace-api'; -export type SandboxTab = 'preview' | 'files'; +export type SandboxTab = 'preview' | 'files' | 'session'; export function useWorkspaceState() { const [deployment, setDeployment] = useState(null); diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index af7c6fb..7548142 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -126,3 +126,11 @@ export function fetchProjectArchive(url: string, conversationId: string) { : {}, }); } + +export function openTranscriptStream(conversationId: string, signal?: AbortSignal) { + return fetch('/transcript', { + method: 'GET', + headers: conversationHeaders(conversationId), + signal, + }); +} diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 6dc0f61..873fc2e 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -11,6 +11,7 @@ import { PanelRight, PanelRightClose, Rocket, + ScrollText, } from 'lucide-react'; import { Button } from '@/app/components/ui/button'; import { @@ -101,6 +102,10 @@ const FilesPanel = dynamic( () => import('@/app/components/files-panel').then((mod) => mod.FilesPanel), { ssr: false, loading: PanelLoading }, ); +const SessionPanel = dynamic( + () => import('@/app/components/session-panel').then((mod) => mod.SessionPanel), + { ssr: false, loading: PanelLoading }, +); export function WorkspaceScreen() { const [language, setLanguage] = useState('zh'); @@ -460,6 +465,10 @@ export function WorkspaceScreen() { {t.workspace.code} {workspace.filesRefreshing && {t.files.refreshing}} + + + {t.workspace.session} +
@@ -575,6 +584,16 @@ export function WorkspaceScreen() { />
)} + + {workspace.sandboxTab === 'session' && ( +
+ +
+ )}
`路由 ${route}`, }, + session: { + empty: '还没有会话记录。', + writing: '会话正在写入…', + loading: '正在加载会话…', + failed: '读取会话失败', + source: 'session.jsonl', + lines: (count: number) => `${count} 行`, + }, }, en: { languageToggleAria: '切换语言为中文', @@ -330,9 +341,10 @@ export const TRANSLATIONS = { gatewayPromptSkip: 'Skip', preview: 'Preview', code: 'Code', + session: 'Session', showPanel: 'Show side panel', hidePanel: 'Hide side panel', - choosePanel: 'Choose Preview or Code', + choosePanel: 'Choose Preview, Code, or Session', refreshPreview: 'Refresh preview', copyPreviewPath: 'Copy current path', previewPathCopied: 'Current path copied', @@ -384,8 +396,17 @@ export const TRANSLATIONS = { }, route: (route: string) => `Route ${route}`, }, + session: { + empty: 'No session transcript yet.', + writing: 'Writing session…', + loading: 'Loading session…', + failed: 'Failed to read session', + source: 'session.jsonl', + lines: (count: number) => `${count} line${count === 1 ? '' : 's'}`, + }, }, } as const; export type UiCopy = (typeof TRANSLATIONS)[Locale]; export type FileCopy = UiCopy['files']; +export type SessionCopy = UiCopy['session']; diff --git a/shared/protocol.ts b/shared/protocol.ts index c4fd05e..4e4f741 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -110,6 +110,18 @@ export type ResumeData = { error?: string; }; +/** Raw Claude JSONL for the Session tab. The file is the source of truth. */ +export type TranscriptData = { + ok?: boolean; + conversation_id?: string; + sessionId?: string; + transcriptPath?: string; + jsonl?: string; + /** The live query still has a turn in flight; more snapshots may follow. */ + live?: boolean; + error?: string; +}; + export type ChatResponse = { ok?: boolean; reply?: string; @@ -210,4 +222,9 @@ export type ResumeStreamEvent = | { type: 'error'; error?: string } | { type: 'ping'; ts?: number }; -export type SessionStreamEvent = ChatStreamEvent | ResumeStreamEvent; +export type TranscriptStreamEvent = + | { type: 'transcript'; data?: TranscriptData } + | { type: 'error'; error?: string } + | { type: 'ping'; ts?: number }; + +export type SessionStreamEvent = ChatStreamEvent | ResumeStreamEvent | TranscriptStreamEvent; diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index 5872bdc..7d193e6 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -67,6 +67,7 @@ const AGENT_ROUTE_FILES = new Set([ 'agents/stop.ts', 'agents/file.ts', 'agents/download.ts', + 'agents/transcript.ts', ]); test('agent routes stay at agents/ and implementation lives in agents/_lib/', async () => { @@ -124,6 +125,7 @@ test('session kernel and makers CLI live under agents/_lib', async () => { for (const target of [ 'agents/_lib/session/store.ts', 'agents/_lib/session/transcript.ts', + 'agents/transcript.ts', 'agents/_lib/session/live.ts', 'agents/_lib/session/projection.ts', 'agents/_lib/makers/session.ts', diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index f6a6432..4f2df92 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -64,6 +64,29 @@ test('initial session restore is one progressive SSE request that can attach a l assert.match(client, /fetch\('\/session',[\s\S]*?method: 'GET'/); }); +test('the session tab reads the raw JSONL transcript and does not project it', async () => { + const route = await readFile('agents/transcript.ts', 'utf8'); + const pipeline = await readFile('agents/_lib/session/transcript.ts', 'utf8'); + const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); + const panel = await readFile('app/components/session-panel.tsx', 'utf8'); + const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); + + assert.match(route, /onRequestGet/); + assert.match(route, /createTranscriptStreamResponse/); + assert.match(route, /getLiveQuery/); + assert.doesNotMatch(route, /onRequestPost/); + assert.match(pipeline, /export async function loadTranscriptJsonl/); + assert.match(pipeline, /export async function createTranscriptStreamResponse/); + assert.match(pipeline, /type: 'transcript'/); + assert.match(client, /fetch\('\/transcript',[\s\S]*?method: 'GET'/); + assert.match(panel, /openTranscriptStream\(/); + assert.match(panel, /consumeEventStream/); + assert.match(panel, /\{state\.jsonl\}/); + assert.doesNotMatch(panel, /setInterval|fetchTranscript/); + assert.doesNotMatch(panel, /projectTranscript|JSON\.stringify\(|JSON\.parse\(/); + assert.match(screen, /value="session"/); +}); + test('file panel performs no automatic or hover prefetch', async () => { const source = await readFile('app/components/files-panel.tsx', 'utf8'); assert.doesNotMatch(source, /prefetch/i); diff --git a/tests/transcript.test.ts b/tests/transcript.test.ts index 9f6ad10..9d08a96 100644 --- a/tests/transcript.test.ts +++ b/tests/transcript.test.ts @@ -1,13 +1,20 @@ import assert from 'node:assert/strict'; -import { mkdtemp, readFile as readDisk, rm, writeFile } from 'node:fs/promises'; +import { mkdir, mkdtemp, readFile as readDisk, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import test from 'node:test'; -import { createMemoryBlobStore, transcriptBlobKey } from '../agents/_lib/session/store.ts'; -import { downloadTranscript, uploadTranscript } from '../agents/_lib/session/transcript.ts'; +import { createMemoryBlobStore, patchConversationRecord, transcriptBlobKey } from '../agents/_lib/session/store.ts'; +import { + createTranscriptStreamResponse, + downloadTranscript, + loadTranscriptJsonl, + resolveClaudeTranscriptPath, + uploadTranscript, +} from '../agents/_lib/session/transcript.ts'; import { projectTranscript } from '../agents/_lib/session/projection.ts'; +import { consumeEventStream } from '../app/features/workspace/sse.ts'; import { applyStreamEvent } from '../shared/timeline.ts'; -import type { PersistedActivityTurn } from '../shared/protocol.ts'; +import type { PersistedActivityTurn, TranscriptStreamEvent } from '../shared/protocol.ts'; test('transcript upload and download stay a single JSONL file', async () => { const directory = await mkdtemp(path.join(tmpdir(), 'transcript-')); @@ -139,10 +146,104 @@ test('live SSE events fold into the same turn model as a JSONL projection', () = } }); +test('GET /transcript streams the JSONL file unaltered', async () => { + const directory = await mkdtemp(path.join(tmpdir(), 'transcript-read-')); + const source = path.join(directory, 'session.jsonl'); + const jsonl = [ + JSON.stringify({ type: 'user', message: { role: 'user', content: 'Hello' } }), + JSON.stringify({ type: 'assistant', message: { role: 'assistant', content: 'Hi.' } }), + '', + ].join('\n'); + await writeFile(source, jsonl); + + const blobStore = createMemoryBlobStore(); + await uploadTranscript({ + context: { blobStore }, + conversationId: 'conv-read', + sessionId: 'sess-read', + sourcePath: source, + }); + + const missing = await createTranscriptStreamResponse({ blobStore }, () => null); + assert.equal(missing.status, 400); + + const response = await createTranscriptStreamResponse( + { blobStore, conversation_id: 'conv-read' }, + () => null, + ); + assert.match(response.headers.get('content-type') || '', /text\/event-stream/); + const events: TranscriptStreamEvent[] = []; + await consumeEventStream(response, (event) => { + if (event.type !== 'ping') events.push(event); + }); + assert.equal(events.length, 1); + assert.equal(events[0]?.type, 'transcript'); + if (events[0]?.type === 'transcript') { + assert.equal(events[0].data?.ok, true); + assert.equal(events[0].data?.sessionId, 'sess-read'); + assert.equal(events[0].data?.jsonl, jsonl); + assert.equal(events[0].data?.live, false); + } + await rm(directory, { recursive: true, force: true }); +}); + +test('a live transcript path streams before the record is uploaded', async () => { + const directory = await mkdtemp(path.join(tmpdir(), 'transcript-live-')); + const source = path.join(directory, 'session.jsonl'); + await writeFile(source, 'line-1\n'); + + const blobStore = createMemoryBlobStore(); + const empty = await loadTranscriptJsonl({ blobStore }, 'conv-live'); + assert.equal(empty, ''); + + let active = true; + const response = await createTranscriptStreamResponse( + { blobStore, conversation_id: 'conv-live' }, + () => ({ path: source, sessionId: 'sess-live', active }), + ); + const snapshots: string[] = []; + const done = consumeEventStream(response, (event) => { + if (event.type !== 'transcript' || typeof event.data?.jsonl !== 'string') return; + snapshots.push(event.data.jsonl); + if (snapshots.length === 1) { + void writeFile(source, 'line-1\nline-2\n').then(() => { + active = false; + }); + } + }); + await done; + assert.deepEqual(snapshots, ['line-1\n', 'line-1\nline-2\n']); + await rm(directory, { recursive: true, force: true }); +}); + +test('Claude JSONL lives under projects//.jsonl, not sessions/', async () => { + const configDir = await mkdtemp(path.join(tmpdir(), 'claude-config-')); + const cwd = '/Users/me/app'; + const sessionId = '28247548-0d9f-4dae-9760-e9380aab5c40'; + const jsonl = `${JSON.stringify({ type: 'user', message: { role: 'user', content: 'Hello' } })}\n`; + const expected = path.join(configDir, 'projects', '-Users-me-app', `${sessionId}.jsonl`); + assert.equal( + resolveClaudeTranscriptPath(sessionId, { configDir, cwd }), + expected, + ); + await mkdir(path.dirname(expected), { recursive: true }); + await writeFile(expected, jsonl); + + const blobStore = createMemoryBlobStore(); + await patchConversationRecord({ blobStore }, 'conv-slug', { claudeSessionId: sessionId }); + const loaded = await loadTranscriptJsonl({ blobStore }, 'conv-slug', '', { configDir, cwd, sessionId }); + assert.equal(loaded, jsonl); + await rm(configDir, { recursive: true, force: true }); +}); + test('compaction re-uploads the local transcript file', async () => { const { readFile } = await import('node:fs/promises'); const live = await readFile('agents/_lib/session/live.ts', 'utf8'); assert.match(live, /subtype === 'compact_boundary'/); assert.match(live, /PostCompact:/); assert.match(live, /persistTranscript\(session\)/); + assert.match(live, /SessionStart/); + assert.match(live, /patchConversationRecord/); + assert.match(live, /transcript_path/); + assert.match(live, /resolveClaudeTranscriptPath/); }); From f5844da0a945fce0f3c46c69b5ab17a598092ec3 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 14:23:39 +0800 Subject: [PATCH 08/26] refactor(workspace): pull UI state and let the host own dest Chat no longer pushes file trees or preview URLs; the frontend reads workspace and files. Dest starts as soon as the sandbox has a project and keeps watching, so a finished turn actually shows in the iframe. --- agents/_lib/makers/compat/run.ts | 7 +- agents/_lib/makers/declarations.ts | 15 +- agents/_lib/makers/project.ts | 9 +- agents/_lib/makers/session.ts | 3 +- agents/_lib/makers/token.ts | 15 +- agents/_lib/models.ts | 5 +- agents/_lib/project/archive.ts | 17 +- agents/_lib/project/commands.ts | 7 +- agents/_lib/project/download.ts | 6 +- agents/_lib/project/fs.ts | 16 +- agents/_lib/project/gateway.ts | 52 +- agents/_lib/project/index.ts | 27 - agents/_lib/project/layout.ts | 75 +++ agents/_lib/project/persistence.ts | 11 +- agents/_lib/project/preview.ts | 34 +- agents/_lib/project/read.ts | 5 +- agents/_lib/project/resume-files.ts | 5 +- agents/_lib/project/scaffold.ts | 86 +--- agents/_lib/project/snapshot.ts | 81 +++ agents/_lib/project/state.ts | 39 +- agents/_lib/project/templates.ts | 5 +- agents/_lib/project/workspace-store.ts | 112 +++++ agents/_lib/project/workspace.ts | 25 +- agents/_lib/prompt.ts | 27 +- agents/_lib/runtime/context.ts | 70 ++- agents/_lib/runtime/request.ts | 54 +- agents/_lib/session/live.ts | 220 ++------ agents/_lib/session/projection.ts | 22 +- agents/_lib/session/prompt-queue.ts | 38 ++ agents/_lib/session/resume.ts | 101 ++-- agents/_lib/session/store.ts | 28 +- agents/_lib/session/stream-projector.ts | 166 ++++++ agents/_lib/session/task.ts | 53 +- agents/_lib/session/transcript.ts | 5 +- agents/_lib/tools/assemble.ts | 9 +- agents/_lib/tools/command-preprocess.ts | 86 ++++ agents/_lib/tools/command-text.ts | 72 +++ agents/_lib/tools/commands-wrap.ts | 410 ++------------- agents/_lib/tools/deploy-command-result.ts | 55 ++ agents/_lib/tools/makers-command.ts | 73 +++ agents/_lib/tools/makers-lifecycle.ts | 21 + agents/_lib/tools/preview-command-result.ts | 98 ++++ agents/_lib/tools/project-tools.ts | 16 +- agents/_lib/turn/auto-fix.ts | 64 +++ agents/_lib/turn/chat.ts | 475 ++++++------------ agents/_lib/turn/checkpoint.ts | 32 +- agents/_lib/turn/deploy.ts | 63 +-- agents/_lib/turn/lifecycle.ts | 7 +- agents/_lib/turn/result.ts | 6 + agents/_lib/types.ts | 32 +- agents/deploy.ts | 16 +- agents/download.ts | 3 +- agents/file.ts | 3 +- agents/preview.ts | 9 +- agents/prompt.ts | 18 +- agents/session.ts | 3 +- agents/stop.ts | 16 +- agents/transcript.ts | 3 +- agents/workspace.ts | 7 + app/components/files-panel.tsx | 2 +- app/components/session-panel.tsx | 2 +- app/features/workspace/hooks/use-live-turn.ts | 124 ++--- .../workspace/hooks/use-preview-surface.ts | 14 +- .../workspace/hooks/use-session-resume.ts | 37 +- .../workspace/hooks/use-workspace-snapshot.ts | 51 ++ app/features/workspace/workspace-api.ts | 49 +- app/features/workspace/workspace-screen.tsx | 17 +- app/hooks/use-file-content-cache.ts | 2 +- app/lib/tool-activity.ts | 2 + app/types/workspace.ts | 1 + shared/protocol.ts | 35 +- shared/user-facing-reply.ts | 10 +- tests/architecture.test.ts | 1 + tests/deploy-task.test.ts | 14 +- tests/gateway-prompt.test.ts | 6 +- tests/helpers/fixtures.ts | 15 + tests/makers-compat.test.ts | 3 +- tests/makers-deploy.test.ts | 6 +- tests/makers-dev.test.ts | 2 +- tests/makers-sub-token.test.ts | 5 +- tests/models.test.ts | 2 +- tests/preview-path.test.ts | 42 +- tests/prompt-single-source.test.ts | 18 +- tests/route-consolidation.test.ts | 22 +- tests/sandbox-timeout.test.ts | 2 +- tests/stopped-turn.test.ts | 2 + 86 files changed, 1948 insertions(+), 1576 deletions(-) delete mode 100644 agents/_lib/project/index.ts create mode 100644 agents/_lib/project/layout.ts create mode 100644 agents/_lib/project/snapshot.ts create mode 100644 agents/_lib/project/workspace-store.ts create mode 100644 agents/_lib/session/prompt-queue.ts create mode 100644 agents/_lib/session/stream-projector.ts create mode 100644 agents/_lib/tools/command-preprocess.ts create mode 100644 agents/_lib/tools/command-text.ts create mode 100644 agents/_lib/tools/deploy-command-result.ts create mode 100644 agents/_lib/tools/makers-command.ts create mode 100644 agents/_lib/tools/makers-lifecycle.ts create mode 100644 agents/_lib/tools/preview-command-result.ts create mode 100644 agents/_lib/turn/auto-fix.ts create mode 100644 agents/_lib/turn/result.ts create mode 100644 agents/workspace.ts create mode 100644 app/features/workspace/hooks/use-workspace-snapshot.ts diff --git a/agents/_lib/makers/compat/run.ts b/agents/_lib/makers/compat/run.ts index 88a1f77..496703c 100644 --- a/agents/_lib/makers/compat/run.ts +++ b/agents/_lib/makers/compat/run.ts @@ -1,3 +1,4 @@ +import { requireSandbox, type AgentContext } from '../../runtime/context.ts'; import { createHash } from 'node:crypto'; import type { ProjectState } from '../../types.ts'; import { runCommandCapturingExit } from '../../project/commands.ts'; @@ -52,7 +53,7 @@ export function buildMakersCompatibilityCommand() { } export async function runMakersCompatibilityCheck( - context: any, + context: AgentContext, state: ProjectState, ) { const [rules, profiles] = await Promise.all([ @@ -63,7 +64,7 @@ export async function runMakersCompatibilityCheck( const scriptPath = `${state.sessionDir}/${COMPAT_SCRIPT_NAME}`; const fingerprint = compatScriptFingerprint(script); const upload = async () => { - await context.sandbox.files.write(scriptPath, script); + await requireSandbox(context).files.write(scriptPath, script); uploadedCompatScripts.set(state.sessionDir, fingerprint); }; @@ -105,7 +106,7 @@ export async function runMakersCompatibilityCheck( * the deployment is broken anyway. */ export async function assertMakersProjectCompatible( - context: any, + context: AgentContext, state: ProjectState, ) { const result = await runMakersCompatibilityCheck(context, state); diff --git a/agents/_lib/makers/declarations.ts b/agents/_lib/makers/declarations.ts index b54b065..03e87d9 100644 --- a/agents/_lib/makers/declarations.ts +++ b/agents/_lib/makers/declarations.ts @@ -7,6 +7,7 @@ * project already declares. Discovering them at the gate costs the user a * failed preview for something nothing had to decide. */ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import type { ProjectState } from '../types.ts'; import { @@ -258,7 +259,7 @@ export function withFrameworkAdapter( * the dependency is already there and only the config wiring is left. */ export async function ensureMakersFrameworkAdapter( - context: any, + context: AgentContext, state: ProjectState, packageJsonContent: string, ): Promise<{ path: string; content: string } | undefined> { @@ -268,12 +269,12 @@ export async function ensureMakersFrameworkAdapter( profiles, ); if (!content) return undefined; - await context.sandbox.files.write(`${state.appDir}/package.json`, content); + await requireSandbox(context).files.write(`${state.appDir}/package.json`, content); return { path: 'package.json', content }; } async function readProjectFile( - context: any, + context: AgentContext, state: ProjectState, relPath: string, ): Promise { @@ -301,7 +302,7 @@ async function readProjectFile( * withholds the framework rather than guessing one. */ async function readAgentImportLines( - context: any, + context: AgentContext, state: ProjectState, ): Promise { try { @@ -337,7 +338,7 @@ let declarationQueue: Promise = Promise.resolve(); * in line costs four reads and no writes. */ export function ensureMakersAgentDeclarations( - context: any, + context: AgentContext, state: ProjectState, ): Promise> { const run = declarationQueue.then( @@ -351,7 +352,7 @@ export function ensureMakersAgentDeclarations( } async function declareMakersAgentFiles( - context: any, + context: AgentContext, state: ProjectState, ): Promise> { const [edgeoneConfig, envExample, packageJson, requirements, agentSources] = await Promise.all([ @@ -372,7 +373,7 @@ async function declareMakersAgentFiles( if (envContent) written.push({ path: '.env.example', content: envContent }); for (const file of written) { - await context.sandbox.files.write(`${state.appDir}/${file.path}`, file.content); + await requireSandbox(context).files.write(`${state.appDir}/${file.path}`, file.content); } return written; } diff --git a/agents/_lib/makers/project.ts b/agents/_lib/makers/project.ts index 7f90905..485160a 100644 --- a/agents/_lib/makers/project.ts +++ b/agents/_lib/makers/project.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../runtime/context.ts'; import { createHash } from 'node:crypto'; import { ConflictError, Makers } from '@edgeone/makers-sdk'; import type { ProjectState } from '../types.ts'; @@ -18,12 +19,12 @@ import { resolveMakersPublishTarget } from '../../../shared/publish-target.ts'; // every turn of the same conversation resolves to the same project. const PROJECT_NAME_PREFIX = 'vibe-coding'; -function pickEnvValue(context: any, key: string) { +function pickEnvValue(context: AgentContext, key: string) { const value = context?.env?.[key]; return typeof value === 'string' ? value.trim() : ''; } -export function resolveMakersProjectName(context: any, state: ProjectState) { +export function resolveMakersProjectName(context: AgentContext, state: ProjectState) { // An explicit name is an operator decision: honour it exactly, including the // consequence that every conversation then shares the one project. const pinned = pickEnvValue(context, 'MAKERS_DEPLOY_PROJECT_NAME'); @@ -107,7 +108,7 @@ export function parsePublishableDotEnv(content: string) { return values; } -async function readSandboxDotEnv(context: any, state: ProjectState) { +async function readSandboxDotEnv(context: AgentContext, state: ProjectState) { try { const content = await context?.sandbox?.files?.read?.(`${state.appDir}/.env`); return typeof content === 'string' ? content : ''; @@ -175,7 +176,7 @@ export async function ensureMakersPublishProject( * `setEnvs` depends on. */ export async function syncSandboxEnvToMakersProject( - context: any, + context: AgentContext, state: ProjectState, masterToken: string, projectName: string, diff --git a/agents/_lib/makers/session.ts b/agents/_lib/makers/session.ts index 004042c..4d7f797 100644 --- a/agents/_lib/makers/session.ts +++ b/agents/_lib/makers/session.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../runtime/context.ts'; import type { ProjectState } from '../types.ts'; import { ensureMakersPublishProject, @@ -26,7 +27,7 @@ export type PreparedMakersSession = { * Preview, deploy, and the commands wrapper all used to do this separately. */ export async function prepareMakersSession( - context: any, + context: AgentContext, state: ProjectState, options: { syncEnv?: boolean } = {}, ): Promise { diff --git a/agents/_lib/makers/token.ts b/agents/_lib/makers/token.ts index 5b97917..d947f94 100644 --- a/agents/_lib/makers/token.ts +++ b/agents/_lib/makers/token.ts @@ -1,7 +1,9 @@ +import type { AgentContext } from '../runtime/context.ts'; import { randomUUID } from 'node:crypto'; import { Makers, MakersError } from '@edgeone/makers-sdk'; import type { ProjectState } from '../types.ts'; import { readProjectGatewayEnv } from '../project/gateway.ts'; +import { bindMakersApiRegion, bindMakersTenantId } from '../project/workspace-store.ts'; // Every preview start, wrapped CLI call and deploy mints its own token, so this // only has to outlive a single CLI invocation. An hour is already far more than @@ -10,12 +12,12 @@ const SUB_TOKEN_TTL_SECONDS = 60 * 60; let cachedPlatformClient: { masterToken: string; client: Makers } | null = null; -function pickEnvValue(context: any, key: string) { +function pickEnvValue(context: AgentContext, key: string) { const value = context?.env?.[key]; return typeof value === 'string' ? value.trim() : ''; } -export function resolveMakersMasterToken(context: any) { +export function resolveMakersMasterToken(context: AgentContext) { return pickEnvValue(context, 'API_TOKEN'); } @@ -28,8 +30,7 @@ export function ensureMakersTenantId(state: ProjectState) { // Keeping it server-generated prevents a client-controlled conversation ID // from selecting another tenant. const tenantId = `vibe-${randomUUID().replaceAll('-', '')}`; - state.makersTenantId = tenantId; - return tenantId; + return bindMakersTenantId(state, tenantId); } function getPlatformClient(masterToken: string) { @@ -80,7 +81,7 @@ export async function issueSandboxMakersSubToken( ? client.region : undefined; if (region) { - state.makersApiRegion = region; + bindMakersApiRegion(state, region); } return created; } catch (error) { @@ -127,7 +128,7 @@ export function describeMissingMakersRuntimeToken(output = '') { const SANDBOX_GATEWAY_KEY = 'AI_GATEWAY_API_KEY'; const SANDBOX_GATEWAY_URL = 'AI_GATEWAY_BASE_URL'; -export function resolveSandboxGatewayEnv(context: any): Record { +export function resolveSandboxGatewayEnv(context: AgentContext): Record { const key = pickEnvValue(context, SANDBOX_GATEWAY_KEY); const url = pickEnvValue(context, SANDBOX_GATEWAY_URL); return { @@ -147,7 +148,7 @@ export function resolveSandboxGatewayEnv(context: any): Record { * this helper's. A skip must still leave preview and deploy able to run. */ export async function prepareSandboxGatewayEnv( - context: any, + context: AgentContext, state: ProjectState, ) { return readProjectGatewayEnv(context, state); diff --git a/agents/_lib/models.ts b/agents/_lib/models.ts index a16bede..c17bd96 100644 --- a/agents/_lib/models.ts +++ b/agents/_lib/models.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from './runtime/context.ts'; import { resolveConfiguredModel, resolveModelCatalog, @@ -12,7 +13,7 @@ export { resolveConfiguredModel, resolveModelCatalog }; * read '' as "no choice" and fall back to the configured model, so a client that * sends an arbitrary string cannot pick what the gateway bills for. */ -export function resolveRequestedModel(context: any, requested: unknown) { +export function resolveRequestedModel(context: AgentContext, requested: unknown) { return resolveSelectedModel(resolveModelCatalog(context), requested); } @@ -21,7 +22,7 @@ export function resolveRequestedModel(context: any, requested: unknown) { * shows this label, and the agent has to say the same words the user is looking * at — a raw ID would name the platform tier no user-facing string names. */ -export function resolveRunningModelLabel(context: any, model: string) { +export function resolveRunningModelLabel(context: AgentContext, model: string) { return resolveModelLabel(resolveModelCatalog(context), model); } diff --git a/agents/_lib/project/archive.ts b/agents/_lib/project/archive.ts index 1a54cbc..9388692 100644 --- a/agents/_lib/project/archive.ts +++ b/agents/_lib/project/archive.ts @@ -1,14 +1,21 @@ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import { ARCHIVE_EXCLUDED_DIRECTORIES, ARCHIVE_EXCLUDED_FILENAMES, DOWNLOAD_ARCHIVE_MAX_BYTES, } from '../constants.ts'; -import type { LegacyProjectSnapshot, ProjectState } from '../types.ts'; +import type { ProjectState } from '../types.ts'; import { safeSegment } from '../utils/paths.ts'; import { runSandboxCommand } from './commands.ts'; import { assertResettableProjectPath } from './state.ts'; import { shellQuote } from '../utils/shell.ts'; +type LegacyProjectSnapshot = { + base64: string; + filename: string; + contentType?: string; +}; + type ProjectArchiveResult = | { // base64, because the Makers proxy only transports text reliably. @@ -49,10 +56,10 @@ function isArchiveBase64Valid( // Zip state.appDir inside the sandbox and return it base64-encoded. files.read // is UTF-8 only and corrupts binary, so the bytes are read out via `base64`. export async function createProjectArchive( - context: any, + context: AgentContext, state: ProjectState, ): Promise { - const sandbox = context.sandbox; + const sandbox = requireSandbox(context); const appDirExists = await sandbox.files.exists(state.appDir); if (!appDirExists) { @@ -185,7 +192,7 @@ export async function createProjectArchive( // sandbox files.write API is UTF-8 only — so we write the base64 as text and // decode + extract with shell, mirroring createProjectArchive's packing path. export async function restoreProjectArchive( - context: any, + context: AgentContext, state: ProjectState, snapshot: LegacyProjectSnapshot, options: { installDependencies?: boolean } = {}, @@ -195,7 +202,7 @@ export async function restoreProjectArchive( } assertResettableProjectPath(state); - const sandbox = context.sandbox; + const sandbox = requireSandbox(context); await sandbox.files.makeDir(state.sessionDir); await sandbox.files.makeDir(state.appDir); diff --git a/agents/_lib/project/commands.ts b/agents/_lib/project/commands.ts index 4e29df9..81f0814 100644 --- a/agents/_lib/project/commands.ts +++ b/agents/_lib/project/commands.ts @@ -1,3 +1,4 @@ +import { requireSandbox, type SandboxCapable } from '../runtime/context.ts'; import { resolveSandboxCommandOptions } from '../project/sandbox-command.ts'; import { parseEchoedExitCode, stripEchoedExit, withExitCodeEcho } from '../makers/tool-phase.ts'; @@ -18,13 +19,13 @@ type SandboxCommandResult = { }; export async function runSandboxCommand( - context: any, + context: SandboxCapable, command: string, options: SandboxCommandOptions = {}, ): Promise { const resolved = resolveSandboxCommandOptions(options); try { - const result = await context.sandbox.commands.run(command, resolved) as SandboxCommandResult; + const result = await requireSandbox(context).commands.run(command, resolved) as SandboxCommandResult; const stdout = typeof result.stdout === 'string' ? result.stdout : ''; const stderr = typeof result.stderr === 'string' ? result.stderr : ''; if (result.exitCode !== 0 && !stdout.trim() && !stderr.trim()) { @@ -45,7 +46,7 @@ export async function runSandboxCommand( } export async function runCommandCapturingExit( - context: any, + context: SandboxCapable, command: string, options: SandboxCommandOptions = {}, ): Promise { diff --git a/agents/_lib/project/download.ts b/agents/_lib/project/download.ts index b326733..1bcfa50 100644 --- a/agents/_lib/project/download.ts +++ b/agents/_lib/project/download.ts @@ -1,8 +1,10 @@ +import type { AgentContext } from '../runtime/context.ts'; import { getProjectState } from '../session/store.ts'; -import { createProjectArchive, restorePersistedProject } from './index.ts'; +import { createProjectArchive } from './archive.ts'; +import { restorePersistedProject } from './persistence.ts'; import { resolveConversationId } from '../runtime/request.ts'; -export async function runProjectDownloadPipeline(context: any): Promise { +export async function runProjectDownloadPipeline(context: AgentContext): Promise { const { conversationId } = resolveConversationId(context, { allowQuery: true }); const jsonError = (error: string, status = 400) => new Response( diff --git a/agents/_lib/project/fs.ts b/agents/_lib/project/fs.ts index 096ed00..da9fae5 100644 --- a/agents/_lib/project/fs.ts +++ b/agents/_lib/project/fs.ts @@ -1,3 +1,4 @@ +import { requireSandbox, type SandboxCapable } from '../runtime/context.ts'; import { FILE_TREE_IGNORED_DIRECTORIES, isIgnoredFileTreePath, @@ -13,13 +14,8 @@ import { } from '../utils/file-preview.ts'; import { readFileExtension } from '../utils/paths.ts'; import { runSandboxCommand } from './commands.ts'; -import { repairNestedAppDirLayout } from './scaffold.ts'; - -export async function getFileTree(context: any, state: ProjectState): Promise { - // Heal sessions that still have the mistaken appDir/appDir/... layout before - // listing, so the Files panel shows package.json at the root. - await repairNestedAppDirLayout(context, state); +export async function getFileTree(context: SandboxCapable, state: ProjectState): Promise { const ignoredDirectoryPruneExpression = FILE_TREE_IGNORED_DIRECTORIES .map((dir) => `-path './${dir}'`) .join(' -o '); @@ -108,7 +104,7 @@ function describeReadFailure(message: string): string { } export async function readFileFromSandbox( - context: any, + context: SandboxCapable, state: ProjectState, relPath: string, ): Promise { @@ -119,10 +115,10 @@ export async function readFileFromSandbox( let content: string; try { - const result = await context.sandbox.files.read(`${state.appDir}/${relPath}`); + const result: unknown = await requireSandbox(context).files.read(`${state.appDir}/${relPath}`); if (typeof result === 'string') { content = result; - } else if (result instanceof Uint8Array) { + } else if (ArrayBuffer.isView(result)) { content = new TextDecoder().decode(result); } else if (result instanceof ArrayBuffer) { content = new TextDecoder().decode(new Uint8Array(result)); @@ -162,7 +158,7 @@ export async function readFileFromSandbox( } export async function readFilesFromSandbox( - context: any, + context: SandboxCapable, state: ProjectState, paths: string[], ): Promise> { diff --git a/agents/_lib/project/gateway.ts b/agents/_lib/project/gateway.ts index 15a6e60..0ad2682 100644 --- a/agents/_lib/project/gateway.ts +++ b/agents/_lib/project/gateway.ts @@ -8,7 +8,8 @@ */ import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; -import { saveProjectState } from '../session/store.ts'; +import { persistWorkspace, setGatewayPending, setGatewaySkipped } from './workspace-store.ts'; +import { requireSandbox, type AgentContext, type SandboxCapable } from '../runtime/context.ts'; import type { ClaudeMcpTool, ProjectState, StreamSend } from '../types.ts'; import { stringifyToolResult } from '../utils/text.ts'; import { getFileTree } from './fs.ts'; @@ -61,9 +62,9 @@ export function envAssignmentValue(content: string, key: string): string { return value.trim(); } -async function readProjectFile(context: any, state: ProjectState, relPath: string) { +async function readProjectFile(context: SandboxCapable, state: ProjectState, relPath: string) { try { - const content = await context.sandbox.files.read(`${state.appDir}/${relPath}`); + const content = await requireSandbox(context).files.read(`${state.appDir}/${relPath}`); return typeof content === 'string' ? content : ''; } catch { return ''; @@ -71,23 +72,23 @@ async function readProjectFile(context: any, state: ProjectState, relPath: strin } export async function projectDeclaresGatewayKeys( - context: any, + context: AgentContext, state: ProjectState, ): Promise { const content = await readProjectFile(context, state, '.env.example'); return Boolean(content) && declaredGatewayKeys(content).length > 0; } -async function projectHasAgentsDirectory(context: any, state: ProjectState) { +async function projectHasAgentsDirectory(context: AgentContext, state: ProjectState) { try { - return Boolean(await context.sandbox.files.exists(`${state.appDir}/agents`)); + return Boolean(await requireSandbox(context).files.exists(`${state.appDir}/agents`)); } catch { return false; } } export async function projectNeedsGatewayKey( - context: any, + context: AgentContext, state: ProjectState, ): Promise { return await projectDeclaresGatewayKeys(context, state) @@ -95,14 +96,14 @@ export async function projectNeedsGatewayKey( } export async function sandboxGatewayKeyIsSet( - context: any, + context: AgentContext, state: ProjectState, ): Promise { const content = await readProjectFile(context, state, '.env'); return Boolean(content) && Boolean(envAssignmentValue(content, 'AI_GATEWAY_API_KEY')); } -async function readProjectAgentFramework(context: any, state: ProjectState) { +async function readProjectAgentFramework(context: AgentContext, state: ProjectState) { const content = await readProjectFile(context, state, 'edgeone.json'); if (!content) return ''; try { @@ -114,7 +115,7 @@ async function readProjectAgentFramework(context: any, state: ProjectState) { } export async function readProjectGatewayEnv( - context: any, + context: AgentContext, state: ProjectState, ): Promise> { const content = await readProjectFile(context, state, '.env'); @@ -142,7 +143,7 @@ function upsertEnvValues(content: string, values: Record) { } export async function writeSandboxGatewayEnv( - context: any, + context: AgentContext, state: ProjectState, values: Record, ) { @@ -150,18 +151,18 @@ export async function writeSandboxGatewayEnv( const envPath = `${state.appDir}/.env`; let current = ''; try { - const existing = await context.sandbox.files.read(envPath); + const existing = await requireSandbox(context).files.read(envPath); if (typeof existing === 'string') current = existing; } catch { current = ''; } const next = upsertEnvValues(current, values); if (next === current.replace(/\r\n/g, '\n').replace(/\n*$/, '\n')) return; - await context.sandbox.files.write(envPath, next); + await requireSandbox(context).files.write(envPath, next); } async function publishFileTreeAfterEnvWrite( - context: any, + context: AgentContext, state: ProjectState, send?: StreamSend, ) { @@ -185,11 +186,11 @@ export type GatewayPromptOptions = { }; export async function askUserForGatewayCredentials( - context: any, + context: AgentContext, state: ProjectState, options: GatewayPromptOptions = {}, ) { - state.gatewayPromptPending = true; + setGatewayPending(state, true); await persistGatewayState(context, options.conversationId || '', state); options.send?.({ type: 'gateway_credentials', @@ -201,7 +202,7 @@ export async function askUserForGatewayCredentials( } export async function shouldPauseForGatewayCredentials( - context: any, + context: AgentContext, state: ProjectState, ): Promise { if (state.gatewaySkipped) return false; @@ -210,7 +211,7 @@ export async function shouldPauseForGatewayCredentials( } export async function pauseForGatewayCredentialsIfNeeded( - context: any, + context: AgentContext, state: ProjectState, options: GatewayPromptOptions = {}, ): Promise { @@ -220,29 +221,28 @@ export async function pauseForGatewayCredentialsIfNeeded( } async function persistGatewayState( - context: any, + context: AgentContext, conversationId: string, state: ProjectState, ) { const id = conversationId.trim(); if (!id) return; try { - await saveProjectState(context, id, state); + await persistWorkspace(context, id, state); } catch { // The card and `.env` write are still useful without a durable flag. } } export async function applyUserGatewayDecision( - context: any, + context: AgentContext, state: ProjectState, conversationId: string, decision: { apiKey?: string; skip?: boolean }, send?: StreamSend, ) { if (decision.skip) { - state.gatewayPromptPending = false; - state.gatewaySkipped = true; + setGatewaySkipped(state, true); await persistGatewayState(context, conversationId, state); return {}; } @@ -257,15 +257,15 @@ export async function applyUserGatewayDecision( ), }; await writeSandboxGatewayEnv(context, state, values); - state.gatewayPromptPending = false; - state.gatewaySkipped = false; + setGatewayPending(state, false); + setGatewaySkipped(state, false); await persistGatewayState(context, conversationId, state); await publishFileTreeAfterEnvWrite(context, state, send); return values; } export function buildRequestGatewayCredentialsTool(options: { - context: any; + context: AgentContext; state: ProjectState; conversationId?: string; send?: StreamSend; diff --git a/agents/_lib/project/index.ts b/agents/_lib/project/index.ts deleted file mode 100644 index 6649821..0000000 --- a/agents/_lib/project/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -export { runCommandCapturingExit, runSandboxCommand } from './commands.ts'; -export { - createProjectState, - resetProjectWorkspace, - separateLegacyMakersDeployment, -} from './state.ts'; -export { - ensureProjectScaffold, - repairNestedAppDirLayout, - runVerification, -} from './scaffold.ts'; -export { - getFileTree, - readFileFromSandbox, - readFilesFromSandbox, - type FileReadResult, -} from './fs.ts'; -export { - resolvePublicLinks, - rewritePreviewAccessToken, - publishRunningPreview, - startPreviewServer, - assertPreviewServerReady, -} from './preview.ts'; -export { createProjectArchive, restoreProjectArchive } from './archive.ts'; -export { resolveMakersProjectName } from '../makers/project.ts'; -export { restorePersistedProject } from './persistence.ts'; diff --git a/agents/_lib/project/layout.ts b/agents/_lib/project/layout.ts new file mode 100644 index 0000000..8c280d2 --- /dev/null +++ b/agents/_lib/project/layout.ts @@ -0,0 +1,75 @@ +import type { ProjectState, ScaffoldLog } from '../types.ts'; +import { requireSandbox, type SandboxCapable } from '../runtime/context.ts'; +import { runSandboxCommand } from './commands.ts'; +import { shellQuote } from '../utils/shell.ts'; + +// Models used to pass `${appDir}/file` into write_project_file, which joined +// appDir again and created appDir/appDir/... . Lift that nested tree back to +// the real project root when we detect the classic nesting marker. +export async function repairNestedAppDirLayout( + context: SandboxCapable, + state: ProjectState, + onLog?: (log: ScaffoldLog) => void, +): Promise { + const nestedRel = state.appDir; + // Probe before running the repair, even though the script's first line is the + // same test. The probe is not what this costs — running the script is, on + // every turn, for a legacy bug that almost no project has. Skipping the probe + // to save a round trip put a command that had barely ever run in production + // in front of the first tool of every conversation. + try { + if (!(await requireSandbox(context).files.exists(`${state.appDir}/${nestedRel}`))) { + return false; + } + } catch { + return false; + } + + let result; + try { + result = await runSandboxCommand( + context, + [ + 'set -e', + `NESTED=${shellQuote(nestedRel)}`, + 'if [ ! -d "$NESTED" ]; then exit 0; fi', + // Classic bug shape: real project under appDir/appDir, root missing package.json. + 'if [ ! -f "$NESTED/package.json" ] && [ ! -f "$NESTED/index.html" ]; then exit 0; fi', + 'if [ -f ./package.json ]; then exit 0; fi', + 'for item in "$NESTED"/*; do', + ' [ -e "$item" ] || continue', + ' name=$(basename "$item")', + ' [ "$name" = "projects" ] && continue', + ' rm -rf "./$name"', + ' mv "$item" "./$name"', + 'done', + 'rm -rf ./projects', + 'echo REPAIRED', + ].join('\n'), + { + cwd: state.appDir, + timeout: 60, + }, + ); + } catch { + // The sandbox raises on a failed command instead of returning its exit + // code, so the check below never sees one and this is the only place a + // failure can be absorbed. Absorbing it is the point: repairing a layout + // almost no project has must not cost a turn to every project that does + // not, and the scaffold that follows reports anything genuinely wrong. + return false; + } + + if (result.exitCode !== 0) { + return false; + } + + const repaired = result.stdout.includes('REPAIRED'); + if (repaired) { + onLog?.({ + stream: 'status', + content: 'Fixed nested project paths and restored files to the workspace root.', + }); + } + return repaired; +} diff --git a/agents/_lib/project/persistence.ts b/agents/_lib/project/persistence.ts index 29ea3ac..c255a4e 100644 --- a/agents/_lib/project/persistence.ts +++ b/agents/_lib/project/persistence.ts @@ -1,15 +1,16 @@ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import type { ProjectState } from '../types.ts'; import { restoreProjectArchive } from './archive.ts'; import { runSandboxCommand } from './commands.ts'; export async function restorePersistedProject( - context: any, + context: AgentContext, conversationId: string, state: ProjectState, options: { installDependencies?: boolean } = {}, ): Promise<{ restored: boolean; error?: string }> { try { - const restored = await context.sandbox.restore({ path: state.appDir }); + const restored = await requireSandbox(context).restore?.({ path: state.appDir }); if (restored?.restored) { if (options.installDependencies !== false) await installDependencies(context, state); return { restored: true }; @@ -20,9 +21,9 @@ export async function restorePersistedProject( return { restored: false }; } -async function installDependencies(context: any, state: ProjectState) { - if (!(await context.sandbox.files.exists(`${state.appDir}/package.json`))) return; - if (await context.sandbox.files.exists(`${state.appDir}/node_modules`)) return; +async function installDependencies(context: AgentContext, state: ProjectState) { + if (!(await requireSandbox(context).files.exists(`${state.appDir}/package.json`))) return; + if (await requireSandbox(context).files.exists(`${state.appDir}/node_modules`)) return; await runSandboxCommand(context, 'npm install --no-audit --no-fund', { cwd: state.appDir, timeout: 300, diff --git a/agents/_lib/project/preview.ts b/agents/_lib/project/preview.ts index 4e1c09e..dc4447d 100644 --- a/agents/_lib/project/preview.ts +++ b/agents/_lib/project/preview.ts @@ -1,3 +1,4 @@ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import { MAKERS_DEV_PORT, PREVIEW_ASSET_PREFIX_ENV, @@ -33,15 +34,17 @@ import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; import { prepareMakersSession } from '../makers/session.ts'; import { resolveConversationPublishArea, resolveMakersProjectName } from '../makers/project.ts'; import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; +import { publishPreview } from './workspace-store.ts'; // Where Makers mounts generated HTTP handlers; both are optional in a project. const CLOUD_FUNCTION_DIRECTORIES = ['cloud-functions', 'edge-functions']; -export async function resolvePublicLinks(context: any) { - const previewHost = context.sandbox.getHost(PREVIEW_PUBLIC_PORT); - const accessToken = context.sandbox.envdAccessToken; +export async function resolvePublicLinks(context: AgentContext) { + const sandbox = requireSandbox(context); + const previewHost = await Promise.resolve(sandbox.getHost?.(PREVIEW_PUBLIC_PORT)); + const accessToken = sandbox.envdAccessToken; const previewBaseUrl = normalizePublicUrl(previewHost); - const sandboxDebugUrl = normalizePublicUrl(context.sandbox.browser?.liveUrl); + const sandboxDebugUrl = normalizePublicUrl(sandbox.browser?.liveUrl); const previewUrl = (previewBaseUrl && accessToken) ? buildPublicPreviewUrl(previewBaseUrl, accessToken) @@ -110,7 +113,7 @@ export function rewritePreviewAccessToken(existingUrl: string, token: string) { * the restart, so re-running them buys a second opinion on the same code. */ export async function startPreviewServer( - context: any, + context: AgentContext, state: ProjectState, options: { verifyRoutes?: boolean } = {}, ) { @@ -247,7 +250,7 @@ const ROUTE_LISTING_COMMAND = [ * sites, and it is why neither gate needs a project-shape flag passed in from * outside — the routes a project declares are the shape. */ -async function assertGeneratedRoutesReady(context: any, state: ProjectState) { +async function assertGeneratedRoutesReady(context: AgentContext, state: ProjectState) { const listing = await runSandboxCommand( context, ROUTE_LISTING_COMMAND, @@ -309,7 +312,7 @@ function smokeFailure(exitCode: number | undefined, detail: string, guidance: st * request still publishes a preview that looks fine until the user clicks. */ async function assertGeneratedApiRoutesReady( - context: any, + context: AgentContext, state: ProjectState, routes: string[], ) { @@ -356,7 +359,7 @@ export function agentRoutesFromListing(stdout: string) { } async function assertGeneratedAgentChatReady( - context: any, + context: AgentContext, state: ProjectState, routes: Set, ) { @@ -412,7 +415,7 @@ async function assertGeneratedAgentChatReady( * of why — an import it cannot resolve, a framework that is not installed. At * the point the route gate fails, that account is the whole answer. */ -async function readMakersDevLog(context: any) { +async function readMakersDevLog(context: AgentContext) { try { const result = await runSandboxCommand( context, @@ -427,7 +430,7 @@ async function readMakersDevLog(context: any) { } export async function publishRunningPreview( - context: any, + context: AgentContext, state: ProjectState, options: { routesAlreadyVerified?: boolean } = {}, ) { @@ -441,10 +444,11 @@ export async function publishRunningPreview( if (!links.previewUrl) { throw new Error(`Makers dev is ready, but the sandbox did not return a public URL for port ${PREVIEW_PUBLIC_PORT}.`); } - state.previewUrl = links.previewUrl; - state.sandboxDebugUrl = links.sandboxDebugUrl; - state.previewKind = 'sandbox'; - state.previewPublished = true; + publishPreview(state, { + url: links.previewUrl, + sandboxDebugUrl: links.sandboxDebugUrl, + kind: 'sandbox', + }); return { url: links.previewUrl, sandboxDebugUrl: links.sandboxDebugUrl, @@ -453,7 +457,7 @@ export async function publishRunningPreview( } export async function assertPreviewServerReady( - context: any, + context: AgentContext, readyPath = PREVIEW_PATH_PREFIX, ) { const result = await runCommandCapturingExit( diff --git a/agents/_lib/project/read.ts b/agents/_lib/project/read.ts index 01fcfcf..05aafe1 100644 --- a/agents/_lib/project/read.ts +++ b/agents/_lib/project/read.ts @@ -1,10 +1,11 @@ +import type { AgentContext } from '../runtime/context.ts'; import { PREVIEW_BATCH_MAX_FILES } from '../constants.ts'; import { getProjectState } from '../session/store.ts'; -import { readFileFromSandbox, readFilesFromSandbox } from './index.ts'; +import { readFileFromSandbox, readFilesFromSandbox } from './fs.ts'; import { toAppRelPath } from '../utils/paths.ts'; import { getRequestQueryParam, resolveConversationId } from '../runtime/request.ts'; -export async function runFileReadPipeline(context: any): Promise { +export async function runFileReadPipeline(context: AgentContext): Promise { const { conversationId } = resolveConversationId(context); const pathParam = getRequestQueryParam(context, 'path'); const pathsParam = getRequestQueryParam(context, 'paths'); diff --git a/agents/_lib/project/resume-files.ts b/agents/_lib/project/resume-files.ts index 4e7bf09..94ca774 100644 --- a/agents/_lib/project/resume-files.ts +++ b/agents/_lib/project/resume-files.ts @@ -1,5 +1,6 @@ +import type { AgentContext } from '../runtime/context.ts'; import { getProjectState } from '../session/store.ts'; -import { readFileFromSandbox } from './index.ts'; +import { readFileFromSandbox } from './fs.ts'; import type { FileTreeItem } from '../types.ts'; import { selectResumeCacheFiles } from './resume-file-cache.ts'; @@ -19,7 +20,7 @@ export type ResumeFileContent = { * clicking an omitted or over-budget file still falls back to /file. */ export async function loadResumeFileContents( - context: any, + context: AgentContext, conversationId: string, items: FileTreeItem[], ): Promise { diff --git a/agents/_lib/project/scaffold.ts b/agents/_lib/project/scaffold.ts index 32cf410..1a43fc5 100644 --- a/agents/_lib/project/scaffold.ts +++ b/agents/_lib/project/scaffold.ts @@ -1,3 +1,4 @@ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import type { BuildResult, BuildStatus, ProjectState, ScaffoldLog } from '../types.ts'; import { detectFatalToolError } from '../utils/text.ts'; import { runCommandCapturingExit, runSandboxCommand } from './commands.ts'; @@ -6,78 +7,9 @@ import { runMakersCompatibilityCheck } from '../makers/compat/run.ts'; import { withFrameworkAdapter } from '../makers/declarations.ts'; import { applyProjectTemplate, listProjectTemplates, resolveProjectTemplate } from './templates.ts'; import type { AppliedTemplate } from './templates.ts'; -import { shellQuote } from '../utils/shell.ts'; +import { repairNestedAppDirLayout } from './layout.ts'; -// Models used to pass `${appDir}/file` into write_project_file, which joined -// appDir again and created appDir/appDir/... . Lift that nested tree back to -// the real project root when we detect the classic nesting marker. -export async function repairNestedAppDirLayout( - context: any, - state: ProjectState, - onLog?: (log: ScaffoldLog) => void, -): Promise { - const nestedRel = state.appDir; - // Probe before running the repair, even though the script's first line is the - // same test. The probe is not what this costs — running the script is, on - // every turn, for a legacy bug that almost no project has. Skipping the probe - // to save a round trip put a command that had barely ever run in production - // in front of the first tool of every conversation. - try { - if (!(await context.sandbox.files.exists(`${state.appDir}/${nestedRel}`))) { - return false; - } - } catch { - return false; - } - - let result; - try { - result = await runSandboxCommand( - context, - [ - 'set -e', - `NESTED=${shellQuote(nestedRel)}`, - 'if [ ! -d "$NESTED" ]; then exit 0; fi', - // Classic bug shape: real project under appDir/appDir, root missing package.json. - 'if [ ! -f "$NESTED/package.json" ] && [ ! -f "$NESTED/index.html" ]; then exit 0; fi', - 'if [ -f ./package.json ]; then exit 0; fi', - 'for item in "$NESTED"/*; do', - ' [ -e "$item" ] || continue', - ' name=$(basename "$item")', - ' [ "$name" = "projects" ] && continue', - ' rm -rf "./$name"', - ' mv "$item" "./$name"', - 'done', - 'rm -rf ./projects', - 'echo REPAIRED', - ].join('\n'), - { - cwd: state.appDir, - timeout: 60, - }, - ); - } catch { - // The sandbox raises on a failed command instead of returning its exit - // code, so the check below never sees one and this is the only place a - // failure can be absorbed. Absorbing it is the point: repairing a layout - // almost no project has must not cost a turn to every project that does - // not, and the scaffold that follows reports anything genuinely wrong. - return false; - } - - if (result.exitCode !== 0) { - return false; - } - - const repaired = result.stdout.includes('REPAIRED'); - if (repaired) { - onLog?.({ - stream: 'status', - content: 'Fixed nested project paths and restored files to the workspace root.', - }); - } - return repaired; -} +export { repairNestedAppDirLayout } from './layout.ts'; /** * What the workspace probe answers, beyond "is anything here". @@ -119,12 +51,12 @@ export type ScaffoldOptions = { const DEPENDENCIES_INSTALLED = 'DEPENDENCIES_INSTALLED'; export async function ensureProjectScaffold( - context: any, + context: AgentContext, state: ProjectState, onLog?: (log: ScaffoldLog) => void, options: ScaffoldOptions = {}, ): Promise { - const sandbox = context.sandbox; + const sandbox = requireSandbox(context); onLog?.({ stream: 'status', content: `Preparing the project workspace ${state.appDir}` }); // appDir is sessionDir plus one segment and the create is recursive, so the @@ -200,7 +132,7 @@ export async function ensureProjectScaffold( * for an optimisation. */ async function applyTemplateIfBaked( - context: any, + context: AgentContext, state: ProjectState, framework: string | undefined, onLog?: (log: ScaffoldLog) => void, @@ -283,7 +215,7 @@ export type VerificationOptions = { * lines is refused instead of run — nothing legitimate needs one, and the value * reaches a shell. */ -async function readDeclaredBuildCommand(context: any, state: ProjectState) { +async function readDeclaredBuildCommand(context: AgentContext, state: ProjectState) { const probe = await runSandboxCommand( context, 'node -e "try { const c=require(\'./edgeone.json\'); process.stdout.write(typeof c.buildCommand === \'string\' ? c.buildCommand : \'\'); } catch (e) { process.stdout.write(\'\'); }"', @@ -298,7 +230,7 @@ export const PRODUCTION_BUILD_DEFERRED = 'Skipped the production build: the preview server compiled this project and passed its smoke tests in this turn, which is the same evidence the build would produce for everything except bundling and prerendering. Publishing runs the real build and reports any production-only failure with its own log.'; export async function runVerification( - context: any, + context: AgentContext, state: ProjectState, options: VerificationOptions = {}, ): Promise { @@ -318,7 +250,7 @@ export async function runVerification( [compatibility.stdout.trim(), stdout.trim()].filter(Boolean).join('\n') ); - const packageExists = await context.sandbox.files.exists(`${state.appDir}/package.json`); + const packageExists = await requireSandbox(context).files.exists(`${state.appDir}/package.json`); if (packageExists) { const hasBuildScript = await runSandboxCommand( context, diff --git a/agents/_lib/project/snapshot.ts b/agents/_lib/project/snapshot.ts new file mode 100644 index 0000000..157d081 --- /dev/null +++ b/agents/_lib/project/snapshot.ts @@ -0,0 +1,81 @@ +import type { WorkspaceSnapshot } from '../../../shared/protocol.ts'; +import { getProjectState } from '../session/store.ts'; +import { getFileTree } from './fs.ts'; +import { resolveConversationId } from '../runtime/request.ts'; +import type { AgentContext } from '../runtime/context.ts'; +import type { FileTreeItem, ProjectState } from '../types.ts'; + +function previewLinkFromState(state: ProjectState) { + if (!state.previewUrl) return {}; + return { + url: state.previewUrl, + sandboxDebugUrl: state.sandboxDebugUrl, + kind: state.previewKind, + }; +} + +function jsonResponse(obj: Record, status = 200) { + return new Response(JSON.stringify(obj), { + status, + headers: { + 'content-type': 'application/json; charset=utf-8', + 'cache-control': 'no-store', + }, + }); +} + +export async function loadWorkspaceSnapshot( + context: AgentContext, + conversationId: string, +): Promise { + const state = await getProjectState(context, conversationId); + let items: FileTreeItem[] = []; + try { + items = await getFileTree(context, state); + } catch { + items = []; + } + const hasFiles = items.some((item) => item.type === 'file'); + const preview = previewLinkFromState(state); + return { + ok: true, + conversation_id: conversationId, + files: { root: state.appDir, items }, + ...(preview.url ? { preview } : {}), + deployment: state.deployment, + build: state.lastBuild, + ...(hasFiles ? { download: { url: '/download', filename: 'source.zip' } } : {}), + }; +} + +export async function runWorkspaceSnapshotPipeline(context: AgentContext): Promise { + const { conversationId } = resolveConversationId(context, { allowQuery: true }); + if (!conversationId) { + return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); + } + try { + return jsonResponse(await loadWorkspaceSnapshot(context, conversationId)); + } catch (error) { + return jsonResponse({ + ok: false, + conversation_id: conversationId, + error: error instanceof Error ? error.message : 'Failed to load the workspace.', + }, 500); + } +} + +export async function runPreviewStatusPipeline(context: AgentContext): Promise { + const { conversationId } = resolveConversationId(context, { allowQuery: true }); + if (!conversationId) { + return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); + } + const state = await getProjectState(context, conversationId); + const preview = previewLinkFromState(state); + return jsonResponse({ + ok: true, + stage: 'preview', + conversation_id: conversationId, + ...(preview.url ? { preview } : {}), + deployment: state.deployment, + }); +} diff --git a/agents/_lib/project/state.ts b/agents/_lib/project/state.ts index 61c52ee..2e033ee 100644 --- a/agents/_lib/project/state.ts +++ b/agents/_lib/project/state.ts @@ -1,7 +1,10 @@ import type { ProjectState } from '../types.ts'; +import { requireSandbox, type SandboxCapable } from '../runtime/context.ts'; import { safeSegment } from '../utils/paths.ts'; import { runSandboxCommand } from './commands.ts'; -import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; +import { resetWorkspaceFields } from './workspace-store.ts'; + +export { separateLegacyMakersDeployment } from './workspace-store.ts'; export function createProjectState(conversationId: string): ProjectState { const sessionDir = `projects/${safeSegment(conversationId)}`; @@ -12,36 +15,13 @@ export function createProjectState(conversationId: string): ProjectState { }; } -/** Migrate persisted state from versions that rendered a deployment as preview. */ -export function separateLegacyMakersDeployment(state: ProjectState) { - const legacyUrl = state.previewUrl; - if ( - !legacyUrl - || (state.previewKind !== 'makers' && !isMakersDeployUrl(legacyUrl)) - ) { - return state; - } - - state.deployment ??= { - status: 'success', - startedAt: 0, - finishedAt: 0, - url: legacyUrl, - }; - state.previewUrl = undefined; - state.sandboxDebugUrl = undefined; - state.previewPublished = undefined; - state.previewKind = undefined; - return state; -} - export async function resetProjectWorkspace( - context: any, + context: SandboxCapable, state: ProjectState, ) { assertResettableProjectPath(state); - const sandbox = context.sandbox; + const sandbox = requireSandbox(context); await sandbox.files.makeDir(state.sessionDir); @@ -61,12 +41,7 @@ export async function resetProjectWorkspace( } await sandbox.files.makeDir(state.appDir); - state.created = false; - state.previewUrl = undefined; - state.sandboxDebugUrl = undefined; - state.previewPublished = undefined; - state.previewKind = undefined; - state.deployment = undefined; + resetWorkspaceFields(state); return appDirExists; } diff --git a/agents/_lib/project/templates.ts b/agents/_lib/project/templates.ts index e7d4100..121e927 100644 --- a/agents/_lib/project/templates.ts +++ b/agents/_lib/project/templates.ts @@ -30,6 +30,7 @@ import { gzipSync } from 'node:zlib'; import { readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; import { PREVIEW_ASSET_PREFIX_ENV } from '../constants.ts'; +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import type { ProjectState, ScaffoldLog } from '../types.ts'; import { safeSegment } from '../utils/paths.ts'; import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; @@ -375,7 +376,7 @@ export type ApplyTemplateOptions = { * the same install, the same handoff, and the same single-npm-process rule. */ export async function applyProjectTemplate( - context: any, + context: AgentContext, state: ProjectState, template: ProjectTemplate, options: ApplyTemplateOptions = {}, @@ -410,7 +411,7 @@ export async function applyProjectTemplate( content: `Writing the ${template.id} project template into ${state.appDir}`, }); - await context.sandbox.files.write(scriptPath, script); + await requireSandbox(context).files.write(scriptPath, script); // set -e so a failed extraction never reaches the warmup: the warmup's first // act is to disable it again, and an install started over a half-written tree diff --git a/agents/_lib/project/workspace-store.ts b/agents/_lib/project/workspace-store.ts new file mode 100644 index 0000000..a7da9b5 --- /dev/null +++ b/agents/_lib/project/workspace-store.ts @@ -0,0 +1,112 @@ +import type { BuildInfo, DeploymentInfo, PreviewKind } from '../../../shared/protocol.ts'; +import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; +import type { PersistCapable } from '../runtime/context.ts'; +import { saveProjectState } from '../session/store.ts'; +import type { ProjectState } from '../types.ts'; + +export type PreviewPublication = { + url: string; + sandboxDebugUrl?: string; + kind?: PreviewKind; +}; + +/** + * The only writer of ProjectState fields. Callers mutate through these + * transitions, then persistWorkspace — saveProjectState has no other callers. + */ +export function publishPreview(state: ProjectState, preview: PreviewPublication) { + state.previewUrl = preview.url; + state.sandboxDebugUrl = preview.sandboxDebugUrl; + state.previewKind = preview.kind || (isMakersDeployUrl(preview.url) ? 'makers' : 'sandbox'); + state.previewPublished = true; + return state; +} + +export function clearPreview(state: ProjectState) { + state.previewUrl = undefined; + state.sandboxDebugUrl = undefined; + state.previewPublished = undefined; + state.previewKind = undefined; + return state; +} + +export function resetWorkspaceFields(state: ProjectState) { + state.created = false; + clearPreview(state); + state.deployment = undefined; + state.lastBuild = undefined; + return state; +} + +export function setDeployment(state: ProjectState, deployment: DeploymentInfo) { + state.deployment = deployment; + return state; +} + +export function markCreated(state: ProjectState) { + state.created = true; + return state; +} + +export function bindSiteDomain(state: ProjectState, siteDomain: string) { + const next = siteDomain.trim(); + if (!next || state.siteDomain === next) return false; + state.siteDomain = next; + return true; +} + +export function setGatewayPending(state: ProjectState, pending: boolean) { + state.gatewayPromptPending = pending; + return state; +} + +export function setGatewaySkipped(state: ProjectState, skipped: boolean) { + state.gatewaySkipped = skipped; + if (skipped) state.gatewayPromptPending = false; + return state; +} + +export function bindMakersTenantId(state: ProjectState, tenantId: string) { + if (!state.makersTenantId) { + state.makersTenantId = tenantId; + } + return state.makersTenantId; +} + +export function bindMakersApiRegion(state: ProjectState, region: 'china' | 'global') { + state.makersApiRegion = region; + return state; +} + +export function setLastBuild(state: ProjectState, build: BuildInfo) { + state.lastBuild = build; + return state; +} + +/** Migrate persisted state from versions that rendered a deployment as preview. */ +export function separateLegacyMakersDeployment(state: ProjectState) { + const legacyUrl = state.previewUrl; + if ( + !legacyUrl + || (state.previewKind !== 'makers' && !isMakersDeployUrl(legacyUrl)) + ) { + return state; + } + + state.deployment ??= { + status: 'success', + startedAt: 0, + finishedAt: 0, + url: legacyUrl, + }; + clearPreview(state); + return state; +} + +export async function persistWorkspace( + context: PersistCapable, + conversationId: string, + state: ProjectState, +) { + await saveProjectState(context, conversationId, state); +} diff --git a/agents/_lib/project/workspace.ts b/agents/_lib/project/workspace.ts index b538d4e..e246421 100644 --- a/agents/_lib/project/workspace.ts +++ b/agents/_lib/project/workspace.ts @@ -1,20 +1,24 @@ -import { getProjectState, saveProjectState } from '../session/store.ts'; +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; +import { getProjectState } from '../session/store.ts'; import { getFileTree } from './fs.ts'; import { restorePersistedProject } from './persistence.ts'; import { separateLegacyMakersDeployment } from './state.ts'; +import { markCreated, persistWorkspace } from './workspace-store.ts'; +import { repairNestedAppDirLayout } from './layout.ts'; import type { ProjectState, StreamSend } from '../types.ts'; import { withTimeout } from '../turn/checkpoint.ts'; const SANDBOX_PROBE_MS = 15_000; const RESTORE_BUDGET_MS = 45_000; -async function ensureWorkspaceDirectories(context: any, state: ProjectState) { - await context.sandbox.files.makeDir(state.sessionDir); - await context.sandbox.files.makeDir(state.appDir); +async function ensureWorkspaceDirectories(context: AgentContext, state: ProjectState) { + const files = requireSandbox(context).files; + await files.makeDir(state.sessionDir); + await files.makeDir(state.appDir); } -async function probeSandboxHasFiles(context: any, state: ProjectState) { - if (!(await context.sandbox.files.exists(state.appDir))) return false; +async function probeSandboxHasFiles(context: AgentContext, state: ProjectState) { + if (!(await requireSandbox(context).files.exists(state.appDir))) return false; const tree = await getFileTree(context, state); return tree.some((item) => item.type === 'file'); } @@ -24,11 +28,12 @@ async function probeSandboxHasFiles(context: any, state: ProjectState) { * prompt and when GET /session rebuilds the workspace. */ export async function restoreProjectWorkspace( - context: any, + context: AgentContext, conversationId: string, options: { send?: StreamSend; mode?: 'prepare' | 'resume' } = {}, ): Promise<{ state: ProjectState; hasFiles: boolean; restoreError?: string }> { const state = separateLegacyMakersDeployment(await getProjectState(context, conversationId)); + await repairNestedAppDirLayout(context, state); const send = options.send; let hasFiles = false; let restoreError: string | undefined; @@ -70,10 +75,10 @@ export async function restoreProjectWorkspace( }); } - if (hasFiles) state.created = true; + if (hasFiles) markCreated(state); if (hasFiles) { try { - await saveProjectState(context, conversationId, state); + await persistWorkspace(context, conversationId, state); } catch { // The sandbox files are still the working copy for this turn. } @@ -83,7 +88,7 @@ export async function restoreProjectWorkspace( } export async function prepareProjectWorkspace( - context: any, + context: AgentContext, conversationId: string, send?: StreamSend, ): Promise { diff --git a/agents/_lib/prompt.ts b/agents/_lib/prompt.ts index 3829f93..ad27c5e 100644 --- a/agents/_lib/prompt.ts +++ b/agents/_lib/prompt.ts @@ -1,9 +1,7 @@ import { - MAKERS_DEV_PORT, PREVIEW_ASSET_PREFIX_ENV, PREVIEW_PATH_PREFIX, PREVIEW_PUBLIC_PORT, - PREVIEW_SERVER_PORT, } from './constants.ts'; import type { ProjectState } from './types.ts'; import { resolveConversationPublishArea } from './makers/project.ts'; @@ -115,7 +113,7 @@ function buildSandboxTools(appDir: string, mcpServerName: string) { // One place says what to do about a missing CLI. The same instruction used // to appear in the workflow and in the code-quality rules as well, and // three copies of a rule are three chances for one of them to go stale. - 'A missing CLI is a platform-capability failure, not a project bug. If makers dev or deploy fails before returning a concrete CLI error, one read-only edgeone --version check is allowed. If any command returns errorCode=MAKERS_CLI_UNAVAILABLE, stop immediately and tell the user the sandbox image does not provide the CLI yet. Do not inspect PATH or installation directories, run command -v/which/npm ls, install packages, use npx, retry, or replace the prescribed command with ad-hoc shell diagnostics.', + 'A missing CLI is a platform-capability failure, not a project bug. If makers deploy fails before returning a concrete CLI error, one read-only edgeone --version check is allowed. If any command returns errorCode=MAKERS_CLI_UNAVAILABLE, stop immediately and tell the user the sandbox image does not provide the CLI yet. Do not inspect PATH or installation directories, run command -v/which/npm ls, install packages, use npx, retry, or replace the prescribed command with ad-hoc shell diagnostics.', 'Never probe or enumerate platform internals to explain a failure: no AI Gateway URLs, no model lists, no generated .edgeone output, no process or port state.', ]; } @@ -124,19 +122,19 @@ function buildSandboxPreview(appDir: string, makersProjectName: string, area: st const quotedProjectName = JSON.stringify(makersProjectName); const publishArea = area === 'overseas' ? 'overseas' : 'global'; return [ - `To publish the right-hand development preview, run edgeone makers dev --port ${MAKERS_DEV_PORT} --skip-env-sync --skip-ai-gateway-sync --name ${quotedProjectName} --area ${publishArea} once through commands with cwd=${appDir}. The commands tool keeps Makers dev running at its root, exposes it through the sandbox path adapter on port ${PREVIEW_SERVER_PORT}, and publishes sandbox.getHost(${PREVIEW_PUBLIC_PORT})${PREVIEW_PATH_PREFIX} to the preview panel. Do not add nohup, start another server, synthesize a public URL, or use a cloud deploy as the normal preview.`, + `The host starts the right-hand development preview as soon as the project workspace exists in this sandbox, and keeps that dest server watching files so later edits show up there. Do not run a preview server, add nohup, start another server, synthesize a public URL, or use a cloud deploy as the normal preview. The sandbox path adapter publishes sandbox.getHost(${PREVIEW_PUBLIC_PORT})${PREVIEW_PATH_PREFIX} to the preview panel.`, // The model has no restart primitive, and it went looking for one: a turn // that changed dependencies under a running server tried to kill it, free // its port, and relaunch it, none of which the host acts on. - 'Rerunning that same command is your only restart mechanism, and whether a restart actually happens is the host\'s decision: it probes the generated endpoints first and restarts the server when one is not mounted. Do not kill processes or free ports to force one — the host terminates the previous server itself before every launch.', + 'The host restarts the preview when generated endpoints are missing. Do not kill processes, free ports, or launch a preview server yourself — the host terminates the previous server before every launch.', // A run installed dependencies and built while the preview was up, and both // lost the race silently: the build reported a Pages Router page the project // does not have, and npm reported ENOTEMPTY on a package the server held. - 'A build or an install cannot run beside the preview, so the host stops the dev server before either and says so in that command\'s output. The preview is then down until you launch it again. Do not report a preview as running across an install or a build you issued after it.', - `Only when the user explicitly asks for a live deployment, run edgeone makers deploy --json once through commands with cwd=${appDir}. The host supplies credentials, pins the project this conversation publishes to, allows the long timeout, parses the final JSON line, and renders the result in its own deployment card.`, + 'A build or an install cannot run beside the preview, so the host stops the dev server before either and says so in that command\'s output. The preview is then down until the host starts it again. Do not report a preview as running across an install or a build you issued after it.', + `Only when the user explicitly asks for a live deployment, run edgeone makers deploy --json once through commands with cwd=${appDir}. This conversation publishes to ${quotedProjectName} with --area ${publishArea}. The host supplies credentials, pins the project this conversation publishes to, allows the long timeout, parses the final JSON line, and renders the result in its own deployment card.`, 'Never pass -n, invent a project name, or retry a failed deploy under a different one: the name identifies the user\'s site, and a deploy under a name you chose publishes somewhere nobody can find again. A deployment never replaces the right-hand preview, so do not tell the user their live site opened there.', 'Declare AI_GATEWAY_API_KEY= and AI_GATEWAY_BASE_URL= in .env.example when the project calls a model. Never write a .env file yourself, and never write an actual API key or gateway URL value into source. Generated agents read them from context.env.', - 'Before preview or deploy of an AI project — one that declares those keys in .env.example, or that has an agents/ directory — call request_gateway_credentials. If the result says the key is already configured, not required, or previously skipped, continue. If it says the user has been asked, stop this turn: do not run edgeone makers dest or deploy, and do not call the tool again. The host shows the input card. Your last user-facing sentence must ask them to enter the key or skip; do not say the preview is ready.', + 'Before preview or deploy of an AI project — one that declares those keys in .env.example, or that has an agents/ directory — call request_gateway_credentials. If the result says the key is already configured, not required, or previously skipped, continue. If it says the user has been asked, stop this turn: do not start a preview or run edgeone makers deploy, and do not call the tool again. The host shows the input card. Your last user-facing sentence must ask them to enter the key or skip; do not say the preview is ready.', 'The user may type a key in the composer in natural language, for example "我的 apikey 是 …,配置好并重新预览", or submit the input card. The host extracts it, writes .env, and the message you see is a masked API Key line — or a skip. After a provided key the host has written .env; after a skip, preview and deploy must still run — a missing key is not a preview or deploy failure. Chat in the generated app may not answer until a key is added later. Never write .env yourself and never quote an API key value, from a file or from the user.', 'The host writes AI_GATEWAY_BASE_URL already shaped for OpenAI-compatible clients. Use that value through the generated env helper; never probe, enumerate, or retry alternate gateway paths, and never concatenate /v1/chat/completions onto the base.', ]; @@ -255,13 +253,13 @@ function buildNewProjectWorkflow(appDir: string) { // nothing runnable. ensure_project_scaffold now answers the right question. `4. Install dependencies inside ${appDir} only when the project has a package.json with dependencies and ensure_project_scaffold reported dependenciesInstalled=false (cd ${appDir} && npm install by default; Python packages are declared in the project's requirements file and installed by the platform). Do not invent nested ${appDir}/${appDir} paths.`, 'Take every dependency name and version range from the reference you loaded for that framework, and copy its dependency block as written. Versions recalled from memory are the usual cause of peer-dependency conflicts and engine mismatches, and each one costs a rewrite plus a reinstall. If a reference pins a version or caps a range, keep the pin instead of widening it to latest.', - '5. Check gateway credentials as the preview section requires, then run edgeone makers dev through commands, with the flags the sandbox preview section gives. When the command result reports a successful preview URL, stop — do not curl/fetch/code_interpreter the public URL and do not start a second preview server. For a CLI failure, quote and act on its actual error; fix generated source when appropriate, then rerun the same preview command once.', + '5. Check gateway credentials as the preview section requires, then stop. The host starts the sandbox preview. Do not curl/fetch/code_interpreter the public URL and do not start a preview server.', ]; } const EXISTING_PROJECT_WORKFLOW = [ 'When ensure_project_scaffold returns created=false, load only the specific Makers references required by the change with load_makers_skill, inspect only the project files directly related to the request, then make the smallest complete change needed.', - 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command, then check gateway credentials as the preview section requires and run edgeone makers dev once through commands.', + 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command, then check gateway credentials as the preview section requires. The host starts the sandbox preview.', ]; const CODE_QUALITY = [ @@ -325,7 +323,7 @@ const FINAL_REPLY = [ // page back every time, and reported the feature working. An HTML body from a // POST to a streaming endpoint is the static site answering in its place. 'An HTML document is not a verified endpoint. When a probe of a project API answers with a page instead of the response that endpoint defines, the request never reached the handler at all — that is a failure to report, not a result to read a meaning into, and never grounds for saying the feature works.', - 'After code changes, check gateway credentials as the preview section requires, then run edgeone makers dev through commands so the user can see the sandbox preview. Do not synthesize preview URLs. Run edgeone makers deploy only when the user explicitly asks to publish a live Makers URL.', + 'After code changes, check gateway credentials as the preview section requires. The host starts the sandbox preview. Do not synthesize preview URLs. Run edgeone makers deploy only when the user explicitly asks to publish a live Makers URL.', 'Do not include preview buttons, preview links, preview URLs, or sandboxDebugUrl in the final response. The sandbox preview is shown only in the right preview panel.', 'A live deployment is the exception: when edgeone makers deploy succeeds, state that the site is live and write its complete URL, query string included, on its own line in the final response. That address is the deliverable and the user has to be able to copy it out of the conversation.', 'Do not take screenshots.', @@ -347,9 +345,16 @@ export function buildPrompt( modelLabel = '', // Fixed for the life of a deployment, so this stays a cacheable prompt. webSearchAvailable = false, + replyLocale: 'zh' | 'en' | '' = '', ) { + const languageRule = replyLocale === 'zh' + ? 'Write all user-facing narration and the final reply in Chinese.' + : replyLocale === 'en' + ? 'Write all user-facing narration and the final reply in English.' + : 'Write all user-facing narration and the final reply in the language of the user request.'; return [ section('Who you are', buildIdentity(modelLabel)), + section('Language', [languageRule]), section('What you take on', SCOPE), section('Where platform knowledge comes from', buildKnowledgeSourcing(webSearchAvailable)), section('What is not a source, and when to stop looking', buildSearchDiscipline(webSearchAvailable)), diff --git a/agents/_lib/runtime/context.ts b/agents/_lib/runtime/context.ts index 9eb5355..820c3fa 100644 --- a/agents/_lib/runtime/context.ts +++ b/agents/_lib/runtime/context.ts @@ -1,5 +1,45 @@ import type { ProjectState } from '../types.ts'; +export type SandboxFiles = { + exists?(path: string): Promise; + read?(path: string): Promise; + write?(path: string, content: string | Uint8Array): Promise; + makeDir?(path: string): Promise; + remove?(path: string): Promise; +}; + +export type SandboxCommands = { + run?(command: string, options?: Record): Promise; +}; + +export type ReadySandboxFiles = { + exists(path: string): Promise; + read(path: string): Promise; + write(path: string, content: string | Uint8Array): Promise; + makeDir(path: string): Promise; + remove?(path: string): Promise; +}; + +export type ReadySandboxCommands = { + run(command: string, options?: Record): Promise; +}; + +export type Sandbox = { + files?: SandboxFiles; + commands?: SandboxCommands; + persist?: (options: { path: string }) => Promise; + restore?: (options: { path: string }) => Promise<{ restored?: boolean } | undefined>; + getHost?: (port: number) => Promise | string | undefined; + envdAccessToken?: string; + browser?: { liveUrl?: string }; + extendTimeout?: (seconds: number) => unknown; +}; + +export type ReadySandbox = Sandbox & { + files: ReadySandboxFiles; + commands: ReadySandboxCommands; +}; + /** The slice of the Makers agent `context` this template actually reads. */ export type AgentContext = { conversation_id?: string; @@ -15,22 +55,7 @@ export type AgentContext = { params?: unknown; [key: string]: unknown; }; - sandbox?: { - files: { - exists: (path: string) => Promise; - read: (path: string) => Promise; - write: (path: string, content: string | Uint8Array) => Promise; - makeDir: (path: string) => Promise; - remove?: (path: string) => Promise; - }; - commands: { run: (command: string, options?: Record) => Promise }; - persist: (options: { path: string }) => Promise; - restore: (options: { path: string }) => Promise<{ restored?: boolean } | undefined>; - getHost?: (port: number) => Promise; - envdAccessToken?: string; - browser?: { liveUrl?: string }; - extendTimeout?: (seconds: number) => unknown; - }; + sandbox?: Sandbox; tools?: { toClaudeMcpServer: (name: string, options?: { alwaysLoad?: boolean }) => { tools: unknown[]; @@ -44,6 +69,19 @@ export type AgentContext = { blobStore?: BlobStoreLike; }; +export type SandboxCapable = Pick; +export type PersistCapable = Pick; +export type RequestCapable = Pick; +export type EnvCapable = Pick; + +export function requireSandbox(context: SandboxCapable): ReadySandbox { + const sandbox = context.sandbox; + if (!sandbox) { + throw new Error('Sandbox is not available'); + } + return sandbox as ReadySandbox; +} + export type BlobStoreLike = { set: (key: string, value: string | ArrayBuffer | Blob | ReadableStream, options?: { onlyIfNew?: boolean }) => Promise; setJSON: (key: string, value: unknown, options?: { onlyIfNew?: boolean }) => Promise; diff --git a/agents/_lib/runtime/request.ts b/agents/_lib/runtime/request.ts index eb0a736..931db35 100644 --- a/agents/_lib/runtime/request.ts +++ b/agents/_lib/runtime/request.ts @@ -1,15 +1,19 @@ -export function getRequestHeader(context: any, name: string): string { +import type { AgentContext, RequestCapable } from './context.ts'; + +export function getRequestHeader(context: RequestCapable, name: string): string { const headers = context?.request?.headers; if (!headers) return ''; - if (typeof headers.get === 'function') { - return String(headers.get(name) || ''); + const maybeHeaders = headers as Headers | Record; + if (typeof (maybeHeaders as Headers).get === 'function') { + return String((maybeHeaders as Headers).get(name) || ''); } + const record = maybeHeaders as Record; const lowerName = name.toLowerCase(); - const directValue = headers[name] ?? headers[lowerName]; + const directValue = record[name] ?? record[lowerName]; const value = directValue - ?? Object.entries(headers).find(([key]) => key.toLowerCase() === lowerName)?.[1]; + ?? Object.entries(record).find(([key]) => key.toLowerCase() === lowerName)?.[1]; return typeof value === 'string' ? value : String(value || ''); } @@ -46,7 +50,10 @@ function getSearchParamFromString(rawValue: unknown, name: string): string { return ''; } -export function getRequestQueryParam(context: any, name: string): { +export function getRequestQueryParam(context: AgentContext & { + query?: unknown; + params?: unknown; +}, name: string): { value: string; source: string; } { @@ -75,15 +82,16 @@ export function getRequestQueryParam(context: any, name: string): { { source: 'context.params', value: context?.params }, ]; for (const query of queryObjects) { - if (query.value && typeof query.value.get === 'function') { - const value = query.value.get(name); + const bag = query.value as { get?: (key: string) => unknown } | Record | undefined; + if (bag && typeof (bag as { get?: unknown }).get === 'function') { + const value = (bag as { get: (key: string) => unknown }).get(name); if (value) { return { value: queryValueToString(value), source: query.source }; } continue; } - if (!query || typeof query !== 'object') continue; - const value = query.value?.[name]; + if (!bag || typeof bag !== 'object') continue; + const value = (bag as Record)[name]; const normalized = queryValueToString(value); if (normalized) { return { value: normalized, source: query.source }; @@ -93,8 +101,16 @@ export function getRequestQueryParam(context: any, name: string): { return { value: '', source: 'none' }; } +export function getRequestBody(context: RequestCapable): Record { + const body = context.request?.body; + if (!body || typeof body !== 'object' || Array.isArray(body)) { + return {}; + } + return body as Record; +} + export function resolveConversationId( - context: any, + context: AgentContext, options?: { allowQuery?: boolean }, ): { conversationId: string; source: string } { const contextConversationId = String(context?.conversation_id || ''); @@ -125,3 +141,19 @@ export function resolveConversationId( return { conversationId: '', source: 'none' }; } + +/** + * Public site root from the incoming Host, used to pick Makers acceleration + * area. Mirrors the browser hostname split: `foo.edgeone.dev` → `edgeone.dev`. + */ +export function resolveRequestSiteDomain(context: RequestCapable): string { + const forwarded = getRequestHeader(context, 'x-forwarded-host'); + const host = (forwarded || getRequestHeader(context, 'host')).split(',')[0].trim(); + const hostname = host.split(':')[0].toLowerCase(); + if (!hostname || hostname === 'localhost' || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { + return ''; + } + const parts = hostname.split('.'); + if (parts.length < 2) return hostname; + return parts.slice(1).join('.'); +} diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts index ea1c8ff..de7d63e 100644 --- a/agents/_lib/session/live.ts +++ b/agents/_lib/session/live.ts @@ -31,62 +31,22 @@ import type { ProjectState, } from '../types.ts'; import { detectFatalToolError, truncateForStream } from '../utils/text.ts'; -import { - resolveNarrationEmit, - sanitizeAssistantText, - sanitizeNarrationText, - summarizeToolInput, - summarizeToolOutput, - type NarrationEmitState, -} from '../../../shared/timeline.ts'; -import { - isInstallCommand, - isMakersDeployCommand, - isPreviewCommand, - parseEchoedExitCode, - shortenToolName, -} from '../makers/tool-phase.ts'; +import { sanitizeAssistantText, summarizeToolOutput } from '../../../shared/timeline.ts'; +import { parseEchoedExitCode } from '../makers/tool-phase.ts'; import { buildPrompt } from '../prompt.ts'; import { resolveMakersProjectName } from '../makers/project.ts'; -import { getConversationRecord, patchConversationRecord } from './store.ts'; +import { getConversationRecord, getLanguagePreference, patchConversationRecord } from './store.ts'; import { downloadTranscript, resolveClaudeTranscriptPath, uploadTranscript } from './transcript.ts'; - -class PromptQueue implements AsyncIterable { - private messages: SDKUserMessage[] = []; - private waiters: Array<(result: IteratorResult) => void> = []; - private closed = false; - - push(message: SDKUserMessage) { - if (this.closed) return; - const waiter = this.waiters.shift(); - if (waiter) waiter({ value: message, done: false }); - else this.messages.push(message); - } - - close() { - this.closed = true; - for (const waiter of this.waiters) { - waiter({ value: undefined as unknown as SDKUserMessage, done: true }); - } - this.waiters = []; - } - - [Symbol.asyncIterator](): AsyncIterator { - return { - next: () => { - if (this.messages.length > 0) { - return Promise.resolve({ value: this.messages.shift()!, done: false as const }); - } - if (this.closed) { - return Promise.resolve({ value: undefined as unknown as SDKUserMessage, done: true as const }); - } - return new Promise>((resolve) => { - this.waiters.push(resolve); - }); - }, - }; - } -} +import { PromptQueue } from './prompt-queue.ts'; +import { + SCAFFOLD_TOOL_NAME, + createProgressEmitter, + extractVisibleNarrationDelta, + extractVisibleTextBlock, + isToolUseContentBlock, + parseToolInputJson, + type StreamingToolUseBlock, +} from './stream-projector.ts'; type TurnWaiter = { callbacks: LiveTurnCallbacks; @@ -118,6 +78,7 @@ export type RunCodingAgentOptions = { onProjectFilesChanged?: LiveTurnCallbacks['onProjectFilesChanged']; onPreviewReady?: LiveTurnCallbacks['onPreviewReady']; onDeploymentStatus?: LiveTurnCallbacks['onDeploymentStatus']; + onWorkspaceReady?: LiveTurnCallbacks['onWorkspaceReady']; abortSignal?: AbortSignal; model?: string; send?: LiveTurnCallbacks['send']; @@ -144,79 +105,6 @@ function buildAnthropicCustomHeaders(customHeaders: string, conversationId: stri ].filter(Boolean).join('\n'); } -function extractSandboxCommand(input: unknown) { - const record = input && typeof input === 'object' ? input as Record : {}; - const command = typeof record.command === 'string' - ? record.command - : typeof record.cmd === 'string' - ? record.cmd - : ''; - return command.trim(); -} - -function extractVisibleNarrationDelta(event: SDKMessage) { - if (event.type !== 'stream_event') return ''; - const streamEvent = (event as { event?: { type?: string; delta?: { type?: string; text?: string } } }).event; - if (streamEvent?.type !== 'content_block_delta') return ''; - const delta = streamEvent.delta; - if (delta?.type === 'text_delta' && typeof delta.text === 'string') { - return sanitizeNarrationText(delta.text); - } - return ''; -} - -type StreamingToolUseBlock = { - id: string; - name: string; - inputJson: string; - input?: unknown; -}; - -function isToolUseContentBlock(block: unknown): block is { - type: string; - id?: string; - name?: string; - input?: unknown; -} { - const record = block && typeof block === 'object' ? block as Record : {}; - return record.type === 'tool_use' || record.type === 'mcp_tool_use'; -} - -function extractVisibleTextBlock(block: unknown) { - const record = block && typeof block === 'object' ? block as Record : {}; - if (record.type !== 'text' || typeof record.text !== 'string') return ''; - return sanitizeNarrationText(record.text); -} - -function parseToolInputJson(rawJson: string, fallback: unknown) { - if (!rawJson.trim()) return fallback ?? {}; - try { - return JSON.parse(rawJson); - } catch { - return fallback ?? {}; - } -} - -type ToolProgressPhase = 'scaffold' | 'code' | 'install' | 'preview' | 'link'; - -function inferToolProgress(name: string, input: unknown): { - phaseHint?: ToolProgressPhase; - fileCount?: number; -} { - const toolName = shortenToolName(name); - if (toolName === 'ensure_project_scaffold') return { phaseHint: 'scaffold' }; - if (toolName === 'files_write' || toolName === 'write_files' || toolName === 'files_make_dir' || toolName === 'files_remove') { - return { phaseHint: 'code' }; - } - if (toolName === 'write_project_file') return { phaseHint: 'code', fileCount: 1 }; - if (toolName === 'commands') { - const cmd = extractSandboxCommand(input); - if (isInstallCommand(cmd)) return { phaseHint: 'install' }; - if (isPreviewCommand(cmd) || isMakersDeployCommand(cmd)) return { phaseHint: 'preview' }; - } - return {}; -} - function userMessage(content: string): SDKUserMessage { return { type: 'user', @@ -248,63 +136,16 @@ async function persistTranscript(session: LiveQuerySession) { }); } + async function pumpSession(session: LiveQuerySession) { - const toolContextById = new Map(); - const toolStartedAtById = new Map(); const pendingToolUseBlocks = new Map(); - const emittedToolUseProgress = new Map(); - let narrationState: NarrationEmitState = { currentTextBlock: '', emittedNarration: '' }; - const scaffoldToolName = `mcp__${SANDBOX_MCP_SERVER_NAME}__ensure_project_scaffold`; + const progress = createProgressEmitter({ + appDir: session.getState().appDir, + onProgress: (event) => session.turn?.onProgress?.(event), + }); let scaffoldHandled = false; let fatalError: string | null = null; - const emitNarration = (rawText: string, uuid: string, complete = false) => { - const resolved = resolveNarrationEmit(narrationState, rawText, complete); - narrationState = resolved.state; - if (!resolved.text) return; - session.turn?.onProgress?.({ - type: 'text_segment', - data: { uuid, text: resolved.text }, - }); - }; - - const emitToolUseProgress = (toolUse: { id?: string; name?: string; input?: unknown }) => { - const toolName = typeof toolUse.name === 'string' ? toolUse.name : ''; - const toolUseId = typeof toolUse.id === 'string' ? toolUse.id : ''; - const shortToolName = shortenToolName(toolName); - const command = shortToolName === 'commands' ? extractSandboxCommand(toolUse.input) : ''; - const progress = typeof toolUse.name === 'string' ? inferToolProgress(toolName, toolUse.input) : {}; - const inputSummary = summarizeToolInput(toolName, toolUse.input, session.getState().appDir); - const progressSignature = JSON.stringify({ - name: toolName, - command, - phaseHint: progress.phaseHint || '', - fileCount: progress.fileCount || 0, - inputSummary, - }); - if (toolUseId) { - if (emittedToolUseProgress.get(toolUseId) === progressSignature) return; - emittedToolUseProgress.set(toolUseId, progressSignature); - } - narrationState = { ...narrationState, currentTextBlock: '' }; - if (toolUseId && typeof toolUse.name === 'string') { - toolContextById.set(toolUseId, { name: toolUse.name, ...(command ? { command } : {}) }); - } - const startedAt = toolUseId ? toolStartedAtById.get(toolUseId) || Date.now() : Date.now(); - if (toolUseId) toolStartedAtById.set(toolUseId, startedAt); - session.turn?.onProgress?.({ - type: 'tool_use', - data: { - id: toolUseId, - name: toolName, - ...(command ? { command } : {}), - ...progress, - inputSummary, - startedAt, - }, - }); - }; - const finishTurn = async (result: CodingAgentResult) => { await persistTranscript(session).catch((error) => { console.warn('[transcript] upload failed', error); @@ -332,7 +173,7 @@ async function pumpSession(session: LiveQuerySession) { if (!session.turn) continue; if (event.type === 'stream_event') { - emitNarration( + progress.emitNarration( extractVisibleNarrationDelta(event), typeof event.uuid === 'string' ? event.uuid : '', false, @@ -341,7 +182,7 @@ async function pumpSession(session: LiveQuerySession) { if (streamEvent?.type === 'content_block_start') { const contentBlock = streamEvent.content_block; if (contentBlock?.type === 'text') { - narrationState = { ...narrationState, currentTextBlock: '' }; + progress.beginTextBlock(); } if (isToolUseContentBlock(contentBlock) && typeof streamEvent.index === 'number') { pendingToolUseBlocks.set(streamEvent.index, { @@ -350,7 +191,7 @@ async function pumpSession(session: LiveQuerySession) { inputJson: '', input: contentBlock.input, }); - emitToolUseProgress({ + progress.emitToolUseProgress({ id: contentBlock.id, name: contentBlock.name, input: contentBlock.input, @@ -370,7 +211,7 @@ async function pumpSession(session: LiveQuerySession) { : undefined; if (pendingToolUse) { pendingToolUseBlocks.delete(streamEvent.index); - emitToolUseProgress({ + progress.emitToolUseProgress({ id: pendingToolUse.id, name: pendingToolUse.name, input: parseToolInputJson(pendingToolUse.inputJson, pendingToolUse.input), @@ -384,13 +225,13 @@ async function pumpSession(session: LiveQuerySession) { const blocks = (event as { message?: { content?: unknown } }).message?.content; if (Array.isArray(blocks)) { for (const block of blocks) { - emitNarration( + progress.emitNarration( extractVisibleTextBlock(block), typeof event.uuid === 'string' ? event.uuid : '', true, ); if (isToolUseContentBlock(block)) { - emitToolUseProgress({ id: block.id, name: block.name, input: block.input }); + progress.emitToolUseProgress({ id: block.id, name: block.name, input: block.input }); } } } @@ -407,7 +248,7 @@ async function pumpSession(session: LiveQuerySession) { ? record.content.map((item: any) => (typeof item?.text === 'string' ? item.text : '')).join(' ') : (typeof record.content === 'string' ? record.content : ''); const toolUseId = typeof record.tool_use_id === 'string' ? record.tool_use_id : ''; - const toolContext = toolContextById.get(toolUseId); + const toolContext = progress.toolContextById.get(toolUseId); const toolName = toolContext?.name || ''; const echoedExit = parseEchoedExitCode(text); const commandFailed = typeof echoedExit === 'number' && echoedExit !== 0; @@ -425,7 +266,7 @@ async function pumpSession(session: LiveQuerySession) { endedAt: Date.now(), }, }); - if (!scaffoldHandled && toolName === scaffoldToolName && record.is_error !== true) { + if (!scaffoldHandled && toolName === SCAFFOLD_TOOL_NAME && record.is_error !== true) { scaffoldHandled = true; try { await session.getCallbacks().onProjectFilesChanged?.(); @@ -476,11 +317,8 @@ async function pumpSession(session: LiveQuerySession) { ...flagsFrom(session), }); } - toolContextById.clear(); - toolStartedAtById.clear(); pendingToolUseBlocks.clear(); - emittedToolUseProgress.clear(); - narrationState = { currentTextBlock: '', emittedNarration: '' }; + progress.resetTurn(); scaffoldHandled = false; fatalError = null; } @@ -604,6 +442,7 @@ async function startLiveQuery(options: RunCodingAgentOptions): Promise { - current = { + const turn: PersistedActivityTurn = { id: `turn-${createdAt}-${turns.length}`, user, assistant: '', @@ -50,7 +50,9 @@ export function projectTranscript(jsonl: string, projectDir = ''): PersistedActi createdAt, activities: [], }; - turns.push(current); + active.turn = turn; + turns.push(turn); + return turn; }; for (const rawLine of jsonl.split('\n')) { @@ -70,11 +72,12 @@ export function projectTranscript(jsonl: string, projectDir = ''): PersistedActi const tools = toolBlocks(content); const text = textFromContent(content); if (tools.some((block) => block.type === 'tool_result')) { - if (!current) continue; + const turn = active.turn; + if (!turn) continue; for (const block of tools) { if (block.type !== 'tool_result') continue; const id = typeof block.tool_use_id === 'string' ? block.tool_use_id : ''; - const existing = current.activities.find( + const existing = turn.activities.find( (activity): activity is Extract => activity.kind === 'tool' && activity.toolUseId === id, ); @@ -93,17 +96,18 @@ export function projectTranscript(jsonl: string, projectDir = ''): PersistedActi continue; } - if (entry.type === 'assistant' && current) { + if (entry.type === 'assistant' && active.turn) { + const turn = active.turn; const text = textFromContent(content); if (text) { - current.activities = appendNarrationChunk(current.activities, text); - current.assistant = text; + turn.activities = appendNarrationChunk(turn.activities, text); + turn.assistant = text; } for (const block of toolBlocks(content)) { if (block.type !== 'tool_use' && block.type !== 'mcp_tool_use') continue; const id = typeof block.id === 'string' ? block.id : ''; const name = typeof block.name === 'string' ? block.name : 'tool'; - current.activities.push({ + turn.activities.push({ kind: 'tool', toolUseId: id, name, diff --git a/agents/_lib/session/prompt-queue.ts b/agents/_lib/session/prompt-queue.ts new file mode 100644 index 0000000..226b70d --- /dev/null +++ b/agents/_lib/session/prompt-queue.ts @@ -0,0 +1,38 @@ +import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'; + +export class PromptQueue implements AsyncIterable { + private messages: SDKUserMessage[] = []; + private waiters: Array<(result: IteratorResult) => void> = []; + private closed = false; + + push(message: SDKUserMessage) { + if (this.closed) return; + const waiter = this.waiters.shift(); + if (waiter) waiter({ value: message, done: false }); + else this.messages.push(message); + } + + close() { + this.closed = true; + for (const waiter of this.waiters) { + waiter({ value: undefined as unknown as SDKUserMessage, done: true }); + } + this.waiters = []; + } + + [Symbol.asyncIterator](): AsyncIterator { + return { + next: () => { + if (this.messages.length > 0) { + return Promise.resolve({ value: this.messages.shift()!, done: false as const }); + } + if (this.closed) { + return Promise.resolve({ value: undefined as unknown as SDKUserMessage, done: true as const }); + } + return new Promise>((resolve) => { + this.waiters.push(resolve); + }); + }, + }; + } +} diff --git a/agents/_lib/session/resume.ts b/agents/_lib/session/resume.ts index f60bec2..3e0182f 100644 --- a/agents/_lib/session/resume.ts +++ b/agents/_lib/session/resume.ts @@ -1,23 +1,23 @@ +import type { AgentContext } from '../runtime/context.ts'; import { getChatTask, getConversationRecord, + getLanguagePreference, getModelPreference, getProjectState, - saveProjectState, } from './store.ts'; import { hasLiveChatTask, isChatTaskActive, iterateLiveChatTaskEvents, markOrphanedTaskFailed } from './task.ts'; import { loadTranscriptJsonl } from './transcript.ts'; import { projectTranscript, turnsToMessages } from './projection.ts'; -import { - assertPreviewServerReady, - getFileTree, - resolvePublicLinks, - rewritePreviewAccessToken, - separateLegacyMakersDeployment, - startPreviewServer, -} from '../project/index.ts'; +import { assertPreviewServerReady, resolvePublicLinks, rewritePreviewAccessToken, startPreviewServer } from '../project/preview.ts'; +import { getFileTree } from '../project/fs.ts'; +import { separateLegacyMakersDeployment } from '../project/state.ts'; import { restoreProjectWorkspace } from '../project/workspace.ts'; -import { loadResumeFileContents } from '../project/resume-files.ts'; +import { + clearPreview, + persistWorkspace, + publishPreview, +} from '../project/workspace-store.ts'; import type { FileTreeItem, PersistedActivity, PersistedActivityTurn, ProjectState } from '../types.ts'; import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; import { mergeSseGenerators } from '../runtime/merge.ts'; @@ -83,11 +83,12 @@ function jsonResponse(obj: Record, status = 200) { }); } -async function loadProjectResumeHistory(context: any, conversationId: string) { - const [record, jsonl, model] = await Promise.all([ +async function loadProjectResumeHistory(context: AgentContext, conversationId: string) { + const [record, jsonl, model, language] = await Promise.all([ getConversationRecord(context, conversationId), loadTranscriptJsonl(context, conversationId), getModelPreference(context, conversationId), + getLanguagePreference(context, conversationId), ]); const state = separateLegacyMakersDeployment(record.projectState); const activityHistory = projectTranscript(jsonl, state.appDir); @@ -122,11 +123,12 @@ async function loadProjectResumeHistory(context: any, conversationId: string) { needsWorkspace: hasProject, deployment: state.deployment, model, + language: language || undefined, gatewayNeeded: state.gatewayPromptPending === true, }; } -async function republishPreviewOnResume(context: any, state: ProjectState) { +async function republishPreviewOnResume(context: AgentContext, state: ProjectState) { if (isMakersPreviewState(state) && state.previewUrl) { return { url: state.previewUrl, @@ -144,8 +146,11 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { const rewritten = rewritePreviewAccessToken(state.previewUrl, accessToken); if (rewritten) { const warmLinks = await resolvePublicLinks(context); - state.previewUrl = rewritten; - state.sandboxDebugUrl = warmLinks.sandboxDebugUrl || state.sandboxDebugUrl; + publishPreview(state, { + url: rewritten, + sandboxDebugUrl: warmLinks.sandboxDebugUrl || state.sandboxDebugUrl, + kind: 'sandbox', + }); return { url: rewritten, sandboxDebugUrl: state.sandboxDebugUrl, @@ -156,8 +161,11 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { const warmLinks = await resolvePublicLinks(context); if (warmLinks.previewUrl) { - state.previewUrl = warmLinks.previewUrl; - state.sandboxDebugUrl = warmLinks.sandboxDebugUrl; + publishPreview(state, { + url: warmLinks.previewUrl, + sandboxDebugUrl: warmLinks.sandboxDebugUrl, + kind: 'sandbox', + }); return { url: warmLinks.previewUrl, sandboxDebugUrl: warmLinks.sandboxDebugUrl, @@ -179,8 +187,11 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { if (!links.previewUrl) { throw new Error('Preview server started but no public preview URL was available.'); } - state.previewUrl = links.previewUrl; - state.sandboxDebugUrl = links.sandboxDebugUrl; + publishPreview(state, { + url: links.previewUrl, + sandboxDebugUrl: links.sandboxDebugUrl, + kind: 'sandbox', + }); return { url: links.previewUrl, sandboxDebugUrl: links.sandboxDebugUrl, @@ -188,16 +199,10 @@ async function republishPreviewOnResume(context: any, state: ProjectState) { }; } -async function runWorkspaceRestoreBody(context: any, conversationId: string) { - const [storedState, chatTask, jsonl] = await Promise.all([ - getProjectState(context, conversationId), - getChatTask(context, conversationId), - loadTranscriptJsonl(context, conversationId), - ]); - const activityHistory = projectTranscript(jsonl, storedState.appDir); +async function runWorkspaceRestoreBody(context: AgentContext, conversationId: string) { + const chatTask = await getChatTask(context, conversationId); const restored = await restoreProjectWorkspace(context, conversationId, { mode: 'resume' }); const state = restored.state; - const hadPreview = projectStateImpliesPreview(state, activityHistory); const generationActive = isChatTaskActive(chatTask) && hasLiveChatTask(conversationId, chatTask.id); if (!restored.hasFiles) { @@ -220,7 +225,7 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { } const hasFileItems = items.some((item) => item.type === 'file'); - const shouldRestartPreview = !generationActive && hasFileItems && hadPreview; + const shouldStartPreview = !generationActive && hasFileItems; let preview: { url?: string; @@ -229,31 +234,25 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { restarted?: boolean; kind?: 'sandbox' | 'makers'; } = {}; - if (shouldRestartPreview) { + if (shouldStartPreview) { try { preview = await withTimeout( republishPreviewOnResume(context, state), PREVIEW_RESTART_BUDGET_MS, 'preview resume', ); - state.previewPublished = true; } catch (error) { - state.previewUrl = undefined; - state.sandboxDebugUrl = undefined; + clearPreview(state); console.warn( '[resume:workspace] preview restart failed:', error instanceof Error ? error.message : error, ); preview = {}; } - } else if (!generationActive && !hadPreview) { - state.previewUrl = undefined; - state.sandboxDebugUrl = undefined; - state.previewKind = undefined; } try { - await saveProjectState(context, conversationId, state); + await persistWorkspace(context, conversationId, state); } catch { // Non-fatal — the files payload below is still useful. } @@ -271,14 +270,10 @@ async function runWorkspaceRestoreBody(context: any, conversationId: string) { }; } -async function runPreviewRefreshBody(context: any, conversationId: string) { - const [storedState, jsonl] = await Promise.all([ - getProjectState(context, conversationId), - loadTranscriptJsonl(context, conversationId), - ]); +async function runPreviewRefreshBody(context: AgentContext, conversationId: string) { + const storedState = await getProjectState(context, conversationId); const state = separateLegacyMakersDeployment(storedState); - const hadPreview = projectStateImpliesPreview(state, projectTranscript(jsonl, state.appDir)); - if (!hadPreview) { + if (!state.created && !state.previewUrl && !state.previewPublished) { return { ok: true as const, stage: 'preview' as const, @@ -290,9 +285,8 @@ async function runPreviewRefreshBody(context: any, conversationId: string) { try { const preview = await republishPreviewOnResume(context, state); - state.previewPublished = true; try { - await saveProjectState(context, conversationId, state); + await persistWorkspace(context, conversationId, state); } catch { // Non-fatal — the fresh URL below is still usable for this session. } @@ -315,7 +309,7 @@ async function runPreviewRefreshBody(context: any, conversationId: string) { } } -export async function runProjectResumePreviewPipeline(context: any): Promise { +export async function runProjectResumePreviewPipeline(context: AgentContext): Promise { const { conversationId } = resolveConversationId(context, { allowQuery: true }); if (!conversationId) { return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); @@ -341,7 +335,7 @@ export async function runProjectResumePreviewPipeline(context: any): Promise { @@ -355,12 +349,9 @@ async function* iterateWorkspaceResumeEvents( yield sseEvent({ type: 'resume_workspace', data: workspace }); const fileItems = workspace.files?.items || []; - if (!signal?.aborted && fileItems.length > 0) { - const contents = await loadResumeFileContents(context, conversationId, fileItems); - for (const file of contents) { - if (signal?.aborted) return; - yield sseEvent({ type: 'resume_file_content', data: file }); - } + const paths = fileItems.filter((item) => item.type === 'file').map((item) => item.path); + if (!signal?.aborted && paths.length > 0) { + yield sseEvent({ type: 'file_changed', data: { paths } }); } } catch (error) { const message = error instanceof Error ? error.message : 'Workspace resume failed.'; @@ -381,7 +372,7 @@ async function* iterateWorkspaceResumeEvents( } } -export async function createProjectResumeStreamResponse(context: any): Promise { +export async function createProjectResumeStreamResponse(context: AgentContext): Promise { const { conversationId } = resolveConversationId(context, { allowQuery: true }); if (!conversationId) { return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); diff --git a/agents/_lib/session/store.ts b/agents/_lib/session/store.ts index 28f0f4d..ddca5d4 100644 --- a/agents/_lib/session/store.ts +++ b/agents/_lib/session/store.ts @@ -1,6 +1,6 @@ import { getStore } from '@edgeone/pages-blob'; import { createProjectState } from '../project/state.ts'; -import type { BlobStoreLike } from '../runtime/context.ts'; +import type { BlobStoreLike, PersistCapable } from '../runtime/context.ts'; import type { ChatTask, ProjectState } from '../types.ts'; const BLOB_STORE_NAME = 'vibe-sessions'; @@ -9,6 +9,7 @@ export type ConversationRecord = { claudeSessionId?: string; transcriptPath?: string; modelPreference?: string; + languagePreference?: 'zh' | 'en'; projectState: ProjectState; chatTask?: ChatTask | null; }; @@ -87,9 +88,12 @@ export function createMemoryBlobStore(): BlobStoreLike { }; } -export function getBlobStore(context?: { blobStore?: BlobStoreLike }): BlobStoreLike { +export function getBlobStore(context?: PersistCapable): BlobStoreLike { if (context?.blobStore) return context.blobStore; - return getStore({ name: BLOB_STORE_NAME, consistency: 'strong' }) as BlobStoreLike; + return (getStore as unknown as (options: { name: string; consistency: 'strong' }) => BlobStoreLike)({ + name: BLOB_STORE_NAME, + consistency: 'strong', + }); } export async function getConversationRecord( @@ -164,3 +168,21 @@ export async function saveModelPreference( ) { await patchConversationRecord(context, conversationId, { modelPreference: model.trim() }); } + +export async function getLanguagePreference( + context: { blobStore?: BlobStoreLike }, + conversationId: string, +) { + const value = (await getConversationRecord(context, conversationId)).languagePreference; + return value === 'zh' || value === 'en' ? value : ''; +} + +export async function saveLanguagePreference( + context: { blobStore?: BlobStoreLike }, + conversationId: string, + language: string, +) { + const next = language.trim(); + if (next !== 'zh' && next !== 'en') return; + await patchConversationRecord(context, conversationId, { languagePreference: next }); +} diff --git a/agents/_lib/session/stream-projector.ts b/agents/_lib/session/stream-projector.ts new file mode 100644 index 0000000..d08eb48 --- /dev/null +++ b/agents/_lib/session/stream-projector.ts @@ -0,0 +1,166 @@ +import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; +import { SANDBOX_MCP_SERVER_NAME } from '../constants.ts'; +import type { AgentProgressEvent } from '../types.ts'; +import { + resolveNarrationEmit, + sanitizeNarrationText, + summarizeToolInput, + type NarrationEmitState, +} from '../../../shared/timeline.ts'; +import { + isInstallCommand, + isMakersDeployCommand, + isPreviewCommand, + shortenToolName, +} from '../makers/tool-phase.ts'; + +export function extractSandboxCommand(input: unknown) { + const record = input && typeof input === 'object' ? input as Record : {}; + const command = typeof record.command === 'string' + ? record.command + : typeof record.cmd === 'string' + ? record.cmd + : ''; + return command.trim(); +} + +export function extractVisibleNarrationDelta(event: SDKMessage) { + if (event.type !== 'stream_event') return ''; + const streamEvent = (event as { event?: { type?: string; delta?: { type?: string; text?: string } } }).event; + if (streamEvent?.type !== 'content_block_delta') return ''; + const delta = streamEvent.delta; + if (delta?.type === 'text_delta' && typeof delta.text === 'string') { + return sanitizeNarrationText(delta.text); + } + return ''; +} + +export type StreamingToolUseBlock = { + id: string; + name: string; + inputJson: string; + input?: unknown; +}; + +export function isToolUseContentBlock(block: unknown): block is { + type: string; + id?: string; + name?: string; + input?: unknown; +} { + const record = block && typeof block === 'object' ? block as Record : {}; + return record.type === 'tool_use' || record.type === 'mcp_tool_use'; +} + +export function extractVisibleTextBlock(block: unknown) { + const record = block && typeof block === 'object' ? block as Record : {}; + if (record.type !== 'text' || typeof record.text !== 'string') return ''; + return sanitizeNarrationText(record.text); +} + +export function parseToolInputJson(rawJson: string, fallback: unknown) { + if (!rawJson.trim()) return fallback ?? {}; + try { + return JSON.parse(rawJson); + } catch { + return fallback ?? {}; + } +} + +type ToolProgressPhase = 'scaffold' | 'code' | 'install' | 'preview' | 'link'; + +export function inferToolProgress(name: string, input: unknown): { + phaseHint?: ToolProgressPhase; + fileCount?: number; +} { + const toolName = shortenToolName(name); + if (toolName === 'ensure_project_scaffold') return { phaseHint: 'scaffold' }; + if (toolName === 'files_write' || toolName === 'write_files' || toolName === 'files_make_dir' || toolName === 'files_remove') { + return { phaseHint: 'code' }; + } + if (toolName === 'write_project_file') return { phaseHint: 'code', fileCount: 1 }; + if (toolName === 'commands') { + const cmd = extractSandboxCommand(input); + if (isInstallCommand(cmd)) return { phaseHint: 'install' }; + if (isPreviewCommand(cmd) || isMakersDeployCommand(cmd)) return { phaseHint: 'preview' }; + } + return {}; +} + +export const SCAFFOLD_TOOL_NAME = `mcp__${SANDBOX_MCP_SERVER_NAME}__ensure_project_scaffold`; + +export function createProgressEmitter(options: { + appDir: string; + onProgress?: (event: AgentProgressEvent) => void; +}) { + const toolContextById = new Map(); + const toolStartedAtById = new Map(); + const emittedToolUseProgress = new Map(); + let narrationState: NarrationEmitState = { currentTextBlock: '', emittedNarration: '' }; + + const emitNarration = (rawText: string, uuid: string, complete = false) => { + const resolved = resolveNarrationEmit(narrationState, rawText, complete); + narrationState = resolved.state; + if (!resolved.text) return; + options.onProgress?.({ + type: 'text_segment', + data: { uuid, text: resolved.text }, + }); + }; + + const emitToolUseProgress = (toolUse: { id?: string; name?: string; input?: unknown }) => { + const toolName = typeof toolUse.name === 'string' ? toolUse.name : ''; + const toolUseId = typeof toolUse.id === 'string' ? toolUse.id : ''; + const shortToolName = shortenToolName(toolName); + const command = shortToolName === 'commands' ? extractSandboxCommand(toolUse.input) : ''; + const progress = typeof toolUse.name === 'string' ? inferToolProgress(toolName, toolUse.input) : {}; + const inputSummary = summarizeToolInput(toolName, toolUse.input, options.appDir); + const progressSignature = JSON.stringify({ + name: toolName, + command, + phaseHint: progress.phaseHint || '', + fileCount: progress.fileCount || 0, + inputSummary, + }); + if (toolUseId) { + if (emittedToolUseProgress.get(toolUseId) === progressSignature) return; + emittedToolUseProgress.set(toolUseId, progressSignature); + } + narrationState = { ...narrationState, currentTextBlock: '' }; + if (toolUseId && typeof toolUse.name === 'string') { + toolContextById.set(toolUseId, { name: toolUse.name, ...(command ? { command } : {}) }); + } + const startedAt = toolUseId ? toolStartedAtById.get(toolUseId) || Date.now() : Date.now(); + if (toolUseId) toolStartedAtById.set(toolUseId, startedAt); + options.onProgress?.({ + type: 'tool_use', + data: { + id: toolUseId, + name: toolName, + ...(command ? { command } : {}), + ...progress, + inputSummary, + startedAt, + }, + }); + }; + + return { + toolContextById, + toolStartedAtById, + emitNarration, + emitToolUseProgress, + resetNarration() { + narrationState = { currentTextBlock: '', emittedNarration: '' }; + }, + beginTextBlock() { + narrationState = { ...narrationState, currentTextBlock: '' }; + }, + resetTurn() { + toolContextById.clear(); + toolStartedAtById.clear(); + emittedToolUseProgress.clear(); + narrationState = { currentTextBlock: '', emittedNarration: '' }; + }, + }; +} diff --git a/agents/_lib/session/task.ts b/agents/_lib/session/task.ts index c2b62b3..6e3bb69 100644 --- a/agents/_lib/session/task.ts +++ b/agents/_lib/session/task.ts @@ -1,8 +1,11 @@ +import type { AgentContext } from '../runtime/context.ts'; import { runChatPipeline } from '../turn/chat.ts'; import { runDeployPipeline } from '../turn/deploy.ts'; import { getChatTask, + getLanguagePreference, saveChatTask, + saveLanguagePreference, saveModelPreference, } from './store.ts'; import { interruptLiveQuery } from './live.ts'; @@ -51,7 +54,7 @@ export function abortLiveChatTask(conversationId: string) { } } -export async function markChatTaskStopped(context: any, conversationId: string) { +export async function markChatTaskStopped(context: AgentContext, conversationId: string) { const trimmed = conversationId.trim(); if (!trimmed) return; try { @@ -68,7 +71,7 @@ export async function markChatTaskStopped(context: any, conversationId: string) } } -export async function markOrphanedTaskFailed(context: any, conversationId: string) { +export async function markOrphanedTaskFailed(context: AgentContext, conversationId: string) { const existing = await getChatTask(context, conversationId); if (!existing || !isChatTaskActive(existing)) return null; if (hasLiveTask(conversationId, existing.id)) return existing; @@ -93,7 +96,7 @@ function createTaskId() { return `${Date.now()}-${Math.random().toString(36).slice(2)}`; } -export function getConversationId(context: any): string { +export function getConversationId(context: AgentContext): string { return resolveConversationId(context).conversationId.trim(); } @@ -128,20 +131,7 @@ function getOrCreateLiveTask(conversationId: string, task: ChatTask): LiveChatTa return liveTask; } -function filePushPath(event: ChatStreamEvent): string { - if (event.type !== 'file_content') return ''; - return event.data?.path || ''; -} - function publish(liveTask: LiveChatTask, event: ChatStreamEvent) { - const supersededPath = filePushPath(event); - if (supersededPath) { - const previousIndex = liveTask.events.findIndex( - (record) => filePushPath(record.event) === supersededPath, - ); - if (previousIndex >= 0) liveTask.events.splice(previousIndex, 1); - } - const record = { sequence: ++liveTask.nextSequence, event, @@ -153,7 +143,9 @@ function publish(liveTask: LiveChatTask, event: ChatStreamEvent) { for (const listener of liveTask.listeners) listener(record); } -export function isChatTaskActive(task: ChatTask | null | undefined): task is ChatTask { +export function isChatTaskActive( + task: ChatTask | null | undefined, +): task is ChatTask & { status: 'queued' | 'running' } { return task?.status === 'queued' || task?.status === 'running'; } @@ -166,13 +158,13 @@ type ChatTaskOptions = { turnId?: string; kind?: ChatTaskKind; model?: string; - siteDomain?: string; + language?: string; apiKey?: string; gatewaySkip?: boolean; }; async function createChatTask( - context: any, + context: AgentContext, message: string, options: ChatTaskOptions = {}, ) { @@ -199,12 +191,11 @@ async function createChatTask( } const requestedModel = (options.model || '').trim(); - const siteDomain = (options.siteDomain || '').trim(); + const language = (options.language || '').trim(); const task: ChatTask = { id: taskId, message, ...(options.kind === 'deploy' ? { kind: 'deploy' as const } : { kind: 'prompt' as const }), - ...(siteDomain ? { siteDomain } : {}), ...(requestedModel ? { model: requestedModel } : {}), status: 'queued', createdAt: Date.now(), @@ -213,17 +204,20 @@ async function createChatTask( if (requestedModel) { await saveModelPreference(context, conversationId, requestedModel); } + if (language === 'zh' || language === 'en') { + await saveLanguagePreference(context, conversationId, language); + } return { ok: true as const, conversationId, task }; } -function withTaskAbortSignal(context: any, signal: AbortSignal) { +function withTaskAbortSignal(context: AgentContext, signal: AbortSignal) { const request = context?.request && typeof context.request === 'object' ? { ...context.request, signal } : { signal }; return { ...context, request }; } -async function executeLiveTask(context: any, liveTask: LiveChatTask) { +async function executeLiveTask(context: AgentContext, liveTask: LiveChatTask) { const runningTask: ChatTask = { ...liveTask.task, status: 'running', @@ -241,10 +235,11 @@ async function executeLiveTask(context: any, liveTask: LiveChatTask) { try { await saveChatTask(taskContext, liveTask.conversationId, runningTask); + const language = await getLanguagePreference(taskContext, liveTask.conversationId); if (liveTask.task.kind === 'deploy') { await runDeployPipeline(taskContext, liveTask.task.message, send, { turnId: liveTask.task.id, - siteDomain: liveTask.task.siteDomain, + language: language || undefined, apiKey: liveTask.gatewayApiKey, gatewaySkip: liveTask.gatewaySkip, }); @@ -252,7 +247,7 @@ async function executeLiveTask(context: any, liveTask: LiveChatTask) { await runChatPipeline(taskContext, liveTask.task.message, send, { turnId: liveTask.task.id, model: liveTask.task.model, - siteDomain: liveTask.task.siteDomain, + language: language || undefined, apiKey: liveTask.gatewayApiKey, gatewaySkip: liveTask.gatewaySkip, }); @@ -295,7 +290,7 @@ async function executeLiveTask(context: any, liveTask: LiveChatTask) { } function ensureChatTaskStarted( - context: any, + context: AgentContext, conversationId: string, task: ChatTask, extras?: { gatewayApiKey?: string; gatewaySkip?: boolean }, @@ -331,7 +326,7 @@ class AsyncEventQueue { const ABORTED = Symbol('aborted'); export async function* iterateLiveChatTaskEvents( - context: any, + context: AgentContext, conversationId: string, task: ChatTask, extras?: { gatewayApiKey?: string; gatewaySkip?: boolean }, @@ -386,7 +381,7 @@ export async function* iterateLiveChatTaskEvents( } function createLiveTaskStreamResponse( - context: any, + context: AgentContext, conversationId: string, task: ChatTask, extras?: { gatewayApiKey?: string; gatewaySkip?: boolean }, @@ -397,7 +392,7 @@ function createLiveTaskStreamResponse( } export async function createChatTaskAndStreamResponse( - context: any, + context: AgentContext, message: string, options: ChatTaskOptions = {}, ) { diff --git a/agents/_lib/session/transcript.ts b/agents/_lib/session/transcript.ts index 089b5b2..4beb61f 100644 --- a/agents/_lib/session/transcript.ts +++ b/agents/_lib/session/transcript.ts @@ -1,3 +1,4 @@ +import type { AgentContext } from '../runtime/context.ts'; import { createReadStream, createWriteStream, existsSync, readdirSync } from 'node:fs'; import { mkdir, stat } from 'node:fs/promises'; import path from 'node:path'; @@ -15,7 +16,7 @@ function toNodeReadable(value: unknown): Readable | null { if (!value) return null; if (value instanceof Readable) return value; if (typeof (value as ReadableStream).getReader === 'function') { - return Readable.fromWeb(value as ReadableStream); + return Readable.fromWeb(value as import('node:stream/web').ReadableStream); } if (typeof value === 'string') { return Readable.from([value]); @@ -182,7 +183,7 @@ function sleep(ms: number, signal?: AbortSignal) { /** GET /transcript — JSONL snapshots while the live file is being written. */ export async function createTranscriptStreamResponse( - context: any, + context: AgentContext, resolveLive: (conversationId: string) => LiveTranscriptRef | null, ): Promise { const { conversationId } = resolveConversationId(context); diff --git a/agents/_lib/tools/assemble.ts b/agents/_lib/tools/assemble.ts index 3c79c75..4fd38b2 100644 --- a/agents/_lib/tools/assemble.ts +++ b/agents/_lib/tools/assemble.ts @@ -17,7 +17,7 @@ import type { ScaffoldLog, StreamSend, } from '../types.ts'; -import { wrapSandboxTools } from './commands-wrap.ts'; +import { wrapSandboxTools, type MakersCommandLifecycle } from './commands-wrap.ts'; import { wrapWebSearchTool } from './web-search-wrap.ts'; import { WEB_SEARCH_API_KEY_ENV, @@ -35,6 +35,8 @@ export type LiveTurnCallbacks = { onProjectFilesChanged?: (file?: { path: string; content: string }) => void | Promise; onPreviewReady?: (preview: { url?: string; sandboxDebugUrl?: string; kind?: PreviewKind }) => void; onDeploymentStatus?: (deployment: DeploymentInfo) => void; + /** Project files exist in this sandbox; the host can start dest. */ + onWorkspaceReady?: () => void; send?: StreamSend; abortSignal?: AbortSignal; }; @@ -93,6 +95,7 @@ export function assembleAgentTools(session: LiveSessionHandle) { ({ created }) => { session.flags.projectTouched = true; session.flags.wasCreated = created; + session.getCallbacks().onWorkspaceReady?.(); }, ); const writeProjectFileTool = buildWriteProjectFileTool( @@ -105,7 +108,7 @@ export function assembleAgentTools(session: LiveSessionHandle) { }, ); const sandboxTools = wrapWebSearchTool(wrapSandboxTools( - edgeoneMcp.tools.filter((tool: { name: string }) => offerSandboxTool(tool.name)) as ClaudeMcpTool[], + (edgeoneMcp.tools as ClaudeMcpTool[]).filter((tool) => offerSandboxTool(tool.name)), { context, get state() { @@ -126,7 +129,7 @@ export function assembleAgentTools(session: LiveSessionHandle) { session.flags.deploymentTouched = true; session.getCallbacks().onDeploymentStatus?.(deployment); }, - } as any, + } as MakersCommandLifecycle, )); const mcpTools = [ ...sandboxTools, diff --git a/agents/_lib/tools/command-preprocess.ts b/agents/_lib/tools/command-preprocess.ts new file mode 100644 index 0000000..1318b0a --- /dev/null +++ b/agents/_lib/tools/command-preprocess.ts @@ -0,0 +1,86 @@ +import { + MAKERS_DEV_PORT, +} from '../constants.ts'; +import { + buildMakersDevStopScript, +} from '../makers/cli-dev.ts'; +import { + buildNpmCacheReclaimScript, + buildNpmWarmupHandoffScript, + buildNpmWarmupWaitScript, +} from '../makers/npm-install.ts'; +import { + isBareInstallCommand, + isInstallCommand, + isScaffolderCommand, + isVerificationCommand, +} from '../makers/tool-phase.ts'; + +export function extractCommand(args: unknown) { + const record = args && typeof args === 'object' ? args as Record : {}; + const command = typeof record.command === 'string' + ? record.command + : typeof record.cmd === 'string' + ? record.cmd + : ''; + return { record, command }; +} + +export function withWrappedCommand(args: unknown, wrapped: string) { + const { record, command } = extractCommand(args); + if (!command || wrapped === command) { + return args; + } + return { + ...record, + ...(typeof record.command === 'string' ? { command: wrapped } : {}), + ...(typeof record.cmd === 'string' ? { cmd: wrapped } : {}), + }; +} + +export function withCommandOptions( + args: unknown, + command: string, + cwd: string, + env: Record, + timeout: number, +) { + const { record } = extractCommand(args); + return { + ...record, + ...(typeof record.command === 'string' ? { command } : { cmd: command }), + cwd, + env: { + ...(record.env && typeof record.env === 'object' + ? record.env as Record + : {}), + ...env, + }, + timeout, + }; +} + +const DEV_SERVER_STOPPED_NOTICE = 'The preview dev server was stopped before this command, ' + + 'because a build or an install in the same directory races it over .next and node_modules. ' + + 'The preview is down until you run `edgeone makers dev` again.'; + + +export function withDevServerStopped(command: string) { + return [buildMakersDevStopScript(MAKERS_DEV_PORT, DEV_SERVER_STOPPED_NOTICE), command].join('\n'); +} + +export function withWarmedInstall(command: string, wrapped: string) { + if (isBareInstallCommand(command)) { + return [buildNpmWarmupHandoffScript(), wrapped, buildNpmCacheReclaimScript()].join('\n'); + } + return [buildNpmWarmupWaitScript(), wrapped].join('\n'); +} + +export function shouldStopDevServer(command: string, isMakersCommand: boolean) { + return !isMakersCommand + && ( + isInstallCommand(command) + || isVerificationCommand(command) + || isScaffolderCommand(command) + ); +} diff --git a/agents/_lib/tools/command-text.ts b/agents/_lib/tools/command-text.ts new file mode 100644 index 0000000..cf115fe --- /dev/null +++ b/agents/_lib/tools/command-text.ts @@ -0,0 +1,72 @@ +import type { ClaudeMcpTool } from '../types.ts'; +import { + MAKERS_CLI_UNAVAILABLE_ERROR_CODE, + MAKERS_CLI_UNAVAILABLE_MESSAGE, +} from '../makers/tool-phase.ts'; +import { redactSecret } from '../makers/cli-deploy.ts'; + +export type ToolHandlerResult = Awaited>; + +export function textContents(result: ToolHandlerResult) { + return (result.content || []) + .flatMap((item) => item && typeof item === 'object' && 'text' in item + && typeof item.text === 'string' ? [item.text] : []) + .join('\n'); +} + +export function commandOutputFromToolResult(result: ToolHandlerResult) { + const raw = textContents(result); + const streams: string[] = []; + for (const item of result.content || []) { + if (!item || typeof item !== 'object' || !('text' in item) || typeof item.text !== 'string') { + continue; + } + try { + const parsed = JSON.parse(item.text) as Record; + if (typeof parsed.stdout === 'string') streams.push(parsed.stdout); + if (typeof parsed.stderr === 'string') streams.push(parsed.stderr); + } catch { + // Some runtime versions return raw stdout instead of a JSON envelope. + } + } + return [...streams, raw].filter(Boolean).join('\n'); +} + +export function appendText(result: ToolHandlerResult, text: string) { + return { + ...result, + content: [ + ...(result.content || []), + { type: 'text' as const, text }, + ], + }; +} + +export function withMakersCliUnavailableError( + result: ToolHandlerResult, + attemptedCommand: string, +) { + return { + ...appendText(result, JSON.stringify({ + status: 'error', + errorCode: MAKERS_CLI_UNAVAILABLE_ERROR_CODE, + retryable: false, + error: MAKERS_CLI_UNAVAILABLE_MESSAGE, + attemptedCommand, + instruction: 'Stop this preview/deploy attempt. Do not inspect PATH or installation directories, install packages, use npx, or retry. Tell the user this is a sandbox image rollout blocker, not a generated-project error.', + })), + isError: true, + }; +} + +export function redactToolResult(result: ToolHandlerResult, secret: string) { + if (!secret) return result; + return { + ...result, + content: (result.content || []).map((item) => ( + item && typeof item === 'object' && 'text' in item && typeof item.text === 'string' + ? { ...item, text: redactSecret(item.text, secret) } + : item + )), + }; +} diff --git a/agents/_lib/tools/commands-wrap.ts b/agents/_lib/tools/commands-wrap.ts index bb696b0..5bf5b76 100644 --- a/agents/_lib/tools/commands-wrap.ts +++ b/agents/_lib/tools/commands-wrap.ts @@ -1,269 +1,41 @@ -import type { - ClaudeMcpTool, - DeploymentInfo, - PreviewKind, - ProjectState, - StreamSend, -} from '../types.ts'; -import { - MAKERS_DEV_PORT, - PREVIEW_ASSET_PREFIX_ENV, - PREVIEW_PATH_PREFIX, - PREVIEW_SERVER_PORT, -} from '../constants.ts'; +import type { ClaudeMcpTool } from '../types.ts'; +import { startPreviewServer } from '../project/preview.ts'; import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; -import { prepareMakersSession } from '../makers/session.ts'; -import { - previewFailureWarrantsRestart, - publishRunningPreview, - startPreviewServer, -} from '../project/preview.ts'; import { pauseForGatewayCredentialsIfNeeded } from '../project/gateway.ts'; -import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; -import { - MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, - MAKERS_DEV_PORT_DRIFT_EXIT, - buildMakersDevBackgroundCommand, - buildMakersDevStopScript, - parseMakersDevExitCode, -} from '../makers/cli-dev.ts'; -import { - buildMakersDeployCommand, - describeMakersDeployment, - readMakersDeployOutcome, - redactSecret, -} from '../makers/cli-deploy.ts'; -import { - buildNpmCacheReclaimScript, - buildNpmWarmupHandoffScript, - buildNpmWarmupWaitScript, -} from '../makers/npm-install.ts'; import { - MAKERS_CLI_UNAVAILABLE_ERROR_CODE, - MAKERS_CLI_UNAVAILABLE_MESSAGE, buildEdgeoneVersionCheckCommand, forbiddenSandboxCommandReason, - isEdgeoneCliUnavailable, - isBareInstallCommand, isEdgeoneVersionCommand, - isInstallCommand, - isScaffolderCommand, isMakersDeployCommand, isMakersDevCommand, - isVerificationCommand, - shortenToolName, parseEdgeoneVersionExitCode, + isEdgeoneCliUnavailable, + shortenToolName, withExitCodeEcho, } from '../makers/tool-phase.ts'; +import { + appendText, + commandOutputFromToolResult, + redactToolResult, + textContents, + withMakersCliUnavailableError, +} from './command-text.ts'; +import { + extractCommand, + shouldStopDevServer, + withDevServerStopped, + withWarmedInstall, + withWrappedCommand, +} from './command-preprocess.ts'; +import { prepareMakersCommand } from './makers-command.ts'; +import type { MakersCommandLifecycle } from './makers-lifecycle.ts'; +import { handleDevCommandResult } from './preview-command-result.ts'; +import { + handleDeployCommandResult, + updateDeploymentStatus, +} from './deploy-command-result.ts'; -type MakersCommandLifecycle = { - context: any; - state: ProjectState; - conversationId?: string; - send?: StreamSend; - signal?: AbortSignal; - onPreviewReady?: (preview: { - url?: string; - sandboxDebugUrl?: string; - kind?: PreviewKind; - }) => void; - onDeploymentStatus?: (deployment: DeploymentInfo) => void; -}; - -function updateDeploymentStatus( - lifecycle: MakersCommandLifecycle, - deployment: DeploymentInfo, -) { - lifecycle.state.deployment = deployment; - lifecycle.onDeploymentStatus?.(deployment); -} - -function extractCommand(args: unknown) { - const record = args && typeof args === 'object' ? args as Record : {}; - const command = typeof record.command === 'string' - ? record.command - : typeof record.cmd === 'string' - ? record.cmd - : ''; - return { record, command }; -} - -function withWrappedCommand(args: unknown, wrapped: string) { - const { record, command } = extractCommand(args); - if (!command || wrapped === command) { - return args; - } - return { - ...record, - ...(typeof record.command === 'string' ? { command: wrapped } : {}), - ...(typeof record.cmd === 'string' ? { cmd: wrapped } : {}), - }; -} - -function withCommandOptions( - args: unknown, - command: string, - cwd: string, - env: Record, - timeout: number, -) { - const { record } = extractCommand(args); - return { - ...record, - ...(typeof record.command === 'string' ? { command } : { cmd: command }), - cwd, - env: { - ...(record.env && typeof record.env === 'object' - ? record.env as Record - : {}), - ...env, - }, - timeout, - }; -} - -function textContents(result: Awaited>) { - return (result.content || []) - .flatMap((item) => item && typeof item === 'object' && 'text' in item - && typeof item.text === 'string' ? [item.text] : []) - .join('\n'); -} - -function commandOutputFromToolResult(result: Awaited>) { - const raw = textContents(result); - const streams: string[] = []; - for (const item of result.content || []) { - if (!item || typeof item !== 'object' || !('text' in item) || typeof item.text !== 'string') { - continue; - } - try { - const parsed = JSON.parse(item.text) as Record; - if (typeof parsed.stdout === 'string') streams.push(parsed.stdout); - if (typeof parsed.stderr === 'string') streams.push(parsed.stderr); - } catch { - // Some runtime versions return raw stdout instead of a JSON envelope. - } - } - return [...streams, raw].filter(Boolean).join('\n'); -} - -function appendText( - result: Awaited>, - text: string, -) { - return { - ...result, - content: [ - ...(result.content || []), - { type: 'text' as const, text }, - ], - }; -} - -function withMakersCliUnavailableError( - result: Awaited>, - attemptedCommand: string, -) { - return { - ...appendText(result, JSON.stringify({ - status: 'error', - errorCode: MAKERS_CLI_UNAVAILABLE_ERROR_CODE, - retryable: false, - error: MAKERS_CLI_UNAVAILABLE_MESSAGE, - attemptedCommand, - instruction: 'Stop this preview/deploy attempt. Do not inspect PATH or installation directories, install packages, use npx, or retry. Tell the user this is a sandbox image rollout blocker, not a generated-project error.', - })), - isError: true, - }; -} - -const DEV_SERVER_STOPPED_NOTICE = 'The preview dev server was stopped before this command, ' - + 'because a build or an install in the same directory races it over .next and node_modules. ' - + 'The preview is down until you run `edgeone makers dev` again.'; - -function withDevServerStopped(command: string) { - return [buildMakersDevStopScript(MAKERS_DEV_PORT, DEV_SERVER_STOPPED_NOTICE), command].join('\n'); -} - -/** - * Let the background install the host started stand in for this one. - * - * Everything waits, because two npm processes on one node_modules is the one - * thing that must never happen — see shared/npm-install.ts. Only a bare install - * is answered outright: the warmup ran that exact command, while an install - * naming a package has to run or the package is never there. - */ -function withWarmedInstall(command: string, wrapped: string) { - if (isBareInstallCommand(command)) { - // The reclaim is only reached when the handoff did not stand in, so it - // follows an install that really ran and really filled the cache. - return [buildNpmWarmupHandoffScript(), wrapped, buildNpmCacheReclaimScript()].join('\n'); - } - return [buildNpmWarmupWaitScript(), wrapped].join('\n'); -} - -function redactToolResult( - result: Awaited>, - secret: string, -) { - if (!secret) return result; - return { - ...result, - content: (result.content || []).map((item) => ( - item && typeof item === 'object' && 'text' in item && typeof item.text === 'string' - ? { ...item, text: redactSecret(item.text, secret) } - : item - )), - }; -} - -async function prepareMakersCommand( - args: unknown, - command: string, - lifecycle: MakersCommandLifecycle, -) { - const makers = await prepareMakersSession(lifecycle.context, lifecycle.state, { - syncEnv: isMakersDeployCommand(command), - }); - - if (isMakersDevCommand(command)) { - return { - args: withCommandOptions( - args, - buildMakersDevBackgroundCommand({ - makersPort: MAKERS_DEV_PORT, - previewPort: PREVIEW_SERVER_PORT, - previewPath: PREVIEW_PATH_PREFIX, - projectName: makers.projectName, - assetPrefixEnvName: PREVIEW_ASSET_PREFIX_ENV, - area: makers.area, - }), - lifecycle.state.appDir, - makers.env, - MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, - ), - kind: 'dev' as const, - sandboxToken: makers.sandboxToken, - gatewayKey: makers.gatewayKey, - }; - } - - return { - args: withCommandOptions( - args, - buildMakersDeployCommand(makers.projectName, command, { - stopDevPort: MAKERS_DEV_PORT, - area: makers.area, - }), - lifecycle.state.appDir, - makers.env, - 600, - ), - kind: 'deploy' as const, - sandboxToken: makers.sandboxToken, - gatewayKey: makers.gatewayKey, - }; -} +export type { MakersCommandLifecycle } from './makers-lifecycle.ts'; export function wrapSandboxTools( tools: ClaudeMcpTool[], @@ -303,18 +75,7 @@ export function wrapSandboxTools( startedAt: deploymentStartedAt, }); } - // An install or a build in the project directory contends with the dev - // server for .next and node_modules, and loses in ways that name - // neither: a build reports a missing file or a Pages Router page it - // does not have, and an install reports ENOTEMPTY renaming a package - // the server holds open. One run spent twenty calls reading its own - // source for a `` import that was never there. - const stopsDevServer = !isMakersCommand - && ( - isInstallCommand(command) - || isVerificationCommand(command) - || isScaffolderCommand(command) - ); + const stopsDevServer = shouldStopDevServer(command, isMakersCommand); let nextArgs = withWrappedCommand( args, isEdgeoneVersionCommand(command) @@ -357,9 +118,6 @@ export function wrapSandboxTools( }; } } - // Generic verification commands keep isError false even when EXIT:N - // is non-zero so the model can fix source. The dedicated CLI branches - // below promote captured lifecycle failures to structured tool errors. let result: Awaited>; try { result = await originalHandler(nextArgs, extra); @@ -394,24 +152,16 @@ export function wrapSandboxTools( return result; } - // Parse deploy output before redaction so a URL query value that - // happens to overlap the CLI credential is never truncated. const makersOutput = commandOutputFromToolResult(result); result = redactToolResult(result, makers.sandboxToken); result = redactToolResult(result, makers.gatewayKey); - // The deploy command stops makers dev so the build does not share its - // output directory. Restart before any of the branches below report, - // including the failing ones: the preview is how the model inspects - // what it just built, and a publish that failed is exactly when it - // needs to look. if (makers.kind === 'deploy') { try { await startPreviewServer(lifecycle.context, lifecycle.state, { verifyRoutes: false, }); } catch { - // Not part of publishing. The next preview command starts it again, - // and failing the deploy over this would call a live site broken. + // Not part of publishing. The next preview command starts it again. } } if (result.isError) { @@ -423,103 +173,17 @@ export function wrapSandboxTools( } if (makers.kind === 'dev') { - const missingRuntimeToken = describeMissingMakersRuntimeToken(makersOutput); - if (missingRuntimeToken) { - return { - ...appendText(result, JSON.stringify({ - status: 'error', - error: missingRuntimeToken, - })), - isError: true, - }; - } - const devExitCode = parseMakersDevExitCode(makersOutput); - if (devExitCode != null && devExitCode !== 0) { - if (isEdgeoneCliUnavailable(makersOutput)) { - return withMakersCliUnavailableError(result, 'edgeone makers dev'); - } - // The one launch failure that says nothing about the project: the - // port was still held, so the CLI came up healthy somewhere the - // proxy does not look. Launching again is the fix, and the launcher - // now clears that port first, so the second attempt is not a repeat - // of the first. - if (devExitCode === MAKERS_DEV_PORT_DRIFT_EXIT) { - return { - ...appendText(result, JSON.stringify({ - status: 'error', - errorCode: 'MAKERS_DEV_PORT_DRIFT', - retryable: true, - error: 'edgeone makers dev started on a port the preview proxy does not forward to, because the previous dev server still held the expected one.', - instruction: 'Run the same preview command once more. Nothing in the generated project caused this, so do not change project files, and do not kill processes or free ports yourself — the launcher terminates the previous server before this next attempt.', - })), - isError: true, - }; - } - return { - ...appendText(result, JSON.stringify({ - status: 'error', - error: describeMissingMakersRuntimeToken(makersOutput) - || `edgeone makers dev exited with code ${devExitCode}.`, - exitCode: devExitCode, - })), - isError: true, - }; - } - try { - let preview; - try { - preview = await publishRunningPreview(lifecycle.context, lifecycle.state); - } catch (error) { - // The smoke test now retries through the rebuild window itself, so - // reaching here means the server never answered — restart it once. - // A generated agent that answers wrongly is reported as-is instead: - // its reply already proves the server and proxy work. - if (!previewFailureWarrantsRestart(error)) throw error; - await startPreviewServer(lifecycle.context, lifecycle.state); - preview = await publishRunningPreview(lifecycle.context, lifecycle.state, { - routesAlreadyVerified: true, - }); - } - lifecycle.onPreviewReady?.(preview); - return appendText(result, JSON.stringify({ - status: 'success', - preview: { - url: preview.url, - kind: preview.kind, - }, - })); - } catch (error) { - return { - ...appendText(result, error instanceof Error ? error.message : String(error)), - isError: true, - }; - } + return handleDevCommandResult(lifecycle, makers, result, makersOutput); } - const outcome = readMakersDeployOutcome(makersOutput, '', makers.sandboxToken); - if (outcome.status === 'cli-missing') { - failDeployment(outcome.error); - return withMakersCliUnavailableError(result, 'edgeone makers deploy'); - } - if (outcome.status === 'error') { - failDeployment(outcome.error); - return { - ...appendText(result, JSON.stringify({ - status: 'error', - error: outcome.error, - ...(outcome.exitCode != null ? { exitCode: outcome.exitCode } : {}), - })), - isError: true, - }; - } - updateDeploymentStatus(lifecycle, describeMakersDeployment(outcome, { - startedAt: deploymentStartedAt, - })); - const { status: _outcomeStatus, ...published } = outcome; - return appendText(result, JSON.stringify({ - status: 'published', - ...published, - })); + return handleDeployCommandResult( + lifecycle, + makers, + result, + makersOutput, + deploymentStartedAt, + failDeployment, + ); }, }; }); diff --git a/agents/_lib/tools/deploy-command-result.ts b/agents/_lib/tools/deploy-command-result.ts new file mode 100644 index 0000000..4a70080 --- /dev/null +++ b/agents/_lib/tools/deploy-command-result.ts @@ -0,0 +1,55 @@ +import { setDeployment } from '../project/workspace-store.ts'; +import { + describeMakersDeployment, + readMakersDeployOutcome, +} from '../makers/cli-deploy.ts'; +import type { DeploymentInfo } from '../types.ts'; +import { + appendText, + withMakersCliUnavailableError, + type ToolHandlerResult, +} from './command-text.ts'; +import type { MakersCommandLifecycle } from './makers-lifecycle.ts'; +import type { PreparedMakersCommand } from './makers-command.ts'; + +export function updateDeploymentStatus( + lifecycle: MakersCommandLifecycle, + deployment: DeploymentInfo, +) { + setDeployment(lifecycle.state, deployment); + lifecycle.onDeploymentStatus?.(deployment); +} + +export function handleDeployCommandResult( + lifecycle: MakersCommandLifecycle, + makers: PreparedMakersCommand, + result: ToolHandlerResult, + makersOutput: string, + deploymentStartedAt: number, + failDeployment: (error: string) => void, +): ToolHandlerResult { + const outcome = readMakersDeployOutcome(makersOutput, '', makers.sandboxToken); + if (outcome.status === 'cli-missing') { + failDeployment(outcome.error); + return withMakersCliUnavailableError(result, 'edgeone makers deploy'); + } + if (outcome.status === 'error') { + failDeployment(outcome.error); + return { + ...appendText(result, JSON.stringify({ + status: 'error', + error: outcome.error, + ...(outcome.exitCode != null ? { exitCode: outcome.exitCode } : {}), + })), + isError: true, + }; + } + updateDeploymentStatus(lifecycle, describeMakersDeployment(outcome, { + startedAt: deploymentStartedAt, + })); + const { status: _outcomeStatus, ...published } = outcome; + return appendText(result, JSON.stringify({ + status: 'published', + ...published, + })); +} diff --git a/agents/_lib/tools/makers-command.ts b/agents/_lib/tools/makers-command.ts new file mode 100644 index 0000000..1a48fa3 --- /dev/null +++ b/agents/_lib/tools/makers-command.ts @@ -0,0 +1,73 @@ +import { + MAKERS_DEV_PORT, + PREVIEW_ASSET_PREFIX_ENV, + PREVIEW_PATH_PREFIX, + PREVIEW_SERVER_PORT, +} from '../constants.ts'; +import { prepareMakersSession } from '../makers/session.ts'; +import { + MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, + buildMakersDevBackgroundCommand, +} from '../makers/cli-dev.ts'; +import { buildMakersDeployCommand } from '../makers/cli-deploy.ts'; +import { + isMakersDeployCommand, + isMakersDevCommand, +} from '../makers/tool-phase.ts'; +import { withCommandOptions } from './command-preprocess.ts'; +import type { MakersCommandLifecycle } from './makers-lifecycle.ts'; + +export type PreparedMakersCommand = { + args: unknown; + kind: 'dev' | 'deploy'; + sandboxToken: string; + gatewayKey: string; +}; + +export async function prepareMakersCommand( + args: unknown, + command: string, + lifecycle: MakersCommandLifecycle, +): Promise { + const makers = await prepareMakersSession(lifecycle.context, lifecycle.state, { + syncEnv: isMakersDeployCommand(command), + }); + + if (isMakersDevCommand(command)) { + return { + args: withCommandOptions( + args, + buildMakersDevBackgroundCommand({ + makersPort: MAKERS_DEV_PORT, + previewPort: PREVIEW_SERVER_PORT, + previewPath: PREVIEW_PATH_PREFIX, + projectName: makers.projectName, + assetPrefixEnvName: PREVIEW_ASSET_PREFIX_ENV, + area: makers.area, + }), + lifecycle.state.appDir, + makers.env, + MAKERS_DEV_LAUNCH_TIMEOUT_SECONDS, + ), + kind: 'dev' as const, + sandboxToken: makers.sandboxToken, + gatewayKey: makers.gatewayKey, + }; + } + + return { + args: withCommandOptions( + args, + buildMakersDeployCommand(makers.projectName, command, { + stopDevPort: MAKERS_DEV_PORT, + area: makers.area, + }), + lifecycle.state.appDir, + makers.env, + 600, + ), + kind: 'deploy' as const, + sandboxToken: makers.sandboxToken, + gatewayKey: makers.gatewayKey, + }; +} diff --git a/agents/_lib/tools/makers-lifecycle.ts b/agents/_lib/tools/makers-lifecycle.ts new file mode 100644 index 0000000..161f1b0 --- /dev/null +++ b/agents/_lib/tools/makers-lifecycle.ts @@ -0,0 +1,21 @@ +import type { AgentContext } from '../runtime/context.ts'; +import type { + DeploymentInfo, + PreviewKind, + ProjectState, + StreamSend, +} from '../types.ts'; + +export type MakersCommandLifecycle = { + context: AgentContext; + state: ProjectState; + conversationId?: string; + send?: StreamSend; + signal?: AbortSignal; + onPreviewReady?: (preview: { + url?: string; + sandboxDebugUrl?: string; + kind?: PreviewKind; + }) => void; + onDeploymentStatus?: (deployment: DeploymentInfo) => void; +}; diff --git a/agents/_lib/tools/preview-command-result.ts b/agents/_lib/tools/preview-command-result.ts new file mode 100644 index 0000000..0e0a577 --- /dev/null +++ b/agents/_lib/tools/preview-command-result.ts @@ -0,0 +1,98 @@ +import { + previewFailureWarrantsRestart, + publishRunningPreview, + startPreviewServer, +} from '../project/preview.ts'; +import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; +import { + MAKERS_DEV_PORT_DRIFT_EXIT, + parseMakersDevExitCode, +} from '../makers/cli-dev.ts'; +import { isEdgeoneCliUnavailable } from '../makers/tool-phase.ts'; +import { + appendText, + withMakersCliUnavailableError, + type ToolHandlerResult, +} from './command-text.ts'; +import type { MakersCommandLifecycle } from './makers-lifecycle.ts'; +import type { PreparedMakersCommand } from './makers-command.ts'; + +export async function handleDevCommandResult( + lifecycle: MakersCommandLifecycle, + makers: PreparedMakersCommand, + result: ToolHandlerResult, + makersOutput: string, +): Promise { + + const missingRuntimeToken = describeMissingMakersRuntimeToken(makersOutput); + if (missingRuntimeToken) { + return { + ...appendText(result, JSON.stringify({ + status: 'error', + error: missingRuntimeToken, + })), + isError: true, + }; + } + const devExitCode = parseMakersDevExitCode(makersOutput); + if (devExitCode != null && devExitCode !== 0) { + if (isEdgeoneCliUnavailable(makersOutput)) { + return withMakersCliUnavailableError(result, 'edgeone makers dev'); + } + // The one launch failure that says nothing about the project: the + // port was still held, so the CLI came up healthy somewhere the + // proxy does not look. Launching again is the fix, and the launcher + // now clears that port first, so the second attempt is not a repeat + // of the first. + if (devExitCode === MAKERS_DEV_PORT_DRIFT_EXIT) { + return { + ...appendText(result, JSON.stringify({ + status: 'error', + errorCode: 'MAKERS_DEV_PORT_DRIFT', + retryable: true, + error: 'edgeone makers dev started on a port the preview proxy does not forward to, because the previous dev server still held the expected one.', + instruction: 'Run the same preview command once more. Nothing in the generated project caused this, so do not change project files, and do not kill processes or free ports yourself — the launcher terminates the previous server before this next attempt.', + })), + isError: true, + }; + } + return { + ...appendText(result, JSON.stringify({ + status: 'error', + error: describeMissingMakersRuntimeToken(makersOutput) + || `edgeone makers dev exited with code ${devExitCode}.`, + exitCode: devExitCode, + })), + isError: true, + }; + } + try { + let preview; + try { + preview = await publishRunningPreview(lifecycle.context, lifecycle.state); + } catch (error) { + // The smoke test now retries through the rebuild window itself, so + // reaching here means the server never answered — restart it once. + // A generated agent that answers wrongly is reported as-is instead: + // its reply already proves the server and proxy work. + if (!previewFailureWarrantsRestart(error)) throw error; + await startPreviewServer(lifecycle.context, lifecycle.state); + preview = await publishRunningPreview(lifecycle.context, lifecycle.state, { + routesAlreadyVerified: true, + }); + } + lifecycle.onPreviewReady?.(preview); + return appendText(result, JSON.stringify({ + status: 'success', + preview: { + url: preview.url, + kind: preview.kind, + }, + })); + } catch (error) { + return { + ...appendText(result, error instanceof Error ? error.message : String(error)), + isError: true, + }; + } +} diff --git a/agents/_lib/tools/project-tools.ts b/agents/_lib/tools/project-tools.ts index 689c1a7..a025bc2 100644 --- a/agents/_lib/tools/project-tools.ts +++ b/agents/_lib/tools/project-tools.ts @@ -1,6 +1,8 @@ +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; import { z } from 'zod'; -import { ensureProjectScaffold } from '../project/index.ts'; +import { ensureProjectScaffold } from '../project/scaffold.ts'; +import { markCreated } from '../project/workspace-store.ts'; import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; import { ensureMakersAgentDeclarations, @@ -95,7 +97,7 @@ export function describeScaffold( } export function buildProjectScaffoldTool( - context: any, + context: AgentContext, state: ProjectState, onLog?: (log: ScaffoldLog) => void, onResult?: (result: { created: boolean }) => void, @@ -113,7 +115,7 @@ export function buildProjectScaffoldTool( onLog, { framework: typeof requested.framework === 'string' ? requested.framework : undefined }, ); - state.created = true; + markCreated(state); onResult?.({ created }); return { content: [{ @@ -134,7 +136,7 @@ export function buildProjectScaffoldTool( ) as ClaudeMcpTool; } export function buildWriteProjectFileTool( - context: any, + context: AgentContext, state: ProjectState, // The content is handed back so the pipeline can push it straight to the // frontend, which then renders the file without a /file round trip. @@ -163,9 +165,9 @@ export function buildWriteProjectFileTool( const parent = relPath.split('/').slice(0, -1).join('/'); if (parent) { - await context.sandbox.files.makeDir(`${state.appDir}/${parent}`); + await requireSandbox(context).files.makeDir(`${state.appDir}/${parent}`); } - await context.sandbox.files.write(`${state.appDir}/${relPath}`, file.content); + await requireSandbox(context).files.write(`${state.appDir}/${relPath}`, file.content); await onResult?.({ written: relPath, content: file.content }); // An agents/ project needs agents.framework and .env.example declared, // and meeting that at the preview gate instead costs the user a failed @@ -191,7 +193,7 @@ export function buildWriteProjectFileTool( declared.push(adapter); adapterAdded = true; } - await context.sandbox.commands + await requireSandbox(context).commands .run(buildNpmWarmupCommand(), { cwd: state.appDir }) .catch(() => undefined); } diff --git a/agents/_lib/turn/auto-fix.ts b/agents/_lib/turn/auto-fix.ts new file mode 100644 index 0000000..153173e --- /dev/null +++ b/agents/_lib/turn/auto-fix.ts @@ -0,0 +1,64 @@ +import { AUTO_FIX_MAX_ATTEMPTS } from '../constants.ts'; +import { runCodingAgent } from '../session/live.ts'; +import type { + AgentProgressEvent, + BuildResult, + CodingAgentResult, + DeploymentInfo, + PreviewKind, + ProjectState, + ScaffoldLog, + StreamSend, +} from '../types.ts'; +import { buildAutoFixPrompt } from '../utils/build-errors.ts'; +import type { AgentContext } from '../runtime/context.ts'; + +export type AutoFixTurnInput = { + context: AgentContext; + conversationId: string; + message: string; + state: ProjectState; + assistantReply: string; + build: BuildResult; + onScaffoldLog: (log: ScaffoldLog) => void; + onProgress: (event: AgentProgressEvent) => void; + onProjectFilesChanged: (file?: { path: string; content: string }) => Promise; + onPreviewReady: (preview: { + url?: string; + sandboxDebugUrl?: string; + kind?: PreviewKind; + }) => void; + onDeploymentStatus: (deployment: DeploymentInfo) => void; + abortSignal?: AbortSignal; + model?: string; + send: StreamSend; +}; + +export async function runAutoFixTurn(input: AutoFixTurnInput): Promise<{ + result: CodingAgentResult; + prompt: string; +}> { + const prompt = buildAutoFixPrompt( + input.message, + input.assistantReply, + input.build, + 1, + AUTO_FIX_MAX_ATTEMPTS, + ); + const result = await runCodingAgent({ + context: input.context, + conversationId: input.conversationId, + userMessage: prompt, + state: input.state, + isNewProject: false, + onScaffoldLog: input.onScaffoldLog, + onProgress: input.onProgress, + onProjectFilesChanged: input.onProjectFilesChanged, + onPreviewReady: input.onPreviewReady, + onDeploymentStatus: input.onDeploymentStatus, + abortSignal: input.abortSignal, + model: input.model, + send: input.send, + }); + return { result, prompt }; +} diff --git a/agents/_lib/turn/chat.ts b/agents/_lib/turn/chat.ts index ce961c0..0e260f7 100644 --- a/agents/_lib/turn/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -1,55 +1,67 @@ -import { runCodingAgent } from '../session/live.ts'; +import type { AgentContext } from '../runtime/context.ts'; import { AUTO_FIX_MAX_ATTEMPTS } from '../constants.ts'; -import { saveProjectState } from '../session/store.ts'; -import { getFileTree, runVerification } from '../project/index.ts'; +import { runCodingAgent } from '../session/live.ts'; +import { runVerification } from '../project/scaffold.ts'; +import { publishRunningPreview, startPreviewServer } from '../project/preview.ts'; +import { + bindSiteDomain, + persistWorkspace, + publishPreview, + setDeployment, + setLastBuild, +} from '../project/workspace-store.ts'; import type { AgentProgressEvent, - BuildStatus, DeploymentInfo, - FileTreeItem, ScaffoldLog, StreamSend, } from '../types.ts'; -import { buildAutoFixPrompt } from '../utils/build-errors.ts'; import { toAppRelPath } from '../utils/paths.ts'; import { sanitizeAssistantText } from '../../../shared/timeline.ts'; -import { resolveConversationId } from '../runtime/request.ts'; +import { resolveConversationId, resolveRequestSiteDomain } from '../runtime/request.ts'; import { - FILE_PUSH_MAX_BYTES, - FILE_PUSH_TURN_BUDGET_BYTES, - buildRequirementConclusionFallback, + GATEWAY_CREDENTIALS_USER_REPLY, compactUserFacingReply, createFileTreePushController, createProjectCheckpointController, extendExistingSandboxTimeout, - GATEWAY_CREDENTIALS_USER_REPLY, isGenericCompletionReply, previewLinkFromState, replyLocaleFor, resolveFinishedTurn, STOPPED_TURN_REPLY, stripReturnedPreviewLinks, - utf8ByteLength, withLiveDeploymentUrl, + buildRequirementConclusionFallback, } from './checkpoint.ts'; import { createTurnLifecycle } from './lifecycle.ts'; import { prepareProjectWorkspace } from '../project/workspace.ts'; -import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; import { applyUserGatewayDecision, isRequestGatewayCredentialsTool, } from '../project/gateway.ts'; import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; +import { runAutoFixTurn } from './auto-fix.ts'; +import { sendTurnResult } from './result.ts'; +import type { ChatResponse } from '../../../shared/protocol.ts'; +import type { ReplyLocale } from '../../../shared/user-facing-reply.ts'; + +function slimResult( + conversationId: string, + extra: Omit, +): ChatResponse { + return { conversation_id: conversationId, ...extra }; +} export async function runChatPipeline( - context: any, + context: AgentContext, message: string, send: StreamSend, options: { turnId?: string; /** Validated model for this turn; '' or absent runs the configured default. */ model?: string; - siteDomain?: string; + language?: ReplyLocale | string; /** Real Models API key from the card or a chat sentence; never persisted. */ apiKey?: string; gatewaySkip?: boolean; @@ -57,36 +69,21 @@ export async function runChatPipeline( ) { const { conversationId } = resolveConversationId(context); const abortSignal = context?.request?.signal as AbortSignal | undefined; - // Every reply this pipeline writes itself — stopped, failed, fallback — has to - // answer in the language of the request, so the language is decided once here - // rather than re-sniffed at each of the five places that needed it. - const replyLocale = replyLocaleFor(message); + const replyLocale = replyLocaleFor(message, options.language); if (!message) { - send({ - type: 'result', - data: { - ok: false, - conversation_id: conversationId, - reply: 'Please describe the page or feature you want to build first.', - build: { status: 'skipped' as BuildStatus }, - preview: {}, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + reply: 'Please describe the page or feature you want to build first.', + })); return; } if (!conversationId) { - send({ - type: 'result', - data: { - ok: false, - conversation_id: '', - reply: 'Missing conversationId. The project workspace cannot be prepared.', - build: { status: 'skipped' as BuildStatus }, - preview: {}, - }, - }); + sendTurnResult(send, slimResult('', { + ok: false, + reply: 'Missing conversationId. The project workspace cannot be prepared.', + })); return; } @@ -97,10 +94,8 @@ export async function runChatPipeline( conversationId, send, ); - const siteDomain = String(options.siteDomain || '').trim(); - if (siteDomain && state.siteDomain !== siteDomain) { - state.siteDomain = siteDomain; - await saveProjectState(context, conversationId, state); + if (bindSiteDomain(state, resolveRequestSiteDomain(context))) { + await persistWorkspace(context, conversationId, state); } const inboundGateway = resolveGatewayUserTurn(message, options.apiKey); message = inboundGateway.message; @@ -121,8 +116,6 @@ export async function runChatPipeline( const activityTurnId = options.turnId || String(context?.run_id || `${Date.now()}-${Math.random().toString(36).slice(2)}`); - // Mid-turn debounced snapshots + exit-path flush so a recycled sandbox still - // has a restorable workspace in project Blob storage. const checkpoint = createProjectCheckpointController(context, conversationId, state, (persistenceError) => { console.warn('[checkpoint]', persistenceError); }); @@ -139,25 +132,22 @@ export async function runChatPipeline( const handleScaffoldLog = (_log: ScaffoldLog) => {}; const forwardProgress = (event: AgentProgressEvent) => { - // Forward structured progress events directly; the frontend renders by type. if (event.type === 'tool_use') { - const name = event.data.name || ''; + const name = event.data?.name || ''; const hideScaffold = !isInitialProjectTurn && (name === 'ensure_project_scaffold' || name.endsWith('__ensure_project_scaffold')); if (hideScaffold || isRequestGatewayCredentialsTool(name)) { - hiddenScaffoldToolUseIds.add(event.data.id); + hiddenScaffoldToolUseIds.add(event.data?.id || ''); return; } } - if (event.type === 'tool_result' && hiddenScaffoldToolUseIds.has(event.data.id)) { + if (event.type === 'tool_result' && hiddenScaffoldToolUseIds.has(event.data?.id || '')) { return; } if (event.type === 'text_segment') { - // Keep the model's step-by-step narration visible; only the final summary - // is compacted. Preview links stay out of the chat. const text = state.previewUrl - ? stripReturnedPreviewLinks(event.data.text, state.previewUrl) - : event.data.text; + ? stripReturnedPreviewLinks(event.data?.text || '', state.previewUrl) + : event.data?.text || ''; if (text.length === 0) { return; } @@ -170,55 +160,31 @@ export async function runChatPipeline( send(event); }; const fileTreePush = createFileTreePushController(context, state, send); - // The model already handed us the full text of every file it wrote, so stream it - // to the frontend instead of making it fetch the file back over /file (which costs - // a sandbox shell round trip per click). Bounded per file and per turn so a large - // asset cannot bloat the stream or the in-process replay buffer — anything over - // budget simply falls back to /file. - let filePushBudgetBytes = FILE_PUSH_TURN_BUDGET_BYTES; const handleProjectFilesChanged = async (file?: { path: string; content: string }) => { if (file) { - const bytes = utf8ByteLength(file.content); - if (bytes <= FILE_PUSH_MAX_BYTES && bytes <= filePushBudgetBytes) { - filePushBudgetBytes -= bytes; - send({ - type: 'file_content', - data: { - path: toAppRelPath(file.path, state.appDir) || file.path, - content: file.content, - size: bytes, - }, - }); - } + const path = toAppRelPath(file.path, state.appDir) || file.path; + send({ type: 'file_changed', data: { paths: [path] } }); } - // The tree follows the content so the panel does not wait for the whole - // turn, but debounced: a scaffold writes a dozen files at once and only the - // last listing is the one anybody sees. fileTreePush.schedule(); - // Debounced store backup while the agent is still writing — covers the long - // window where files live only in the volatile sandbox. checkpoint.schedule(); }; - // Switch the iframe the moment a direct Makers CLI command returns a URL, - // without waiting for verification or the finalize behind it, which can take - // several more seconds. - const handlePreviewReady = (preview: { url?: string; sandboxDebugUrl?: string; kind?: 'sandbox' | 'makers' }) => { - if (!preview.url) { + const handlePreviewReady = async (preview: { url?: string; sandboxDebugUrl?: string; kind?: 'sandbox' | 'makers' }) => { + const url = preview.url; + if (!url) { return; } - state.previewUrl = preview.url; - state.sandboxDebugUrl = preview.sandboxDebugUrl; - state.previewKind = preview.kind || (isMakersDeployUrl(preview.url) ? 'makers' : 'sandbox'); - state.previewPublished = true; - // Persist before the turn finishes so a refresh during verification still - // resumes into the preview pane and restarts the live server. - void saveProjectState(context, conversationId, state); + publishPreview(state, { + url, + sandboxDebugUrl: preview.sandboxDebugUrl, + kind: preview.kind, + }); + await persistWorkspace(context, conversationId, state); send({ type: 'preview_ready', data: { preview: { - url: preview.url, + url, sandboxDebugUrl: preview.sandboxDebugUrl, kind: state.previewKind, }, @@ -226,18 +192,40 @@ export async function runChatPipeline( }, }); }; + let hostPreviewInFlight: Promise | null = null; + const startHostPreview = async (reason: string) => { + if (hostPreviewInFlight) return hostPreviewInFlight; + hostPreviewInFlight = (async () => { + try { + await startPreviewServer(context, state); + const preview = await publishRunningPreview(context, state, { routesAlreadyVerified: true }); + await handlePreviewReady(preview); + return Boolean(state.previewUrl); + } catch (error) { + console.warn( + reason, + error instanceof Error ? error.message : error, + ); + return false; + } finally { + hostPreviewInFlight = null; + } + })(); + return hostPreviewInFlight; + }; const handleDeploymentStatus = (deployment: DeploymentInfo) => { - state.deployment = deployment; - // The final turn commit is authoritative. This eager save keeps a completed - // deployment recoverable if the browser refreshes during later model output. - void saveProjectState(context, conversationId, state); + setDeployment(state, deployment); + void persistWorkspace(context, conversationId, state); send({ type: 'deployment_status', data: deployment, }); }; - // The model handles creative code work; build and service steps remain deterministic. + if (state.created) { + void startHostPreview('[preview] workspace ready:'); + } + const modelResult = await runCodingAgent({ context, conversationId, @@ -249,6 +237,9 @@ export async function runChatPipeline( onProjectFilesChanged: handleProjectFilesChanged, onPreviewReady: handlePreviewReady, onDeploymentStatus: handleDeploymentStatus, + onWorkspaceReady: () => { + void startHostPreview('[preview] after scaffold:'); + }, abortSignal, model: options.model, send, @@ -259,26 +250,20 @@ export async function runChatPipeline( await finalizeTurn(stoppedReply, 'stopped', { withSnapshot: modelResult.projectTouched, }); - send({ - type: 'result', - data: { - ok: false, - stopped: true, - reply: stoppedReply, - conversation_id: conversationId, - build: { status: 'skipped' as BuildStatus }, - preview: previewLinkFromState(state), - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + stopped: true, + reply: stoppedReply, + })); return; } - // Dest was skipped so the user can type a key. That is not a missing preview - // and not a failed build — running verification here would paint the model's - // wrap-up as a red error and then wipe the input card when the turn ended. if (state.gatewayPromptPending) { const pauseReply = GATEWAY_CREDENTIALS_USER_REPLY[replyLocale]; + send({ + type: 'gateway_credentials', + data: { status: 'needed' }, + }); send({ type: 'agent', data: { @@ -287,39 +272,16 @@ export async function runChatPipeline( }, }); - let fileTree: FileTreeItem[] = []; if (modelResult.projectTouched) { - // The Files panel can update without waiting for a Blob snapshot. Persist - // used to run here and again in finalizeTurn, so a slow or failing - // sandbox.persist held the result event — and the API key card, which is - // gated on it — for tens of seconds after the pause reply was already on - // screen. - fileTree = await fileTreePush.flush('Failed to read the file list.'); + await fileTreePush.flush('Failed to read the file list.'); } await finalizeTurn(pauseReply, 'completed', { withSnapshot: false, }); - send({ - type: 'result', - data: { - ok: true, - reply: pauseReply, - conversation_id: conversationId, - gatewayNeeded: true, - project: { - dir: state.appDir, - created: modelResult.wasCreated, - }, - build: { status: 'skipped' as BuildStatus }, - files: { - root: state.appDir, - items: fileTree, - }, - download: { url: '/download', filename: 'source.zip' }, - preview: previewLinkFromState(state), - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: true, + reply: pauseReply, + })); if (modelResult.projectTouched) { void checkpoint.flush(); } @@ -337,8 +299,6 @@ export async function runChatPipeline( const rawAssistantReply = stripReturnedPreviewLinks(sanitizeAssistantText( modelOutput || fallbackReply ) || fallbackReply, state.previewUrl); - // Only a deployment from this turn: state.deployment outlives the turn, and - // re-appending yesterday's URL to every later reply would be worse than none. const liveDeploymentUrl = modelResult.deploymentTouched && state.deployment?.status === 'success' ? state.deployment.url @@ -363,37 +323,26 @@ export async function runChatPipeline( await finalizeTurn(assistantReply, 'failed', { withSnapshot: modelResult.projectTouched, }); - - send({ - type: 'result', - data: { - ok: false, - reply: assistantReply, - conversation_id: conversationId, - build: { - status: 'skipped' as BuildStatus, - stderr: modelResult.error || assistantReply, - }, - preview: {}, - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + reply: assistantReply, + error: modelResult.error || undefined, + })); return; } - if ( - !modelResult.projectTouched - && (modelResult.previewTouched || modelResult.deploymentTouched) - ) { - if (modelResult.previewTouched && state.previewUrl) { + if (!modelResult.projectTouched) { + // The model no longer launches preview. A finished project with no URL + // still needs the host to start it — including Q&A turns after a write + // that never set projectTouched, or a previous turn that skipped dest. + if (state.created && !state.previewUrl) { + await checkpoint.flush(); + await startHostPreview('[preview] host start failed:'); + } else if (modelResult.previewTouched && state.previewUrl) { send({ type: 'preview_ready', data: { - preview: { - url: state.previewUrl, - sandboxDebugUrl: state.sandboxDebugUrl, - kind: state.previewKind, - }, + preview: previewLinkFromState(state), }, }); } @@ -402,112 +351,53 @@ export async function runChatPipeline( const deploymentReady = !modelResult.deploymentTouched || state.deployment?.status === 'success'; const operationOk = modelResult.success && previewReady && deploymentReady; - await finalizeTurn(assistantReply, operationOk ? 'completed' : 'failed'); - - send({ - type: 'result', - data: { - ok: operationOk, - reply: assistantReply, - conversation_id: conversationId, - build: { status: 'skipped' as BuildStatus }, - preview: modelResult.previewTouched - ? { - url: state.previewUrl, - sandboxDebugUrl: state.sandboxDebugUrl, - kind: state.previewKind, - ...(!state.previewUrl ? { error: 'The agent did not complete the Makers CLI preview.' } : {}), - } - : previewLinkFromState(state), - deployment: state.deployment, - }, + await finalizeTurn(assistantReply, operationOk ? 'completed' : 'failed', { + withState: Boolean(state.previewUrl) || modelResult.deploymentTouched, }); + sendTurnResult(send, slimResult(conversationId, { + ok: operationOk, + reply: assistantReply, + })); return; } - if (!modelResult.projectTouched) { - await finalizeTurn(assistantReply, modelResult.success ? 'completed' : 'failed', { - withState: false, - }); + await checkpoint.flush(); - send({ - type: 'result', - data: { - ok: modelResult.success, - reply: assistantReply, - conversation_id: conversationId, - build: { status: 'skipped' as BuildStatus }, - preview: {}, - deployment: state.deployment, - }, - }); - return; + let previewVerified = Boolean(state.previewUrl); + if (!previewVerified) { + previewVerified = await startHostPreview('[preview] host start failed:'); } - // Files are on disk now — flush before verification/auto-fix so that long - // build window cannot recycle the sandbox with only an in-memory project. - await checkpoint.flush(); - - let fileTree = await fileTreePush.flush('Failed to read the file list.'); - // A preview that came up in this turn already compiled this turn's code and - // answered its smoke tests, so the production build has nothing left to prove - // here that publishing does not prove for real. Without one, the build is the - // only evidence the project assembles at all, so it runs. + await fileTreePush.flush('Failed to read the file list.'); let build = await runVerification(context, state, { - previewVerified: modelResult.previewTouched && Boolean(state.previewUrl), + previewVerified, }); let autoFixAttempts = 0; let autoFixApplied = false; let autoFixReply = ''; - // The project has files on disk from here on, so expose a download link. The - // archive is built on demand by /download; this is just a pointer (the - // authoritative filename comes from the /download response). - const downloadLink = { url: '/download', filename: 'source.zip' }; - if (build.fatal) { + setLastBuild(state, build); + await persistWorkspace(context, conversationId, state); const fatalReply = build.stderr || 'The task failed, and the remaining workflow was stopped.'; await finalizeTurn(fatalReply, 'failed', { withSnapshot: true }); - - send({ - type: 'result', - data: { - ok: false, - reply: fatalReply, - conversation_id: conversationId, - project: { - dir: state.appDir, - created: modelResult.wasCreated, - }, - build, - files: { - root: state.appDir, - items: fileTree, - }, - download: downloadLink, - preview: {}, - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + reply: fatalReply, + })); return; } if (build.status === 'failed' && modelResult.success) { autoFixAttempts = AUTO_FIX_MAX_ATTEMPTS; autoFixApplied = true; - const autoFixPrompt = buildAutoFixPrompt( - message, - assistantReply, - build, - 1, - AUTO_FIX_MAX_ATTEMPTS, - ); - const autoFixResult = await runCodingAgent({ + const { result: autoFixResult } = await runAutoFixTurn({ context, conversationId, - userMessage: autoFixPrompt, + message, state, - isNewProject: false, + assistantReply, + build, onScaffoldLog: handleScaffoldLog, onProgress: forwardProgress, onProjectFilesChanged: handleProjectFilesChanged, @@ -520,18 +410,11 @@ export async function runChatPipeline( if (autoFixResult.stopped || abortSignal?.aborted) { const stoppedReply = STOPPED_TURN_REPLY[replyLocale]; await finalizeTurn(stoppedReply, 'stopped', { withSnapshot: true }); - send({ - type: 'result', - data: { - ok: false, - stopped: true, - reply: stoppedReply, - conversation_id: conversationId, - build: { status: 'skipped' as BuildStatus }, - preview: previewLinkFromState(state), - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + stopped: true, + reply: stoppedReply, + })); return; } const rawAutoFixReply = stripReturnedPreviewLinks(sanitizeAssistantText( @@ -557,53 +440,39 @@ export async function runChatPipeline( }); } - fileTree = await fileTreePush.flush('Failed to read the file list after auto-fix.'); - // Deliberately the full verification, preview or not: getting here means - // something was already broken, and the repair is exactly when the cheaper - // evidence is worth the least. + await fileTreePush.flush('Failed to read the file list after auto-fix.'); build = await runVerification(context, state); if (build.fatal) { + setLastBuild(state, build); + await persistWorkspace(context, conversationId, state); const fatalReply = build.stderr || 'The task failed, and the remaining workflow was stopped.'; await finalizeTurn(fatalReply, 'failed', { withSnapshot: true }); - - send({ - type: 'result', - data: { - ok: false, - reply: fatalReply, - conversation_id: conversationId, - project: { - dir: state.appDir, - created: modelResult.wasCreated, - }, - build, - files: { - root: state.appDir, - items: fileTree, - }, - download: downloadLink, - preview: {}, - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: false, + reply: fatalReply, + })); return; } + + if (!previewVerified) { + previewVerified = await startHostPreview('[preview] host start after auto-fix failed:'); + } } build = { ...build, ...(autoFixAttempts > 0 ? { autoFixAttempts, autoFixApplied } : {}), }; + setLastBuild(state, build); + await persistWorkspace(context, conversationId, state); - // Makers dev owns preview state; deployments are streamed separately. if (state.previewUrl) { send({ type: 'preview_ready', data: { preview: { - url: state.previewUrl, - sandboxDebugUrl: state.sandboxDebugUrl, - kind: state.previewKind, + ...previewLinkFromState(state), + ...(modelResult.filesWritten ? { restarted: true } : {}), }, }, }); @@ -611,8 +480,6 @@ export async function runChatPipeline( const isChinese = replyLocale === 'zh'; const outcome = resolveFinishedTurn({ - // Scaffolding sets projectTouched and the workflow asks for it every turn, - // so it cannot stand in for this. filesWritten: modelResult.filesWritten !== false, previewUrl: state.previewUrl, buildFailed: build.status === 'failed', @@ -628,38 +495,14 @@ export async function runChatPipeline( ? (isChinese ? '项目已生成,但检查未通过,我还需要继续修复。' : 'The project was generated, but checks still fail and need another fix.') : (isChinese ? '项目已生成,但预览暂时不可用,请重试。' : 'The project was generated, but the preview is temporarily unavailable. Please retry.'), }); - const previewMissing = outcome.previewMissing; const turnFailed = outcome.failed; const reply = withLiveDeploymentUrl(outcome.reply, liveDeploymentUrl); - // Code first, then state, then conversation — so a crash mid-finalize still - // leaves a restorable workspace for resume after sandbox recycle. const turnOk = modelResult.success && !turnFailed; await finalizeTurn(reply, turnOk ? 'completed' : 'failed', { withSnapshot: true }); - send({ - type: 'result', - data: { - ok: turnOk, - reply, - conversation_id: conversationId, - project: { - dir: state.appDir, - created: modelResult.wasCreated, - }, - build, - files: { - root: state.appDir, - items: fileTree, - }, - download: downloadLink, - preview: { - url: state.previewUrl, - sandboxDebugUrl: state.sandboxDebugUrl, - kind: state.previewKind, - ...(previewMissing ? { error: 'The agent did not complete the Makers CLI preview.' } : {}), - }, - deployment: state.deployment, - }, - }); + sendTurnResult(send, slimResult(conversationId, { + ok: turnOk, + reply, + })); } diff --git a/agents/_lib/turn/checkpoint.ts b/agents/_lib/turn/checkpoint.ts index 9f856ee..939fbe6 100644 --- a/agents/_lib/turn/checkpoint.ts +++ b/agents/_lib/turn/checkpoint.ts @@ -1,4 +1,6 @@ -import { getFileTree, runSandboxCommand } from '../project/index.ts'; +import { requireSandbox, type AgentContext } from '../runtime/context.ts'; +import { getFileTree } from '../project/fs.ts'; +import { runSandboxCommand } from '../project/commands.ts'; import type { FileTreeItem, ProjectState, StreamSend } from '../types.ts'; export { compactUserFacingReply, @@ -26,12 +28,12 @@ export function previewLinkFromState(state: ProjectState) { * carries source without node_modules, and both the preview server and the * Makers build need dependencies on disk. */ -export async function ensureProjectDependencies(context: any, state: ProjectState) { - const hasPackageJson = await context.sandbox.files.exists(`${state.appDir}/package.json`); +export async function ensureProjectDependencies(context: AgentContext, state: ProjectState) { + const hasPackageJson = await requireSandbox(context).files.exists(`${state.appDir}/package.json`); if (!hasPackageJson) { return false; } - const hasNodeModules = await context.sandbox.files.exists(`${state.appDir}/node_modules`); + const hasNodeModules = await requireSandbox(context).files.exists(`${state.appDir}/node_modules`); if (hasNodeModules) { return true; } @@ -44,18 +46,6 @@ export async function ensureProjectDependencies(context: any, state: ProjectStat const SANDBOX_EXTENSION_SECONDS = 1800; -// Caps for streaming generated file contents to the frontend (see -// handleProjectFilesChanged). Per-file keeps a single large asset off the stream; -// the per-turn budget bounds how much the replay buffer can hold. -export const FILE_PUSH_MAX_BYTES = 96 * 1024; -export const FILE_PUSH_TURN_BUDGET_BYTES = 2 * 1024 * 1024; - -const utf8Encoder = new TextEncoder(); - -export function utf8ByteLength(value: string) { - return utf8Encoder.encode(value).length; -} - /** Reject if `promise` does not settle within `ms`. Clears the timer on settle. */ export async function withTimeout(promise: Promise, ms: number, label: string): Promise { let timer: ReturnType | undefined; @@ -137,7 +127,7 @@ export function isGenericCompletionReply(text: string) { || /^theagentdidnotreturnanythingdisplayable$/i.test(normalized); } -export async function extendExistingSandboxTimeout(context: any) { +export async function extendExistingSandboxTimeout(context: AgentContext) { const sandbox = context?.sandbox as SandboxWithTimeoutExtension | undefined; if (!sandbox || typeof sandbox.extendTimeout !== 'function') { return; @@ -157,12 +147,12 @@ export async function extendExistingSandboxTimeout(context: any) { // Persist the project through the sandbox SDK. Archive bytes travel directly from // the sandbox to project Blob storage and never enter conversation metadata. export async function persistProjectSnapshot( - context: any, + context: AgentContext, conversationId: string, state: ProjectState, ): Promise { try { - await context.sandbox.persist({ path: state.appDir }); + await requireSandbox(context).persist?.({ path: state.appDir }); return true; } catch (error) { // Losing a snapshot silently means the next resume rebuilds from an older @@ -191,7 +181,7 @@ export type ProjectCheckpointController = { // Mid-turn + exit-path persistence controller. schedule() is cheap and coalesces; // flush() forces a final sandbox-to-Blob write on stop/fatal/success paths. export function createProjectCheckpointController( - context: any, + context: AgentContext, conversationId: string, state: ProjectState, onFailure?: (message: string) => void, @@ -256,7 +246,7 @@ export type FileTreePushController = { // shows the newest listing. Bursts now collapse into a single read, and reads // never overlap. export function createFileTreePushController( - context: any, + context: AgentContext, state: ProjectState, send: StreamSend, ): FileTreePushController { diff --git a/agents/_lib/turn/deploy.ts b/agents/_lib/turn/deploy.ts index 008d82b..d71f1f0 100644 --- a/agents/_lib/turn/deploy.ts +++ b/agents/_lib/turn/deploy.ts @@ -1,12 +1,14 @@ +import type { AgentContext } from '../runtime/context.ts'; import { MAKERS_DEV_PORT } from '../constants.ts'; -import { saveProjectState } from '../session/store.ts'; -import { getFileTree, runSandboxCommand } from '../project/index.ts'; +import { getFileTree } from '../project/fs.ts'; +import { runSandboxCommand } from '../project/commands.ts'; +import { startPreviewServer } from '../project/preview.ts'; +import { bindSiteDomain, persistWorkspace, setDeployment } from '../project/workspace-store.ts'; import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; import { resolveConversationPublishArea, resolveMakersProjectName, } from '../makers/project.ts'; -import { startPreviewServer } from '../project/preview.ts'; import { applyUserGatewayDecision, askUserForGatewayCredentials, @@ -31,16 +33,17 @@ import { readMakersDeployOutcome, redactSecret, } from '../makers/cli-deploy.ts'; -import { resolveConversationId } from '../runtime/request.ts'; +import { resolveConversationId, resolveRequestSiteDomain } from '../runtime/request.ts'; import { createProjectCheckpointController, ensureProjectDependencies, - extendExistingSandboxTimeout, - previewLinkFromState, - withLiveDeploymentUrl, + extendExistingSandboxTimeout, + replyLocaleFor, + withLiveDeploymentUrl, } from './checkpoint.ts'; import { createTurnLifecycle } from './lifecycle.ts'; import { prepareProjectWorkspace } from '../project/workspace.ts'; +import { sendTurnResult } from './result.ts'; /** Used when an API caller asks to publish without wording the request itself. */ const DEPLOY_TIMEOUT_SECONDS = 600; @@ -111,7 +114,7 @@ function summarizeDeployError(error: string) { * turn a live site into a reported failure. */ async function publishWithProgress( - context: any, + context: AgentContext, target: { projectName: string; appDir: string; env: Record; area: string }, onTail: (tail: string) => void, ): Promise<{ log: string; timedOut: boolean }> { @@ -168,29 +171,25 @@ async function publishWithProgress( * is what keeps a publish and a generation from touching the sandbox at once. */ export async function runDeployPipeline( - context: any, + context: AgentContext, message: string, send: StreamSend, options: { turnId?: string; - siteDomain?: string; + language?: string; apiKey?: string; gatewaySkip?: boolean; } = {}, ) { const { conversationId } = resolveConversationId(context); const request = message.trim() || 'Deploy this project'; - const copy = /[\u3400-\u9fff]/.test(request) ? COPY.zh : COPY.en; + const copy = replyLocaleFor(request, options.language) === 'zh' ? COPY.zh : COPY.en; if (!conversationId) { - send({ - type: 'result', - data: { - ok: false, - conversation_id: '', - reply: copy.missingConversation, - preview: {}, - }, + sendTurnResult(send, { + ok: false, + conversation_id: '', + reply: copy.missingConversation, }); return; } @@ -198,10 +197,8 @@ export async function runDeployPipeline( await extendExistingSandboxTimeout(context); const state = await prepareProjectWorkspace(context, conversationId, send); - const siteDomain = String(options.siteDomain || '').trim(); - if (siteDomain && state.siteDomain !== siteDomain) { - state.siteDomain = siteDomain; - await saveProjectState(context, conversationId, state); + if (bindSiteDomain(state, resolveRequestSiteDomain(context))) { + await persistWorkspace(context, conversationId, state); } const turn = createTurnLifecycle({ context, @@ -218,16 +215,10 @@ export async function runDeployPipeline( // clear whatever the last generation said about the project. const finish = async (reply: string, status: 'completed' | 'failed') => { await turn.finalize(reply, status); - send({ - type: 'result', - data: { - ok: status === 'completed', - reply, - conversation_id: conversationId, - ...(state.gatewayPromptPending ? { gatewayNeeded: true } : {}), - preview: previewLinkFromState(state), - deployment: state.deployment, - }, + sendTurnResult(send, { + ok: status === 'completed', + reply, + conversation_id: conversationId, }); }; @@ -264,10 +255,8 @@ export async function runDeployPipeline( send(event); }; const publish = (deployment: DeploymentInfo) => { - state.deployment = deployment; - // Eagerly persisted so a refresh mid-publish resumes into the same state - // the deployment bar was showing. - void saveProjectState(context, conversationId, state); + setDeployment(state, deployment); + void persistWorkspace(context, conversationId, state); send({ type: 'deployment_status', data: deployment }); }; // `detail` is whatever the CLI printed when it failed without phrasing the diff --git a/agents/_lib/turn/lifecycle.ts b/agents/_lib/turn/lifecycle.ts index a2c7d8f..65d49f6 100644 --- a/agents/_lib/turn/lifecycle.ts +++ b/agents/_lib/turn/lifecycle.ts @@ -1,4 +1,5 @@ -import { saveProjectState } from '../session/store.ts'; +import type { AgentContext } from '../runtime/context.ts'; +import { persistWorkspace } from '../project/workspace-store.ts'; import type { AgentProgressEvent, ProjectState } from '../types.ts'; import type { ProjectCheckpointController } from './checkpoint.ts'; import { applyStreamEvent } from '../../../shared/timeline.ts'; @@ -7,7 +8,7 @@ import type { PersistedActivityTurn } from '../../../shared/protocol.ts'; type TurnStatus = 'completed' | 'failed' | 'stopped'; type TurnLifecycleOptions = { - context: any; + context: AgentContext; conversationId: string; message: string; turnId: string; @@ -49,7 +50,7 @@ export function createTurnLifecycle(options: TurnLifecycleOptions) { if (finalizeOptions?.withSnapshot === true) await options.checkpoint.flush(); if (finalizeOptions?.withState !== false) { - await saveProjectState(options.context, options.conversationId, options.state); + await persistWorkspace(options.context, options.conversationId, options.state); } }; diff --git a/agents/_lib/turn/result.ts b/agents/_lib/turn/result.ts new file mode 100644 index 0000000..8225ffd --- /dev/null +++ b/agents/_lib/turn/result.ts @@ -0,0 +1,6 @@ +import type { ChatResponse } from '../../../shared/protocol.ts'; +import type { StreamSend } from '../types.ts'; + +export function sendTurnResult(send: StreamSend, data: ChatResponse) { + send({ type: 'result', data }); +} diff --git a/agents/_lib/types.ts b/agents/_lib/types.ts index 5966aef..952ca0d 100644 --- a/agents/_lib/types.ts +++ b/agents/_lib/types.ts @@ -1,6 +1,6 @@ import type { SdkMcpToolDefinition } from '@anthropic-ai/claude-agent-sdk'; import type { - ActivityStatus, + BuildInfo, BuildStatus, ChatStreamEvent, DeploymentInfo, @@ -9,9 +9,12 @@ import type { export type { ActivityStatus, + AssistantActivity as PersistedActivity, + BuildInfo, BuildStatus, DeploymentInfo, FileTreeItem, + PersistedActivityTurn, PreviewKind, } from '../../shared/protocol.ts'; @@ -32,6 +35,8 @@ export type ProjectState = { previewKind?: PreviewKind; /** Latest live deployment, kept separate from the sandbox preview iframe. */ deployment?: DeploymentInfo; + /** Last verification result; GET /workspace exposes it independently of the chat stream. */ + lastBuild?: BuildInfo; /** The host is waiting for a Models API key in the next user turn. */ gatewayPromptPending?: boolean; /** The user skipped the Models API key for this conversation. */ @@ -62,31 +67,6 @@ export type ChatTask = { error?: string; }; -export type PersistedActivity = - | { - kind: 'text'; - content: string; - } - | { - kind: 'tool'; - toolUseId: string; - name: string; - status: ActivityStatus; - inputSummary?: string; - outputSummary?: string; - startedAt: number; - endedAt?: number; - }; - -export type PersistedActivityTurn = { - id: string; - user: string; - assistant: string; - status: 'completed' | 'failed' | 'stopped'; - createdAt: number; - activities: PersistedActivity[]; -}; - export type StreamSend = (event: ChatStreamEvent) => void; export type ScaffoldLog = { diff --git a/agents/deploy.ts b/agents/deploy.ts index 3127a32..bf03550 100644 --- a/agents/deploy.ts +++ b/agents/deploy.ts @@ -1,16 +1,18 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { createChatTaskAndStreamResponse } from './_lib/session/task.ts'; +import { getRequestBody } from './_lib/runtime/request.ts'; /** Publish the current project. Deterministic — does not call the model. */ -export async function onRequestPost(context: any) { - const body = context?.request?.body || {}; +export async function onRequestPost(context: AgentContext) { + const body = getRequestBody(context); try { - const apiKey = String(body?.apiKey || '').trim(); - return await createChatTaskAndStreamResponse(context, String(body?.message || '').trim(), { + const apiKey = String(body.apiKey || '').trim(); + return await createChatTaskAndStreamResponse(context, String(body.message || '').trim(), { kind: 'deploy', - turnId: String(body?.turnId || '').trim() || undefined, - siteDomain: String(body?.siteDomain || '').trim() || undefined, + turnId: String(body.turnId || '').trim() || undefined, + language: String(body.language || '').trim() || undefined, ...(apiKey ? { apiKey } : {}), - ...(body?.gatewaySkip === true ? { gatewaySkip: true } : {}), + ...(body.gatewaySkip === true ? { gatewaySkip: true } : {}), }); } catch (error) { return new Response(JSON.stringify({ diff --git a/agents/download.ts b/agents/download.ts index c728b78..0177005 100644 --- a/agents/download.ts +++ b/agents/download.ts @@ -1,5 +1,6 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { runProjectDownloadPipeline } from './_lib/project/download.ts'; -export async function onRequest(context: any) { +export async function onRequest(context: AgentContext) { return runProjectDownloadPipeline(context); } diff --git a/agents/file.ts b/agents/file.ts index 8f89eb9..f6c0378 100644 --- a/agents/file.ts +++ b/agents/file.ts @@ -1,5 +1,6 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { runFileReadPipeline } from './_lib/project/read.ts'; -export async function onRequest(context: any) { +export async function onRequest(context: AgentContext) { return runFileReadPipeline(context); } diff --git a/agents/preview.ts b/agents/preview.ts index dda7540..16cb685 100644 --- a/agents/preview.ts +++ b/agents/preview.ts @@ -1,6 +1,13 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { runProjectResumePreviewPipeline } from './_lib/session/resume.ts'; +import { runPreviewStatusPipeline } from './_lib/project/snapshot.ts'; + +/** Current preview URL without restarting the server. */ +export async function onRequestGet(context: AgentContext) { + return runPreviewStatusPipeline(context); +} /** Re-mint the public preview URL without restoring the full workspace. */ -export async function onRequestPost(context: any) { +export async function onRequestPost(context: AgentContext) { return runProjectResumePreviewPipeline(context); } diff --git a/agents/prompt.ts b/agents/prompt.ts index 99e5796..7b30ff8 100644 --- a/agents/prompt.ts +++ b/agents/prompt.ts @@ -1,10 +1,12 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { createChatTaskAndStreamResponse } from './_lib/session/task.ts'; import { resolveRequestedModel } from './_lib/models.ts'; +import { getRequestBody } from './_lib/runtime/request.ts'; /** Submit a user message. Generation streams back as SSE. */ -export async function onRequestPost(context: any) { - const body = context?.request?.body || {}; - const message = String(body?.message || '').trim(); +export async function onRequestPost(context: AgentContext) { + const body = getRequestBody(context); + const message = String(body.message || '').trim(); if (!message) { return new Response(JSON.stringify({ ok: false, @@ -16,14 +18,14 @@ export async function onRequestPost(context: any) { } try { - const apiKey = String(body?.apiKey || '').trim(); + const apiKey = String(body.apiKey || '').trim(); return await createChatTaskAndStreamResponse(context, message, { kind: 'prompt', - turnId: String(body?.turnId || '').trim() || undefined, - model: resolveRequestedModel(context, body?.model), - siteDomain: String(body?.siteDomain || '').trim() || undefined, + turnId: String(body.turnId || '').trim() || undefined, + model: resolveRequestedModel(context, body.model), + language: String(body.language || '').trim() || undefined, ...(apiKey ? { apiKey } : {}), - ...(body?.gatewaySkip === true ? { gatewaySkip: true } : {}), + ...(body.gatewaySkip === true ? { gatewaySkip: true } : {}), }); } catch (error) { return new Response(JSON.stringify({ diff --git a/agents/session.ts b/agents/session.ts index c0a580a..fdf49ef 100644 --- a/agents/session.ts +++ b/agents/session.ts @@ -1,6 +1,7 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { createProjectResumeStreamResponse } from './_lib/session/resume.ts'; /** Session entry: history, workspace, and an in-flight task's SSE on one GET. */ -export async function onRequestGet(context: any) { +export async function onRequestGet(context: AgentContext) { return createProjectResumeStreamResponse(context); } diff --git a/agents/stop.ts b/agents/stop.ts index feb55ce..295e51d 100644 --- a/agents/stop.ts +++ b/agents/stop.ts @@ -1,9 +1,13 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { abortLiveChatTask, markChatTaskStopped } from './_lib/session/task.ts'; -import { getProjectState, saveProjectState } from './_lib/session/store.ts'; +import { getProjectState } from './_lib/session/store.ts'; import { persistProjectSnapshot } from './_lib/turn/checkpoint.ts'; +import { markCreated, persistWorkspace } from './_lib/project/workspace-store.ts'; +import { getRequestBody } from './_lib/runtime/request.ts'; -export async function onRequest(context: any) { - const conversationId = String(context?.request?.body?.conversation_id || '').trim(); +export async function onRequest(context: AgentContext) { + const body = getRequestBody(context); + const conversationId = String(body.conversation_id || '').trim(); if (!conversationId) { return new Response(JSON.stringify({ ok: false, error: 'missing conversation_id' }), { status: 400, @@ -12,7 +16,7 @@ export async function onRequest(context: any) { } try { - const discardProject = context?.request?.body?.discardProject === true; + const discardProject = body.discardProject === true; abortLiveChatTask(conversationId); await markChatTaskStopped(context, conversationId); const result = await context.utils?.abortActiveRun?.(conversationId); @@ -23,8 +27,8 @@ export async function onRequest(context: any) { const saved = await persistProjectSnapshot(context, conversationId, state); persisted = saved; if (saved && !state.created) { - state.created = true; - await saveProjectState(context, conversationId, state); + markCreated(state); + await persistWorkspace(context, conversationId, state); } } catch (error) { console.warn('[stop] project snapshot failed', error); diff --git a/agents/transcript.ts b/agents/transcript.ts index 3c4fbf1..c463614 100644 --- a/agents/transcript.ts +++ b/agents/transcript.ts @@ -1,8 +1,9 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; import { getLiveQuery } from './_lib/session/live.ts'; import { createTranscriptStreamResponse, resolveClaudeTranscriptPath } from './_lib/session/transcript.ts'; /** Session source of truth: stream the Claude JSONL file, unprojected. */ -export async function onRequestGet(context: any) { +export async function onRequestGet(context: AgentContext) { return createTranscriptStreamResponse(context, (conversationId) => { const live = getLiveQuery(conversationId); if (!live) return null; diff --git a/agents/workspace.ts b/agents/workspace.ts new file mode 100644 index 0000000..060c458 --- /dev/null +++ b/agents/workspace.ts @@ -0,0 +1,7 @@ +import type { AgentContext } from './_lib/runtime/context.ts'; +import { runWorkspaceSnapshotPipeline } from './_lib/project/snapshot.ts'; + +/** Current files, preview, deployment, and download — independent of the chat stream. */ +export async function onRequestGet(context: AgentContext) { + return runWorkspaceSnapshotPipeline(context); +} diff --git a/app/components/files-panel.tsx b/app/components/files-panel.tsx index 2e47b2d..8cc13ec 100644 --- a/app/components/files-panel.tsx +++ b/app/components/files-panel.tsx @@ -184,7 +184,7 @@ export const FilesPanel = memo(function FilesPanel({ // Open the path the parent asked for (first generated file). Prefer waiting until // the tree lists it so parent dirs can expand; fall back to cache-only so a - // file_content that arrives before file_tree still shows immediately. + // file_changed that arrives before file_tree still shows immediately. useEffect(() => { if (!focusPath || focusedPathRef.current === focusPath) { return; diff --git a/app/components/session-panel.tsx b/app/components/session-panel.tsx index 796dc88..ac88490 100644 --- a/app/components/session-panel.tsx +++ b/app/components/session-panel.tsx @@ -4,7 +4,7 @@ import { memo, useEffect, useState } from 'react'; import type { SessionCopy } from '../i18n'; import { consumeEventStream } from '../features/workspace/sse'; import { openTranscriptStream } from '../features/workspace/workspace-api'; -import type { TranscriptData, TranscriptStreamEvent } from '../../../shared/protocol'; +import type { TranscriptData, TranscriptStreamEvent } from '../../shared/protocol'; import { Spinner } from './spinner'; type SessionState = diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index 48991ea..5b739f5 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -2,9 +2,8 @@ import { useEffect, useRef, useState, type MutableRefObject } from 'react'; import { - appendNarrationChunk, + applyStreamEvent, dropTrailingSummaryEcho, - sanitizeThinkingContent, } from '../../../../shared/timeline'; import { extractApiKeyFromUserText } from '../../../../shared/gateway-secret'; import { STOPPED_TURN_REPLY } from '../../../../shared/user-facing-reply'; @@ -13,13 +12,10 @@ import { cacheConversationId, createConversationId, createMessageId, - extractProjectName, getOrCreateCachedConversationId, markLastTurnStopped, } from '@/app/lib/conversation'; -import type { FileContentCache } from '@/app/hooks/use-file-content-cache'; import type { - AssistantActivity, AssistantStatus, ChatMessage, ChatResponse, @@ -34,6 +30,8 @@ import { } from '../workspace-api'; import type { PreviewSurfaceApi } from './use-preview-surface'; import type { WorkspaceStateApi } from './use-workspace-state'; +import type { WorkspaceSnapshotApi } from './use-workspace-snapshot'; +import type { PersistedActivityTurn } from '../../../../shared/protocol'; type LiveCopy = { noDisplay: string; @@ -47,9 +45,9 @@ export function useLiveTurn(options: { language: Locale; model: string; t: { response: LiveCopy; workspace: { deployRequest: string; gatewayPromptApiKey: string; gatewayPromptSkip: string } }; - fileCache: FileContentCache; workspace: WorkspaceStateApi; preview: PreviewSurfaceApi; + snapshot: WorkspaceSnapshotApi; conversationId: string | null; setConversationId: (id: string | null) => void; conversationIdRef: MutableRefObject; @@ -60,9 +58,9 @@ export function useLiveTurn(options: { language, model, t, - fileCache, workspace, preview, + snapshot, conversationId, setConversationId, conversationIdRef, @@ -134,48 +132,23 @@ export function useLiveTurn(options: { ); }; - const appendTextActivity = (text: string) => { + const foldActivityEvent = (event: ChatStreamEvent) => { setMessages((current) => current.map((item) => { if (item.id !== assistantMessageId) return item; - const nextText = sanitizeThinkingContent(text); - if (!nextText) return item; - return { - ...item, - activities: appendNarrationChunk(item.activities ?? [], nextText), - }; + const folded = applyStreamEvent({ + id: item.id, + user: '', + assistant: item.content, + status: 'completed', + createdAt: 0, + activities: item.activities ?? [], + } satisfies PersistedActivityTurn, event); + return { ...item, activities: folded.activities }; }), ); }; - const upsertToolActivity = ( - toolUseId: string, - patch: Partial>, - ) => { - setMessages((current) => current.map((item) => { - if (item.id !== assistantMessageId) return item; - const activities = [...(item.activities ?? [])]; - const index = activities.findIndex( - (activity) => activity.kind === 'tool' && activity.toolUseId === toolUseId, - ); - if (index >= 0) { - activities[index] = { ...activities[index], ...patch } as AssistantActivity; - } else { - activities.push({ - kind: 'tool', - toolUseId, - name: patch.name || '', - status: patch.status || 'running', - inputSummary: patch.inputSummary, - outputSummary: patch.outputSummary, - startedAt: patch.startedAt || Date.now(), - endedAt: patch.endedAt, - }); - } - return { ...item, activities }; - })); - }; - const finalizeAssistant = ( finalContent: string, finalStatus: AssistantStatus, @@ -215,29 +188,13 @@ export function useLiveTurn(options: { cacheConversationId(data.conversation_id); setConversationId(data.conversation_id); } - if (data.preview) { - preview.activatePreview(data.preview, activatedPreviewRevisions); - } - if (data.deployment) { - workspace.setDeployment(data.deployment); - } - if (data.download) { - workspace.setDownload(data.download); - } - if (data.build) { - workspace.setBuild(data.build); - } - if (data.files) { - workspace.setFileTree(data.files); - } - if (data.gatewayNeeded) { - workspace.setGatewayNeeded(true); - } workspace.setFilesRefreshing(false); const finalText = data.reply || data.error || t.response.noDisplay; const finalStatus: AssistantStatus = data.stopped ? 'stopped' : data.ok === false ? 'error' : 'done'; finalizeAssistant(finalText, finalStatus); + const cid = data.conversation_id || sessionOptions.requestConversationId; + if (cid) void snapshot.refresh(cid); }; const handleStreamEvent = (event: ChatStreamEvent) => { @@ -276,40 +233,21 @@ export function useLiveTurn(options: { patchAssistant({ content: text }); return; } - if (event.type === 'text_segment' && event.data?.text) { - appendTextActivity(event.data.text); - return; - } - if (event.type === 'tool_use' && event.data) { - sawProjectActivity = true; - upsertToolActivity(event.data.id || '', { - name: event.data.name || '', - status: 'running', - inputSummary: event.data.inputSummary || event.data.command, - ...(event.data.outputSummary ? { outputSummary: event.data.outputSummary } : {}), - startedAt: event.data.startedAt, - }); + if (event.type === 'text_segment' || event.type === 'tool_use' || event.type === 'tool_result') { + if (event.type !== 'text_segment') sawProjectActivity = true; + foldActivityEvent(event); return; } - if (event.type === 'tool_result' && event.data) { + if (event.type === 'file_changed' && event.data?.paths?.length) { sawProjectActivity = true; - upsertToolActivity(event.data.id || '', { - name: event.data.toolName || '', - status: event.data.status || (event.data.ok === false ? 'failed' : 'completed'), - outputSummary: event.data.outputSummary || event.data.preview, - endedAt: event.data.endedAt || Date.now(), - }); - return; - } - if (event.type === 'file_content' && event.data?.path) { - const content = event.data.content || ''; - fileCache.write(event.data.path, { - content, - size: typeof event.data.size === 'number' ? event.data.size : content.length, - truncated: false, - }); - if (!openedFirstFile) { - pendingFirstFilePath = event.data.path; + const paths = event.data.paths.filter(Boolean); + const cid = conversationIdRef.current || sessionOptions.requestConversationId; + if (cid && paths.length > 0) { + void snapshot.pullFiles(cid, paths).then(() => { + if (!openedFirstFile && paths[0]) revealFirstFile(paths[0]); + }); + } else if (!openedFirstFile && paths[0]) { + pendingFirstFilePath = paths[0]; } return; } @@ -493,9 +431,9 @@ export function useLiveTurn(options: { ? await startDeployTurn({ conversationId: requestConversationId, turnId: assistantMessageId, + language, ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), - siteDomain: extractProjectName().domain, signal: requestAbortController.signal, }) : await startPromptTurn({ @@ -503,9 +441,9 @@ export function useLiveTurn(options: { message: displayMessage, turnId: assistantMessageId, model: modelRef.current, + language, ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), - siteDomain: extractProjectName().domain, signal: requestAbortController.signal, }); await attachChatStream({ diff --git a/app/features/workspace/hooks/use-preview-surface.ts b/app/features/workspace/hooks/use-preview-surface.ts index 624fa49..530bb15 100644 --- a/app/features/workspace/hooks/use-preview-surface.ts +++ b/app/features/workspace/hooks/use-preview-surface.ts @@ -3,7 +3,7 @@ import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react'; import { isMakersDeployUrl } from '../../../../shared/makers-url'; import { previewDeepLink } from '../../../../shared/preview-link'; -import type { FileTree, LinkInfo } from '@/app/types/workspace'; +import type { LinkInfo } from '@/app/types/workspace'; import { fetchPreviewRefresh } from '../workspace-api'; const PREVIEW_CREDENTIAL_REFRESH_MS = 8 * 60_000; @@ -35,8 +35,7 @@ export function usePreviewSurface(options: { conversationIdRef: MutableRefObject; loadingRef: MutableRefObject; workspaceRestoringRef: MutableRefObject; - setFileTree: (tree: FileTree) => void; - setDownload: (download: LinkInfo) => void; + refreshWorkspace?: (conversationId: string) => Promise; }) { const [preview, setPreview] = useState(null); const [previewViewport, setPreviewViewport] = useState<'desktop' | 'mobile'>('desktop'); @@ -143,12 +142,7 @@ export function usePreviewSurface(options: { applyFreshPreviewUrl(data.preview.url, data.preview.sandboxDebugUrl, { remountIframe: willRemount || data.preview.restarted === true, }); - if (data.files?.items?.length) { - options.setFileTree(data.files); - } - if (data.download?.url) { - options.setDownload(data.download); - } + void options.refreshWorkspace?.(id); return true; } if (refreshOptions?.showLoading) { @@ -311,7 +305,7 @@ export function usePreviewSurface(options: { setPreviewRefreshFailed(false); previewRefreshedAtRef.current = Date.now(); let revision = activatedPreviewRevisions.get(nextPreview.url); - if (revision === undefined) { + if (revision === undefined || nextPreview.restarted) { revision = previewRevisionRef.current + 1; previewRevisionRef.current = revision; activatedPreviewRevisions.set(nextPreview.url, revision); diff --git a/app/features/workspace/hooks/use-session-resume.ts b/app/features/workspace/hooks/use-session-resume.ts index 8e1abd3..b2985bc 100644 --- a/app/features/workspace/hooks/use-session-resume.ts +++ b/app/features/workspace/hooks/use-session-resume.ts @@ -2,7 +2,7 @@ import { useEffect, useRef, useState, type MutableRefObject } from 'react'; import { dropTrailingSummaryEcho } from '../../../../shared/timeline'; -import type { FileContentCache } from '@/app/hooks/use-file-content-cache'; +import type { Locale } from '@/app/i18n'; import { clearCachedConversationId, createMessageId, @@ -18,27 +18,27 @@ import type { import { consumeEventStream } from '../sse'; import { openSessionStream } from '../workspace-api'; import type { LiveTurnApi } from './use-live-turn'; -import type { PreviewSurfaceApi } from './use-preview-surface'; import type { WorkspaceStateApi } from './use-workspace-state'; +import type { WorkspaceSnapshotApi } from './use-workspace-snapshot'; export function useSessionResume(options: { workspace: WorkspaceStateApi; - preview: PreviewSurfaceApi; live: LiveTurnApi; - fileCache: FileContentCache; + snapshot: WorkspaceSnapshotApi; setConversationId: (id: string | null) => void; setModel: (model: string) => void; + setLanguage: (language: Locale) => void; conversationIdRef: MutableRefObject; workspaceEpochRef: MutableRefObject; workspaceRestoringRef: MutableRefObject; }) { const { workspace, - preview, live, - fileCache, + snapshot, setConversationId, setModel, + setLanguage, conversationIdRef, workspaceEpochRef, workspaceRestoringRef, @@ -75,6 +75,9 @@ export function useSessionResume(options: { if (data.model) { setModel(data.model); } + if (data.language === 'zh' || data.language === 'en') { + setLanguage(data.language); + } const activityHistory = Array.isArray(data.activityHistory) ? data.activityHistory : []; let nextMessages: ChatMessage[] = activityHistory.length > 0 ? activityHistory.flatMap((turn) => [ @@ -178,16 +181,7 @@ export function useSessionResume(options: { const applyWorkspace = (data: ResumeData) => { if (data.gatewayNeeded) workspace.setGatewayNeeded(true); - if (data.files) { - workspace.setFileTree(data.files); - } - if (data.download?.url) { - workspace.setDownload(data.download); - } - if (data.deployment) { - workspace.setDeployment(data.deployment); - } - preview.applyResumedPreview(data.preview); + snapshot.applySnapshot(data); }; const resumeController = new AbortController(); @@ -236,15 +230,8 @@ export function useSessionResume(options: { return; } - if (event.type === 'resume_file_content' && event.data?.path && typeof event.data.content === 'string') { - fileCache.write(event.data.path, { - content: event.data.content, - size: typeof event.data.size === 'number' - ? event.data.size - : new TextEncoder().encode(event.data.content).byteLength, - truncated: Boolean(event.data.truncated), - mtime: event.data.mtime, - }); + if (event.type === 'file_changed' && event.data?.paths?.length) { + void snapshot.pullFiles(existing, event.data.paths.filter(Boolean)); return; } diff --git a/app/features/workspace/hooks/use-workspace-snapshot.ts b/app/features/workspace/hooks/use-workspace-snapshot.ts new file mode 100644 index 0000000..5af1494 --- /dev/null +++ b/app/features/workspace/hooks/use-workspace-snapshot.ts @@ -0,0 +1,51 @@ +'use client'; + +import { useCallback } from 'react'; +import type { FileContentCache } from '@/app/hooks/use-file-content-cache'; +import type { ResumeData, WorkspaceSnapshot } from '../../../../shared/protocol'; +import { + fetchFileBatch, + fetchWorkspaceSnapshot, +} from '../workspace-api'; +import type { PreviewSurfaceApi } from './use-preview-surface'; +import type { WorkspaceStateApi } from './use-workspace-state'; + +export function useWorkspaceSnapshot(options: { + workspace: WorkspaceStateApi; + preview: PreviewSurfaceApi; + fileCache: FileContentCache; +}) { + const { workspace, preview, fileCache } = options; + + const applySnapshot = useCallback((data: WorkspaceSnapshot | ResumeData) => { + if (data.files) workspace.setFileTree(data.files); + if (data.download?.url) workspace.setDownload(data.download); + if (data.deployment) workspace.setDeployment(data.deployment); + if ('build' in data && data.build) workspace.setBuild(data.build); + if (data.preview?.url) preview.applyResumedPreview(data.preview); + }, [preview, workspace]); + + const refresh = useCallback(async (conversationId: string) => { + const snapshot = await fetchWorkspaceSnapshot(conversationId); + if (snapshot?.ok) applySnapshot(snapshot); + return snapshot; + }, [applySnapshot]); + + const pullFiles = useCallback(async (conversationId: string, paths: string[]) => { + if (paths.length === 0) return []; + const files = await fetchFileBatch(conversationId, paths); + for (const file of files) { + if (!file.path || !file.ok || typeof file.content !== 'string') continue; + fileCache.write(file.path, { + content: file.content, + size: typeof file.size === 'number' ? file.size : file.content.length, + truncated: Boolean(file.truncated), + }); + } + return files; + }, [fileCache]); + + return { applySnapshot, refresh, pullFiles }; +} + +export type WorkspaceSnapshotApi = ReturnType; diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index 7548142..3832138 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -1,8 +1,10 @@ import type { PersistedActivityTurn, ResumeData, + WorkspaceSnapshot, } from '../../../shared/protocol'; import type { ModelOption } from '../../../shared/models'; +import type { Locale } from '@/app/i18n'; function conversationHeaders(conversationId: string): HeadersInit { return { @@ -58,7 +60,7 @@ export function startPromptTurn(options: { message: string; turnId: string; model?: string; - siteDomain?: string; + language?: Locale; apiKey?: string; gatewaySkip?: boolean; signal?: AbortSignal; @@ -70,7 +72,7 @@ export function startPromptTurn(options: { message: options.message, turnId: options.turnId, ...(options.model ? { model: options.model } : {}), - ...(options.siteDomain ? { siteDomain: options.siteDomain } : {}), + ...(options.language ? { language: options.language } : {}), ...(options.apiKey ? { apiKey: options.apiKey } : {}), ...(options.gatewaySkip ? { gatewaySkip: true } : {}), }), @@ -81,7 +83,7 @@ export function startPromptTurn(options: { export function startDeployTurn(options: { conversationId: string; turnId: string; - siteDomain?: string; + language?: Locale; apiKey?: string; gatewaySkip?: boolean; signal?: AbortSignal; @@ -91,7 +93,7 @@ export function startDeployTurn(options: { headers: conversationHeaders(options.conversationId), body: JSON.stringify({ turnId: options.turnId, - ...(options.siteDomain ? { siteDomain: options.siteDomain } : {}), + ...(options.language ? { language: options.language } : {}), ...(options.apiKey ? { apiKey: options.apiKey } : {}), ...(options.gatewaySkip ? { gatewaySkip: true } : {}), }), @@ -134,3 +136,42 @@ export function openTranscriptStream(conversationId: string, signal?: AbortSigna signal, }); } + +export function fetchWorkspaceSnapshot(conversationId: string, signal?: AbortSignal) { + return fetch('/workspace', { + method: 'GET', + headers: conversationHeaders(conversationId), + signal, + }).then((response) => readJson(response)).catch(() => null); +} + +export type FileBatchEntry = { + path: string; + ok?: boolean; + content?: string; + size?: number; + truncated?: boolean; + error?: string; +}; + +const FILE_BATCH_MAX = 12; + +export async function fetchFileBatch( + conversationId: string, + paths: string[], + signal?: AbortSignal, +): Promise { + const unique = [...new Set(paths.map((path) => path.trim()).filter(Boolean))]; + const files: FileBatchEntry[] = []; + for (let index = 0; index < unique.length; index += FILE_BATCH_MAX) { + const batch = unique.slice(index, index + FILE_BATCH_MAX); + const response = await fetch(`/file?paths=${encodeURIComponent(batch.join(','))}`, { + method: 'GET', + headers: conversationHeaders(conversationId), + signal, + }); + const data = await readJson<{ ok?: boolean; files?: FileBatchEntry[] }>(response); + if (Array.isArray(data?.files)) files.push(...data.files); + } + return files; +} diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 873fc2e..f5f425b 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -53,6 +53,7 @@ import { useLiveTurn } from './hooks/use-live-turn'; import { usePreviewSurface } from './hooks/use-preview-surface'; import { useSessionResume } from './hooks/use-session-resume'; import { useWorkspaceState, type SandboxTab } from './hooks/use-workspace-state'; +import { useWorkspaceSnapshot } from './hooks/use-workspace-snapshot'; function ResultPanelToggle({ open, @@ -124,22 +125,28 @@ export function WorkspaceScreen() { const fileCache = useFileContentCache(); const workspace = useWorkspaceState(); + const snapshotRefreshRef = useRef<(conversationId: string) => Promise>(async () => null); const preview = usePreviewSurface({ conversationIdRef, loadingRef, workspaceRestoringRef, - setFileTree: workspace.setFileTree, - setDownload: workspace.setDownload, + refreshWorkspace: (id) => snapshotRefreshRef.current(id), }); + const snapshot = useWorkspaceSnapshot({ + workspace, + preview, + fileCache, + }); + snapshotRefreshRef.current = snapshot.refresh; const t = TRANSLATIONS[language]; const live = useLiveTurn({ language, model, t, - fileCache, workspace, preview, + snapshot, conversationId, setConversationId, conversationIdRef, @@ -148,11 +155,11 @@ export function WorkspaceScreen() { }); const resume = useSessionResume({ workspace, - preview, live, - fileCache, + snapshot, setConversationId, setModel, + setLanguage, conversationIdRef, workspaceEpochRef, workspaceRestoringRef, diff --git a/app/hooks/use-file-content-cache.ts b/app/hooks/use-file-content-cache.ts index fe54f2b..8cf46ac 100644 --- a/app/hooks/use-file-content-cache.ts +++ b/app/hooks/use-file-content-cache.ts @@ -14,7 +14,7 @@ type FileCacheEntry = { // Caches generated file contents so opening a file does not cost a /file request // (each one still wakes the agent route and crosses the sandbox boundary). Entries come -// from two places: the file_content events the agent pushes as it writes, and +// from two places: file_changed events that trigger GET /file?paths=, and // /file responses for files it never wrote this session. The sandbox mtime/size // reported by the file tree decides when an entry is still good. export function useFileContentCache() { diff --git a/app/lib/tool-activity.ts b/app/lib/tool-activity.ts index 481be6f..f6090bf 100644 --- a/app/lib/tool-activity.ts +++ b/app/lib/tool-activity.ts @@ -7,6 +7,8 @@ export { presentToolActivity, resolveDeployOffer, toolActionTier, + type DeployOfferMessage, type ReferenceTopic, type ToolAction, + type ToolPresentation, } from '../../shared/timeline.ts'; diff --git a/app/types/workspace.ts b/app/types/workspace.ts index d7b76b3..41da705 100644 --- a/app/types/workspace.ts +++ b/app/types/workspace.ts @@ -11,6 +11,7 @@ export type { ResumeData, ResumeStreamEvent, SessionStreamEvent, + WorkspaceSnapshot, } from '../../shared/protocol'; export type AssistantStatus = 'running' | 'done' | 'error' | 'stopped'; diff --git a/shared/protocol.ts b/shared/protocol.ts index 4e4f741..0f3cfd0 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -105,11 +105,24 @@ export type ResumeData = { activeTask?: ActiveChatTask | null; /** Model chosen for this conversation; '' or absent means the deployment default. */ model?: string; + /** UI language chosen for this conversation. */ + language?: 'zh' | 'en'; /** Resume should show the Models API key card. */ gatewayNeeded?: boolean; error?: string; }; +/** Workspace projection the frontend can fetch without the chat stream. */ +export type WorkspaceSnapshot = { + ok?: boolean; + conversation_id?: string; + files?: FileTree; + preview?: LinkInfo; + deployment?: DeploymentInfo; + download?: LinkInfo; + build?: BuildInfo; +}; + /** Raw Claude JSONL for the Session tab. The file is the source of truth. */ export type TranscriptData = { ok?: boolean; @@ -126,15 +139,8 @@ export type ChatResponse = { ok?: boolean; reply?: string; conversation_id?: string; - build?: BuildInfo; - files?: FileTree; - preview?: LinkInfo; - deployment?: DeploymentInfo; - download?: LinkInfo; error?: string; stopped?: boolean; - /** Keep the Models API key card up after this turn ends. */ - gatewayNeeded?: boolean; }; type ProgressPhase = 'scaffold' | 'modify' | 'code' | 'install' | 'preview' | 'link'; @@ -152,8 +158,8 @@ export type ChatStreamEvent = | { type: 'agent'; data?: Pick } | { type: 'file_tree'; data?: FileTree } | { - type: 'file_content'; - data?: { path?: string; content?: string; size?: number }; + type: 'file_changed'; + data?: { paths?: string[] }; } | { type: 'preview_ready'; @@ -209,16 +215,7 @@ export type ChatStreamEvent = export type ResumeStreamEvent = | { type: 'resume_history'; data?: ResumeData } | { type: 'resume_workspace'; data?: ResumeData } - | { - type: 'resume_file_content'; - data?: { - path?: string; - content?: string; - size?: number; - truncated?: boolean; - mtime?: number; - }; - } + | { type: 'file_changed'; data?: { paths?: string[] } } | { type: 'error'; error?: string } | { type: 'ping'; ts?: number }; diff --git a/shared/user-facing-reply.ts b/shared/user-facing-reply.ts index 239eaea..7415fe0 100644 --- a/shared/user-facing-reply.ts +++ b/shared/user-facing-reply.ts @@ -10,12 +10,12 @@ const CJK_PATTERN = /[\u3400-\u9fff]/; export type ReplyLocale = 'zh' | 'en'; /** - * Which language a reply should be written in. There is no locale on the wire - * for a turn — the agent runtime only ever sees the prompt — so the request - * itself decides, and every reply built server-side has to ask the same way or - * one turn answers in the wrong language. + * Which language a reply should be written in. An explicit locale from the + * conversation preference wins; the request text is only a fallback so a + * Chinese UI with an English prompt still gets Chinese replies. */ -export function replyLocaleFor(text: string): ReplyLocale { +export function replyLocaleFor(text: string, explicit?: ReplyLocale | string): ReplyLocale { + if (explicit === 'zh' || explicit === 'en') return explicit; return CJK_PATTERN.test(text) ? 'zh' : 'en'; } diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index 7d193e6..05ff998 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -68,6 +68,7 @@ const AGENT_ROUTE_FILES = new Set([ 'agents/file.ts', 'agents/download.ts', 'agents/transcript.ts', + 'agents/workspace.ts', ]); test('agent routes stay at agents/ and implementation lives in agents/_lib/', async () => { diff --git a/tests/deploy-task.test.ts b/tests/deploy-task.test.ts index 92522a3..55a55b5 100644 --- a/tests/deploy-task.test.ts +++ b/tests/deploy-task.test.ts @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; import { presentToolActivity } from '../app/lib/tool-activity.ts'; +import { readCommandsWrapSource } from './helpers/fixtures.ts'; // Publishing and generating both drive the same sandbox, so they share the one // task slot: whichever starts first makes the other wait, and a refresh @@ -16,9 +17,11 @@ test('publishing occupies the chat task slot instead of a route of its own', asy assert.match(tasks, /kind === 'deploy'[\s\S]*?runDeployPipeline/); assert.match(route, /kind: 'deploy'/); - assert.match(route, /siteDomain: String\(body\?\.siteDomain/); + assert.match(route, /language: String\(body\.language/); assert.match(client, /fetch\('\/deploy'/); - assert.match(client, /siteDomain: options\.siteDomain/); + assert.match(client, /language: options\.language/); + assert.doesNotMatch(route, /siteDomain: String\(body\?\.siteDomain/); + assert.doesNotMatch(client, /siteDomain: options\.siteDomain/); assert.doesNotMatch(resume, /streamUrl: `\/chat\?runId=/); assert.match(resume, /iterateLiveChatTaskEvents/); }); @@ -63,7 +66,7 @@ test('publishing stops the preview dev server before the build starts', async () readFile('agents/_lib/makers/cli-deploy.ts', 'utf8'), readFile('agents/_lib/makers/cli-dev.ts', 'utf8'), readFile('agents/_lib/turn/deploy.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), ]); // Stopping is part of the command, so it cannot be skipped by a caller. @@ -95,7 +98,7 @@ test('publishing restarts the preview without paying for the smoke gates again', const [preview, pipeline, wrapper] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), readFile('agents/_lib/turn/deploy.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), ]); // The gates cost a real model call, and the project did not change. @@ -217,7 +220,8 @@ test('the deploy button is disabled until a project exists and nothing is runnin /const canDeployProject = hasDeployableProject && !deployRunning && !resume\.workspaceRestoring/, ); assert.match(screen, /sendMessage\(t\.workspace\.deployRequest, \{ deploy: true \}\)/); - assert.match(live, /siteDomain: extractProjectName\(\)\.domain/); + assert.match(live, /language,/); + assert.doesNotMatch(live, /siteDomain: extractProjectName\(\)\.domain/); assert.match(screen, /disabled=\{!canDeployProject\}/); assert.match(screen, /className="workspace-icon-button is-publish"/); assert.doesNotMatch(screen, /is-running/); diff --git a/tests/gateway-prompt.test.ts b/tests/gateway-prompt.test.ts index 3a9566a..78b53ac 100644 --- a/tests/gateway-prompt.test.ts +++ b/tests/gateway-prompt.test.ts @@ -278,7 +278,8 @@ test('the conversation card asks for API Key and submits a masked chat turn', as live.indexOf('const applyResponse'), ); assert.doesNotMatch(finalize, /setGatewayNeeded\(false\)/); - assert.match(live, /if \(data\.gatewayNeeded\) \{\s*workspace\.setGatewayNeeded\(true\);/); + assert.match(live, /event\.type === 'gateway_credentials'/); + assert.match(live, /workspace\.setGatewayNeeded\(true\)/); }); test('the API key card waits until the assistant turn has finished', async () => { @@ -308,7 +309,8 @@ test('a turn waiting for the API key is completed, not a red error', async () => assert.match(helpers, /GATEWAY_CREDENTIALS_USER_REPLY/); assert.match(pause, /GATEWAY_CREDENTIALS_USER_REPLY\[replyLocale\]/); - assert.match(pause, /gatewayNeeded: true/); + assert.match(pause, /type: 'gateway_credentials'/); + assert.match(pause, /status: 'needed'/); assert.match(pause, /ok: true,\s*\n\s*reply: pauseReply/); // The card is gated on result/loading, so a Blob snapshot that hangs or // fails must not sit in front of that event. Persist after it, unawaited. diff --git a/tests/helpers/fixtures.ts b/tests/helpers/fixtures.ts index 7593a10..2e9219f 100644 --- a/tests/helpers/fixtures.ts +++ b/tests/helpers/fixtures.ts @@ -1,3 +1,4 @@ +import { readFile } from 'node:fs/promises'; import type { ProjectState } from '../../agents/_lib/types.ts'; /** @@ -20,3 +21,17 @@ export function projectState( ...overrides, }; } + +export const COMMANDS_WRAP_FILES = [ + 'agents/_lib/tools/commands-wrap.ts', + 'agents/_lib/tools/makers-command.ts', + 'agents/_lib/tools/preview-command-result.ts', + 'agents/_lib/tools/deploy-command-result.ts', + 'agents/_lib/tools/command-preprocess.ts', + 'agents/_lib/tools/command-text.ts', + 'agents/_lib/tools/makers-lifecycle.ts', +] as const; + +export async function readCommandsWrapSource() { + return (await Promise.all(COMMANDS_WRAP_FILES.map((file) => readFile(file, 'utf8')))).join('\n'); +} diff --git a/tests/makers-compat.test.ts b/tests/makers-compat.test.ts index 57ac2e6..09faf42 100644 --- a/tests/makers-compat.test.ts +++ b/tests/makers-compat.test.ts @@ -7,6 +7,7 @@ import { MAKERS_REFERENCE_SKILL_NAMES, resolveMakersSkillDirectory, } from '../agents/_lib/tools/makers-skills.ts'; +import { readCommandsWrapSource } from './helpers/fixtures.ts'; const skillsRoot = '.claude/skills'; @@ -77,7 +78,7 @@ test('direct sandbox CLI replaces custom tools while retaining relevant compatib const [agent, projectTools, commandTools, compatibility] = await Promise.all([ readFile('agents/_lib/session/live.ts', 'utf8'), readFile('agents/_lib/tools/project-tools.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), readFile('agents/_lib/makers/compat/lint-script.ts', 'utf8'), ]); const paths = await readFile('agents/_lib/utils/paths.ts', 'utf8'); diff --git a/tests/makers-deploy.test.ts b/tests/makers-deploy.test.ts index 05ce3f9..b0c1e1f 100644 --- a/tests/makers-deploy.test.ts +++ b/tests/makers-deploy.test.ts @@ -29,7 +29,7 @@ import { resolveMakersProjectName, syncSandboxEnvToMakersProject, } from '../agents/_lib/makers/project.ts'; -import { projectState } from './helpers/fixtures.ts'; +import { projectState, readCommandsWrapSource } from './helpers/fixtures.ts'; test('builds a non-interactive direct CLI deploy command', () => { const production = buildMakersDeployCommand('vibe-coding-playground'); @@ -593,7 +593,7 @@ test('each conversation owns one project, for preview and deploy alike', () => { test('preview and deploy resolve the project through the same function', async () => { const [previewSource, commandSource, sessionSource] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), readFile('agents/_lib/makers/session.ts', 'utf8'), ]); @@ -792,7 +792,7 @@ test('deploy does not copy .env when there is no master token', async () => { test('deploy copies .env with the runtime master token, not the sandbox tenant token', async () => { const [session, wrap, helper] = await Promise.all([ readFile('agents/_lib/makers/session.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), readFile('agents/_lib/makers/project.ts', 'utf8'), ]); diff --git a/tests/makers-dev.test.ts b/tests/makers-dev.test.ts index 49f743b..9f98a14 100644 --- a/tests/makers-dev.test.ts +++ b/tests/makers-dev.test.ts @@ -1055,7 +1055,7 @@ test('sandbox preview publishes the fixed gateway path through a local adapter', assert.match(preview, /prepareMakersSession/); assert.match(session, /ensureMakersPublishProject/); assert.doesNotMatch(preview, /syncSandboxEnvToMakersProject/); - assert.match(preview, /getHost\(PREVIEW_PUBLIC_PORT\)/); + assert.match(preview, /getHost\?\.\(PREVIEW_PUBLIC_PORT\)/); assert.match( preview, /127\.0\.0\.1:\$\{PREVIEW_SERVER_PORT\}\$\{PREVIEW_PATH_PREFIX\}chat/, diff --git a/tests/makers-sub-token.test.ts b/tests/makers-sub-token.test.ts index c7c2cfe..b62e59c 100644 --- a/tests/makers-sub-token.test.ts +++ b/tests/makers-sub-token.test.ts @@ -11,6 +11,7 @@ import { resolveSandboxMakersToken, } from '../agents/_lib/makers/token.ts'; import { projectState } from './helpers/fixtures.ts'; +import { readCommandsWrapSource } from './helpers/fixtures.ts'; test('sandbox Makers tenant IDs are generated server-side and remain stable', () => { const first = projectState(); @@ -171,7 +172,7 @@ test('the master credential is exchanged, never handed to the sandbox', async () test('the tenant token is redacted out of CLI output', async () => { const [previewSource, commandSource] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), ]); assert.match(previewSource, /redactSecret\(\s*failure,\s*makers\.sandboxToken,?\s*\)/); @@ -183,7 +184,7 @@ test('direct CLI calls route the runtime credential through one resolver', async const [tokenSource, previewSource, commandSource, sessionSource, packageSource] = await Promise.all([ readFile('agents/_lib/makers/token.ts', 'utf8'), readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), readFile('agents/_lib/makers/session.ts', 'utf8'), readFile('package.json', 'utf8'), ]); diff --git a/tests/models.test.ts b/tests/models.test.ts index 0e22675..f653af7 100644 --- a/tests/models.test.ts +++ b/tests/models.test.ts @@ -241,7 +241,7 @@ test('the composer model travels on /prompt, not a session-model route', async ( assert.doesNotMatch(client, /\/session-model/); assert.match(client, /\.\.\.\(options\.model \? \{ model: options\.model \} : \{\}\)/); assert.match(live, /model: modelRef\.current/); - assert.match(prompt, /resolveRequestedModel\(context, body\?\.model\)/); + assert.match(prompt, /resolveRequestedModel\(context, body\.model\)/); assert.doesNotMatch(task, /getModelPreference/); assert.match(task, /requestedModel \? \{ model: requestedModel \}/); assert.match(task, /saveModelPreference\(context, conversationId, requestedModel\)/); diff --git a/tests/preview-path.test.ts b/tests/preview-path.test.ts index b1c0cb0..2670ec3 100644 --- a/tests/preview-path.test.ts +++ b/tests/preview-path.test.ts @@ -13,6 +13,7 @@ import { } from '../agents/_lib/makers/cli-dev.ts'; import { agentRoutesFromListing, generatedRoutesFromListing } from '../agents/_lib/project/preview.ts'; import { previewDisplayPathFromPath } from '../shared/preview-display-path.ts'; +import { readCommandsWrapSource } from './helpers/fixtures.ts'; test('preview address bar shows the application route without the gateway prefix', async () => { const screen = await readFile('app/features/workspace/workspace-screen.tsx', 'utf8'); @@ -165,7 +166,7 @@ test('sandbox preview strips the public prefix before forwarding to makers-dev', assert.match(preview, /makers-dev/); assert.match(preview, /buildMakersDevLaunchCommand/); assert.match(preview, /assertMakersProjectCompatible/); - assert.match(preview, /getHost\(PREVIEW_PUBLIC_PORT\)/); + assert.match(preview, /getHost\?\.\(PREVIEW_PUBLIC_PORT\)/); assert.match(makersDev, /edgeone makers dev/); assert.match(makersDev, /skip-env-sync/); assert.match(makersDev, /skip-ai-gateway-sync/); @@ -439,7 +440,7 @@ test('an unmounted route is restarted, while a bad reply is reported as-is', asy test('a preview publish never probes the generated agent twice in a row', async () => { const [preview, wrap] = await Promise.all([ readFile('agents/_lib/project/preview.ts', 'utf8'), - readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), + readCommandsWrapSource(), ]); // Each probe is a real model call against the generated agent, so the publish @@ -531,3 +532,40 @@ test('dynamic and catch-all routes stay out of the probe list', () => { assert.deepEqual(functionRoutes, ['/api/health']); }); + +test('the host starts dest with the workspace and keeps it watching files', async () => { + const [chat, snapshot, live, apply, assemble, resume, preview, prompt] = await Promise.all([ + readFile('agents/_lib/turn/chat.ts', 'utf8'), + readFile('agents/_lib/project/snapshot.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-workspace-snapshot.ts', 'utf8'), + readFile('agents/_lib/tools/assemble.ts', 'utf8'), + readFile('agents/_lib/session/resume.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-preview-surface.ts', 'utf8'), + readFile('agents/_lib/prompt.ts', 'utf8'), + ]); + + assert.match(chat, /const startHostPreview = async/); + assert.match(chat, /if \(state\.created\) \{\s*\n\s*void startHostPreview\('\[preview\] workspace ready:'\)/); + assert.match(chat, /onWorkspaceReady: \(\) => \{\s*\n\s*void startHostPreview\('\[preview\] after scaffold:'\)/); + assert.match(chat, /state\.created && !state\.previewUrl/); + assert.match(chat, /await persistWorkspace\(context, conversationId, state\)/); + assert.match(chat, /let previewVerified = Boolean\(state\.previewUrl\)/); + assert.match(chat, /filesWritten \? \{ restarted: true \}/); + assert.doesNotMatch( + chat, + /previewTouched && Boolean\(state\.previewUrl\)/, + 'host preview must not wait for the model to have launched dest', + ); + assert.match(assemble, /onWorkspaceReady\?\.\(\)/); + assert.match(resume, /const shouldStartPreview = !generationActive && hasFileItems/); + assert.doesNotMatch(resume, /&& hadPreview/); + assert.match(preview, /revision === undefined \|\| nextPreview\.restarted/); + assert.match(prompt, /keeps that dest server watching files/); + assert.match(snapshot, /\.\.\.\(preview\.url \? \{ preview \} : \{\}\)/); + assert.match(apply, /if \(data\.preview\?\.url\) preview\.applyResumedPreview\(data\.preview\)/); + assert.match( + live, + /if \(event\.type === 'file_changed' && event\.data\?\.paths\?\.length\) \{\s*\n\s*sawProjectActivity = true;/, + ); +}); diff --git a/tests/prompt-single-source.test.ts b/tests/prompt-single-source.test.ts index 5f76871..7c6f11d 100644 --- a/tests/prompt-single-source.test.ts +++ b/tests/prompt-single-source.test.ts @@ -8,7 +8,6 @@ import { PREVIEW_ASSET_PREFIX_ENV, PREVIEW_PATH_PREFIX, PREVIEW_PUBLIC_PORT, - PREVIEW_SERVER_PORT, } from '../agents/_lib/constants.ts'; import { MAKERS_REFERENCE_SKILL_NAMES } from '../agents/_lib/tools/makers-skills.ts'; import { projectState } from './helpers/fixtures.ts'; @@ -128,12 +127,12 @@ test('the prompt keeps the sandbox corrections the skills cannot know about', () const prompt = renderPrompt(); assert.match(prompt, /target sandbox image is expected to provide the EdgeOne CLI/); assert.match(prompt, /Run it directly with the commands tool/); - assert.match( + assert.match(prompt, /The host starts the right-hand development preview/); + assert.doesNotMatch( prompt, new RegExp(`edgeone makers dev --port ${MAKERS_DEV_PORT} --skip-env-sync --skip-ai-gateway-sync`), ); assert.match(prompt, /--area global/); - assert.match(prompt, new RegExp(`path adapter on port ${PREVIEW_SERVER_PORT}`)); assert.match( prompt, new RegExp(`sandbox\\.getHost\\(${PREVIEW_PUBLIC_PORT}\\).*${PREVIEW_PATH_PREFIX}`), @@ -218,7 +217,7 @@ test('the prompt keeps its tool contracts and workspace boundary', () => { assert.ok(prompt.includes(state.appDir), 'prompt must name the writable project directory'); assert.match(prompt, /ensure_project_scaffold as the first tool/); assert.match(prompt, /write_project_file accepts exactly one file per call/); - assert.match(prompt, /When the command result reports a successful preview URL, stop/); + assert.match(prompt, /The host starts the sandbox preview/); assert.match(prompt, /Run edgeone makers deploy only when the user explicitly asks/); assert.doesNotMatch(prompt, /publish_preview|deploy_to_makers|get_preview_link/); assert.match(prompt, /I can only help create or modify web projects/); @@ -318,9 +317,8 @@ test('the prompt closes the three ways a run can talk itself into a false finish // There is no restart primitive to reach for: the host restarts the server // when its own route probe finds an endpoint unmounted. - assert.match(prompt, /only restart mechanism/); - assert.match(prompt, /restarts the server when one is not mounted/); - assert.match(prompt, /Do not kill processes or free ports/); + assert.match(prompt, /The host restarts the preview when generated endpoints are missing/); + assert.match(prompt, /Do not kill processes, free ports, or launch a preview server yourself/); // The static site answers a POST to an unmounted route with 200 and a page, // so an HTML body is the one reply that must never read as a success. @@ -504,11 +502,11 @@ test('an install or a build is described as taking the preview down', () => { const prompt = renderPrompt(); assert.match(prompt, /A build or an install cannot run beside the preview/); - assert.match(prompt, /down until you launch it again/); + assert.match(prompt, /down until the host starts it again/); // The restart rule has to stay consistent with it: the host frees the port, // which is what makes relaunching work rather than repeat. - assert.match(prompt, /terminates the previous server itself before every launch/); - assert.match(prompt, /Do not kill processes or free ports/); + assert.match(prompt, /terminates the previous server before every launch/); + assert.match(prompt, /Do not kill processes, free ports/); }); // When the key is absent the tool is withheld from the tool list, and a rule diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index 4f2df92..c98accc 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -42,7 +42,8 @@ test('session is GET restore; turns go through /prompt and /deploy', async () => assert.doesNotMatch(client, /resetProject/); assert.match(preview, /onRequestPost/); assert.match(preview, /runProjectResumePreviewPipeline/); - assert.doesNotMatch(preview, /onRequestGet/); + assert.match(preview, /onRequestGet/); + assert.match(preview, /runPreviewStatusPipeline/); assert.match(client, /fetch\('\/preview',[\s\S]*?method: 'POST'/); await assert.rejects(access('agents/chat.ts')); await assert.rejects(access('agents/resume.ts')); @@ -143,13 +144,26 @@ test('workspace persistence uses the sandbox SDK and Blob state.json, not contex const persistence = await readFile('agents/_lib/project/persistence.ts', 'utf8'); const store = await readFile('agents/_lib/session/store.ts', 'utf8'); - assert.match(helpers, /context\.sandbox\.persist\(\{ path: state\.appDir \}\)/); - assert.match(persistence, /context\.sandbox\.restore\(\{ path: state\.appDir \}\)/); + assert.match(helpers, /requireSandbox\(context\)\.persist\?\.\(\{ path: state\.appDir \}\)/); + assert.match(persistence, /requireSandbox\(context\)\.restore\?\.\(\{ path: state\.appDir \}\)/); assert.doesNotMatch(persistence, /getLegacyProjectSnapshot/); assert.doesNotMatch(persistence, /clearLegacyProjectSnapshot/); - assert.match(store, /getStore\(\{ name: BLOB_STORE_NAME, consistency: 'strong' \}\)/); + assert.match(store, /BLOB_STORE_NAME/); + assert.match(store, /consistency: 'strong'/); assert.doesNotMatch(store, /context\.store/); assert.doesNotMatch(store, /saveProjectSnapshot/); assert.doesNotMatch(store, /listConversations/); assert.doesNotMatch(store, /deleteConversation/); }); + +test('workspace snapshot and preview status are pullable without the chat stream', async () => { + const workspace = await readFile('agents/workspace.ts', 'utf8'); + const preview = await readFile('agents/preview.ts', 'utf8'); + const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); + + assert.match(workspace, /onRequestGet/); + assert.match(workspace, /runWorkspaceSnapshotPipeline/); + assert.match(preview, /onRequestGet/); + assert.match(client, /fetch\('\/workspace'/); + assert.match(client, /fetch\(`\/file\?paths=/); +}); diff --git a/tests/sandbox-timeout.test.ts b/tests/sandbox-timeout.test.ts index 21a4a98..17459b8 100644 --- a/tests/sandbox-timeout.test.ts +++ b/tests/sandbox-timeout.test.ts @@ -21,5 +21,5 @@ test('explicit timeoutMs is preserved', () => { test('runSandboxCommand forwards resolved timeoutMs to the sandbox API', async () => { const source = await readFile('agents/_lib/project/commands.ts', 'utf8'); assert.match(source, /resolveSandboxCommandOptions\(options\)/); - assert.match(source, /context\.sandbox\.commands\.run\(command, resolved\)/); + assert.match(source, /requireSandbox\(context\)\.commands\.run\(command, resolved\)/); }); diff --git a/tests/stopped-turn.test.ts b/tests/stopped-turn.test.ts index c782979..15129a7 100644 --- a/tests/stopped-turn.test.ts +++ b/tests/stopped-turn.test.ts @@ -99,6 +99,8 @@ test('earlier turns keep their identity so memoized turns do not re-render', () test('the stopped reply is one definition, in the language of the request', () => { assert.equal(replyLocaleFor('做一个留言板'), 'zh'); assert.equal(replyLocaleFor('build a guestbook'), 'en'); + assert.equal(replyLocaleFor('build a guestbook', 'zh'), 'zh'); + assert.equal(replyLocaleFor('做一个留言板', 'en'), 'en'); // Mixed input follows the CJK it contains, which is how the request reads. assert.equal(replyLocaleFor('给 landing page 加个表单'), 'zh'); From 4a1b3a04e054d430aa5235fd855451d3d47b6620 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 14:42:16 +0800 Subject: [PATCH 09/26] feat(chat): show the rest of the SDK stream in the conversation Thinking, tool progress, compact/usage, and other system events were arriving from the query and then dropped. Fold them into the turn and render an expanded skeleton so the chat is not just a one-line narration. --- agents/_lib/session/live.ts | 63 ++++- agents/_lib/session/projection.ts | 67 +++-- agents/_lib/session/stream-projector.ts | 254 +++++++++++++++++- agents/_lib/turn/chat.ts | 6 + agents/_lib/types.ts | 2 +- app/components/agent-conversation.tsx | 57 +++- app/features/workspace/hooks/use-live-turn.ts | 4 +- app/features/workspace/workspace-screen.tsx | 5 + app/i18n.ts | 10 + app/lib/assistant-timeline.ts | 2 + app/styles/conversation.css | 46 +++- shared/protocol.ts | 21 ++ shared/timeline.ts | 68 ++++- tests/activity.test.ts | 6 +- tests/assistant-timeline.test.ts | 16 ++ tests/stream-events.test.ts | 111 ++++++++ tests/transcript.test.ts | 50 ++++ 17 files changed, 756 insertions(+), 32 deletions(-) create mode 100644 tests/stream-events.test.ts diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts index de7d63e..4089bcd 100644 --- a/agents/_lib/session/live.ts +++ b/agents/_lib/session/live.ts @@ -41,8 +41,13 @@ import { PromptQueue } from './prompt-queue.ts'; import { SCAFFOLD_TOOL_NAME, createProgressEmitter, + describeSdkMessage, extractVisibleNarrationDelta, extractVisibleTextBlock, + extractVisibleThinkingBlock, + extractVisibleThinkingDelta, + formatResultUsage, + isThinkingContentBlock, isToolUseContentBlock, parseToolInputJson, type StreamingToolUseBlock, @@ -178,12 +183,23 @@ async function pumpSession(session: LiveQuerySession) { typeof event.uuid === 'string' ? event.uuid : '', false, ); + progress.emitThinking( + extractVisibleThinkingDelta(event), + typeof event.uuid === 'string' ? event.uuid : '', + false, + ); const streamEvent = (event as { event?: Record }).event; if (streamEvent?.type === 'content_block_start') { const contentBlock = streamEvent.content_block; if (contentBlock?.type === 'text') { progress.beginTextBlock(); } + if (isThinkingContentBlock(contentBlock)) { + progress.beginThinkingBlock(); + if (contentBlock?.type === 'redacted_thinking') { + progress.emitThinking('(redacted)', typeof event.uuid === 'string' ? event.uuid : '', true); + } + } if (isToolUseContentBlock(contentBlock) && typeof streamEvent.index === 'number') { pendingToolUseBlocks.set(streamEvent.index, { id: typeof contentBlock.id === 'string' ? contentBlock.id : '', @@ -203,7 +219,18 @@ async function pumpSession(session: LiveQuerySession) { ? pendingToolUseBlocks.get(streamEvent.index) : undefined; if (pendingToolUse && delta?.type === 'input_json_delta' && typeof delta.partial_json === 'string') { + const previousLength = pendingToolUse.inputJson.length; pendingToolUse.inputJson += delta.partial_json; + const parsed = parseToolInputJson(pendingToolUse.inputJson, pendingToolUse.input); + const crossedChunk = Math.floor(previousLength / 120) !== Math.floor(pendingToolUse.inputJson.length / 120); + if (crossedChunk || parsed !== pendingToolUse.input) { + progress.emitToolUseProgress({ + id: pendingToolUse.id, + name: pendingToolUse.name, + input: parsed, + inputJson: pendingToolUse.inputJson, + }); + } } } else if (streamEvent?.type === 'content_block_stop') { const pendingToolUse = typeof streamEvent.index === 'number' @@ -215,6 +242,7 @@ async function pumpSession(session: LiveQuerySession) { id: pendingToolUse.id, name: pendingToolUse.name, input: parseToolInputJson(pendingToolUse.inputJson, pendingToolUse.input), + inputJson: pendingToolUse.inputJson, }); } } @@ -230,6 +258,11 @@ async function pumpSession(session: LiveQuerySession) { typeof event.uuid === 'string' ? event.uuid : '', true, ); + progress.emitThinking( + extractVisibleThinkingBlock(block), + typeof event.uuid === 'string' ? event.uuid : '', + true, + ); if (isToolUseContentBlock(block)) { progress.emitToolUseProgress({ id: block.id, name: block.name, input: block.input }); } @@ -260,7 +293,7 @@ async function pumpSession(session: LiveQuerySession) { toolName, ...(toolContext?.command ? { command: toolContext.command } : {}), ok: !toolFailed, - preview: truncateForStream(text, 500), + preview: truncateForStream(text, 8_000), outputSummary: summarizeToolOutput(text, session.getState().appDir, toolName), status: toolFailed ? 'failed' : 'completed', endedAt: Date.now(), @@ -294,8 +327,32 @@ async function pumpSession(session: LiveQuerySession) { continue; } + if (event.type === 'tool_progress') { + const progressEvent = event as SDKMessage & { + tool_use_id?: string; + tool_name?: string; + elapsed_time_seconds?: number; + }; + const toolUseId = typeof progressEvent.tool_use_id === 'string' ? progressEvent.tool_use_id : ''; + const toolContext = progress.toolContextById.get(toolUseId); + const elapsed = typeof progressEvent.elapsed_time_seconds === 'number' + ? Math.max(0, Math.round(progressEvent.elapsed_time_seconds)) + : 0; + progress.emitToolUseProgress({ + id: toolUseId, + name: progressEvent.tool_name || toolContext?.name || '', + outputSummary: elapsed ? `${elapsed}s` : 'running', + }); + continue; + } + if (event.type === 'result') { const resultMessage = event as SDKResultMessage; + progress.emitInfo({ + infoType: 'usage', + title: 'Usage', + content: formatResultUsage(resultMessage), + }); const modelRun = describeModelRun(session.model, resultMessage.modelUsage); if (modelRun.mismatch) { console.warn('[model]', `${modelRun.line} — the gateway served a model this turn did not request`); @@ -321,7 +378,11 @@ async function pumpSession(session: LiveQuerySession) { progress.resetTurn(); scaffoldHandled = false; fatalError = null; + continue; } + + const info = describeSdkMessage(event); + if (info) progress.emitInfo(info); } } catch (error) { const message = error instanceof Error ? error.message : String(error); diff --git a/agents/_lib/session/projection.ts b/agents/_lib/session/projection.ts index a8d5eb2..756b285 100644 --- a/agents/_lib/session/projection.ts +++ b/agents/_lib/session/projection.ts @@ -1,7 +1,9 @@ import type { AssistantActivity, PersistedActivityTurn } from '../../../shared/protocol.ts'; import { appendNarrationChunk, + appendThinkingChunk, sanitizeAssistantText, + sanitizeThinkingContent, summarizeToolInput, summarizeToolOutput, } from '../../../shared/timeline.ts'; @@ -96,25 +98,60 @@ export function projectTranscript(jsonl: string, projectDir = ''): PersistedActi continue; } + if (entry.type === 'system' && entry.subtype === 'compact_boundary' && active.turn) { + const compact = asRecord(entry.compact_metadata); + active.turn.activities.push({ + kind: 'info', + infoType: 'compact', + title: 'Compact', + content: [ + compact.trigger ? `trigger=${compact.trigger}` : '', + compact.pre_tokens != null ? `pre_tokens=${compact.pre_tokens}` : '', + compact.post_tokens != null ? `post_tokens=${compact.post_tokens}` : '', + ].filter(Boolean).join('\n'), + }); + continue; + } + if (entry.type === 'assistant' && active.turn) { const turn = active.turn; const text = textFromContent(content); - if (text) { + if (text) turn.assistant = text; + if (Array.isArray(content)) { + for (const block of content) { + const record = asRecord(block); + if (record.type === 'thinking') { + const thinking = typeof record.thinking === 'string' + ? record.thinking + : typeof record.text === 'string' ? record.text : ''; + if (thinking) { + turn.activities = appendThinkingChunk(turn.activities, sanitizeThinkingContent(thinking)); + } + continue; + } + if (record.type === 'redacted_thinking') { + turn.activities = appendThinkingChunk(turn.activities, '(redacted)'); + continue; + } + if (record.type === 'text' && typeof record.text === 'string') { + const narration = sanitizeAssistantText(record.text); + if (narration) turn.activities = appendNarrationChunk(turn.activities, narration); + continue; + } + if (record.type !== 'tool_use' && record.type !== 'mcp_tool_use') continue; + const id = typeof record.id === 'string' ? record.id : ''; + const name = typeof record.name === 'string' ? record.name : 'tool'; + turn.activities.push({ + kind: 'tool', + toolUseId: id, + name, + status: 'completed', + inputSummary: summarizeToolInput(name, record.input, projectDir), + startedAt: createdAt, + }); + } + } else if (text) { turn.activities = appendNarrationChunk(turn.activities, text); - turn.assistant = text; - } - for (const block of toolBlocks(content)) { - if (block.type !== 'tool_use' && block.type !== 'mcp_tool_use') continue; - const id = typeof block.id === 'string' ? block.id : ''; - const name = typeof block.name === 'string' ? block.name : 'tool'; - turn.activities.push({ - kind: 'tool', - toolUseId: id, - name, - status: 'completed', - inputSummary: summarizeToolInput(name, block.input, projectDir), - startedAt: createdAt, - }); } } } diff --git a/agents/_lib/session/stream-projector.ts b/agents/_lib/session/stream-projector.ts index d08eb48..0cc920b 100644 --- a/agents/_lib/session/stream-projector.ts +++ b/agents/_lib/session/stream-projector.ts @@ -1,10 +1,13 @@ -import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; +import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; import { SANDBOX_MCP_SERVER_NAME } from '../constants.ts'; import type { AgentProgressEvent } from '../types.ts'; +import type { SystemInfoType } from '../../../shared/protocol.ts'; import { resolveNarrationEmit, sanitizeNarrationText, + sanitizeThinkingContent, summarizeToolInput, + summarizeToolOutput, type NarrationEmitState, } from '../../../shared/timeline.ts'; import { @@ -35,6 +38,214 @@ export function extractVisibleNarrationDelta(event: SDKMessage) { return ''; } +export function extractVisibleThinkingDelta(event: SDKMessage) { + if (event.type !== 'stream_event') return ''; + const streamEvent = (event as { + event?: { type?: string; delta?: { type?: string; thinking?: string; text?: string } }; + }).event; + if (streamEvent?.type !== 'content_block_delta') return ''; + const delta = streamEvent.delta; + if (delta?.type !== 'thinking_delta' && delta?.type !== 'thinking') return ''; + const text = typeof delta.thinking === 'string' ? delta.thinking : delta.text; + return typeof text === 'string' ? sanitizeThinkingContent(text) : ''; +} + +export function isThinkingContentBlock(block: unknown): boolean { + const record = block && typeof block === 'object' ? block as Record : {}; + return record.type === 'thinking' || record.type === 'redacted_thinking'; +} + +export function extractVisibleThinkingBlock(block: unknown) { + const record = block && typeof block === 'object' ? block as Record : {}; + if (record.type === 'redacted_thinking') return '(redacted)'; + if (record.type !== 'thinking') return ''; + if (typeof record.thinking === 'string') return sanitizeThinkingContent(record.thinking); + if (typeof record.text === 'string') return sanitizeThinkingContent(record.text); + return ''; +} + +export type SystemInfoPayload = { + infoType: SystemInfoType; + title: string; + content: string; +}; + +function asRecord(value: unknown): Record { + return value && typeof value === 'object' ? value as Record : {}; +} + +function compactJson(value: unknown, limit = 1_500) { + const omit = new Set(['uuid', 'session_id', 'message', 'event']); + try { + const json = JSON.stringify(value, (key, nested) => (omit.has(key) ? undefined : nested)); + if (!json) return ''; + return json.length > limit ? `${json.slice(0, limit)}\n... truncated` : json; + } catch { + return ''; + } +} + +export function formatResultUsage(result: SDKResultMessage) { + const lines = [ + `subtype=${result.subtype} turns=${result.num_turns}` + + ` duration=${(result.duration_ms / 1000).toFixed(1)}s` + + ` cost=$${Number(result.total_cost_usd || 0).toFixed(4)}`, + ]; + const usage = result.usage as { input_tokens?: number; output_tokens?: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number } | undefined; + if (usage) { + lines.push( + `input=${usage.input_tokens ?? 0} output=${usage.output_tokens ?? 0}` + + ` cacheRead=${usage.cache_read_input_tokens ?? 0}` + + ` cacheWrite=${usage.cache_creation_input_tokens ?? 0}`, + ); + } + const models = Object.entries(result.modelUsage || {}); + for (const [id, modelUsage] of models) { + lines.push( + `${id} in=${modelUsage.inputTokens} out=${modelUsage.outputTokens} cost=$${Number(modelUsage.costUSD || 0).toFixed(4)}`, + ); + } + if ('errors' in result && Array.isArray(result.errors) && result.errors.length > 0) { + lines.push(result.errors.join('\n')); + } + if (result.permission_denials?.length) { + lines.push(`denied=${result.permission_denials.map((item) => item.tool_name).join(', ')}`); + } + return lines.join('\n'); +} + +export function describeSdkMessage(event: SDKMessage): SystemInfoPayload | null { + if ( + event.type === 'stream_event' + || event.type === 'assistant' + || event.type === 'user' + || event.type === 'result' + || event.type === 'tool_progress' + ) { + return null; + } + + if (event.type === 'system') { + const record = event as SDKMessage & { subtype?: string }; + const subtype = typeof record.subtype === 'string' ? record.subtype : ''; + if (subtype === 'init') { + const init = event as SDKMessage & { + model?: string; + tools?: string[]; + mcp_servers?: { name?: string; status?: string }[]; + skills?: string[]; + }; + const tools = Array.isArray(init.tools) ? init.tools : []; + const servers = Array.isArray(init.mcp_servers) ? init.mcp_servers : []; + const skills = Array.isArray(init.skills) ? init.skills : []; + return { + infoType: 'system', + title: 'Session', + content: [ + `model=${init.model || ''}`, + `tools=${tools.length}${tools.length ? ` ${tools.slice(0, 12).join(', ')}` : ''}`, + servers.length ? `mcp=${servers.map((server) => `${server.name}:${server.status}`).join(', ')}` : '', + skills.length ? `skills=${skills.slice(0, 12).join(', ')}` : '', + ].filter(Boolean).join('\n'), + }; + } + if (subtype === 'compact_boundary') { + const compact = asRecord((event as { compact_metadata?: unknown }).compact_metadata); + return { + infoType: 'compact', + title: 'Compact', + content: [ + `trigger=${compact.trigger || ''}`, + compact.pre_tokens != null ? `pre_tokens=${compact.pre_tokens}` : '', + compact.post_tokens != null ? `post_tokens=${compact.post_tokens}` : '', + compact.duration_ms != null ? `duration_ms=${compact.duration_ms}` : '', + ].filter(Boolean).join('\n'), + }; + } + if (subtype === 'status') { + const status = event as SDKMessage & { status?: string | null; compact_result?: string; compact_error?: string }; + return { + infoType: 'status', + title: 'Status', + content: [status.status, status.compact_result, status.compact_error].filter(Boolean).join('\n'), + }; + } + if (subtype === 'notification') { + const note = event as SDKMessage & { text?: string; key?: string }; + return { + infoType: 'system', + title: note.key || 'Notification', + content: note.text || '', + }; + } + if (subtype === 'permission_denied') { + const denied = event as SDKMessage & { tool_name?: string; message?: string; decision_reason?: string }; + return { + infoType: 'system', + title: `Denied ${denied.tool_name || 'tool'}`, + content: [denied.message, denied.decision_reason].filter(Boolean).join('\n'), + }; + } + if (subtype === 'api_retry') { + const retry = event as SDKMessage & { attempt?: number; max_retries?: number; retry_delay_ms?: number; error?: string }; + return { + infoType: 'status', + title: 'API retry', + content: `attempt ${retry.attempt}/${retry.max_retries} delay=${retry.retry_delay_ms}ms ${retry.error || ''}`.trim(), + }; + } + if (subtype === 'local_command_output') { + const output = event as SDKMessage & { content?: string }; + return { + infoType: 'system', + title: 'Command output', + content: typeof output.content === 'string' ? output.content.slice(0, 4_000) : '', + }; + } + if (subtype === 'task_started' || subtype === 'task_progress' || subtype === 'task_updated' || subtype === 'task_notification') { + const task = event as SDKMessage & { description?: string; summary?: string; task_id?: string; last_tool_name?: string }; + return { + infoType: 'status', + title: subtype.replace('task_', 'Task '), + content: [task.description, task.summary, task.last_tool_name, task.task_id].filter(Boolean).join('\n'), + }; + } + if (subtype === 'files_persisted') { + const persisted = event as SDKMessage & { files?: { filename?: string }[]; failed?: { filename?: string; error?: string }[] }; + const names = (persisted.files || []).map((file) => file.filename).filter(Boolean); + const failed = (persisted.failed || []).map((file) => `${file.filename}: ${file.error}`).filter(Boolean); + return { + infoType: 'system', + title: 'Files persisted', + content: [...names, ...failed].join('\n'), + }; + } + return { + infoType: 'sdk', + title: subtype || 'system', + content: compactJson(event), + }; + } + + if (event.type === 'tool_use_summary') { + const summary = event as SDKMessage & { summary?: string }; + return { infoType: 'system', title: 'Tool summary', content: summary.summary || '' }; + } + if (event.type === 'rate_limit_event') { + return { infoType: 'status', title: 'Rate limit', content: compactJson(event) }; + } + if (event.type === 'prompt_suggestion') { + const suggestion = event as SDKMessage & { suggestion?: string }; + return { infoType: 'system', title: 'Prompt suggestion', content: suggestion.suggestion || '' }; + } + + return { + infoType: 'sdk', + title: event.type, + content: compactJson(event), + }; +} + export type StreamingToolUseBlock = { id: string; name: string; @@ -97,6 +308,7 @@ export function createProgressEmitter(options: { const toolStartedAtById = new Map(); const emittedToolUseProgress = new Map(); let narrationState: NarrationEmitState = { currentTextBlock: '', emittedNarration: '' }; + let thinkingState: NarrationEmitState = { currentTextBlock: '', emittedNarration: '' }; const emitNarration = (rawText: string, uuid: string, complete = false) => { const resolved = resolveNarrationEmit(narrationState, rawText, complete); @@ -108,19 +320,48 @@ export function createProgressEmitter(options: { }); }; - const emitToolUseProgress = (toolUse: { id?: string; name?: string; input?: unknown }) => { + const emitThinking = (rawText: string, uuid: string, complete = false) => { + const resolved = resolveNarrationEmit(thinkingState, rawText, complete); + thinkingState = resolved.state; + if (!resolved.text) return; + options.onProgress?.({ + type: 'thinking_segment', + data: { uuid, text: resolved.text }, + }); + }; + + const emitInfo = (info: SystemInfoPayload) => { + if (!info.content.trim() && !info.title.trim()) return; + options.onProgress?.({ + type: 'system_info', + data: info, + }); + }; + + const emitToolUseProgress = (toolUse: { + id?: string; + name?: string; + input?: unknown; + inputJson?: string; + outputSummary?: string; + }) => { const toolName = typeof toolUse.name === 'string' ? toolUse.name : ''; const toolUseId = typeof toolUse.id === 'string' ? toolUse.id : ''; const shortToolName = shortenToolName(toolName); const command = shortToolName === 'commands' ? extractSandboxCommand(toolUse.input) : ''; const progress = typeof toolUse.name === 'string' ? inferToolProgress(toolName, toolUse.input) : {}; - const inputSummary = summarizeToolInput(toolName, toolUse.input, options.appDir); + const hasInput = toolUse.input !== undefined || Boolean(toolUse.inputJson); + const parsedSummary = hasInput ? summarizeToolInput(toolName, toolUse.input, options.appDir) : ''; + const inputSummary = parsedSummary + || (toolUse.inputJson ? summarizeToolOutput(toolUse.inputJson, options.appDir) : ''); + const outputSummary = toolUse.outputSummary || ''; const progressSignature = JSON.stringify({ name: toolName, command, phaseHint: progress.phaseHint || '', fileCount: progress.fileCount || 0, inputSummary, + outputSummary, }); if (toolUseId) { if (emittedToolUseProgress.get(toolUseId) === progressSignature) return; @@ -140,6 +381,7 @@ export function createProgressEmitter(options: { ...(command ? { command } : {}), ...progress, inputSummary, + ...(outputSummary ? { outputSummary } : {}), startedAt, }, }); @@ -149,6 +391,8 @@ export function createProgressEmitter(options: { toolContextById, toolStartedAtById, emitNarration, + emitThinking, + emitInfo, emitToolUseProgress, resetNarration() { narrationState = { currentTextBlock: '', emittedNarration: '' }; @@ -156,11 +400,15 @@ export function createProgressEmitter(options: { beginTextBlock() { narrationState = { ...narrationState, currentTextBlock: '' }; }, + beginThinkingBlock() { + thinkingState = { ...thinkingState, currentTextBlock: '' }; + }, resetTurn() { toolContextById.clear(); toolStartedAtById.clear(); emittedToolUseProgress.clear(); narrationState = { currentTextBlock: '', emittedNarration: '' }; + thinkingState = { currentTextBlock: '', emittedNarration: '' }; }, }; } diff --git a/agents/_lib/turn/chat.ts b/agents/_lib/turn/chat.ts index 0e260f7..96c094f 100644 --- a/agents/_lib/turn/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -156,6 +156,12 @@ export async function runChatPipeline( send(narration); return; } + if (event.type === 'thinking_segment' && !event.data?.text) { + return; + } + if (event.type === 'system_info' && !event.data?.content && !event.data?.title) { + return; + } recordProgress(event); send(event); }; diff --git a/agents/_lib/types.ts b/agents/_lib/types.ts index 952ca0d..32b366d 100644 --- a/agents/_lib/types.ts +++ b/agents/_lib/types.ts @@ -104,7 +104,7 @@ export type BuildResult = { export type AgentProgressEvent = Extract< ChatStreamEvent, - { type: 'tool_use' | 'tool_result' | 'text_segment' } + { type: 'tool_use' | 'tool_result' | 'text_segment' | 'thinking_segment' | 'system_info' } >; export type ClaudeMcpTool = SdkMcpToolDefinition; diff --git a/app/components/agent-conversation.tsx b/app/components/agent-conversation.tsx index 3850b73..d972d38 100644 --- a/app/components/agent-conversation.tsx +++ b/app/components/agent-conversation.tsx @@ -59,6 +59,11 @@ type ConversationCopy = { stopped: string; input: string; output: string; + thinking: string; + info: string; + usage: string; + compact: string; + status: string; placeholder: string; send: string; stop: string; @@ -85,10 +90,46 @@ export type GatewayPromptCopy = { skip: string; }; +function infoLabel( + infoType: Extract['infoType'], + copy: ConversationCopy, +) { + if (infoType === 'usage') return copy.usage; + if (infoType === 'compact') return copy.compact; + if (infoType === 'status') return copy.status; + return copy.info; +} + function actionLabel(action: ToolAction, copy: ConversationCopy) { return copy.toolActions[action]; } +function ThinkingBlock({ content, copy }: { content: string; copy: ConversationCopy }) { + return ( +
+ {copy.thinking} +
{content}
+
+ ); +} + +function InfoBlock({ + activity, + copy, +}: { + activity: Extract; + copy: ConversationCopy; +}) { + const label = infoLabel(activity.infoType, copy); + const title = activity.title && activity.title !== label ? `${label} · ${activity.title}` : label; + return ( +
+ {title} + {activity.content ?
{activity.content}
: null} +
+ ); +} + /** What the row names: a topic for reference loads, a path or command otherwise. */ function targetLabel(presentation: ToolPresentation, copy: ConversationCopy) { if (!presentation.topic) return withoutPlatformName(presentation.target || ''); @@ -137,7 +178,7 @@ function ToolActivityRow({ item, copy, previouslyReadPaths }: { copy: ConversationCopy; previouslyReadPaths: ReadonlySet; }) { - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(true); const steps = [item.activity, ...item.repeats]; const status = rowStatus(steps); const presentation = presentToolActivity(item.activity, previouslyReadPaths); @@ -320,6 +361,12 @@ const AssistantTurn = memo(function AssistantTurn({ message, copy }: { if (block.kind === 'text') { return ; } + if (block.kind === 'thinking') { + return ; + } + if (block.kind === 'info') { + return ; + } return (
@@ -418,9 +465,11 @@ export function AgentConversation({ message.id, message.status, message.content, - message.activities?.map((activity) => activity.kind === 'text' - ? activity.content - : `${activity.toolUseId}:${activity.status}:${activity.outputSummary || ''}`).join('|'), + message.activities?.map((activity) => { + if (activity.kind === 'text' || activity.kind === 'thinking') return activity.content; + if (activity.kind === 'info') return `${activity.infoType}:${activity.content}`; + return `${activity.toolUseId}:${activity.status}:${activity.inputSummary || ''}:${activity.outputSummary || ''}`; + }).join('|'), ].join(':')).join('\n'); useEffect(() => { diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index 5b739f5..9e92a3a 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -233,8 +233,8 @@ export function useLiveTurn(options: { patchAssistant({ content: text }); return; } - if (event.type === 'text_segment' || event.type === 'tool_use' || event.type === 'tool_result') { - if (event.type !== 'text_segment') sawProjectActivity = true; + if (event.type === 'text_segment' || event.type === 'thinking_segment' || event.type === 'system_info' || event.type === 'tool_use' || event.type === 'tool_result') { + if (event.type === 'tool_use' || event.type === 'tool_result') sawProjectActivity = true; foldActivityEvent(event); return; } diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index f5f425b..99cf7ad 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -215,6 +215,11 @@ export function WorkspaceScreen() { stopped: t.workspace.activityStopped, input: t.workspace.activityInput, output: t.workspace.activityOutput, + thinking: t.workspace.activityThinking, + info: t.workspace.activityInfo, + usage: t.workspace.activityUsage, + compact: t.workspace.activityCompact, + status: t.workspace.activityStatus, placeholder: t.workspace.changePlaceholder, send: t.workspace.send, stop: t.workspace.stop, diff --git a/app/i18n.ts b/app/i18n.ts index 0fdbbcc..4ec7bd9 100644 --- a/app/i18n.ts +++ b/app/i18n.ts @@ -110,6 +110,11 @@ export const TRANSLATIONS = { activityStopped: '已停止', activityInput: '输入', activityOutput: '输出', + activityThinking: '思考', + activityInfo: '系统', + activityUsage: '用量', + activityCompact: '上下文压缩', + activityStatus: '状态', toolActions: { 'Environment Preparing': '环境准备', Glob: '搜索文件', @@ -296,6 +301,11 @@ export const TRANSLATIONS = { activityStopped: 'Stopped', activityInput: 'Input', activityOutput: 'Output', + activityThinking: 'Thinking', + activityInfo: 'System', + activityUsage: 'Usage', + activityCompact: 'Context compacted', + activityStatus: 'Status', toolActions: { 'Environment Preparing': 'Environment Preparing', Glob: 'Glob', diff --git a/app/lib/assistant-timeline.ts b/app/lib/assistant-timeline.ts index f069f5a..03c5c84 100644 --- a/app/lib/assistant-timeline.ts +++ b/app/lib/assistant-timeline.ts @@ -3,7 +3,9 @@ export { lastTimelineText, trailingTimelineContent, type AssistantTimelineBlock, + type AssistantTimelineInfoBlock, type AssistantTimelineTextBlock, + type AssistantTimelineThinkingBlock, type AssistantTimelineToolBlock, type AssistantTimelineToolItem, } from '../../shared/timeline.ts'; diff --git a/app/styles/conversation.css b/app/styles/conversation.css index 235a2d9..3045bf8 100644 --- a/app/styles/conversation.css +++ b/app/styles/conversation.css @@ -312,8 +312,52 @@ text-transform: uppercase; } +.conversation-skeleton { + min-width: 0; + margin: 0; + border-left: 2px solid var(--n-200); + padding: 2px 0 6px 10px; +} + +.conversation-skeleton summary { + cursor: pointer; + color: var(--n-500); + font-size: var(--fs-2xs); + font-weight: 650; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.conversation-skeleton pre { + max-height: 320px; + margin: 6px 0 0; + overflow: auto; + white-space: pre-wrap; + color: var(--n-700); + font-family: var(--font-mono); + font-size: var(--fs-xs); + line-height: 1.55; + overflow-wrap: anywhere; +} + +.conversation-thinking { + border-left-color: color-mix(in srgb, var(--brand) 35%, var(--n-200)); +} + +.conversation-info { + border-left-color: var(--n-200); +} + +.agent-markdown + .conversation-skeleton, +.conversation-skeleton + .agent-markdown, +.conversation-tool-chain + .conversation-skeleton, +.conversation-skeleton + .conversation-tool-chain, +.conversation-skeleton + .conversation-skeleton { + margin-top: 10px; +} + .tool-activity-detail pre { - max-height: 180px; + max-height: 280px; overflow: auto; white-space: pre-wrap; color: var(--n-700); diff --git a/shared/protocol.ts b/shared/protocol.ts index 0f3cfd0..5488aba 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -8,11 +8,23 @@ export type BuildStatus = 'success' | 'failed' | 'skipped'; export type ActivityStatus = 'running' | 'completed' | 'failed' | 'stopped'; +export type SystemInfoType = 'compact' | 'usage' | 'status' | 'system' | 'sdk'; + export type AssistantActivity = | { kind: 'text'; content: string; } + | { + kind: 'thinking'; + content: string; + } + | { + kind: 'info'; + infoType: SystemInfoType; + title: string; + content: string; + } | { kind: 'tool'; toolUseId: string; @@ -201,6 +213,15 @@ export type ChatStreamEvent = }; } | { type: 'text_segment'; data?: { uuid?: string; text?: string } } + | { type: 'thinking_segment'; data?: { uuid?: string; text?: string } } + | { + type: 'system_info'; + data?: { + infoType?: SystemInfoType; + title?: string; + content?: string; + }; + } | { type: 'gateway_credentials'; data?: { diff --git a/shared/timeline.ts b/shared/timeline.ts index 8d25c3c..17e0d85 100644 --- a/shared/timeline.ts +++ b/shared/timeline.ts @@ -159,7 +159,7 @@ export function resolveNarrationEmit( }; } -const SUMMARY_LIMIT = 2_000; +const SUMMARY_LIMIT = 8_000; const SENSITIVE_KEY = /(authorization|cookie|password|passwd|secret|token|api[_-]?key|private[_-]?key|credential)/i; function truncate(value: string, limit = SUMMARY_LIMIT) { @@ -323,6 +323,24 @@ export function appendNarrationChunk( return list; } +export function appendThinkingChunk( + activities: readonly AssistantActivity[], + text: string, +): AssistantActivity[] { + const list = [...activities]; + const last = list.at(-1); + if (last?.kind !== 'thinking') { + list.push({ kind: 'thinking', content: text }); + return list; + } + const trimmed = text.trim(); + if (trimmed.length >= MIN_REPLAY_CHUNK && last.content.includes(trimmed)) { + return list; + } + list[list.length - 1] = { ...last, content: `${last.content}${text}` }; + return list; +} + function withoutUrls(text: string) { return text.replace(/https?:\/\/\S+/g, '').replace(/\s+/g, ''); } @@ -508,6 +526,18 @@ export type AssistantTimelineTextBlock = { content: string; }; +export type AssistantTimelineThinkingBlock = { + kind: 'thinking'; + index: number; + content: string; +}; + +export type AssistantTimelineInfoBlock = { + kind: 'info'; + index: number; + activity: Extract; +}; + export type AssistantTimelineToolItem = { index: number; activity: ToolActivity; @@ -519,7 +549,11 @@ export type AssistantTimelineToolBlock = { items: AssistantTimelineToolItem[]; }; -export type AssistantTimelineBlock = AssistantTimelineTextBlock | AssistantTimelineToolBlock; +export type AssistantTimelineBlock = + | AssistantTimelineTextBlock + | AssistantTimelineThinkingBlock + | AssistantTimelineInfoBlock + | AssistantTimelineToolBlock; export function normalizeTimelineText(value: string) { return value.replace(/\s+/g, ' ').trim(); @@ -541,6 +575,16 @@ export function buildAssistantTimeline(activities: AssistantActivity[]): Assista blocks.push({ kind: 'text', index, content: activity.content }); continue; } + if (activity.kind === 'thinking') { + if (!activity.content.trim()) continue; + blocks.push({ kind: 'thinking', index, content: activity.content }); + continue; + } + if (activity.kind === 'info') { + if (!activity.content.trim() && !activity.title.trim()) continue; + blocks.push({ kind: 'info', index, activity }); + continue; + } let chain = blocks.at(-1); if (chain?.kind !== 'tools') { @@ -597,6 +641,26 @@ export function applyStreamEvent( activities: appendNarrationChunk(turn.activities, event.data.text), }; } + if (event.type === 'thinking_segment' && event.data?.text) { + return { + ...turn, + activities: appendThinkingChunk(turn.activities, event.data.text), + }; + } + if (event.type === 'system_info' && (event.data?.content || event.data?.title)) { + return { + ...turn, + activities: [ + ...turn.activities, + { + kind: 'info', + infoType: event.data.infoType || 'sdk', + title: event.data.title || event.data.infoType || 'sdk', + content: event.data.content || '', + }, + ], + }; + } if (event.type === 'tool_use' && event.data?.id) { const existing = turn.activities.find( (item): item is Extract => diff --git a/tests/activity.test.ts b/tests/activity.test.ts index ab4772a..abfc3d9 100644 --- a/tests/activity.test.ts +++ b/tests/activity.test.ts @@ -45,8 +45,8 @@ test('specific Makers skill activity shows its reference and hides the document assert.match(summarizeToolOutput('Unable to load Makers skill: missing', '', name), /Unable to load/); }); -test('tool output is capped at two kilobytes', () => { - const summary = summarizeToolOutput('x'.repeat(3_000)); - assert.ok(summary.length < 2_100); +test('tool output is capped at eight kilobytes', () => { + const summary = summarizeToolOutput('x'.repeat(10_000)); + assert.ok(summary.length < 8_200); assert.match(summary, /truncated$/); }); diff --git a/tests/assistant-timeline.test.ts b/tests/assistant-timeline.test.ts index a583ee0..d65c482 100644 --- a/tests/assistant-timeline.test.ts +++ b/tests/assistant-timeline.test.ts @@ -123,3 +123,19 @@ test('trailingTimelineContent keeps leftover reply after the last streamed text' assert.equal(trailingTimelineContent('Thinking', 'Boom', 'error'), 'Boom'); assert.equal(trailingTimelineContent('Thinking', 'Thinking more', 'running'), ''); }); + +test('buildAssistantTimeline keeps thinking and system info as their own blocks', () => { + const blocks = buildAssistantTimeline([ + { kind: 'thinking', content: 'Need a form first.' }, + { kind: 'text', content: 'I will add the form.' }, + { + kind: 'info', + infoType: 'usage', + title: 'Usage', + content: 'turns=2 cost=$0.01', + }, + ]); + assert.deepEqual(blocks.map((block) => block.kind), ['thinking', 'text', 'info']); + assert.equal(blocks[0].kind === 'thinking' && blocks[0].content, 'Need a form first.'); + assert.equal(lastTimelineText(blocks)?.content, 'I will add the form.'); +}); diff --git a/tests/stream-events.test.ts b/tests/stream-events.test.ts new file mode 100644 index 0000000..d8cd6a2 --- /dev/null +++ b/tests/stream-events.test.ts @@ -0,0 +1,111 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; +import { + describeSdkMessage, + extractVisibleThinkingDelta, + formatResultUsage, +} from '../agents/_lib/session/stream-projector.ts'; +import { applyStreamEvent } from '../shared/timeline.ts'; +import type { PersistedActivityTurn } from '../shared/protocol.ts'; + +test('thinking_delta text is extracted from a stream_event', () => { + const event = { + type: 'stream_event', + event: { + type: 'content_block_delta', + delta: { type: 'thinking_delta', thinking: 'Need a form first.' }, + }, + } as unknown as SDKMessage; + assert.equal(extractVisibleThinkingDelta(event), 'Need a form first.'); +}); + +test('compact_boundary and session init become system_info payloads', () => { + const compact = describeSdkMessage({ + type: 'system', + subtype: 'compact_boundary', + compact_metadata: { trigger: 'auto', pre_tokens: 80_000, post_tokens: 12_000 }, + uuid: 'u1', + session_id: 's1', + } as unknown as SDKMessage); + assert.equal(compact?.infoType, 'compact'); + assert.match(compact?.content || '', /pre_tokens=80000/); + + const init = describeSdkMessage({ + type: 'system', + subtype: 'init', + model: 'claude-sonnet', + tools: ['Skill', 'Read'], + mcp_servers: [{ name: 'sandbox', status: 'connected' }], + skills: ['edgeone-makers-tools'], + uuid: 'u2', + session_id: 's1', + } as unknown as SDKMessage); + assert.equal(init?.infoType, 'system'); + assert.match(init?.content || '', /model=claude-sonnet/); + assert.match(init?.content || '', /mcp=sandbox:connected/); +}); + +test('result usage is a readable info block', () => { + const result = { + type: 'result', + subtype: 'success', + duration_ms: 12_400, + duration_api_ms: 11_000, + is_error: false, + num_turns: 3, + result: 'done', + stop_reason: 'end_turn', + total_cost_usd: 0.0123, + usage: { + input_tokens: 1000, + output_tokens: 200, + cache_read_input_tokens: 800, + cache_creation_input_tokens: 50, + }, + modelUsage: { + 'claude-sonnet': { + inputTokens: 1000, + outputTokens: 200, + cacheReadInputTokens: 800, + cacheCreationInputTokens: 50, + webSearchRequests: 0, + costUSD: 0.0123, + contextWindow: 200000, + maxOutputTokens: 16000, + }, + }, + permission_denials: [], + uuid: 'u3', + session_id: 's1', + } as unknown as SDKResultMessage; + const text = formatResultUsage(result); + assert.match(text, /turns=3/); + assert.match(text, /cost=\$0\.0123/); + assert.match(text, /cacheRead=800/); +}); + +test('tool_use patches keep a growing outputSummary on the same row', () => { + let turn: PersistedActivityTurn = { + id: 'turn-1', + user: 'Build', + assistant: '', + status: 'completed', + createdAt: 1, + activities: [], + }; + turn = applyStreamEvent(turn, { + type: 'tool_use', + data: { id: 't1', name: 'commands', inputSummary: 'npm install' }, + }); + turn = applyStreamEvent(turn, { + type: 'tool_use', + data: { id: 't1', name: 'commands', outputSummary: '12s' }, + }); + const tool = turn.activities[0]; + assert.equal(tool.kind, 'tool'); + if (tool.kind === 'tool') { + assert.equal(tool.inputSummary, 'npm install'); + assert.equal(tool.outputSummary, '12s'); + } +}); diff --git a/tests/transcript.test.ts b/tests/transcript.test.ts index 9d08a96..9b89782 100644 --- a/tests/transcript.test.ts +++ b/tests/transcript.test.ts @@ -146,6 +146,53 @@ test('live SSE events fold into the same turn model as a JSONL projection', () = } }); +test('JSONL thinking blocks survive projection', () => { + const jsonl = [ + JSON.stringify({ + type: 'user', + timestamp: '2026-01-01T00:00:00.000Z', + message: { role: 'user', content: 'Build a page' }, + }), + JSON.stringify({ + type: 'assistant', + timestamp: '2026-01-01T00:00:01.000Z', + message: { + role: 'assistant', + content: [ + { type: 'thinking', thinking: 'A landing page needs a form.' }, + { type: 'text', text: 'Writing files.' }, + ], + }, + }), + ].join('\n'); + + const turns = projectTranscript(jsonl); + assert.equal(turns[0].activities[0]?.kind, 'thinking'); + assert.equal(turns[0].activities[0]?.kind === 'thinking' && turns[0].activities[0].content, 'A landing page needs a form.'); + assert.equal(turns[0].activities[1]?.kind, 'text'); +}); + +test('live SSE folds thinking and usage into the turn', () => { + let turn: PersistedActivityTurn = { + id: 'turn-1', + user: 'Build a page', + assistant: '', + status: 'completed', + createdAt: 1, + activities: [], + }; + turn = applyStreamEvent(turn, { type: 'thinking_segment', data: { text: 'Need a form.' } }); + turn = applyStreamEvent(turn, { type: 'text_segment', data: { text: 'Writing files.' } }); + turn = applyStreamEvent(turn, { + type: 'system_info', + data: { infoType: 'usage', title: 'Usage', content: 'turns=1 cost=$0.01' }, + }); + assert.equal(turn.activities[0]?.kind, 'thinking'); + assert.equal(turn.activities[1]?.kind, 'text'); + assert.equal(turn.activities[2]?.kind, 'info'); + assert.equal(turn.activities[2]?.kind === 'info' && turn.activities[2].infoType, 'usage'); +}); + test('GET /transcript streams the JSONL file unaltered', async () => { const directory = await mkdtemp(path.join(tmpdir(), 'transcript-read-')); const source = path.join(directory, 'session.jsonl'); @@ -246,4 +293,7 @@ test('compaction re-uploads the local transcript file', async () => { assert.match(live, /patchConversationRecord/); assert.match(live, /transcript_path/); assert.match(live, /resolveClaudeTranscriptPath/); + assert.match(live, /extractVisibleThinkingDelta/); + assert.match(live, /describeSdkMessage/); + assert.match(live, /formatResultUsage/); }); From 6235de364145be196c462ef52404a3b44049ba6a Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 15:13:24 +0800 Subject: [PATCH 10/26] feat(chat): dump each tool call instead of friendly labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversation was still mapping skills and globs through presentToolActivity, so a load_makers_skill became "查阅文档 / AI 接口" and a Glob became "搜索文件 **/*". Render one open skeleton per call with the raw name, ids, input, and output. --- agents/_lib/session/projection.ts | 12 ++ agents/_lib/session/resume.ts | 4 +- app/components/agent-conversation.tsx | 224 +++++--------------- app/features/workspace/workspace-screen.tsx | 11 +- app/lib/assistant-timeline.ts | 1 - app/styles/conversation.css | 202 +----------------- shared/protocol.ts | 7 +- shared/timeline.ts | 73 ++----- tests/activity.test.ts | 30 ++- tests/app-shell.test.ts | 2 +- tests/assistant-timeline.test.ts | 49 ++--- tests/stream-events.test.ts | 30 +++ tests/tool-activity.test.ts | 5 +- 13 files changed, 171 insertions(+), 479 deletions(-) diff --git a/agents/_lib/session/projection.ts b/agents/_lib/session/projection.ts index 756b285..55617af 100644 --- a/agents/_lib/session/projection.ts +++ b/agents/_lib/session/projection.ts @@ -27,6 +27,16 @@ function textFromContent(content: unknown): string { ); } +function commandFromInput(input: unknown) { + const record = asRecord(input); + const command = typeof record.command === 'string' + ? record.command + : typeof record.cmd === 'string' + ? record.cmd + : ''; + return command.trim(); +} + function toolBlocks(content: unknown): JsonRecord[] { if (!Array.isArray(content)) return []; return content.filter((block) => { @@ -141,11 +151,13 @@ export function projectTranscript(jsonl: string, projectDir = ''): PersistedActi if (record.type !== 'tool_use' && record.type !== 'mcp_tool_use') continue; const id = typeof record.id === 'string' ? record.id : ''; const name = typeof record.name === 'string' ? record.name : 'tool'; + const command = commandFromInput(record.input); turn.activities.push({ kind: 'tool', toolUseId: id, name, status: 'completed', + ...(command ? { command } : {}), inputSummary: summarizeToolInput(name, record.input, projectDir), startedAt: createdAt, }); diff --git a/agents/_lib/session/resume.ts b/agents/_lib/session/resume.ts index 3e0182f..06b3c4c 100644 --- a/agents/_lib/session/resume.ts +++ b/agents/_lib/session/resume.ts @@ -39,7 +39,7 @@ function toolNameImpliesProject(name: string) { function activityIsMakersCli(activity: PersistedActivity) { if (activity.kind !== 'tool' || !activity.name.includes('commands')) return false; - const command = activity.inputSummary || ''; + const command = activity.command || activity.inputSummary || ''; return isMakersDevCommand(command) || isMakersDeployCommand(command); } @@ -58,7 +58,7 @@ function activityHistoryImpliesPreview(activityHistory: PersistedActivityTurn[]) activity.kind === 'tool' && activity.status === 'completed' && activity.name.includes('commands') - && isMakersDevCommand(activity.inputSummary || ''), + && isMakersDevCommand(activity.command || activity.inputSummary || ''), ), ); } diff --git a/app/components/agent-conversation.tsx b/app/components/agent-conversation.tsx index d972d38..47bc015 100644 --- a/app/components/agent-conversation.tsx +++ b/app/components/agent-conversation.tsx @@ -2,24 +2,11 @@ import { FormEvent, ReactNode, memo, useEffect, useMemo, useRef, useState } from 'react'; import { - AppWindow, ArrowUp, - BookOpen, Check, - ChevronRight, CircleAlert, Copy, - FilePenLine, - FilePlus2, - FolderPlus, - FolderSearch, - Monitor, - Rocket, - Search, Square, - SquareTerminal, - Trash2, - X, } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; @@ -27,21 +14,10 @@ import { buildAssistantTimeline, lastTimelineText, trailingTimelineContent, - type AssistantTimelineToolItem, } from '../lib/assistant-timeline'; -import { - presentToolActivity, - toolActionTier, - type ReferenceTopic, - type ToolAction, - type ToolPresentation, -} from '../lib/tool-activity'; import { withoutPlatformName } from '../../shared/platform-name'; import { ModelPicker } from './model-picker'; -import type { - ActivityStatus, - AssistantActivity, -} from '../../shared/protocol'; +import type { AssistantActivity } from '../../shared/protocol'; import type { ModelOption } from '../../shared/models'; export type ConversationMessage = { @@ -57,8 +33,6 @@ type ConversationCopy = { completed: string; failed: string; stopped: string; - input: string; - output: string; thinking: string; info: string; usage: string; @@ -68,9 +42,6 @@ type ConversationCopy = { send: string; stop: string; modelLabel: string; - toolActions: Record; - referenceTopics: Record; - referenceDetail: string; copyLink: string; linkCopied: string; }; @@ -100,8 +71,52 @@ function infoLabel( return copy.info; } -function actionLabel(action: ToolAction, copy: ConversationCopy) { - return copy.toolActions[action]; +function statusLabel(status: Extract['status'], copy: ConversationCopy) { + if (status === 'running') return copy.running; + if (status === 'failed') return copy.failed; + if (status === 'stopped') return copy.stopped; + return copy.completed; +} + +function formatTimestamp(value?: number) { + if (!value) return ''; + try { + return new Date(value).toISOString(); + } catch { + return String(value); + } +} + +function maybeJson(value?: string) { + if (!value) return undefined; + const trimmed = value.trim(); + if (trimmed.startsWith('{') || trimmed.startsWith('[')) { + try { + return JSON.parse(trimmed); + } catch { + return value; + } + } + return value; +} + +function formatToolDump(activity: Extract, copy: ConversationCopy) { + return JSON.stringify({ + name: activity.name, + id: activity.toolUseId, + status: activity.status, + statusLabel: statusLabel(activity.status, copy), + command: activity.command || undefined, + phaseHint: activity.phaseHint || undefined, + fileCount: activity.fileCount, + startedAt: formatTimestamp(activity.startedAt) || undefined, + endedAt: formatTimestamp(activity.endedAt) || undefined, + durationMs: activity.startedAt && activity.endedAt + ? activity.endedAt - activity.startedAt + : undefined, + input: maybeJson(activity.inputSummary), + output: maybeJson(activity.outputSummary), + }, null, 2); } function ThinkingBlock({ content, copy }: { content: string; copy: ConversationCopy }) { @@ -130,112 +145,18 @@ function InfoBlock({ ); } -/** What the row names: a topic for reference loads, a path or command otherwise. */ -function targetLabel(presentation: ToolPresentation, copy: ConversationCopy) { - if (!presentation.topic) return withoutPlatformName(presentation.target || ''); - const topic = copy.referenceTopics[presentation.topic]; - return presentation.detailed ? `${topic} · ${copy.referenceDetail}` : topic; -} - -function ActionIcon({ action }: { action: ToolAction }) { - const props = { className: 'tool-activity-action-icon', 'aria-hidden': true } as const; - if (action === 'Environment Preparing') return ; - if (action === 'Glob') return ; - if (action === 'Read file') return ; - if (action === 'Write file') return ; - if (action === 'Edit file') return ; - if (action === 'Create folder') return ; - if (action === 'Delete file') return ; - if (action === 'Create preview') return ; - if (action === 'Deploy project') return ; - if (action === 'Load skill') return ; - return ; -} - -function ActivityIcon({ status, action }: { status: ActivityStatus; action: ToolAction }) { - if (status === 'running') { - return ; - } - if (status === 'failed') return ; - if (status === 'stopped') return ; - return ; -} - -/** - * One status for a row that stands for several calls: a run still going says so - * until its last step lands, and a step that broke outranks the ones that did - * not, because the row is the only place it can be reported. - */ -function rowStatus(steps: readonly Extract[]): ActivityStatus { - for (const status of ['running', 'failed', 'stopped'] as const) { - if (steps.some((step) => step.status === status)) return status; - } - return 'completed'; -} - -function ToolActivityRow({ item, copy, previouslyReadPaths }: { - item: AssistantTimelineToolItem; +function ToolBlock({ + activity, + copy, +}: { + activity: Extract; copy: ConversationCopy; - previouslyReadPaths: ReadonlySet; }) { - const [open, setOpen] = useState(true); - const steps = [item.activity, ...item.repeats]; - const status = rowStatus(steps); - const presentation = presentToolActivity(item.activity, previouslyReadPaths); - const target = targetLabel(presentation, copy); - const label = status === 'running' - ? copy.running - : status === 'completed' - ? copy.completed - : status === 'failed' - ? copy.failed - : copy.stopped; - // Every folded call keeps its own input and output, so the panel reads as one - // section per call and the row hides a line rather than the work behind it. - const details = steps.filter((step) => step.inputSummary || step.outputSummary); - return ( -
- - {open && ( -
- {details.length === 0 ? ( -

{label}

- ) : details.map((step, position) => ( -
- {step.inputSummary && ( -
- {copy.input} -
{withoutPlatformName(step.inputSummary)}
-
- )} - {step.outputSummary && ( -
- {copy.output} -
{withoutPlatformName(step.outputSummary)}
-
- )} -
- ))} -
- )} -
+
+ {activity.name} · {statusLabel(activity.status, copy)} +
{formatToolDump(activity, copy)}
+
); } @@ -323,31 +244,12 @@ function Markdown({ content, copy }: { content: string; copy: ConversationCopy } ); } -// Memoized because the streaming turn is the only one that changes: the chat -// reducer hands back every other message unchanged, so without this each token -// rebuilt the timeline and the read-path scan for the whole conversation. const AssistantTurn = memo(function AssistantTurn({ message, copy }: { message: ConversationMessage; copy: ConversationCopy; }) { const activities = message.activities ?? []; const blocks = useMemo(() => buildAssistantTimeline(activities), [activities]); - // What each tool row may treat as already-read, so a repeated Read of the same - // file can render as a revisit. Built as a running prefix, hence one snapshot - // per activity rather than one shared set. - const previouslyReadPaths = useMemo(() => { - const readPaths = new Set(); - return activities.map((activity) => { - const snapshot = new Set(readPaths); - if (activity.kind === 'tool') { - const presentation = presentToolActivity(activity); - if (presentation.action === 'Read file' && presentation.target) { - readPaths.add(presentation.target); - } - } - return snapshot; - }); - }, [activities]); const lastText = lastTimelineText(blocks); const trailing = trailingTimelineContent(lastText?.content, message.content, message.status); const hasRunningTool = activities.some( @@ -367,19 +269,7 @@ const AssistantTurn = memo(function AssistantTurn({ message, copy }: { if (block.kind === 'info') { return ; } - - return ( -
- {block.items.map((item) => ( - - ))} -
- ); + return ; })} {trailing && ( message.status === 'error' ? ( diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 99cf7ad..02dd308 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -213,8 +213,6 @@ export function WorkspaceScreen() { completed: t.workspace.activityCompleted, failed: t.workspace.activityFailed, stopped: t.workspace.activityStopped, - input: t.workspace.activityInput, - output: t.workspace.activityOutput, thinking: t.workspace.activityThinking, info: t.workspace.activityInfo, usage: t.workspace.activityUsage, @@ -224,9 +222,6 @@ export function WorkspaceScreen() { send: t.workspace.send, stop: t.workspace.stop, modelLabel: t.workspace.modelLabel, - toolActions: t.workspace.toolActions, - referenceTopics: t.workspace.referenceTopics, - referenceDetail: t.workspace.referenceDetail, copyLink: t.workspace.copyLink, linkCopied: t.workspace.linkCopied, }), [t]); @@ -343,7 +338,11 @@ export function WorkspaceScreen() { className="size-8 animate-spin rounded-full border-2 border-primary/30 border-t-primary" aria-hidden="true" /> -

{t.workspace.resuming}

+

+ {resume.prepStage + ? t.workspace.prepStages[resume.prepStage] + : t.workspace.resuming} +

); } diff --git a/app/lib/assistant-timeline.ts b/app/lib/assistant-timeline.ts index 03c5c84..698fd31 100644 --- a/app/lib/assistant-timeline.ts +++ b/app/lib/assistant-timeline.ts @@ -7,5 +7,4 @@ export { type AssistantTimelineTextBlock, type AssistantTimelineThinkingBlock, type AssistantTimelineToolBlock, - type AssistantTimelineToolItem, } from '../../shared/timeline.ts'; diff --git a/app/styles/conversation.css b/app/styles/conversation.css index 3045bf8..db9e9dc 100644 --- a/app/styles/conversation.css +++ b/app/styles/conversation.css @@ -62,22 +62,15 @@ line-height: 1.55; } -.agent-markdown + .agent-markdown, -.conversation-tool-chain + .agent-markdown, -.agent-markdown + .conversation-tool-chain, -.tool-activity-row + .agent-markdown, -.agent-markdown + .tool-activity-row { +.agent-markdown + .agent-markdown { margin-top: 10px; } -.conversation-assistant-turn .agent-markdown + .conversation-tool-chain, -.conversation-assistant-turn .conversation-tool-chain + .agent-markdown, -.conversation-assistant-turn .conversation-tool-chain + .assistant-error-message, .conversation-assistant-turn .agent-markdown + .assistant-error-message { margin-top: 12px; } -.conversation-tool-chain + .agent-waiting, +.conversation-skeleton + .agent-waiting, .agent-markdown + .agent-waiting, .assistant-error-message + .agent-waiting { margin-top: 8px; @@ -156,163 +149,8 @@ height: 12px; } -/* ---- Tool activity ------------------------------------------------------ - Three tiers of weight: plain file operations stay quiet, Makers platform - operations carry the accent, and the activity chain never competes with the - assistant's prose. */ - -.conversation-tool-chain { - display: flex; - min-width: 0; - flex-direction: column; - gap: 1px; -} - -.tool-activity-row { - min-width: 0; -} - -.tool-activity-trigger { - display: flex; - position: relative; - width: 100%; - min-width: 0; - align-items: center; - gap: 8px; - border-radius: var(--r-sm); - padding: 3px 5px 3px 2px; - color: var(--n-600); - font-size: var(--fs-sm); - line-height: 20px; - text-align: left; - transition: - color var(--t-fast) ease, - background var(--t-fast) ease; -} - -.tool-activity-trigger:hover { - background: var(--n-50); - color: var(--foreground); -} - -.tool-activity-status { - display: grid; - width: 14px; - height: 20px; - flex: 0 0 14px; - place-items: center; - color: var(--n-500); -} - -.tool-activity-status svg { - width: 11px; - height: 11px; - stroke-width: 1.8; -} - -.tool-activity-action-icon, -.tool-activity-status .tool-activity-action-icon { - width: 14px; - height: 14px; - color: var(--n-500); - stroke-width: 1.7; -} - -/* Platform work (skills, compatibility lint, dev, deploy) is the part of the - run that only exists on Makers, so it is the only tier that gets colour — - carried by the glyph alone. A tile behind it made a 14px icon in a list of - plain ones look like a button, and the rows are not clickable one by one. */ -.tool-activity-trigger[data-tier='platform'] .tool-activity-status { - color: var(--brand); -} - -.tool-activity-trigger[data-tier='platform'] .tool-activity-action-icon { - color: var(--brand); -} - -.tool-activity-spinner { - width: 12px; - height: 12px; - border: 1.5px solid color-mix(in srgb, var(--brand) 20%, transparent); - border-top-color: var(--brand); - border-radius: 50%; - animation: spin 700ms linear infinite; -} - -.tool-activity-copy { - display: flex; - min-width: 0; - flex: 1; - align-items: baseline; - gap: 6px; -} - -.tool-activity-copy > span { - flex: 0 0 auto; -} - -.tool-activity-copy strong { - min-width: 0; - overflow: hidden; - color: var(--n-900); - font-family: var(--font-mono); - font-size: var(--fs-xs); - font-weight: 600; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tool-activity-chevron { - width: 11px; - height: 11px; - flex: 0 0 11px; - color: var(--n-400); - transition: transform var(--t-base) ease; -} - -.tool-activity-running { - color: var(--n-700); -} - -.tool-activity-failed, -.tool-activity-failed .tool-activity-status { - color: var(--danger); -} - -.tool-activity-stopped { - color: var(--n-500); -} - -.tool-activity-detail { - display: grid; - gap: 9px; - margin: 3px 0 7px; - border-radius: var(--r-sm); - background: var(--n-25); - padding: 7px 10px; - animation: trace-list-in var(--t-base) ease-out both; -} - -/* A row can stand for several calls, so the panel is sectioned: one block per - call, ruled off from the next. */ -.tool-activity-step { - display: grid; - gap: 9px; -} - -.tool-activity-step + .tool-activity-step { - border-top: 1px solid var(--n-100); - padding-top: 9px; -} - -.tool-activity-detail span { - color: var(--n-500); - font-size: var(--fs-2xs); - font-weight: 650; - text-transform: uppercase; -} - .conversation-skeleton { + position: relative; min-width: 0; margin: 0; border-left: 2px solid var(--n-200); @@ -344,47 +182,17 @@ border-left-color: color-mix(in srgb, var(--brand) 35%, var(--n-200)); } -.conversation-info { +.conversation-info, +.conversation-tool { border-left-color: var(--n-200); } .agent-markdown + .conversation-skeleton, .conversation-skeleton + .agent-markdown, -.conversation-tool-chain + .conversation-skeleton, -.conversation-skeleton + .conversation-tool-chain, .conversation-skeleton + .conversation-skeleton { margin-top: 10px; } -.tool-activity-detail pre { - max-height: 280px; - overflow: auto; - white-space: pre-wrap; - color: var(--n-700); - font-family: var(--font-mono); - font-size: var(--fs-xs); - line-height: 1.55; - overflow-wrap: anywhere; -} - -.tool-activity-empty { - margin: 0; - color: var(--n-500); - font-size: var(--fs-xs); - line-height: 1.45; -} - -@keyframes trace-list-in { - from { - opacity: 0; - transform: translateY(-3px); - } - to { - opacity: 1; - transform: none; - } -} - .assistant-error-message { display: flex; align-items: flex-start; diff --git a/shared/protocol.ts b/shared/protocol.ts index 5488aba..4e4439a 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -8,6 +8,8 @@ export type BuildStatus = 'success' | 'failed' | 'skipped'; export type ActivityStatus = 'running' | 'completed' | 'failed' | 'stopped'; +export type ProgressPhase = 'scaffold' | 'modify' | 'code' | 'install' | 'preview' | 'link'; + export type SystemInfoType = 'compact' | 'usage' | 'status' | 'system' | 'sdk'; export type AssistantActivity = @@ -30,6 +32,9 @@ export type AssistantActivity = toolUseId: string; name: string; status: ActivityStatus; + command?: string; + phaseHint?: ProgressPhase; + fileCount?: number; inputSummary?: string; outputSummary?: string; startedAt?: number; @@ -155,8 +160,6 @@ export type ChatResponse = { stopped?: boolean; }; -type ProgressPhase = 'scaffold' | 'modify' | 'code' | 'install' | 'preview' | 'link'; - export type ChatStreamEvent = | { type: 'task_started'; diff --git a/shared/timeline.ts b/shared/timeline.ts index 17e0d85..ef72d0a 100644 --- a/shared/timeline.ts +++ b/shared/timeline.ts @@ -199,11 +199,6 @@ export function summarizeToolInput(name: string, input: unknown, projectDir = '' const record = input && typeof input === 'object' ? input as Record : {}; const shortName = name.replace(/^mcp__[^_]+__/, ''); - if (shortName === 'Skill' || shortName === 'load_makers_skill') { - const skill = typeof record.skill === 'string' ? record.skill : ''; - const ref = typeof record.ref === 'string' ? record.ref.trim() : ''; - return truncate(ref ? JSON.stringify({ skill, ref }) : skill, 200); - } if (shortName === 'write_project_file' || shortName === 'files_write' || shortName === 'write_files') { if (typeof record.path !== 'string' && typeof record.content !== 'string') return ''; const path = typeof record.path === 'string' ? record.path : ''; @@ -218,31 +213,11 @@ export function summarizeToolInput(name: string, input: unknown, projectDir = '' : ''; return truncate(redactInlineSecrets(projectDir ? command.split(projectDir).join('') : command)); } - if ( - shortName === 'files_make_dir' - || shortName === 'files_remove' - || shortName === 'files_exists' - || shortName === 'files_read' - || shortName === 'files_list' - ) { - const path = typeof record.path === 'string' - ? record.path - : typeof record.file_path === 'string' - ? record.file_path - : ''; - return path ? truncate(projectDir ? path.split(projectDir).join('') : path) : ''; - } return truncate(JSON.stringify(safeValue(record, projectDir), null, 2)); } -export function summarizeToolOutput(value: string, projectDir = '', name = '') { - if (name.replace(/^mcp__[^_]+__/, '') === 'Skill' && /^launching skill:/i.test(value.trim())) { - return ''; - } - if (name.replace(/^mcp__[^_]+__/, '') === 'load_makers_skill' && /^---\s*\nname:/i.test(value.trim())) { - return ''; - } +export function summarizeToolOutput(value: string, projectDir = '', _name = '') { const withoutProjectPath = projectDir ? value.split(projectDir).join('') : value; return truncate(redactInlineSecrets(withoutProjectPath)); } @@ -415,8 +390,8 @@ export function presentToolActivity( const structuredTarget = readStructuredTarget(activity.inputSummary); const target = structuredTarget || cleanSummaryTarget(activity.inputSummary); - if (name.includes('ensure project scaffold') || name.includes('environment')) { - return { action: 'Environment Preparing' }; + if (name.includes('environment')) { + return { action: 'Environment Preparing', target }; } if (name === 'skill' || name === 'load makers skill') { const request = readReferenceRequest(activity.inputSummary); @@ -538,15 +513,10 @@ export type AssistantTimelineInfoBlock = { activity: Extract; }; -export type AssistantTimelineToolItem = { +export type AssistantTimelineToolBlock = { + kind: 'tool'; index: number; activity: ToolActivity; - repeats: ToolActivity[]; -}; - -export type AssistantTimelineToolBlock = { - kind: 'tools'; - items: AssistantTimelineToolItem[]; }; export type AssistantTimelineBlock = @@ -559,14 +529,8 @@ export function normalizeTimelineText(value: string) { return value.replace(/\s+/g, ' ').trim(); } -function referenceRowKey(activity: ToolActivity) { - const { topic, detailed } = presentToolActivity(activity); - return topic ? `${topic}:${detailed ? 'detail' : 'overview'}` : ''; -} - export function buildAssistantTimeline(activities: AssistantActivity[]): AssistantTimelineBlock[] { const blocks: AssistantTimelineBlock[] = []; - const referenceRows = new Map(); for (let index = 0; index < activities.length; index += 1) { const activity = activities[index]; @@ -585,25 +549,7 @@ export function buildAssistantTimeline(activities: AssistantActivity[]): Assista blocks.push({ kind: 'info', index, activity }); continue; } - - let chain = blocks.at(-1); - if (chain?.kind !== 'tools') { - const opened: AssistantTimelineToolBlock = { kind: 'tools', items: [] }; - blocks.push(opened); - referenceRows.clear(); - chain = opened; - } - - const key = referenceRowKey(activity); - const open = key ? referenceRows.get(key) : undefined; - if (open) { - open.repeats.push(activity); - continue; - } - - const item: AssistantTimelineToolItem = { index, activity, repeats: [] }; - if (key) referenceRows.set(key, item); - chain.items.push(item); + blocks.push({ kind: 'tool', index, activity }); } return blocks; } @@ -668,6 +614,9 @@ export function applyStreamEvent( ); if (existing) { existing.name = event.data.name || existing.name; + existing.command = event.data.command || existing.command; + existing.phaseHint = event.data.phaseHint || existing.phaseHint; + existing.fileCount = event.data.fileCount ?? existing.fileCount; existing.inputSummary = event.data.inputSummary || existing.inputSummary; existing.outputSummary = event.data.outputSummary || existing.outputSummary; return { ...turn, activities: [...turn.activities] }; @@ -681,6 +630,9 @@ export function applyStreamEvent( toolUseId: event.data.id, name: event.data.name || 'tool', status: 'running', + command: event.data.command, + phaseHint: event.data.phaseHint, + fileCount: event.data.fileCount, inputSummary: event.data.inputSummary, outputSummary: event.data.outputSummary, startedAt: event.data.startedAt || Date.now(), @@ -696,6 +648,7 @@ export function applyStreamEvent( ? { ...activity, status: event.data.status || (event.data.ok ? 'completed' : 'failed'), + command: event.data.command || activity.command, outputSummary: event.data.outputSummary || event.data.preview || activity.outputSummary, endedAt: event.data.endedAt || Date.now(), } diff --git a/tests/activity.test.ts b/tests/activity.test.ts index abfc3d9..677ce5a 100644 --- a/tests/activity.test.ts +++ b/tests/activity.test.ts @@ -28,23 +28,37 @@ test('a streamed single-file call stays blank until its path arrives', () => { assert.equal(summarizeToolInput('write_project_file', {}), ''); }); -test('directory tools summarize as a path, not JSON', () => { - assert.equal(summarizeToolInput('mcp__edgeone-sandbox__files_make_dir', { path: 'src/lib' }), 'src/lib'); +test('directory tools keep the path in the dumped input', () => { + assert.match(summarizeToolInput('mcp__edgeone-sandbox__files_make_dir', { path: 'src/lib' }), /src\/lib/); }); -test('Skill activity shows the skill name and drops the echoed launch line', () => { - assert.equal(summarizeToolInput('Skill', { skill: 'edgeone-makers-tools' }), 'edgeone-makers-tools'); - assert.equal(summarizeToolOutput('Launching skill: edgeone-makers-tools', '', 'Skill'), ''); +test('Skill activity keeps the skill name and the tool output', () => { + assert.match(summarizeToolInput('Skill', { skill: 'edgeone-makers-tools' }), /edgeone-makers-tools/); + assert.match(summarizeToolOutput('Launching skill: edgeone-makers-tools', '', 'Skill'), /Launching skill/); assert.match(summarizeToolOutput('Skill not found: nope', '', 'Skill'), /Skill not found/); }); -test('specific Makers skill activity shows its reference and hides the document body', () => { +test('specific Makers skill activity keeps the document body', () => { const name = 'mcp__edgeone-sandbox__load_makers_skill'; - assert.equal(summarizeToolInput(name, { skill: 'makers-agents' }), 'makers-agents'); - assert.equal(summarizeToolOutput('---\nname: edgeone-makers-agents\n---\nGuide', '', name), ''); + assert.match(summarizeToolInput(name, { skill: 'makers-agents' }), /makers-agents/); + assert.match(summarizeToolOutput('---\nname: edgeone-makers-agents\n---\nGuide', '', name), /makers-agents/); assert.match(summarizeToolOutput('Unable to load Makers skill: missing', '', name), /Unable to load/); }); +test('glob and skill inputs keep every field instead of a short label', () => { + const glob = summarizeToolInput('Glob', { pattern: '**/*', path: 'src' }); + assert.match(glob, /pattern/); + assert.match(glob, /\*\*\/\*/); + assert.match(glob, /"path": "src"/); + + const skill = summarizeToolInput('mcp__edgeone-sandbox__load_makers_skill', { + skill: 'makers-agents', + ref: 'platform/sse-protocol.md', + }); + assert.match(skill, /makers-agents/); + assert.match(skill, /platform\/sse-protocol\.md/); +}); + test('tool output is capped at eight kilobytes', () => { const summary = summarizeToolOutput('x'.repeat(10_000)); assert.ok(summary.length < 8_200); diff --git a/tests/app-shell.test.ts b/tests/app-shell.test.ts index a8fc593..4208e20 100644 --- a/tests/app-shell.test.ts +++ b/tests/app-shell.test.ts @@ -37,7 +37,7 @@ test('scroll containers are containing blocks, so sr-only labels cannot stretch const css = await stylesheet(); assert.match(css, /\.conversation-scroll \{[^}]*position: relative;/); - assert.match(css, /\.tool-activity-trigger \{[^}]*position: relative;/); + assert.match(css, /\.conversation-skeleton \{[^}]*position: relative;/); }); test('the stacked workspace fits one viewport instead of scrolling past its panes', async () => { diff --git a/tests/assistant-timeline.test.ts b/tests/assistant-timeline.test.ts index d65c482..b1966d3 100644 --- a/tests/assistant-timeline.test.ts +++ b/tests/assistant-timeline.test.ts @@ -15,7 +15,7 @@ const writeFile = (id: string, path: string): AssistantActivity => ({ inputSummary: path, }); -test('buildAssistantTimeline interleaves text with consecutive tool chains', () => { +test('buildAssistantTimeline interleaves text with consecutive tool calls', () => { const blocks = buildAssistantTimeline([ { kind: 'text', content: 'Starting the landing page.' }, writeFile('t1', 'package.json'), @@ -24,18 +24,14 @@ test('buildAssistantTimeline interleaves text with consecutive tool chains', () writeFile('t3', 'styles.css'), ]); - assert.equal(blocks.length, 4); + assert.equal(blocks.length, 5); assert.equal(blocks[0].kind, 'text'); assert.equal(blocks[0].kind === 'text' && blocks[0].content, 'Starting the landing page.'); - assert.equal(blocks[1].kind, 'tools'); - assert.deepEqual( - blocks[1].kind === 'tools' ? blocks[1].items.map((item) => item.activity.toolUseId) : [], - ['t1', 't2'], - ); - assert.equal(blocks[2].kind, 'text'); - assert.equal(blocks[2].kind === 'text' && blocks[2].content, 'Preview is ready.'); - assert.equal(blocks[3].kind, 'tools'); - assert.equal(blocks[3].kind === 'tools' && blocks[3].items[0]?.activity.toolUseId, 't3'); + assert.equal(blocks[1].kind, 'tool'); + assert.equal(blocks[1].kind === 'tool' && blocks[1].activity.toolUseId, 't1'); + assert.equal(blocks[2].kind === 'tool' && blocks[2].activity.toolUseId, 't2'); + assert.equal(blocks[3].kind, 'text'); + assert.equal(blocks[4].kind === 'tool' && blocks[4].activity.toolUseId, 't3'); }); test('buildAssistantTimeline skips empty text and keeps tool order', () => { @@ -45,16 +41,12 @@ test('buildAssistantTimeline skips empty text and keeps tool order', () => { { kind: 'text', content: 'Done.' }, ]); assert.equal(blocks.length, 2); - assert.equal(blocks[0].kind, 'tools'); + assert.equal(blocks[0].kind, 'tool'); assert.equal(blocks[1].kind, 'text'); assert.equal(lastTimelineText(blocks)?.content, 'Done.'); }); -// Reading up on a subject takes an overview and then several documents beneath -// it, and every one of those calls prints the same label — the run that -// prompted this showed "AI endpoints, in depth" three times in a row, which -// reads as a stuck timeline rather than as three steps of progress. -test('reference loads of one topic collapse into a single row', () => { +test('reference loads stay one block per call', () => { const load = (id: string, skill: string, ref?: string): AssistantActivity => ({ kind: 'tool', toolUseId: id, @@ -63,7 +55,7 @@ test('reference loads of one topic collapse into a single row', () => { inputSummary: ref ? JSON.stringify({ skill, ref }) : skill, }); - const [chain] = buildAssistantTimeline([ + const blocks = buildAssistantTimeline([ load('s1', 'makers-agents'), load('s2', 'makers-agents', 'platform/node-entry.md'), load('s3', 'makers-agents', 'platform/sse-protocol.md'), @@ -71,28 +63,21 @@ test('reference loads of one topic collapse into a single row', () => { writeFile('t1', 'app/page.tsx'), ]); - assert.equal(chain.kind, 'tools'); - const items = chain.kind === 'tools' ? chain.items : []; assert.deepEqual( - items.map((item) => item.activity.toolUseId), - ['s1', 's2', 's4', 't1'], - 'the overview, the documents under it, and a second topic are three rows', + blocks.map((block) => block.kind === 'tool' ? block.activity.toolUseId : block.kind), + ['s1', 's2', 's3', 's4', 't1'], ); - // Nothing is dropped: the folded calls stay on the row that speaks for them, - // which is what lets the panel list every document it read. - assert.deepEqual(items[1].repeats.map((repeat) => repeat.toolUseId), ['s3']); - assert.deepEqual(items.map((item) => item.repeats.length), [0, 1, 0, 0]); - assert.deepEqual(items.map((item) => item.index), [0, 1, 3, 4]); + assert.equal(blocks[1].kind === 'tool' && blocks[1].activity.inputSummary, JSON.stringify({ + skill: 'makers-agents', + ref: 'platform/node-entry.md', + })); - // Narration says what the agent turns to next, so a load after it is a new - // step even when it lands on a topic already read. const resumed = buildAssistantTimeline([ load('s1', 'makers-agents', 'platform/node-entry.md'), { kind: 'text', content: 'Now the streaming protocol.' }, load('s2', 'makers-agents', 'platform/sse-protocol.md'), ]); - assert.deepEqual(resumed.map((block) => block.kind), ['tools', 'text', 'tools']); - assert.equal(resumed[2].kind === 'tools' && resumed[2].items.length, 1); + assert.deepEqual(resumed.map((block) => block.kind), ['tool', 'text', 'tool']); }); test('trailingTimelineContent keeps leftover reply after the last streamed text', () => { diff --git a/tests/stream-events.test.ts b/tests/stream-events.test.ts index d8cd6a2..3c2d0c5 100644 --- a/tests/stream-events.test.ts +++ b/tests/stream-events.test.ts @@ -109,3 +109,33 @@ test('tool_use patches keep a growing outputSummary on the same row', () => { assert.equal(tool.outputSummary, '12s'); } }); + +test('tool_use keeps command and phase fields on the same row', () => { + let turn: PersistedActivityTurn = { + id: 'turn-1', + user: 'Build', + assistant: '', + status: 'completed', + createdAt: 1, + activities: [], + }; + turn = applyStreamEvent(turn, { + type: 'tool_use', + data: { + id: 't1', + name: 'Glob', + phaseHint: 'code', + fileCount: 4, + inputSummary: JSON.stringify({ pattern: '**/*', path: 'src' }, null, 2), + }, + }); + const tool = turn.activities[0]; + assert.equal(tool.kind, 'tool'); + if (tool.kind === 'tool') { + assert.equal(tool.name, 'Glob'); + assert.equal(tool.phaseHint, 'code'); + assert.equal(tool.fileCount, 4); + assert.match(tool.inputSummary || '', /pattern/); + assert.match(tool.inputSummary || '', /\*\*\/\*/); + } +}); diff --git a/tests/tool-activity.test.ts b/tests/tool-activity.test.ts index f616d99..2a44d2f 100644 --- a/tests/tool-activity.test.ts +++ b/tests/tool-activity.test.ts @@ -98,10 +98,9 @@ test('a deeper document is the same topic, marked as going further', () => { }); assert.equal(overview.topic, 'storage'); assert.equal(overview.detailed, false); - assert.equal( + assert.match( summarizeToolInput('load_makers_skill', { skill: 'makers-storage' }), - 'makers-storage', - 'an overview keeps the plain summary every earlier conversation persisted', + /makers-storage/, ); }); From 0bd7c5d1d84bbe824bad79f009bb299a728db453 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 15:58:45 +0800 Subject: [PATCH 11/26] feat(session): prepare sandbox and agent before the first turn Move environment warmup onto GET /session so the model starts from an empty workspace, and drop the baked templates that used to fill it. --- agents/_lib/project/scaffold.ts | 184 +- agents/_lib/project/templates.ts | 445 - agents/_lib/project/workspace.ts | 2 +- agents/_lib/prompt.ts | 78 +- agents/_lib/session/live.ts | 124 +- agents/_lib/session/prepare.ts | 148 + agents/_lib/session/resume.ts | 62 +- agents/_lib/session/stream-projector.ts | 4 - agents/_lib/tools/assemble.ts | 27 +- agents/_lib/tools/project-tools.ts | 122 +- agents/_lib/turn/auto-fix.ts | 3 - agents/_lib/turn/chat.ts | 18 +- agents/_lib/types.ts | 4 +- app/features/workspace/hooks/use-live-turn.ts | 114 +- .../workspace/hooks/use-session-resume.ts | 13 +- app/features/workspace/workspace-api.ts | 22 +- app/i18n.ts | 16 + app/types/workspace.ts | 2 + edgeone.json | 6 +- package.json | 1 - scripts/bake-templates.mjs | 1180 -- shared/protocol.ts | 19 + templates/astro/README.md | 43 - templates/astro/_gitignore | 24 - templates/astro/astro.config.mjs | 5 - templates/astro/package-lock.json | 5585 -------- templates/astro/package.json | 20 - templates/astro/public/favicon.ico | Bin 655 -> 0 bytes templates/astro/public/favicon.svg | 9 - templates/astro/src/pages/index.astro | 17 - templates/astro/tsconfig.json | 5 - templates/deepagents/.env.example | 2 - templates/deepagents/agents/chat.ts | 123 - templates/deepagents/edgeone.json | 6 - templates/deepagents/package-lock.json | 628 - templates/deepagents/package.json | 16 - templates/langgraph/.env.example | 2 - templates/langgraph/agents/chat.ts | 151 - templates/langgraph/edgeone.json | 5 - templates/langgraph/package-lock.json | 345 - templates/langgraph/package.json | 11 - templates/manifest.json | 87 - templates/nextjs/README.md | 36 - templates/nextjs/_gitignore | 41 - templates/nextjs/app/favicon.ico | Bin 25931 -> 0 bytes templates/nextjs/app/globals.css | 26 - templates/nextjs/app/layout.tsx | 34 - templates/nextjs/app/page.tsx | 103 - templates/nextjs/eslint.config.mjs | 40 - templates/nextjs/next-env.d.ts | 6 - templates/nextjs/next.config.ts | 7 - templates/nextjs/package-lock.json | 6377 --------- templates/nextjs/package.json | 27 - templates/nextjs/postcss.config.mjs | 5 - templates/nextjs/public/file.svg | 1 - templates/nextjs/public/globe.svg | 1 - templates/nextjs/public/next.svg | 1 - templates/nextjs/public/vercel.svg | 1 - templates/nextjs/public/window.svg | 1 - templates/nextjs/tsconfig.json | 27 - templates/nuxt/README.md | 75 - templates/nuxt/_gitignore | 24 - templates/nuxt/app/app.vue | 6 - templates/nuxt/nuxt.config.ts | 5 - templates/nuxt/package-lock.json | 11135 ---------------- templates/nuxt/package.json | 17 - templates/nuxt/public/favicon.ico | Bin 4286 -> 0 bytes templates/nuxt/public/robots.txt | 2 - templates/nuxt/tsconfig.json | 18 - templates/react-router/.dockerignore | 4 - templates/react-router/Dockerfile | 22 - templates/react-router/README.md | 87 - templates/react-router/_gitignore | 7 - templates/react-router/app/app.css | 15 - templates/react-router/app/root.tsx | 75 - templates/react-router/app/routes.ts | 3 - templates/react-router/app/routes/home.tsx | 13 - .../react-router/app/welcome/logo-dark.svg | 23 - .../react-router/app/welcome/logo-light.svg | 23 - .../react-router/app/welcome/welcome.tsx | 89 - templates/react-router/package-lock.json | 5001 ------- templates/react-router/package.json | 31 - templates/react-router/public/favicon.ico | Bin 15086 -> 0 bytes templates/react-router/react-router.config.ts | 7 - templates/react-router/tsconfig.json | 26 - templates/react-router/vite.config.ts | 9 - templates/sveltekit/.npmrc | 1 - templates/sveltekit/README.md | 42 - templates/sveltekit/_gitignore | 23 - templates/sveltekit/package-lock.json | 2233 ---- templates/sveltekit/package.json | 24 - templates/sveltekit/src/app.d.ts | 13 - templates/sveltekit/src/app.html | 12 - .../sveltekit/src/lib/assets/favicon.svg | 1 - templates/sveltekit/src/lib/index.ts | 1 - templates/sveltekit/src/routes/+layout.svelte | 11 - templates/sveltekit/src/routes/+page.svelte | 2 - templates/sveltekit/static/robots.txt | 3 - templates/sveltekit/tsconfig.json | 20 - templates/sveltekit/vite.config.ts | 20 - templates/tanstack-start/.cta.json | 17 - templates/tanstack-start/README.md | 187 - templates/tanstack-start/_gitignore | 13 - templates/tanstack-start/package-lock.json | 5304 -------- templates/tanstack-start/package.json | 43 - .../tanstack-start/src/components/Footer.tsx | 44 - .../tanstack-start/src/components/Header.tsx | 78 - .../src/components/ThemeToggle.tsx | 81 - templates/tanstack-start/src/routeTree.gen.ts | 77 - templates/tanstack-start/src/router.tsx | 19 - .../tanstack-start/src/routes/__root.tsx | 61 - templates/tanstack-start/src/routes/about.tsx | 23 - templates/tanstack-start/src/routes/index.tsx | 87 - templates/tanstack-start/src/styles.css | 464 - templates/tanstack-start/tsconfig.json | 28 - templates/tanstack-start/tsr.config.json | 3 - templates/tanstack-start/vite.config.ts | 22 - templates/vike/README.md | 46 - templates/vike/_gitignore | 152 - templates/vike/assets/logo.svg | 68 - templates/vike/components/Link.tsx | 12 - templates/vike/package.json | 23 - templates/vike/pages/+Head.tsx | 7 - templates/vike/pages/+Layout.tsx | 74 - templates/vike/pages/+config.ts | 15 - templates/vike/pages/+onPageTransitionEnd.ts | 4 - .../vike/pages/+onPageTransitionStart.ts | 9 - templates/vike/pages/Layout.css | 29 - templates/vike/pages/_error/+Page.tsx | 19 - templates/vike/pages/index/+Page.tsx | 16 - templates/vike/pages/index/Counter.tsx | 11 - templates/vike/pages/star-wars/@id/+Page.tsx | 16 - templates/vike/pages/star-wars/@id/+data.ts | 32 - .../vike/pages/star-wars/index/+Page.tsx | 21 - templates/vike/pages/star-wars/index/+data.ts | 32 - templates/vike/pages/star-wars/moviesData.ts | 48 - templates/vike/pages/star-wars/types.ts | 10 - templates/vike/pages/todo/+Page.tsx | 10 - templates/vike/pages/todo/+data.ts | 10 - templates/vike/pages/todo/TodoList.tsx | 32 - templates/vike/tsconfig.json | 25 - templates/vike/vite.config.ts | 8 - templates/vite-spa/.oxlintrc.json | 8 - templates/vite-spa/README.md | 32 - templates/vite-spa/_gitignore | 24 - templates/vite-spa/index.html | 13 - templates/vite-spa/package.json | 25 - templates/vite-spa/public/favicon.svg | 1 - templates/vite-spa/public/icons.svg | 24 - templates/vite-spa/src/App.css | 184 - templates/vite-spa/src/App.tsx | 122 - templates/vite-spa/src/assets/hero.png | Bin 13057 -> 0 bytes templates/vite-spa/src/assets/react.svg | 1 - templates/vite-spa/src/assets/vite.svg | 1 - templates/vite-spa/src/index.css | 111 - templates/vite-spa/src/main.tsx | 10 - templates/vite-spa/tsconfig.app.json | 26 - templates/vite-spa/tsconfig.json | 7 - templates/vite-spa/tsconfig.node.json | 23 - templates/vite-spa/vite.config.ts | 7 - tests/architecture.test.ts | 3 + tests/preview-path.test.ts | 4 +- tests/project-templates.test.ts | 644 - tests/prompt-single-source.test.ts | 28 +- tests/route-consolidation.test.ts | 5 +- tests/scaffold.test.ts | 142 +- tests/session-prep.test.ts | 188 + tests/tool-activity.test.ts | 9 + 168 files changed, 710 insertions(+), 43747 deletions(-) delete mode 100644 agents/_lib/project/templates.ts create mode 100644 agents/_lib/session/prepare.ts delete mode 100644 scripts/bake-templates.mjs delete mode 100644 templates/astro/README.md delete mode 100644 templates/astro/_gitignore delete mode 100644 templates/astro/astro.config.mjs delete mode 100644 templates/astro/package-lock.json delete mode 100644 templates/astro/package.json delete mode 100644 templates/astro/public/favicon.ico delete mode 100644 templates/astro/public/favicon.svg delete mode 100644 templates/astro/src/pages/index.astro delete mode 100644 templates/astro/tsconfig.json delete mode 100644 templates/deepagents/.env.example delete mode 100644 templates/deepagents/agents/chat.ts delete mode 100644 templates/deepagents/edgeone.json delete mode 100644 templates/deepagents/package-lock.json delete mode 100644 templates/deepagents/package.json delete mode 100644 templates/langgraph/.env.example delete mode 100644 templates/langgraph/agents/chat.ts delete mode 100644 templates/langgraph/edgeone.json delete mode 100644 templates/langgraph/package-lock.json delete mode 100644 templates/langgraph/package.json delete mode 100644 templates/manifest.json delete mode 100644 templates/nextjs/README.md delete mode 100644 templates/nextjs/_gitignore delete mode 100644 templates/nextjs/app/favicon.ico delete mode 100644 templates/nextjs/app/globals.css delete mode 100644 templates/nextjs/app/layout.tsx delete mode 100644 templates/nextjs/app/page.tsx delete mode 100644 templates/nextjs/eslint.config.mjs delete mode 100644 templates/nextjs/next-env.d.ts delete mode 100644 templates/nextjs/next.config.ts delete mode 100644 templates/nextjs/package-lock.json delete mode 100644 templates/nextjs/package.json delete mode 100644 templates/nextjs/postcss.config.mjs delete mode 100644 templates/nextjs/public/file.svg delete mode 100644 templates/nextjs/public/globe.svg delete mode 100644 templates/nextjs/public/next.svg delete mode 100644 templates/nextjs/public/vercel.svg delete mode 100644 templates/nextjs/public/window.svg delete mode 100644 templates/nextjs/tsconfig.json delete mode 100644 templates/nuxt/README.md delete mode 100644 templates/nuxt/_gitignore delete mode 100644 templates/nuxt/app/app.vue delete mode 100644 templates/nuxt/nuxt.config.ts delete mode 100644 templates/nuxt/package-lock.json delete mode 100644 templates/nuxt/package.json delete mode 100644 templates/nuxt/public/favicon.ico delete mode 100644 templates/nuxt/public/robots.txt delete mode 100644 templates/nuxt/tsconfig.json delete mode 100644 templates/react-router/.dockerignore delete mode 100644 templates/react-router/Dockerfile delete mode 100644 templates/react-router/README.md delete mode 100644 templates/react-router/_gitignore delete mode 100644 templates/react-router/app/app.css delete mode 100644 templates/react-router/app/root.tsx delete mode 100644 templates/react-router/app/routes.ts delete mode 100644 templates/react-router/app/routes/home.tsx delete mode 100644 templates/react-router/app/welcome/logo-dark.svg delete mode 100644 templates/react-router/app/welcome/logo-light.svg delete mode 100644 templates/react-router/app/welcome/welcome.tsx delete mode 100644 templates/react-router/package-lock.json delete mode 100644 templates/react-router/package.json delete mode 100644 templates/react-router/public/favicon.ico delete mode 100644 templates/react-router/react-router.config.ts delete mode 100644 templates/react-router/tsconfig.json delete mode 100644 templates/react-router/vite.config.ts delete mode 100644 templates/sveltekit/.npmrc delete mode 100644 templates/sveltekit/README.md delete mode 100644 templates/sveltekit/_gitignore delete mode 100644 templates/sveltekit/package-lock.json delete mode 100644 templates/sveltekit/package.json delete mode 100644 templates/sveltekit/src/app.d.ts delete mode 100644 templates/sveltekit/src/app.html delete mode 100644 templates/sveltekit/src/lib/assets/favicon.svg delete mode 100644 templates/sveltekit/src/lib/index.ts delete mode 100644 templates/sveltekit/src/routes/+layout.svelte delete mode 100644 templates/sveltekit/src/routes/+page.svelte delete mode 100644 templates/sveltekit/static/robots.txt delete mode 100644 templates/sveltekit/tsconfig.json delete mode 100644 templates/sveltekit/vite.config.ts delete mode 100644 templates/tanstack-start/.cta.json delete mode 100644 templates/tanstack-start/README.md delete mode 100644 templates/tanstack-start/_gitignore delete mode 100644 templates/tanstack-start/package-lock.json delete mode 100644 templates/tanstack-start/package.json delete mode 100644 templates/tanstack-start/src/components/Footer.tsx delete mode 100644 templates/tanstack-start/src/components/Header.tsx delete mode 100644 templates/tanstack-start/src/components/ThemeToggle.tsx delete mode 100644 templates/tanstack-start/src/routeTree.gen.ts delete mode 100644 templates/tanstack-start/src/router.tsx delete mode 100644 templates/tanstack-start/src/routes/__root.tsx delete mode 100644 templates/tanstack-start/src/routes/about.tsx delete mode 100644 templates/tanstack-start/src/routes/index.tsx delete mode 100644 templates/tanstack-start/src/styles.css delete mode 100644 templates/tanstack-start/tsconfig.json delete mode 100644 templates/tanstack-start/tsr.config.json delete mode 100644 templates/tanstack-start/vite.config.ts delete mode 100644 templates/vike/README.md delete mode 100644 templates/vike/_gitignore delete mode 100644 templates/vike/assets/logo.svg delete mode 100644 templates/vike/components/Link.tsx delete mode 100644 templates/vike/package.json delete mode 100644 templates/vike/pages/+Head.tsx delete mode 100644 templates/vike/pages/+Layout.tsx delete mode 100644 templates/vike/pages/+config.ts delete mode 100644 templates/vike/pages/+onPageTransitionEnd.ts delete mode 100644 templates/vike/pages/+onPageTransitionStart.ts delete mode 100644 templates/vike/pages/Layout.css delete mode 100644 templates/vike/pages/_error/+Page.tsx delete mode 100644 templates/vike/pages/index/+Page.tsx delete mode 100644 templates/vike/pages/index/Counter.tsx delete mode 100644 templates/vike/pages/star-wars/@id/+Page.tsx delete mode 100644 templates/vike/pages/star-wars/@id/+data.ts delete mode 100644 templates/vike/pages/star-wars/index/+Page.tsx delete mode 100644 templates/vike/pages/star-wars/index/+data.ts delete mode 100644 templates/vike/pages/star-wars/moviesData.ts delete mode 100644 templates/vike/pages/star-wars/types.ts delete mode 100644 templates/vike/pages/todo/+Page.tsx delete mode 100644 templates/vike/pages/todo/+data.ts delete mode 100644 templates/vike/pages/todo/TodoList.tsx delete mode 100644 templates/vike/tsconfig.json delete mode 100644 templates/vike/vite.config.ts delete mode 100644 templates/vite-spa/.oxlintrc.json delete mode 100644 templates/vite-spa/README.md delete mode 100644 templates/vite-spa/_gitignore delete mode 100644 templates/vite-spa/index.html delete mode 100644 templates/vite-spa/package.json delete mode 100644 templates/vite-spa/public/favicon.svg delete mode 100644 templates/vite-spa/public/icons.svg delete mode 100644 templates/vite-spa/src/App.css delete mode 100644 templates/vite-spa/src/App.tsx delete mode 100644 templates/vite-spa/src/assets/hero.png delete mode 100644 templates/vite-spa/src/assets/react.svg delete mode 100644 templates/vite-spa/src/assets/vite.svg delete mode 100644 templates/vite-spa/src/index.css delete mode 100644 templates/vite-spa/src/main.tsx delete mode 100644 templates/vite-spa/tsconfig.app.json delete mode 100644 templates/vite-spa/tsconfig.json delete mode 100644 templates/vite-spa/tsconfig.node.json delete mode 100644 templates/vite-spa/vite.config.ts delete mode 100644 tests/project-templates.test.ts create mode 100644 tests/session-prep.test.ts diff --git a/agents/_lib/project/scaffold.ts b/agents/_lib/project/scaffold.ts index 1a43fc5..fc75378 100644 --- a/agents/_lib/project/scaffold.ts +++ b/agents/_lib/project/scaffold.ts @@ -1,193 +1,11 @@ import { requireSandbox, type AgentContext } from '../runtime/context.ts'; -import type { BuildResult, BuildStatus, ProjectState, ScaffoldLog } from '../types.ts'; +import type { BuildResult, BuildStatus, ProjectState } from '../types.ts'; import { detectFatalToolError } from '../utils/text.ts'; import { runCommandCapturingExit, runSandboxCommand } from './commands.ts'; -import { loadMakersFrameworkProfiles } from '../makers/compat/skill-rules.ts'; import { runMakersCompatibilityCheck } from '../makers/compat/run.ts'; -import { withFrameworkAdapter } from '../makers/declarations.ts'; -import { applyProjectTemplate, listProjectTemplates, resolveProjectTemplate } from './templates.ts'; -import type { AppliedTemplate } from './templates.ts'; -import { repairNestedAppDirLayout } from './layout.ts'; export { repairNestedAppDirLayout } from './layout.ts'; -/** - * What the workspace probe answers, beyond "is anything here". - * - * Whether the dependencies are installed is the second question, and it used to - * have no answer at all: the listing prunes node_modules, because a populated - * tree is hundreds of thousands of paths, and nothing else reported it. So a - * workspace that arrived with its dependencies already installed looked - * identical to one that did not, and the model did the only safe-looking thing - * and ran `npm install` — which on a 1.1G sandbox does not fit beside a tree - * that is already there. One session spent four minutes that way: the install - * filled the disk, died halfway, took the working tree with it, and ended with - * the project less runnable than when it started. - */ -export type ScaffoldOutcome = { - created: boolean; - dependenciesInstalled: boolean; - /** Present only when this call filled an empty workspace from a baked tree. */ - template?: AppliedTemplate; - /** - * The baked tree ids, carried back only when this call left the workspace - * empty. The miss is worth reporting because the caller cannot see it: an - * empty workspace and an empty workspace that could have been a baked chat - * agent read identically from the outside, and the model reads the first one - * as licence to write the tree by hand. - */ - available?: readonly string[]; -}; - -export type ScaffoldOptions = { - /** - * The framework the request named, if it named one. Only ever a hint: an - * unknown name, a framework with no baked template, and an omitted value all - * leave the workspace empty for the scaffolder path to fill. - */ - framework?: string; -}; - -const DEPENDENCIES_INSTALLED = 'DEPENDENCIES_INSTALLED'; - -export async function ensureProjectScaffold( - context: AgentContext, - state: ProjectState, - onLog?: (log: ScaffoldLog) => void, - options: ScaffoldOptions = {}, -): Promise { - const sandbox = requireSandbox(context); - onLog?.({ stream: 'status', content: `Preparing the project workspace ${state.appDir}` }); - - // appDir is sessionDir plus one segment and the create is recursive, so the - // second call only ever remade a directory the first had already made. - await sandbox.files.makeDir(state.appDir); - - await repairNestedAppDirLayout(context, state, onLog); - - const existing = await runSandboxCommand( - context, - [ - // .bin is the tell, not node_modules itself: an install that died partway - // leaves the directory standing with its executables gone, which is the - // state that has to read as "not installed" so the retry happens. - `if [ -n "$(ls -A node_modules/.bin 2>/dev/null)" ]; then echo ${DEPENDENCIES_INSTALLED}; fi`, - [ - 'find . -mindepth 1 -maxdepth 2', - "\\( -path './node_modules' -o -path './.next' -o -path './.git' -o -path './dist' -o -path './build' \\) -prune", - '-o -print', - ].join(' '), - ].join('\n'), - { - cwd: state.appDir, - timeout: 60, - }, - ); - if (existing.exitCode !== 0) { - throw new Error(existing.stderr || existing.stdout || 'Workspace inspection failed.'); - } - const lines = existing.stdout.split('\n').map((line) => line.trim()).filter(Boolean); - const dependenciesInstalled = lines.includes(DEPENDENCIES_INSTALLED); - const files = lines.filter((line) => line !== DEPENDENCIES_INSTALLED); - - // One conversation_id maps to one long-lived project. Reuse existing business - // files without overwriting them. - if (files.length) { - onLog?.({ - stream: 'status', - content: dependenciesInstalled - ? 'Existing project workspace detected, dependencies already installed; skipping initialization.' - : 'Existing project workspace detected; skipping initialization.', - }); - return { created: false, dependenciesInstalled }; - } - - const { template, available } = await applyTemplateIfBaked( - context, - state, - options.framework, - onLog, - ); - if (template) { - return { created: true, dependenciesInstalled, template }; - } - - onLog?.({ stream: 'status', content: 'Prepared an empty project workspace. Waiting for the agent to generate project files.' }); - - return { - created: true, - dependenciesInstalled, - ...(available?.length ? { available } : {}), - }; -} - -/** - * Fill the empty workspace from a baked tree, or leave it empty. - * - * Best effort in every direction, because the scaffolder path it replaces is - * still there: a framework nobody baked, a manifest that will not parse, a - * sandbox that refuses the write — each of them returns nothing, and the run - * goes on to load the reference and run the scaffolder exactly as before. The - * one thing this must not do is fail the first tool call of the conversation - * for an optimisation. - */ -async function applyTemplateIfBaked( - context: AgentContext, - state: ProjectState, - framework: string | undefined, - onLog?: (log: ScaffoldLog) => void, -): Promise<{ template?: AppliedTemplate; available?: readonly string[] }> { - try { - const template = await resolveProjectTemplate(framework); - if (!template) { - // Said out loud, and said differently for the three causes. A framework - // nobody baked is the expected miss. A build holding no baked trees at - // all is a packaging fault — it went unnoticed through every deployed - // conversation because falling back to the scaffolder looks like the - // model choosing to, and nothing here disagreed with that reading. - // - // The third is a request that names no framework: it arrives with nothing - // to resolve, and the log used to be gated on having a name to print, so - // the miss went out silent. The alias table in ./templates.ts records - // what that silence cost. - const baked = await listProjectTemplates(); - const available = baked.map((item) => item.id); - onLog?.({ - stream: 'status', - content: available.length === 0 - ? `This build carries no baked templates, so ${framework?.trim() || 'this project'} falls back to its own scaffolder.` - : framework?.trim() - ? `No baked template for ${framework}; using its own scaffolder.` - : `The request named no framework, so no baked template was applied. Baked: ${available.join(', ')}.`, - }); - return { available }; - } - const profiles = await loadMakersFrameworkProfiles(); - - const applied = await applyProjectTemplate(context, state, template, { - onLog, - // The adapter injection is hooked to write_project_file, and a template's - // package.json does not come through it. Without this, a framework whose - // platform adapter is unconditional would reach the preview gate missing - // the one dependency the install about to start could have picked up. - adaptPackageJson: (content) => withFrameworkAdapter( - { status: 'present', content }, - profiles, - ), - }); - - return { template: applied }; - } catch (error) { - onLog?.({ - stream: 'status', - content: `Could not use the baked ${framework?.trim() || 'project'} template (${ - error instanceof Error ? error.message : String(error) - }); falling back to the framework's own scaffolder.`, - }); - return {}; - } -} - /** * What the production build is still for once a preview has come up. * diff --git a/agents/_lib/project/templates.ts b/agents/_lib/project/templates.ts deleted file mode 100644 index 121e927..0000000 --- a/agents/_lib/project/templates.ts +++ /dev/null @@ -1,445 +0,0 @@ -/** - * The scaffolder's output, already on disk here, so a new project does not wait - * for it to be produced again. - * - * What a run used to spend before its first project file existed: a round trip - * to load the framework reference, then `npx create-next-app@latest`, which - * fetches the create package, fetches a template, and installs as it goes. The - * tree it produces is the same every time and depends on nothing about the - * request, so `npm run bake:templates` produces it once and commits it. - * - * Two things follow from that, and the second is the larger one: - * - * The install starts at the scaffold instead of two minutes into the turn. It - * needs only package.json, which arrives here in the first tool call rather - * than after the model has written a dozen files — the gap a measured session - * lost 73 seconds to, on top of the scaffolder's own install. - * - * And a scaffolder that cannot reach its template stops being the run's - * problem. Several of them — the ones built on giget — fetch from GitHub at run - * time rather than shipping a template in their npm package, so they fail - * wherever the sandbox's egress does not reach it, and the run degrades to - * writing a framework's boilerplate by hand. A baked template needs the - * network only on the machine that baked it. - * - * A framework with no baked template resolves to nothing and the run takes the - * old path, which is why this stays an accelerator rather than an allowlist. - */ - -import { gzipSync } from 'node:zlib'; -import { readdir, readFile } from 'node:fs/promises'; -import path from 'node:path'; -import { PREVIEW_ASSET_PREFIX_ENV } from '../constants.ts'; -import { requireSandbox, type AgentContext } from '../runtime/context.ts'; -import type { ProjectState, ScaffoldLog } from '../types.ts'; -import { safeSegment } from '../utils/paths.ts'; -import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; -import { runSandboxCommand } from './commands.ts'; - -export type ProjectTemplate = { - id: string; - /** The framework reference whose Scaffold command produced this tree. */ - ref: string; - /** That command, recorded so a skill sync that changes it fails a test. */ - command: string; - bakedAt: string; - files: number; - bytes: number; -}; - -/** - * Spellings of a framework that should reach the same template. - * - * Only for names that do not survive normalization into a template id — the ids - * themselves are matched without being listed. `react` maps to the Vite - * template because that is what the prompt already asks for when a request - * names React without a framework around it; `vue` deliberately maps to - * nothing, since the baked Vite tree is the React one and handing a Vue request - * a React app is worse than scaffolding it the slow way. - */ -const TEMPLATE_ALIASES: Readonly> = { - next: 'nextjs', - nextapp: 'nextjs', - vite: 'vite-spa', - vitereact: 'vite-spa', - react: 'vite-spa', - reactts: 'vite-spa', - reacttypescript: 'vite-spa', - svelte: 'sveltekit', - remix: 'react-router', - reactrouterv7: 'react-router', - tanstack: 'tanstack-start', - nuxtjs: 'nuxt', - nuxt3: 'nuxt', - nuxt4: 'nuxt', - astrojs: 'astro', - // The agent side, where a request names the app it wants and not a framework. - // "Make an AI chat assistant" offers nothing an id can match, so without - // these the baked chat tree is reachable only by a model that already knows - // the id `deepagents` — and nothing tells it that. It went unused for - // exactly the prompts it was baked for, while the model hand-wrote a - // package.json beside it. - // - // Between the two baked agent trees deepagents is the general - // streaming-chat one, so a bare `agent` or `chat` lands there; the scaffold - // result names the tree it got, which is what lets a model that wanted the - // other one change course. - // - // The agent frameworks nobody baked — crewai, openai-agents-sdk, - // claude-agent-sdk — deliberately stay out. Handing one of those a - // deepagents tree is the `vue` mistake above: their own reference is a - // better start than the wrong template. - chat: 'deepagents', - chatbot: 'deepagents', - aichat: 'deepagents', - assistant: 'deepagents', - aiassistant: 'deepagents', - chatassistant: 'deepagents', - aichatassistant: 'deepagents', - agent: 'deepagents', - aiagent: 'deepagents', - deepagent: 'deepagents', - langgraphjs: 'langgraph', -}; - -/** Punctuation and case are what separate "Next.js" from the id "nextjs". */ -function normalizeFrameworkName(value: string) { - return value.toLowerCase().replace(/[^a-z0-9]/g, ''); -} - -/** - * Where the baked trees are, which is not one place. - * - * Locally the agent runs with cwd at the repository root, so `templates/` is - * right there. Deployed, it runs from the bundle the CLI uploads, and the - * builder puts everything named in `agents.includeFiles` under an - * `included_files/` namespace of its own — the tree keeps its shape and moves - * one level down. Only `.claude/skills` arrives at the root, and only because - * the builder copies that one by name. - * - * Naming both is what keeps a deployed run from silently taking the scaffolder - * path while the same checkout works on a laptop. - */ -const TEMPLATE_ROOTS = ['templates', path.join('included_files', 'templates')] as const; - -const NO_TEMPLATES: readonly ProjectTemplate[] = Object.freeze([]); - -type BakedTemplates = { root: string; templates: readonly ProjectTemplate[] }; - -let bakedCache: BakedTemplates | undefined; - -/** - * The manifest and the directory it was found in, resolved together so a later - * file read cannot go looking in the other candidate. - * - * Nothing is remembered until a manifest parses. Memoising the miss is what - * turned one unreadable manifest into a process that never used a baked - * template again, and the deployed runtime is exactly where that miss happens. - */ -async function loadBakedTemplates(): Promise { - if (bakedCache) return bakedCache; - - for (const candidate of TEMPLATE_ROOTS) { - const root = path.join(process.cwd(), candidate); - try { - const raw = await readFile(path.join(root, 'manifest.json'), 'utf8'); - const parsed = JSON.parse(raw) as { templates?: ProjectTemplate[] }; - bakedCache = { root, templates: Object.freeze(parsed.templates ?? []) }; - return bakedCache; - } catch { - // The other candidate, and then nothing: a build carrying no baked trees - // is a working build, and every caller treats "no template" as the old - // scaffolder path. - } - } - return undefined; -} - -export async function listProjectTemplates(): Promise { - return (await loadBakedTemplates())?.templates ?? NO_TEMPLATES; -} - -export async function resolveProjectTemplate( - framework: string | undefined, -): Promise { - if (!framework?.trim()) return undefined; - const normalized = normalizeFrameworkName(framework); - if (!normalized) return undefined; - - const templates = await listProjectTemplates(); - const wanted = TEMPLATE_ALIASES[normalized] ?? normalized; - return templates.find((template) => ( - template.id === wanted || normalizeFrameworkName(template.id) === wanted - )); -} - -type TemplateFile = { p: string; t?: string; d?: string }; - -/** - * The name a template's `.gitignore` is committed under. - * - * It cannot be committed as one: inside templates/ it would be a live ignore - * file for its own directory, and the Next.js tree's copy lists next-env.d.ts — - * so the template was a file short of what it was baked from, on a fresh clone - * only. The bake script renames it; this puts the name back on the way in. - */ -const GITIGNORE_STORED_AS = '_gitignore'; - -async function readTemplateFiles(id: string): Promise { - const baked = await loadBakedTemplates(); - if (!baked) { - throw new Error('the baked trees are not in this build'); - } - const root = path.join(baked.root, id); - const files: TemplateFile[] = []; - - async function walk(dir: string) { - for (const entry of await readdir(dir, { withFileTypes: true })) { - const target = path.join(dir, entry.name); - if (entry.isDirectory()) { - await walk(target); - continue; - } - const relative = path - .relative(root, entry.name === GITIGNORE_STORED_AS - ? path.join(dir, '.gitignore') - : target) - .replaceAll(path.sep, '/'); - const bytes = await readFile(target); - // Text as text, so the payload gzips against the whole tree rather than - // against base64 of it — the difference on a lockfile is most of the - // transfer. Binary is real here and not a hypothetical: the Next.js tree - // carries a favicon and the Vite one a PNG. - const asText = bytes.toString('utf8'); - files.push(Buffer.from(asText, 'utf8').equals(bytes) - ? { p: relative, t: asText } - : { p: relative, d: bytes.toString('base64') }); - } - } - - await walk(root); - files.sort((a, b) => a.p.localeCompare(b.p)); - return files; -} - -export const TEMPLATE_FILES_MARKER = 'TEMPLATE_FILES:'; - -/** - * A self-contained extractor, rather than a payload plus a script to read it. - * - * Two round trips is the budget — one write, one command — and inlining the - * payload spends neither on quoting: base64 is already shell-safe and - * JS-string-safe, so nothing here has to be escaped on the way through. - * - * `.cjs` because the tree being written may declare `"type": "module"`, and a - * plain `.js` extractor would then be parsed as ESM and fail on its own - * requires. - */ -function buildExtractorScript(payload: string) { - return [ - "const zlib = require('node:zlib');", - "const fs = require('node:fs');", - "const path = require('node:path');", - `const files = JSON.parse(zlib.gunzipSync(Buffer.from('${payload}', 'base64')).toString('utf8'));`, - 'for (const file of files) {', - ' const target = path.resolve(process.cwd(), file.p);', - ' fs.mkdirSync(path.dirname(target), { recursive: true });', - " fs.writeFileSync(target, file.d === undefined ? file.t : Buffer.from(file.d, 'base64'));", - '}', - `process.stdout.write('${TEMPLATE_FILES_MARKER}' + files.length + '\\n');`, - ].join('\n'); -} - -const fileCache = new Map>(); - -function cachedTemplateFiles(id: string) { - const cached = fileCache.get(id); - if (cached) return cached; - const reading = readTemplateFiles(id).then((files) => Object.freeze(files)); - fileCache.set(id, reading); - return reading; -} - -export type AppliedTemplate = { - id: string; - files: number; - /** Whether adaptPackageJson changed the manifest before it was written. */ - adapted: boolean; -}; - -/** - * Put the preview prefix option into a framework config that does not have it. - * - * The official scaffolder output never mentions the environment variable the - * host exports, so without this the model has to load makers-frameworks just - * to rewrite one line — and often rewrites the rest of the file with it. - * Returning undefined means the file is already correct or is not a config - * this function knows how to touch. - */ -export function withPreviewAssetPrefix( - relativePath: string, - content: string, -): string | undefined { - if (!content || content.includes(PREVIEW_ASSET_PREFIX_ENV)) return undefined; - const file = relativePath.replaceAll('\\', '/'); - const env = `process.env.${PREVIEW_ASSET_PREFIX_ENV}`; - - // SvelteKit is the one framework here that types its prefix as a template - // literal — `"" | \`/${string}\`` — rather than as a string, and an - // environment variable is only ever `string`. The assignment is correct at - // runtime and unprovable at compile time, so it needs the assertion said out - // loud. It goes in only for a TypeScript target: `svelte.config.js` is - // checked too, under `checkJs`, but `as` is not JavaScript. - const kitBase = /\.ts$/.test(file) ? `${env} as \`/\${string}\`` : env; - - if (/(?:^|\/)next\.config\.(?:ts|js|mjs)$/.test(file)) { - return injectAfterOpen(content, /=\s*\{/, `assetPrefix: ${env},`); - } - if (/(?:^|\/)nuxt\.config\.(?:ts|js|mjs)$/.test(file)) { - return injectAfterOpen(content, /defineNuxtConfig\(\s*\{/, `app: { baseURL: ${env} },`); - } - if (/(?:^|\/)svelte\.config\.(?:ts|js|mjs)$/.test(file)) { - return injectAfterOpen( - content, - /kit:\s*\{/, - `...(${env} ? { paths: { base: ${kitBase} } } : {}),`, - ); - } - // The second half of React Router's prefix, and the half without which the - // first does nothing. - // - // Vite's `base` moves the asset URLs and strips the prefix off the request - // before the framework sees it — but React Router's dev adapter puts it - // straight back (`nodeReq.url = nodeReq.originalUrl`, so that "React Router - // is aware of the full path"). It then matches that prefixed path against a - // basename still defaulting to '/', finds nothing, and answers every - // navigation with `No route matches URL "/preview"`. A `base` on its own is - // not an incomplete configuration here, it is an unusable one. - // - // Set in the framework's config rather than the Vite one because that is - // where React Router reads it: the value is baked into the server build and - // handed to the static handler, which is what makes the SSR side match. The - // framework also refuses to start in dev unless the basename begins with the - // base, which the shared environment variable satisfies by construction. - if (/(?:^|\/)react-router\.config\.(?:ts|js|mjs)$/.test(file)) { - return injectAfterOpen(content, /export default\s*\{/, `basename: ${env} ?? "/",`); - } - if (/(?:^|\/)(?:vite|astro)\.config\.(?:ts|js|mjs)$/.test(file)) { - // SvelteKit's documented option is kit.paths.base. The current scaffolder - // puts that kit object on the vite plugin, so the injection follows it - // rather than setting Vite's `base`, which SvelteKit ignores. - if (/\bsveltekit\s*\(/.test(content)) { - return injectAfterOpen( - content, - /sveltekit\(\s*\{/, - `...(${env} ? { paths: { base: ${kitBase} } } : {}),`, - ); - } - return injectAfterOpen(content, /defineConfig\(\s*\{/, `base: ${env},`); - } - return undefined; -} - -function injectAfterOpen(content: string, opener: RegExp, property: string) { - const match = opener.exec(content); - if (!match) return undefined; - const insertAt = match.index + match[0].length; - const after = content.slice(insertAt); - const indentMatch = /^\r?\n([ \t]+)/.exec(after); - const indent = indentMatch?.[1] ?? ' '; - return `${content.slice(0, insertAt)}\n${indent}${property}${ - indentMatch ? after : `\n${after}` - }`; -} - -export type ApplyTemplateOptions = { - onLog?: (log: ScaffoldLog) => void; - /** - * A last chance to change package.json, taken before anything is written. - * - * The ordering is the whole reason this is a hook rather than a second write - * afterwards. The install starts in the same command as the extraction, and - * it stamps package.json as it starts; a manifest edited after that no longer - * matches the stamp, so the handoff discards the finished install and the - * project pays for a second one. Editing here means there is only ever one. - */ - adaptPackageJson?: (content: string) => Promise | string | undefined; -}; - -/** - * Write the template into the workspace and start its install in one command. - * - * Chained rather than issued separately because the install is the thing being - * raced: every round trip between the files landing and `npm install` starting - * is time the user waits for at the end of the turn. The warmup is the existing - * one, so a project created from a template and one written by hand converge on - * the same install, the same handoff, and the same single-npm-process rule. - */ -export async function applyProjectTemplate( - context: AgentContext, - state: ProjectState, - template: ProjectTemplate, - options: ApplyTemplateOptions = {}, -): Promise { - const { onLog, adaptPackageJson } = options; - const files = [...(await cachedTemplateFiles(template.id))]; - - let adapted = false; - const manifestIndex = files.findIndex((file) => file.p === 'package.json'); - const manifest = manifestIndex >= 0 ? files[manifestIndex].t : undefined; - if (adaptPackageJson && manifest !== undefined) { - const replacement = await adaptPackageJson(manifest); - if (replacement !== undefined && replacement !== manifest) { - files[manifestIndex] = { p: 'package.json', t: replacement }; - adapted = true; - } - } - - for (let i = 0; i < files.length; i += 1) { - const file = files[i]; - if (file.t === undefined) continue; - const next = withPreviewAssetPrefix(file.p, file.t); - if (next !== undefined) files[i] = { p: file.p, t: next }; - } - - const payload = gzipSync(Buffer.from(JSON.stringify(files), 'utf8')).toString('base64'); - const script = buildExtractorScript(payload); - const scriptPath = `/tmp/eo-template-${safeSegment(template.id)}-${process.pid}.cjs`; - - onLog?.({ - stream: 'status', - content: `Writing the ${template.id} project template into ${state.appDir}`, - }); - - await requireSandbox(context).files.write(scriptPath, script); - - // set -e so a failed extraction never reaches the warmup: the warmup's first - // act is to disable it again, and an install started over a half-written tree - // is the failure this codebase already pays the most to avoid. - const result = await runSandboxCommand( - context, - [ - 'set -e', - `node ${scriptPath}`, - `rm -f ${scriptPath}`, - buildNpmWarmupCommand(), - ].join('\n'), - { cwd: state.appDir, timeout: 120 }, - ); - - const written = Number( - String(result.stdout || '').match(new RegExp(`${TEMPLATE_FILES_MARKER}(\\d+)`))?.[1], - ); - if (!written) { - throw new Error( - result.stderr || result.stdout || `Failed to write the ${template.id} template.`, - ); - } - - onLog?.({ - stream: 'status', - content: `Wrote ${written} files from the ${template.id} template and started installing its dependencies.`, - }); - - return { id: template.id, files: written, adapted }; -} diff --git a/agents/_lib/project/workspace.ts b/agents/_lib/project/workspace.ts index e246421..5ce89fd 100644 --- a/agents/_lib/project/workspace.ts +++ b/agents/_lib/project/workspace.ts @@ -11,7 +11,7 @@ import { withTimeout } from '../turn/checkpoint.ts'; const SANDBOX_PROBE_MS = 15_000; const RESTORE_BUDGET_MS = 45_000; -async function ensureWorkspaceDirectories(context: AgentContext, state: ProjectState) { +export async function ensureWorkspaceDirectories(context: AgentContext, state: ProjectState) { const files = requireSandbox(context).files; await files.makeDir(state.sessionDir); await files.makeDir(state.appDir); diff --git a/agents/_lib/prompt.ts b/agents/_lib/prompt.ts index ad27c5e..b6b9991 100644 --- a/agents/_lib/prompt.ts +++ b/agents/_lib/prompt.ts @@ -208,59 +208,24 @@ function buildToolContracts(appDir: string) { function buildNewProjectWorkflow(appDir: string) { return [ - 'When ensure_project_scaffold returns created=true, work through these steps in order.', - '1. Load the references this request needs with load_makers_skill and follow them for layout, routing, handler signatures, configuration files, and storage. Prefer static HTML/CSS/JS or Vite static output for ordinary UI. Do not put styles, scripts, and markup into one large index.html unless the user explicitly asks for a single-file page.', - // Eight commands went into excavating one framework's "official template": - // npm view, then tarballs downloaded and unpacked in /tmp, then a package's - // own source read to find where it fetches templates from, then the same - // again for its replacement. Every step was reasonable and the sequence had - // no bottom, because each answer was only ever "the template is elsewhere". - // The scaffolder is where it ends: it holds both the structure and the - // version set, and running it costs one command. - // - // Which command that is, though, is the reference's to say. The two copies - // this step used to carry had already drifted from it: the Next.js one was - // down to `. --yes` while the document specifies four more flags, and the - // flags are the whole difference between a scaffolder and a prompt nobody - // is there to answer. - // The scaffolder was run at build time for the frameworks with a baked - // template, so for those this step is already done before the model reads - // it. Saying so here rather than only in the tool result, because the - // instruction it contradicts is this one: a run that reaches step 2 with - // its workspace already populated would otherwise put a scaffolder into a - // directory that is no longer empty, which every one of them refuses. - // A measured Next.js turn still loaded the frameworks index and nextjs.md - // after the template landed, then rewrote next.config just to add the - // prefix line the host now writes. Both loads exist to answer Scaffold and - // assetPrefix; neither is a question once the template is applied. - 'A templateApplied in the ensure_project_scaffold result means that framework\'s scaffolder has already been run for you and its files are in place. Skip the rest of this step and go to step 3 — do not run a scaffold command, and do not re-create files that are already there. Do not load makers-frameworks just to read the Scaffold command or the asset-prefix snippet: both are already done, and the prefix option is already in the framework config. Load it only for an adapter location, a 404 convention, or an unsupported-feature rule you are about to use. Load makers-storage, makers-agents, or makers-cloud-functions only when the request actually needs those.', - `2. When the request names a framework and no template was applied, the reference loaded in step 1 gives its scaffold command under Scaffold. Copy that command exactly and run it once through commands with cwd=${appDir}, into the current directory. Do not compose one from memory and do not drop or add a flag — the flags documented there are what keep it non-interactive, and a scaffolder that stops to ask a question in a sandbox hangs the turn. ${appDir} is empty here, which those tools require, and a generous timeout is needed because it installs as it goes. This is the one case where a command may create project source files.`, + `The host has already prepared an empty project directory at ${appDir} and started the coding agent. The workspace has no files yet. Work through these steps in order.`, + '1. Load the references this request needs with load_makers_skill and follow them for layout, routing, handler signatures, configuration files, and storage. Prefer static HTML/CSS/JS or Vite static output for ordinary UI. Do not put styles, scripts, and markup into one large index.html unless the user explicitly asks for a single-file page. load_makers_skill is the first tool of a new project — do not write files or run commands before the required references are loaded.', + `2. When the request names a framework, the reference loaded in step 1 gives its scaffold command under Scaffold. Copy that command exactly and run it once through commands with cwd=${appDir}, into the current directory. Do not compose one from memory and do not drop or add a flag — the flags documented there are what keep it non-interactive, and a scaffolder that stops to ask a question in a sandbox hangs the turn. ${appDir} is empty here, which those tools require, and a generous timeout is needed because it installs as it goes. This is the one case where a command may create project source files.`, 'A framework whose reference lists no scaffold command has none worth running: write its files yourself from the values that document gives. If the scaffolder prompts, hangs, or fails, that is one attempt and it is over: write the files yourself and let the build report what is wrong. Do not try a second scaffolder, a different package name, or a flag variation.', - // Sourcing the command from the references must not read as an allowlist of - // framework names. What the platform bounds is the output shape, not the - // name: it runs any build and uploads any output directory, so static is - // unbounded, while a server bundle needs an adapter that exists. 'A framework the references do not cover is still one this platform builds, so never decline a request for not finding it listed. Derive what it needs the way makers-frameworks describes — an adapter only if it emits a server bundle, its build command and output directory declared in edgeone.json, its own asset-prefix option — then build it and report what happened.', - // The tool's own mechanics — one file per call, paths relative to appDir, - // one call per message — are stated once in the tool contracts above. What - // belongs here is only the order, which is what this workflow decides. '3. After the required references are loaded, write the project with write_project_file, one complete file per call and in dependency order. When a scaffolder ran, keep what it produced and use these calls to adapt it — the platform declarations and the entry route — rather than rewriting files it already got right. If agents/chat.ts is already in the workspace, edit that file; do not also write agents/chat/index.ts — both mount POST /chat. Otherwise write configuration and dependencies first, then styles and small modules, then the entry HTML, then any platform function or agent directories. Dependencies come before agent code specifically: the platform declarations an agent project needs are derived from the packages it declares, so a dependency file that arrives later cannot inform them.', - // "a scaffolder has not already installed them" asked the wrong question. - // A workspace can arrive with its dependencies installed by something that - // is not a scaffolder, and then this rule reads as permission to install - // over a tree that is already there — which is how a turn spent four - // minutes filling the disk, breaking the tree it had, and ending with - // nothing runnable. ensure_project_scaffold now answers the right question. - `4. Install dependencies inside ${appDir} only when the project has a package.json with dependencies and ensure_project_scaffold reported dependenciesInstalled=false (cd ${appDir} && npm install by default; Python packages are declared in the project's requirements file and installed by the platform). Do not invent nested ${appDir}/${appDir} paths.`, + `4. The host starts npm install in the background the moment package.json is written. When you run npm install yourself, that command waits for the background install and reports its result — it does not install twice. Run npm install inside ${appDir} only when the project has a package.json with dependencies that are not yet on disk (cd ${appDir} && npm install by default; Python packages are declared in the project's requirements file and installed by the platform). Do not invent nested ${appDir}/${appDir} paths.`, 'Take every dependency name and version range from the reference you loaded for that framework, and copy its dependency block as written. Versions recalled from memory are the usual cause of peer-dependency conflicts and engine mismatches, and each one costs a rewrite plus a reinstall. If a reference pins a version or caps a range, keep the pin instead of widening it to latest.', '5. Check gateway credentials as the preview section requires, then stop. The host starts the sandbox preview. Do not curl/fetch/code_interpreter the public URL and do not start a preview server.', ]; } -const EXISTING_PROJECT_WORKFLOW = [ - 'When ensure_project_scaffold returns created=false, load only the specific Makers references required by the change with load_makers_skill, inspect only the project files directly related to the request, then make the smallest complete change needed.', - 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command, then check gateway credentials as the preview section requires. The host starts the sandbox preview.', -]; +function buildExistingProjectWorkflow(appDir: string) { + return [ + `When ${appDir} already contains project files, load only the specific Makers references required by the change with load_makers_skill, inspect only the project files directly related to the request, then make the smallest complete change needed.`, + 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command, then check gateway credentials as the preview section requires. The host starts the sandbox preview.', + ]; +} const CODE_QUALITY = [ // Three deliverable classes, not two: an AI agent endpoint is what most of @@ -288,9 +253,7 @@ const CODE_QUALITY = [ 'If you generate a package.json, include scripts.build. For a static HTML/CSS/JS site use "scripts": { "build": "echo skip" }. Vite/Next must use their real build script.', // The config file's extension used to be pinned to .js/.mjs here, and that // cost a delete and a rewrite on every Next.js project: create-next-app - // writes next.config.ts, so the baked template ships one, and the rule sent - // the model to replace a typed config it had just been given with one - // recalled from memory. Nothing needed it — Next has read a TypeScript config + // writes next.config.ts. Nothing needed it — Next has read a TypeScript config // since 15, and this repo deploys to the same platform with one. `If you generate a Next.js project, use the App Router and do not set basePath to ${PREVIEW_PATH_PREFIX}.`, `If you generate a Vite React project, install @vitejs/plugin-react and configure plugins: [react()]. Set base from process.env.${PREVIEW_ASSET_PREFIX_ENV} as described above, never to a literal.`, @@ -299,18 +262,9 @@ const CODE_QUALITY = [ function buildNarration(appDir: string) { return [ - 'If the user request requires creating or modifying a project, first respond with one brief natural-language sentence that you are starting, then call ensure_project_scaffold as the first tool to prepare the workspace. Do not call any other tool before ensure_project_scaffold — including Skill, load_makers_skill, files_list, files_make_dir, files_write, commands, or write_project_file.', - // The whole saving rides on this argument arriving in the first call. It is - // the only point at which the host can still put the files down and start - // the install before the model spends a turn on anything else, and the name - // is in the user's message — nothing has to be loaded to know it. - 'Pass framework to that call whenever the request names one, in whatever spelling the user used. Omit it for a plain HTML/CSS/JS page and when no framework was named — it is what the workspace is prepared from, not a decision to make on the user\'s behalf.', - `Before calling ensure_project_scaffold, do not read, write, or execute anything under ${appDir}.`, - 'That first sentence must be concise, user-visible progress narration, not a plan. Use the user language when obvious. Example: 我先准备项目环境,然后开始实现。 / I will prepare the workspace first, then start building.', + `The host has already prepared an empty workspace at ${appDir} and started the coding agent. If the user request requires creating or modifying a project, first respond with one brief natural-language sentence that you are starting, then call load_makers_skill as the first tool. Do not call write_project_file, files_write, files_list, files_make_dir, or commands before the references this request needs are loaded.`, + 'That first sentence must be concise, user-visible progress narration, not a plan. Use the user language when obvious. Example: 我先查一下这个框架的官方用法,然后开始实现。 / I will look up the framework guide first, then start building.', 'Keep narrating as you work: before each tool call or parallel group of tool calls, write one short sentence saying what you are about to do and, when you just read an error, what you think is wrong. This narration is shown to the user, so always write it in the user language, never as internal English notes, raw logs, status codes, or command lines. Example: 我先修好前端请求地址,再刷新预览。 One sentence per step — do not restate the plan or repeat what you already said.', - // The user is here for EdgeOne; Makers, its CLI and its reference documents are - // machinery they never asked about, and a sentence that names them reads as the - // agent talking about itself instead of about their project. 'Narration and the final reply are product copy. Never write the words Makers, load_makers_skill, or a makers-* document id in them, and never name your own tools, the sandbox, or the CLI. Say what the work is about instead: 我先查一下持久化存储的官方用法。 not 我先加载 makers-storage 技能。, and 预览已经启动。 not 我运行了 edgeone makers dev。 When the platform itself has to be named, call it EdgeOne.', ]; } @@ -369,13 +323,13 @@ export function buildPrompt( section('Sandbox: browser calls and visitor context', buildSandboxDataPlane()), section('Tool contracts', buildToolContracts(state.appDir)), section('Workflow: a new project', buildNewProjectWorkflow(state.appDir), true), - section('Workflow: an existing project', EXISTING_PROJECT_WORKFLOW), + section('Workflow: an existing project', buildExistingProjectWorkflow(state.appDir)), section('Code quality', CODE_QUALITY), section('Narration', buildNarration(state.appDir)), section('Final reply', FINAL_REPLY), isNewProject - ? 'The project workspace may not have been prepared yet.' - : 'This conversation has already prepared a project workspace.', + ? 'The project workspace is empty and ready for you to write files.' + : 'This conversation already has a project workspace with files in it.', ].join('\n\n'); } diff --git a/agents/_lib/session/live.ts b/agents/_lib/session/live.ts index 4089bcd..ff22270 100644 --- a/agents/_lib/session/live.ts +++ b/agents/_lib/session/live.ts @@ -39,7 +39,6 @@ import { getConversationRecord, getLanguagePreference, patchConversationRecord } import { downloadTranscript, resolveClaudeTranscriptPath, uploadTranscript } from './transcript.ts'; import { PromptQueue } from './prompt-queue.ts'; import { - SCAFFOLD_TOOL_NAME, createProgressEmitter, describeSdkMessage, extractVisibleNarrationDelta, @@ -68,24 +67,31 @@ type LiveQuerySession = LiveSessionHandle & { turn?: TurnWaiter; state: ProjectState; pump: Promise; + idleTimer?: ReturnType; }; +/** Give up waiting for the CLI's SessionStart hook and let /prompt reuse the process. */ +export const WARM_LIVE_QUERY_BUDGET_MS = 20_000; +/** Close a warmed process that never received a turn, so abandoned visits do not leak one. */ +export const LIVE_QUERY_IDLE_MS = 5 * 60 * 1000; + const liveQueries = new Map(); -export type RunCodingAgentOptions = { +export type StartLiveQueryOptions = { context: AgentContext; conversationId: string; - userMessage: string; state: ProjectState; isNewProject: boolean; - onScaffoldLog?: LiveTurnCallbacks['onScaffoldLog']; + abortSignal?: AbortSignal; + model?: string; +}; + +export type RunCodingAgentOptions = StartLiveQueryOptions & { + userMessage: string; onProgress?: (event: AgentProgressEvent) => void; onProjectFilesChanged?: LiveTurnCallbacks['onProjectFilesChanged']; onPreviewReady?: LiveTurnCallbacks['onPreviewReady']; onDeploymentStatus?: LiveTurnCallbacks['onDeploymentStatus']; - onWorkspaceReady?: LiveTurnCallbacks['onWorkspaceReady']; - abortSignal?: AbortSignal; - model?: string; send?: LiveTurnCallbacks['send']; }; @@ -127,10 +133,58 @@ function flagsFrom(session: LiveQuerySession): Pick< filesWritten: session.flags.filesWritten, previewTouched: session.flags.previewTouched, deploymentTouched: session.flags.deploymentTouched, - wasCreated: session.flags.wasCreated, + wasCreated: false, }; } +function clearIdleTimer(session: LiveQuerySession) { + if (!session.idleTimer) return; + clearTimeout(session.idleTimer); + session.idleTimer = undefined; +} + +function scheduleIdleClose(session: LiveQuerySession) { + clearIdleTimer(session); + session.idleTimer = setTimeout(() => { + if (session.turn) return; + void disposeLiveQuery(session.conversationId); + }, LIVE_QUERY_IDLE_MS); +} + +function disposeLiveQuery(conversationId: string) { + const session = liveQueries.get(conversationId); + if (!session || session.turn) return false; + clearIdleTimer(session); + liveQueries.delete(conversationId); + try { + session.query.close(); + } catch (error) { + console.warn('[agent] failed to close the idle SDK query', error); + } + session.queue.close(); + return true; +} + +function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +async function waitForLiveSessionId( + session: LiveQuerySession, + budgetMs: number, + signal?: AbortSignal, +) { + const startedAt = Date.now(); + while (!session.sessionId && Date.now() - startedAt < budgetMs) { + if (signal?.aborted) return false; + if (liveQueries.get(session.conversationId) !== session) return false; + await sleep(50); + } + return Boolean(session.sessionId); +} + async function persistTranscript(session: LiveQuerySession) { if (!session.sessionId || !session.transcriptPath) return; await uploadTranscript({ @@ -148,7 +202,6 @@ async function pumpSession(session: LiveQuerySession) { appDir: session.getState().appDir, onProgress: (event) => session.turn?.onProgress?.(event), }); - let scaffoldHandled = false; let fatalError: string | null = null; const finishTurn = async (result: CodingAgentResult) => { @@ -299,14 +352,6 @@ async function pumpSession(session: LiveQuerySession) { endedAt: Date.now(), }, }); - if (!scaffoldHandled && toolName === SCAFFOLD_TOOL_NAME && record.is_error !== true) { - scaffoldHandled = true; - try { - await session.getCallbacks().onProjectFilesChanged?.(); - } catch (error) { - console.warn('[scaffold-done] onProjectFilesChanged failed', error); - } - } if (record.is_error === true && !fatalError) { const fatal = detectFatalToolError(text); if (fatal) { @@ -376,7 +421,6 @@ async function pumpSession(session: LiveQuerySession) { } pendingToolUseBlocks.clear(); progress.resetTurn(); - scaffoldHandled = false; fatalError = null; continue; } @@ -402,6 +446,7 @@ async function pumpSession(session: LiveQuerySession) { })); } liveQueries.delete(session.conversationId); + clearIdleTimer(session); try { session.query.close(); } catch (error) { @@ -411,7 +456,7 @@ async function pumpSession(session: LiveQuerySession) { } } -async function startLiveQuery(options: RunCodingAgentOptions): Promise { +async function startLiveQuery(options: StartLiveQueryOptions): Promise { const { context, conversationId } = options; const apiKey = pickEnvValue(context, 'AI_GATEWAY_API_KEY') || pickEnvValue(context, 'ANTHROPIC_API_KEY') @@ -448,7 +493,6 @@ async function startLiveQuery(options: RunCodingAgentOptions): Promise { + if (options.abortSignal?.aborted) { + return { ok: false, reused: false, error: 'aborted' }; + } + + let session = liveQueries.get(options.conversationId); + const reused = Boolean(session); + if (!session) { + const started = await startLiveQuery(options); + if (!('queue' in started)) { + return { + ok: false, + reused: false, + error: started.error || 'The coding agent could not start.', + }; + } + session = started; + } else { + session.context = options.context; + session.state = options.state; + } + + if (!session.turn) scheduleIdleClose(session); + await waitForLiveSessionId(session, WARM_LIVE_QUERY_BUDGET_MS, options.abortSignal); + if (options.abortSignal?.aborted) { + return { ok: false, reused, error: 'aborted' }; + } + return { ok: true, reused }; +} + export async function interruptLiveQuery(conversationId: string) { const live = liveQueries.get(conversationId); if (!live) return false; @@ -605,7 +685,7 @@ export async function runCodingAgent(options: RunCodingAgentOptions): Promise { void interruptLiveQuery(options.conversationId); @@ -616,11 +696,9 @@ export async function runCodingAgent(options: RunCodingAgentOptions): Promise((resolve) => { session!.turn = { callbacks: { - onScaffoldLog: options.onScaffoldLog, onProjectFilesChanged: options.onProjectFilesChanged, onPreviewReady: options.onPreviewReady, onDeploymentStatus: options.onDeploymentStatus, - onWorkspaceReady: options.onWorkspaceReady, send: options.send, abortSignal: options.abortSignal, }, diff --git a/agents/_lib/session/prepare.ts b/agents/_lib/session/prepare.ts new file mode 100644 index 0000000..55101c6 --- /dev/null +++ b/agents/_lib/session/prepare.ts @@ -0,0 +1,148 @@ +import type { AgentContext } from '../runtime/context.ts'; +import { mergeSseGenerators } from '../runtime/merge.ts'; +import { getRequestQueryParam } from '../runtime/request.ts'; +import { sseEvent } from '../runtime/sse.ts'; +import { extendExistingSandboxTimeout } from '../turn/checkpoint.ts'; +import { ensureWorkspaceDirectories } from '../project/workspace.ts'; +import { getProjectState, patchConversationRecord } from './store.ts'; +import { warmLiveQuery } from './live.ts'; +import type { + SessionPrepData, + SessionPrepMode, + SessionPrepStage, + SessionPrepStatus, +} from '../../../shared/protocol.ts'; + +export function resolveSessionPrepMode(context: AgentContext): SessionPrepMode { + return getRequestQueryParam(context, 'mode').value === 'create' ? 'create' : 'restore'; +} + +export function sessionPrepSse( + mode: SessionPrepMode, + stage: SessionPrepStage, + status: SessionPrepStatus, +): string { + const data: SessionPrepData = { mode, stage, status }; + return sseEvent({ type: 'session_prep', data }); +} + +export async function persistConversationPreferences( + context: AgentContext, + conversationId: string, + options: { model?: string; language?: string } = {}, +) { + const model = (options.model || '').trim(); + const language = (options.language || '').trim(); + await patchConversationRecord(context, conversationId, { + ...(model ? { modelPreference: model } : {}), + ...(language === 'zh' || language === 'en' ? { languagePreference: language } : {}), + }); +} + +export async function prepareSandboxWorkspace(context: AgentContext, conversationId: string) { + await extendExistingSandboxTimeout(context); + const state = await getProjectState(context, conversationId); + await ensureWorkspaceDirectories(context, state); + return state; +} + +export async function* iterateConversationPrep( + context: AgentContext, + conversationId: string, + options: { + mode: SessionPrepMode; + model?: string; + language?: string; + signal?: AbortSignal; + }, +): AsyncGenerator { + const { mode, signal } = options; + yield sessionPrepSse(mode, 'conversation', 'running'); + try { + await persistConversationPreferences(context, conversationId, { + model: options.model, + language: options.language, + }); + if (signal?.aborted) return; + yield sessionPrepSse(mode, 'conversation', 'done'); + } catch (error) { + console.warn( + '[session:prep] conversation', + error instanceof Error ? error.message : error, + ); + if (!signal?.aborted) yield sessionPrepSse(mode, 'conversation', 'failed'); + } +} + +export async function* iterateSandboxPrepEvents( + context: AgentContext, + conversationId: string, + options: { + mode: SessionPrepMode; + signal?: AbortSignal; + }, +): AsyncGenerator { + const { mode, signal } = options; + yield sessionPrepSse(mode, 'sandbox', 'running'); + try { + await prepareSandboxWorkspace(context, conversationId); + if (signal?.aborted) return; + yield sessionPrepSse(mode, 'sandbox', 'done'); + } catch (error) { + console.warn( + '[session:prep] sandbox', + error instanceof Error ? error.message : error, + ); + if (!signal?.aborted) yield sessionPrepSse(mode, 'sandbox', 'failed'); + } +} + +export async function* iterateAgentWarmupEvents( + context: AgentContext, + conversationId: string, + options: { + mode: SessionPrepMode; + isNewProject: boolean; + model?: string; + signal?: AbortSignal; + }, +): AsyncGenerator { + const { mode, signal } = options; + yield sessionPrepSse(mode, 'agent', 'running'); + try { + const state = await getProjectState(context, conversationId); + const warmed = await warmLiveQuery({ + context, + conversationId, + state, + isNewProject: options.isNewProject, + model: options.model, + abortSignal: signal, + }); + if (signal?.aborted) return; + yield sessionPrepSse(mode, 'agent', warmed.ok ? 'done' : 'failed'); + } catch (error) { + console.warn( + '[session:prep] agent', + error instanceof Error ? error.message : error, + ); + if (!signal?.aborted) yield sessionPrepSse(mode, 'agent', 'failed'); + } +} + +/** Activate the sandbox and pre-warm the CLI together; the coding turn waits for both. */ +export async function* iterateSandboxAndAgentPrep( + context: AgentContext, + conversationId: string, + options: { + mode: SessionPrepMode; + isNewProject: boolean; + model?: string; + signal?: AbortSignal; + }, +): AsyncGenerator { + yield* mergeSseGenerators([ + iterateSandboxPrepEvents(context, conversationId, options), + iterateAgentWarmupEvents(context, conversationId, options), + ], options.signal); +} diff --git a/agents/_lib/session/resume.ts b/agents/_lib/session/resume.ts index 06b3c4c..b60c4a7 100644 --- a/agents/_lib/session/resume.ts +++ b/agents/_lib/session/resume.ts @@ -23,8 +23,14 @@ import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; import { mergeSseGenerators } from '../runtime/merge.ts'; import { isMakersDeployUrl } from '../../../shared/makers-url.ts'; import { isMakersDeployCommand, isMakersDevCommand } from '../makers/tool-phase.ts'; -import { resolveConversationId } from '../runtime/request.ts'; +import { resolveConversationId, getRequestQueryParam } from '../runtime/request.ts'; import { ensureProjectDependencies, withTimeout } from '../turn/checkpoint.ts'; +import { + iterateConversationPrep, + iterateSandboxAndAgentPrep, + resolveSessionPrepMode, + sessionPrepSse, +} from './prepare.ts'; function isMakersPreviewState(state: ProjectState) { return state.previewKind === 'makers' || isMakersDeployUrl(state.previewUrl); @@ -32,14 +38,13 @@ function isMakersPreviewState(state: ProjectState) { function toolNameImpliesProject(name: string) { return name.includes('write_project_file') - || name.includes('ensure_project_scaffold') || name.includes('write_files') || /__files_write$/.test(name); } function activityIsMakersCli(activity: PersistedActivity) { if (activity.kind !== 'tool' || !activity.name.includes('commands')) return false; - const command = activity.command || activity.inputSummary || ''; + const command = activity.inputSummary || ''; return isMakersDevCommand(command) || isMakersDeployCommand(command); } @@ -337,8 +342,10 @@ export async function runProjectResumePreviewPipeline(context: AgentContext): Pr async function* iterateWorkspaceResumeEvents( context: AgentContext, conversationId: string, + mode: ReturnType, signal?: AbortSignal, ): AsyncGenerator { + yield sessionPrepSse(mode, 'workspace', 'running'); try { const workspace = await withTimeout( runWorkspaceRestoreBody(context, conversationId), @@ -347,6 +354,10 @@ async function* iterateWorkspaceResumeEvents( ); if (signal?.aborted) return; yield sseEvent({ type: 'resume_workspace', data: workspace }); + yield sessionPrepSse(mode, 'workspace', 'done'); + if (workspace.preview && 'url' in workspace.preview && workspace.preview.url) { + yield sessionPrepSse(mode, 'preview', 'done'); + } const fileItems = workspace.files?.items || []; const paths = fileItems.filter((item) => item.type === 'file').map((item) => item.path); @@ -357,6 +368,7 @@ async function* iterateWorkspaceResumeEvents( const message = error instanceof Error ? error.message : 'Workspace resume failed.'; console.warn('[resume:stream]', message); if (!signal?.aborted) { + yield sessionPrepSse(mode, 'workspace', 'failed'); yield sseEvent({ type: 'resume_workspace', data: { @@ -378,24 +390,54 @@ export async function createProjectResumeStreamResponse(context: AgentContext): return jsonResponse({ ok: false, error: 'missing conversation_id' }, 400); } + const mode = resolveSessionPrepMode(context); + const model = getRequestQueryParam(context, 'model').value; + const language = getRequestQueryParam(context, 'language').value; + return createSSEResponse(async function* (signal) { + yield* iterateConversationPrep(context, conversationId, { + mode, + model, + language, + signal, + }); + if (signal?.aborted) return; + + if (mode === 'create') { + yield* iterateSandboxAndAgentPrep(context, conversationId, { + mode, + isNewProject: true, + model, + signal, + }); + if (!signal?.aborted) yield sessionPrepSse(mode, 'ready', 'done'); + return; + } + const history = await loadProjectResumeHistory(context, conversationId); yield sseEvent({ type: 'resume_history', data: history }); - if (signal?.aborted) return; const storedTask = await getChatTask(context, conversationId); const liveTask = isChatTaskActive(storedTask) && hasLiveChatTask(conversationId, storedTask.id) ? storedTask : null; - const generators: Array> = []; + const generators: Array> = [ + iterateSandboxAndAgentPrep(context, conversationId, { + mode, + isNewProject: !history.hasProject, + model: model || history.model, + signal, + }), + ]; if (history.needsWorkspace) { - generators.push(iterateWorkspaceResumeEvents(context, conversationId, signal)); + generators.push(iterateWorkspaceResumeEvents(context, conversationId, mode, signal)); } - if (liveTask) { - generators.push(iterateLiveChatTaskEvents(context, conversationId, liveTask, undefined, signal)); - } - if (generators.length === 0) return; yield* mergeSseGenerators(generators, signal); + if (!signal?.aborted) yield sessionPrepSse(mode, 'ready', 'done'); + + if (liveTask && !signal?.aborted) { + yield* iterateLiveChatTaskEvents(context, conversationId, liveTask, undefined, signal); + } }, context?.request?.signal); } diff --git a/agents/_lib/session/stream-projector.ts b/agents/_lib/session/stream-projector.ts index 0cc920b..2c7ea6e 100644 --- a/agents/_lib/session/stream-projector.ts +++ b/agents/_lib/session/stream-projector.ts @@ -1,5 +1,4 @@ import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; -import { SANDBOX_MCP_SERVER_NAME } from '../constants.ts'; import type { AgentProgressEvent } from '../types.ts'; import type { SystemInfoType } from '../../../shared/protocol.ts'; import { @@ -285,7 +284,6 @@ export function inferToolProgress(name: string, input: unknown): { fileCount?: number; } { const toolName = shortenToolName(name); - if (toolName === 'ensure_project_scaffold') return { phaseHint: 'scaffold' }; if (toolName === 'files_write' || toolName === 'write_files' || toolName === 'files_make_dir' || toolName === 'files_remove') { return { phaseHint: 'code' }; } @@ -298,8 +296,6 @@ export function inferToolProgress(name: string, input: unknown): { return {}; } -export const SCAFFOLD_TOOL_NAME = `mcp__${SANDBOX_MCP_SERVER_NAME}__ensure_project_scaffold`; - export function createProgressEmitter(options: { appDir: string; onProgress?: (event: AgentProgressEvent) => void; diff --git a/agents/_lib/tools/assemble.ts b/agents/_lib/tools/assemble.ts index 4fd38b2..cc519e0 100644 --- a/agents/_lib/tools/assemble.ts +++ b/agents/_lib/tools/assemble.ts @@ -1,8 +1,5 @@ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; -import { - MAKERS_SKILL_NAMES, - SANDBOX_MCP_SERVER_NAME, -} from '../constants.ts'; +import { SANDBOX_MCP_SERVER_NAME } from '../constants.ts'; import { buildRequestGatewayCredentialsTool, REQUEST_GATEWAY_CREDENTIALS_TOOL, @@ -14,7 +11,6 @@ import type { DeploymentInfo, PreviewKind, ProjectState, - ScaffoldLog, StreamSend, } from '../types.ts'; import { wrapSandboxTools, type MakersCommandLifecycle } from './commands-wrap.ts'; @@ -25,18 +21,12 @@ import { isWebSearchToolName, } from '../../../shared/web-search.ts'; import { buildLoadMakersSkillTool } from './makers-skills.ts'; -import { - buildProjectScaffoldTool, - buildWriteProjectFileTool, -} from './project-tools.ts'; +import { buildWriteProjectFileTool } from './project-tools.ts'; export type LiveTurnCallbacks = { - onScaffoldLog?: (log: ScaffoldLog) => void; onProjectFilesChanged?: (file?: { path: string; content: string }) => void | Promise; onPreviewReady?: (preview: { url?: string; sandboxDebugUrl?: string; kind?: PreviewKind }) => void; onDeploymentStatus?: (deployment: DeploymentInfo) => void; - /** Project files exist in this sandbox; the host can start dest. */ - onWorkspaceReady?: () => void; send?: StreamSend; abortSignal?: AbortSignal; }; @@ -51,7 +41,6 @@ export type LiveSessionHandle = { filesWritten: boolean; previewTouched: boolean; deploymentTouched: boolean; - wasCreated: boolean; }; }; @@ -88,16 +77,6 @@ export function assembleAgentTools(session: LiveSessionHandle) { && !isGenericProjectWriteToolName(name) && (webSearchAvailable || !isWebSearchToolName(name)); - const scaffoldTool = buildProjectScaffoldTool( - context, - session.getState(), - (log) => session.getCallbacks().onScaffoldLog?.(log), - ({ created }) => { - session.flags.projectTouched = true; - session.flags.wasCreated = created; - session.getCallbacks().onWorkspaceReady?.(); - }, - ); const writeProjectFileTool = buildWriteProjectFileTool( context, session.getState(), @@ -133,7 +112,6 @@ export function assembleAgentTools(session: LiveSessionHandle) { )); const mcpTools = [ ...sandboxTools, - scaffoldTool, buildLoadMakersSkillTool(), writeProjectFileTool, buildRequestGatewayCredentialsTool({ @@ -149,7 +127,6 @@ export function assembleAgentTools(session: LiveSessionHandle) { ]; const mcpAllowedTools = [ ...edgeoneMcp.allowedTools.filter(offerSandboxTool), - `mcp__${mcpServerName}__ensure_project_scaffold`, `mcp__${mcpServerName}__load_makers_skill`, `mcp__${mcpServerName}__write_project_file`, `mcp__${mcpServerName}__${REQUEST_GATEWAY_CREDENTIALS_TOOL}`, diff --git a/agents/_lib/tools/project-tools.ts b/agents/_lib/tools/project-tools.ts index a025bc2..fa0ce39 100644 --- a/agents/_lib/tools/project-tools.ts +++ b/agents/_lib/tools/project-tools.ts @@ -1,15 +1,13 @@ import { requireSandbox, type AgentContext } from '../runtime/context.ts'; import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; import { z } from 'zod'; -import { ensureProjectScaffold } from '../project/scaffold.ts'; import { markCreated } from '../project/workspace-store.ts'; import { buildNpmWarmupCommand } from '../makers/npm-install.ts'; import { ensureMakersAgentDeclarations, ensureMakersFrameworkAdapter, } from '../makers/declarations.ts'; -import type { ScaffoldOutcome } from '../project/scaffold.ts'; -import type { ClaudeMcpTool, ProjectState, ScaffoldLog } from '../types.ts'; +import type { ClaudeMcpTool, ProjectState } from '../types.ts'; import { getBlockedProjectWriteReason, toAppRelPath } from '../utils/paths.ts'; import { stringifyToolResult } from '../utils/text.ts'; @@ -20,121 +18,6 @@ const writeProjectFileInputSchema = { content: z.string().describe('Complete UTF-8 contents for that one file.'), }; -const scaffoldInputSchema = { - framework: z - .string() - .optional() - .describe( - 'The web framework the user asked for, if the request named one — for example "Next.js", "Vite", "Nuxt", "Astro", "SvelteKit". Omit it for a plain HTML/CSS/JS page or when no framework was named. When a baked template exists for it, the workspace comes back already holding that framework\'s project files with the install running, and no scaffolder needs to be run.', - ), -}; - -/** - * What the model is told about the workspace it just asked for. - * - * One function rather than a literal with two conditional spreads in it, - * because both of them wanted to set installHint and the second silently won. - * There is only ever one right answer to "should I install", and the order it - * is decided in here is the order the cases actually rank: a populated - * node_modules settles it whatever else happened, then an install this call - * started, then nothing to say. - */ -export function describeScaffold( - state: ProjectState, - outcome: ScaffoldOutcome, -): Record { - const { created, dependenciesInstalled, template, available } = outcome; - return { - created, - appDir: state.appDir, - dependenciesInstalled, - // The scaffolder step, reported as already done. The model has no listing - // of the workspace, so without this it reaches step 2 of the workflow and - // runs a scaffolder into a directory that is no longer empty. - ...(template - ? { - templateApplied: template.id, - templateFiles: template.files, - scaffolderHint: [ - `The ${template.id} scaffolder has already been run for you and its ${template.files} files are in ${state.appDir}. Do not run a scaffold command. Adapt what is there — the platform declarations and the entry route — rather than rewriting files it already got right. The preview asset-prefix option is already in the framework config; do not set it again.`, - ...(template.id === 'deepagents' || template.id === 'langgraph' - ? ['The chat endpoint is agents/chat.ts. Edit that file; do not create agents/chat/index.ts — both mount POST /chat.'] - : []), - ].join(' '), - ...(template.adapted - ? { - adapterHint: 'This framework\'s platform adapter was added to package.json before the install started, so the dependency is already on its way. Wiring it into the framework config is still yours to do; makers-frameworks says where it goes.', - } - : {}), - } - // The trees that were there and went unused, named so the miss is - // recoverable. A status log is not enough — only this result reaches the - // model, so a gap here reads to it as "there is no template for this" - // rather than "you did not ask for one", and it goes on to write the tree - // by hand beside a baked one. - : available?.length - ? { - templatesAvailable: available, - templatesHint: `No baked template was applied, because the framework argument matched none. These are baked and ready: ${available.join(', ')}. If one of them fits what you are about to build, call ensure_project_scaffold again with that id as framework — the workspace is still empty, so it will be filled from the baked tree, install and all. Prefer that over writing package.json and an entry file by hand. If none fits, carry on and generate the project yourself.`, - } - : {}), - // Said outright, because the listing above cannot show it and the model's - // default reading of a project it did not install is that it needs - // installing. The disk is the reason it must not: the cache npm fills to - // install is about as large as the tree it installs, and only one of them - // fits beside the other here. - ...(dependenciesInstalled - ? { - installHint: 'node_modules is already populated and its executables work. Do not run npm install — the download cache would not fit beside the existing tree, and a failed install leaves the tree unusable. Install only when you add a dependency, and then name it (npm install ).', - } - : template - ? { - installHint: 'The install for this template is already running against this package.json. Run npm install only after you add a package to it.', - } - : {}), - writePathHint: 'write_project_file path is relative to appDir (e.g. package.json, src/App.tsx), never prefix with appDir', - }; -} - -export function buildProjectScaffoldTool( - context: AgentContext, - state: ProjectState, - onLog?: (log: ScaffoldLog) => void, - onResult?: (result: { created: boolean }) => void, -) { - return defineClaudeTool( - 'ensure_project_scaffold', - 'Prepare or reuse the project workspace in the EdgeOne sandbox before any project file reads or writes. Always pass framework: the framework the request names, or, when it names none, the kind of app being built (chat, agent, react). Baked templates are matched from it, and a workspace prepared from one arrives with its files and its install already started; omitting it is what leaves the workspace empty.', - scaffoldInputSchema, - async (input) => { - try { - const requested = input as { framework?: unknown }; - const { created, dependenciesInstalled, template } = await ensureProjectScaffold( - context, - state, - onLog, - { framework: typeof requested.framework === 'string' ? requested.framework : undefined }, - ); - markCreated(state); - onResult?.({ created }); - return { - content: [{ - type: 'text' as const, - text: stringifyToolResult( - describeScaffold(state, { created, dependenciesInstalled, template }), - ), - }], - }; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - return { - content: [{ type: 'text' as const, text: message }], - isError: true, - }; - } - }, - ) as ClaudeMcpTool; -} export function buildWriteProjectFileTool( context: AgentContext, state: ProjectState, @@ -168,6 +51,7 @@ export function buildWriteProjectFileTool( await requireSandbox(context).files.makeDir(`${state.appDir}/${parent}`); } await requireSandbox(context).files.write(`${state.appDir}/${relPath}`, file.content); + markCreated(state); await onResult?.({ written: relPath, content: file.content }); // An agents/ project needs agents.framework and .env.example declared, // and meeting that at the preview gate instead costs the user a failed @@ -223,4 +107,4 @@ export function buildWriteProjectFileTool( } }, ) as ClaudeMcpTool; -} \ No newline at end of file +} diff --git a/agents/_lib/turn/auto-fix.ts b/agents/_lib/turn/auto-fix.ts index 153173e..a99fdaa 100644 --- a/agents/_lib/turn/auto-fix.ts +++ b/agents/_lib/turn/auto-fix.ts @@ -7,7 +7,6 @@ import type { DeploymentInfo, PreviewKind, ProjectState, - ScaffoldLog, StreamSend, } from '../types.ts'; import { buildAutoFixPrompt } from '../utils/build-errors.ts'; @@ -20,7 +19,6 @@ export type AutoFixTurnInput = { state: ProjectState; assistantReply: string; build: BuildResult; - onScaffoldLog: (log: ScaffoldLog) => void; onProgress: (event: AgentProgressEvent) => void; onProjectFilesChanged: (file?: { path: string; content: string }) => Promise; onPreviewReady: (preview: { @@ -51,7 +49,6 @@ export async function runAutoFixTurn(input: AutoFixTurnInput): Promise<{ userMessage: prompt, state: input.state, isNewProject: false, - onScaffoldLog: input.onScaffoldLog, onProgress: input.onProgress, onProjectFilesChanged: input.onProjectFilesChanged, onPreviewReady: input.onPreviewReady, diff --git a/agents/_lib/turn/chat.ts b/agents/_lib/turn/chat.ts index 96c094f..91ce97f 100644 --- a/agents/_lib/turn/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -13,7 +13,6 @@ import { import type { AgentProgressEvent, DeploymentInfo, - ScaffoldLog, StreamSend, } from '../types.ts'; import { toAppRelPath } from '../utils/paths.ts'; @@ -111,8 +110,7 @@ export async function runChatPipeline( send, ); } - const isInitialProjectTurn = !state.created; - const hiddenScaffoldToolUseIds = new Set(); + const hiddenToolUseIds = new Set(); const activityTurnId = options.turnId || String(context?.run_id || `${Date.now()}-${Math.random().toString(36).slice(2)}`); @@ -130,18 +128,15 @@ export async function runChatPipeline( const recordProgress = turn.recordProgress; const finalizeTurn = turn.finalize; - const handleScaffoldLog = (_log: ScaffoldLog) => {}; const forwardProgress = (event: AgentProgressEvent) => { if (event.type === 'tool_use') { const name = event.data?.name || ''; - const hideScaffold = !isInitialProjectTurn - && (name === 'ensure_project_scaffold' || name.endsWith('__ensure_project_scaffold')); - if (hideScaffold || isRequestGatewayCredentialsTool(name)) { - hiddenScaffoldToolUseIds.add(event.data?.id || ''); + if (isRequestGatewayCredentialsTool(name)) { + hiddenToolUseIds.add(event.data?.id || ''); return; } } - if (event.type === 'tool_result' && hiddenScaffoldToolUseIds.has(event.data?.id || '')) { + if (event.type === 'tool_result' && hiddenToolUseIds.has(event.data?.id || '')) { return; } if (event.type === 'text_segment') { @@ -238,14 +233,10 @@ export async function runChatPipeline( userMessage: message, state, isNewProject: !state.created, - onScaffoldLog: handleScaffoldLog, onProgress: forwardProgress, onProjectFilesChanged: handleProjectFilesChanged, onPreviewReady: handlePreviewReady, onDeploymentStatus: handleDeploymentStatus, - onWorkspaceReady: () => { - void startHostPreview('[preview] after scaffold:'); - }, abortSignal, model: options.model, send, @@ -404,7 +395,6 @@ export async function runChatPipeline( state, assistantReply, build, - onScaffoldLog: handleScaffoldLog, onProgress: forwardProgress, onProjectFilesChanged: handleProjectFilesChanged, onPreviewReady: handlePreviewReady, diff --git a/agents/_lib/types.ts b/agents/_lib/types.ts index 32b366d..c45ff9c 100644 --- a/agents/_lib/types.ts +++ b/agents/_lib/types.ts @@ -81,9 +81,7 @@ export type CodingAgentResult = { projectTouched: boolean; /** * Whether this turn wrote a project file, as opposed to merely reaching the - * project. Scaffolding sets projectTouched and the workflow asks for it on - * every turn, so that flag cannot tell a build apart from a turn that only - * answered a question — and answering one is not a build that failed. + * project. Answering a question is not a build that failed. */ filesWritten?: boolean; previewTouched?: boolean; diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index 9e92a3a..336695f 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -20,6 +20,9 @@ import type { ChatMessage, ChatResponse, ChatStreamEvent, + SessionPrepData, + SessionPrepStage, + SessionStreamEvent, } from '@/app/types/workspace'; import { consumeEventStream } from '../sse'; import { @@ -41,10 +44,61 @@ type LiveCopy = { agentFlowEnded: string; }; +type PrepStageCopy = Record; + +function sessionPrepToChatEvents( + data: SessionPrepData, + labels: PrepStageCopy, +): ChatStreamEvent[] { + if (data.stage === 'ready') { + return (['conversation', 'sandbox', 'agent'] as const).map((stage) => ({ + type: 'tool_result' as const, + data: { + id: `session-prep-${stage}`, + ok: true, + status: 'completed' as const, + endedAt: Date.now(), + }, + })); + } + + const id = `session-prep-${data.stage}`; + const label = labels[data.stage] || data.stage; + if (data.status === 'running') { + return [{ + type: 'tool_use', + data: { + id, + name: 'environment', + inputSummary: label, + startedAt: Date.now(), + }, + }]; + } + + return [{ + type: 'tool_result', + data: { + id, + ok: data.status === 'done', + status: data.status === 'failed' ? 'failed' : 'completed', + endedAt: Date.now(), + }, + }]; +} + export function useLiveTurn(options: { language: Locale; model: string; - t: { response: LiveCopy; workspace: { deployRequest: string; gatewayPromptApiKey: string; gatewayPromptSkip: string } }; + t: { + response: LiveCopy; + workspace: { + deployRequest: string; + gatewayPromptApiKey: string; + gatewayPromptSkip: string; + prepStages: PrepStageCopy; + }; + }; workspace: WorkspaceStateApi; preview: PreviewSurfaceApi; snapshot: WorkspaceSnapshotApi; @@ -409,24 +463,50 @@ export function useLiveTurn(options: { const requestAbortController = new AbortController(); chatAbortControllerRef.current = requestAbortController; stoppingRef.current = false; - if (isStartingFromHome) { - try { - const resumeResponse = await openSessionStream( - requestConversationId, - requestAbortController.signal, - ); - const resumeType = resumeResponse.headers.get('content-type') || ''; - if ( - resumeResponse.ok - && resumeResponse.body - && resumeType.includes('text/event-stream') - ) { - await consumeEventStream(resumeResponse, () => {}); + if (isStartingFromHome) { + try { + const resumeResponse = await openSessionStream( + requestConversationId, + requestAbortController.signal, + { + model: modelRef.current, + language, + mode: 'create', + }, + ); + const resumeType = resumeResponse.headers.get('content-type') || ''; + if ( + resumeResponse.ok + && resumeResponse.body + && resumeType.includes('text/event-stream') + ) { + await consumeEventStream(resumeResponse, (event) => { + if (event.type !== 'session_prep' || !event.data) return; + const prepEvents = sessionPrepToChatEvents(event.data, t.workspace.prepStages); + setMessages((current) => + current.map((item) => { + if (item.id !== assistantMessageId) return item; + let activities = item.activities ?? []; + for (const prepEvent of prepEvents) { + const folded = applyStreamEvent({ + id: item.id, + user: '', + assistant: item.content, + status: 'completed', + createdAt: 0, + activities, + } satisfies PersistedActivityTurn, prepEvent); + activities = folded.activities; + } + return { ...item, activities }; + }), + ); + }); + } + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') throw error; } - } catch (error) { - if (error instanceof Error && error.name === 'AbortError') throw error; } - } const response = isDeploy ? await startDeployTurn({ conversationId: requestConversationId, diff --git a/app/features/workspace/hooks/use-session-resume.ts b/app/features/workspace/hooks/use-session-resume.ts index b2985bc..abd12f8 100644 --- a/app/features/workspace/hooks/use-session-resume.ts +++ b/app/features/workspace/hooks/use-session-resume.ts @@ -13,6 +13,7 @@ import type { ChatMessage, ChatStreamEvent, ResumeData, + SessionPrepStage, SessionStreamEvent, } from '@/app/types/workspace'; import { consumeEventStream } from '../sse'; @@ -46,6 +47,7 @@ export function useSessionResume(options: { const [resumeChecked, setResumeChecked] = useState(true); const [workspaceRestoring, setWorkspaceRestoring] = useState(false); + const [prepStage, setPrepStage] = useState(null); const resumeAbortControllerRef = useRef(null); useEffect(() => { @@ -194,7 +196,9 @@ export function useSessionResume(options: { } | null, }; try { - const response = await openSessionStream(existing, resumeController.signal); + const response = await openSessionStream(existing, resumeController.signal, { + mode: 'restore', + }); const contentType = response.headers.get('content-type') || ''; if (!response.ok || !response.body || !contentType.includes('text/event-stream')) { return; @@ -203,6 +207,11 @@ export function useSessionResume(options: { await consumeEventStream(response, (event) => { if (cancelled || workspaceEpoch !== workspaceEpochRef.current || event.type === 'ping') return; + if (event.type === 'session_prep' && event.data?.stage) { + setPrepStage(event.data.stage); + return; + } + if (event.type === 'resume_history' && event.data?.ok) { const historyData = event.data; const { restored, liveTaskId } = applyHistory(historyData); @@ -244,6 +253,7 @@ export function useSessionResume(options: { } finally { if (!cancelled) { setResumeChecked(true); + setPrepStage(null); if (workspaceEpoch === workspaceEpochRef.current) { setWorkspaceRestoring(false); liveAttach.session?.finish(); @@ -267,6 +277,7 @@ export function useSessionResume(options: { setResumeChecked, workspaceRestoring, setWorkspaceRestoring, + prepStage, resumeAbortControllerRef, }; } diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index 3832138..978ee65 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -1,10 +1,11 @@ +import type { Locale } from '@/app/i18n'; +import type { ModelOption } from '../../../shared/models'; import type { PersistedActivityTurn, ResumeData, + SessionPrepMode, WorkspaceSnapshot, } from '../../../shared/protocol'; -import type { ModelOption } from '../../../shared/models'; -import type { Locale } from '@/app/i18n'; function conversationHeaders(conversationId: string): HeadersInit { return { @@ -18,8 +19,21 @@ async function readJson(response: Response): Promise { return response.json().catch(() => null) as Promise; } -export function openSessionStream(conversationId: string, signal?: AbortSignal) { - return fetch('/session', { +export function openSessionStream( + conversationId: string, + signal?: AbortSignal, + options: { + model?: string; + language?: Locale; + mode?: SessionPrepMode; + } = {}, +) { + const params = new URLSearchParams(); + if (options.model) params.set('model', options.model); + if (options.language) params.set('language', options.language); + if (options.mode) params.set('mode', options.mode); + const query = params.toString(); + return fetch(`/session${query ? `?${query}` : ''}`, { method: 'GET', headers: conversationHeaders(conversationId), signal, diff --git a/app/i18n.ts b/app/i18n.ts index 4ec7bd9..0815dec 100644 --- a/app/i18n.ts +++ b/app/i18n.ts @@ -193,6 +193,14 @@ export const TRANSLATIONS = { resuming: '正在加载对话…', restoringWorkspace: '正在还原代码与预览…', previewStarting: '预览启动中…', + prepStages: { + conversation: '正在创建会话…', + sandbox: '正在启动沙箱…', + agent: '正在唤醒编码代理…', + workspace: '正在还原代码…', + preview: '正在启动预览…', + ready: '环境已就绪', + }, downloadFailed: '下载失败,请重试。', loadingPreview: '正在加载实时预览...', previewUnavailable: '预览连接已失效,正在等待重新连接。', @@ -374,6 +382,14 @@ export const TRANSLATIONS = { resuming: 'Loading conversation…', restoringWorkspace: 'Restoring code and preview…', previewStarting: 'Starting preview…', + prepStages: { + conversation: 'Creating the conversation…', + sandbox: 'Starting the sandbox…', + agent: 'Waking the coding agent…', + workspace: 'Restoring project files…', + preview: 'Starting preview…', + ready: 'Environment ready', + }, downloadFailed: 'Download failed, please retry.', loadingPreview: 'Loading live preview...', previewUnavailable: 'The preview connection expired. Reconnect to continue.', diff --git a/app/types/workspace.ts b/app/types/workspace.ts index 41da705..352b113 100644 --- a/app/types/workspace.ts +++ b/app/types/workspace.ts @@ -10,6 +10,8 @@ export type { LinkInfo, ResumeData, ResumeStreamEvent, + SessionPrepData, + SessionPrepStage, SessionStreamEvent, WorkspaceSnapshot, } from '../../shared/protocol'; diff --git a/edgeone.json b/edgeone.json index 5f41ce2..fd4a15f 100644 --- a/edgeone.json +++ b/edgeone.json @@ -4,10 +4,6 @@ "timeout": 1800, "sandbox":{ "timeout": 1800 - }, - "includeFiles": ["templates/**", "templates/**/.*"] - }, - "cloudFunctions": { - "includeFiles": [] + } } } diff --git a/package.json b/package.json index c008880..d782c42 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "build": "next build --turbopack", "start": "next start", "sync:skills": "node scripts/sync-skills.mjs", - "bake:templates": "node scripts/bake-templates.mjs", "test": "node --experimental-strip-types --test tests/*.test.ts", "typecheck": "tsc --noEmit && tsc -p tsconfig.agents.json" }, diff --git a/scripts/bake-templates.mjs b/scripts/bake-templates.mjs deleted file mode 100644 index 8bab661..0000000 --- a/scripts/bake-templates.mjs +++ /dev/null @@ -1,1180 +0,0 @@ -/** - * Run the official scaffolders once, here, so that no conversation has to. - * - * A new project used to reach its first file four round trips and a package - * download later: ensure_project_scaffold, load_makers_skill for the framework - * reference, then `npx create-next-app@latest` — which fetches the create - * package, fetches a template, and installs as it goes. All of it produces the - * same tree every time, and none of it depends on the request. - * - * So it happens at bake time instead. What lands in templates/ is the - * scaffolder's own output, byte for byte, minus the directories no project - * needs carried around; the agent writes it into the sandbox in one command and - * starts the install in the same breath. - * - * The command is not written down here. It lives in the framework reference the - * agent already loads, this script parses it out of there, and the manifest - * records which command produced which tree so a skill sync that changes a - * scaffolder fails the test instead of silently leaving a stale template. - * - * Usage: - * node scripts/bake-templates.mjs # every bakeable framework - * node scripts/bake-templates.mjs nextjs # just this one - * node scripts/bake-templates.mjs --list # what could be baked, and how - */ - -import { execFile } from 'node:child_process'; -import { - cp, - mkdir, - mkdtemp, - readdir, - readFile, - rename, - rm, - stat, - writeFile, -} from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import process from 'node:process'; -import { promisify } from 'node:util'; - -const run = promisify(execFile); - -const repoRoot = path.resolve(import.meta.dirname, '..'); -const skillsDir = path.join(repoRoot, '.claude/skills/edgeone-makers-tools/references'); -const referencesDir = path.join(skillsDir, 'makers-frameworks/references'); -const templatesDir = path.join(repoRoot, 'templates'); -const manifestPath = path.join(templatesDir, 'manifest.json'); - -/** - * Directories that are an artifact of running the scaffolder rather than part - * of what it produced. node_modules is the one that matters — the whole point - * is to ship the tree without it — but a scaffolder that builds or inits a repo - * on its way out leaves the rest behind too. - */ -const BUILD_ARTIFACTS = [ - 'node_modules', - '.git', - '.next', - '.nuxt', - '.svelte-kit', - '.astro', - '.output', - '.vercel', - '.turbo', - 'dist', - 'build', -]; - -/** - * Agent and editor configuration, which scaffolders increasingly write and this - * one place must not keep. - * - * Committed under templates/ these stop being a description of the generated - * project and become live configuration for the repository holding them: a - * nested AGENTS.md applies to its own subtree, and the one create-next-app - * writes is the very rule block this repo already carries at its root. - * - * Fidelity is the rule everywhere else here, so the deciding argument is the - * one that leaves no choice: `.agents` is in this repo's own .gitignore, so a - * template containing it loses those files on the way into git and arrives at a - * fresh clone three files short of its manifest. A tree that cannot be - * committed whole is worse shipped in part than not shipped. - * - * Nothing is lost that the project needs. None of it is application code, no - * build or deployment reads it, and the Next.js copy is regenerated by - * `next dev` in the user's project the first time they run it. - */ -const AGENT_AND_EDITOR_CONFIG = [ - '.agents', - '.claude', - '.cursor', - '.vscode', - '.idea', - 'AGENTS.md', - 'CLAUDE.md', -]; - -const NOT_PART_OF_THE_TEMPLATE = new Set([ - ...BUILD_ARTIFACTS, - ...AGENT_AND_EDITOR_CONFIG, -]); - -/** - * A scaffolder this script can drive unattended. - * - * Hugo is the shape being excluded: `hugo new site .` is documented the same - * way, but it needs a Go binary that neither this machine nor the sandbox is - * promised to have, and a template baked from a missing binary is an empty - * directory that reads as success. - */ -const RUNS_UNATTENDED = /^(?:npm|npx|pnpm|yarn|bun)\s/; - -/** - * What a template's own `.gitignore` is stored as, and why it cannot stay one. - * - * A .gitignore committed inside templates/ is a live ignore file for the - * directory it sits in, so it hides parts of the very tree it belongs to: the - * Next.js one lists next-env.d.ts, which meant the template was one file short - * of what it was baked from and only on a fresh clone, where the count no - * longer matched the manifest. The same file also ignores .env* and *.pem, so - * any template that grew one of those would lose it silently. - * - * Renaming is the same move npm makes for the same reason — a published - * create-* package cannot contain a .gitignore either — and the runtime - * restores the name on the way into the sandbox. Keep the two in step: the - * matching read is in agents/_lib/project/templates.ts, and a test asserts the - * round trip rather than trusting this comment. - */ -const GITIGNORE_STORED_AS = '_gitignore'; - -/** - * A version range the scaffolder left open, which a baked template cannot keep. - * - * `latest` resolves against the registry on every install, so it dates the - * lockfile baked beside it the moment either moves and npm has to re-resolve - * the graph instead of reading it. Two projects created a week apart also stop - * being the same project, which is the opposite of what baking is for. - */ -const FLOATING_RANGE = /^(?:latest|\*)?$/; - -/** - * Corrections applied to a scaffolder's package.json, by template. - * - * A scaffolder pins what its own template wants, and that is not always what - * this platform can build. TanStack Start is the case that forced this: its - * scaffolder asks for Vite 8, `@edgeone/tanstack-start` peers on Vite 7 or - * below, and `@vitejs/plugin-react` 6 requires the Vite the adapter rejects — - * so the two have to move together, and `@vitejs/plugin-react` 5.2 is the - * release that spans both. One session spent four installs and seven minutes - * finding that out by trying it. - * - * Astro is the same correction with two reasons converging on it. 5 is the - * version the reference recommends — the platform runs 6 as well — and it is - * also the only major the sandbox can start: the scaffolder ships 7, which - * refuses to run below Node 22.12 while the sandbox is on 20. Not npm's - * EBADENGINE warning but a hard exit, so `astro dev` never binds a port and the - * preview times out against a proxy with nothing to connect to, naming neither - * Astro nor Node anywhere in the failure. - * - * 5.18.2 is the newest release supported on Node 20 rather than the newest that - * runs there: 6.0.0 through 6.0.5 carried a temporary Node 20 allowance for - * StackBlitz that 6.0.6 withdrew. A pin into a five-patch window whose support - * was already retracted is not worth having over one into a maintained line. - * - * Unlike the Nuxt pin below, this one does not lift when the sandbox moves up. - * The recommendation is what holds it here; Node is only what agrees with it. - * - * The adapter is declared here too. For a framework whose adapter is - * `required: always` the runtime injects it into package.json anyway, but it - * does that after this lockfile is written, which leaves the two disagreeing - * before the project has been touched. Declared at bake time it is in the - * lockfile like everything else. - * - * Kept in this script rather than edited into templates/, because a bake - * overwrites that directory wholesale and a hand edit survives exactly until - * the next run. - */ -const DEPENDENCY_PINS = { - // Both of these are exact versions the install line already asked for, and - // npm widened on its way to package.json: `npm install pkg@1.2.3` records - // `^1.2.3`. For most packages that is what you want and this script does it - // deliberately elsewhere. These two are the exceptions. - // - // `deepagents` is what the rest of the line satisfies — its own - // peerDependencies name the ranges every `@langchain/*` above sits in — so a - // caret that reaches a release with a different peer contract invalidates the - // set as a whole rather than moving one package. - // - // `@langchain/openai` stops at 1.5.8 because 1.5.9 raised its engine floor to - // Node 22 while the sandbox runs 20. npm calls that EBADENGINE and installs - // anyway, so the caret does not fail the bake or the install — it fails - // later, inside a package that was never run on this Node. Off when the - // sandbox moves up, like the Nuxt pin and unlike the Astro one. - deepagents: { - dependencies: { - '@langchain/openai': '1.5.8', - deepagents: '1.13.3', - }, - }, - // The same `@langchain/openai` exception as above, for the same reason and - // with one more effect worth naming: held at 1.5.8 the tree resolves `openai` - // on the 6 line, which declares no engine floor at all. A caret reaches - // 1.5.11, which pulls `openai` 7 and carries the Node 22 claim back in - // transitively — so this single pin is what makes the whole langgraph tree - // clean rather than just this one package. - langgraph: { - dependencies: { '@langchain/openai': '1.5.8' }, - }, - astro: { - engines: { node: '>=20.3.0' }, - dependencies: { astro: '^5.18.2' }, - }, - nuxt: { - // The one pin here with no range at all. Nuxt moved its Node floor to - // 22.12 in 4.4.6 and to 22.19 in 4.5, so both `^4.4.4` and `~4.4.4` still - // reach a release the sandbox cannot start — only an exact version holds. - // - // Unlike the Astro pin this is not a platform limit. The reference supports - // all of Nuxt 4, and it is the sandbox's Node that cannot keep up, so this - // comes off when the sandbox moves rather than when anything upstream does. - // - // vue-router is deliberately not pinned alongside it: the scaffolder asks - // for ^5.3.0, which is the pairing for Nuxt 4.5, and whether 4.4.4 accepts - // it is for the install below to answer rather than for this comment. - dependencies: { nuxt: '4.4.4' }, - }, - 'react-router': { - // `@edgeone/react-router` peers on react-router 7 and on Vite 5, 6, or 7, - // while the scaffolder now writes 8 on Vite 8 — outside both. An adapter - // that does not apply is the expensive kind of wrong here, because it - // costs nothing at preview time and produces a deploy nobody can serve. - // - // Pinning the scaffolder instead does not work, which one bake proved by - // producing a byte-identical tree: `create-react-router` fetches its - // default template from the main branch of remix-run/react-router- - // templates, so `create-react-router@7` writes the same version 8 - // manifest `@latest` does. - // - // Pinning Vite back to 7 also strands the config written for 8. The repair - // is in SOURCE_PATCHES; vite-tsconfig-paths below is the half of it that - // has to be in the lockfile. - // - // The adapter is declared here even though this framework's is - // `required: server-output` rather than `always`, which is the condition - // the runtime's own injection tests. That condition exists because the file - // deciding server-versus-static usually has not been written when - // package.json lands, and installing an adapter a static project never - // needs is worse than asking for it later. A baked tree removes the - // uncertainty it was protecting against: react-router.config.ts arrives in - // the same write, and it says `ssr: true`. Leaving it out cost one measured - // session a 65-second second install plus the minute spent working out that - // it was needed. vike, also server-output, has carried its adapter since - // its scaffolder grew an --edgeone flag; this is the same call made by hand. - dependencies: { - '@react-router/node': '^7.18.1', - '@react-router/serve': '^7.18.1', - 'react-router': '^7.18.1', - }, - devDependencies: { - '@edgeone/react-router': '^1.1.10', - '@react-router/dev': '^7.18.1', - 'vite': '^7.0.0', - 'vite-tsconfig-paths': '^5.1.4', - }, - }, - 'tanstack-start': { - dependencies: { '@edgeone/tanstack-start': '^1.1.0' }, - devDependencies: { - 'vite': '^7.0.0', - '@vitejs/plugin-react': '^5.2.0', - 'vite-tsconfig-paths': '^5.1.4', - }, - }, - sveltekit: { - devDependencies: { - '@edgeone/sveltekit': '^1.1.1', - // Not the scaffolder's omission but ours. The preview prefix is injected - // as `process.env.…`, and SvelteKit's generated tsconfig puts - // `../vite.config.ts` in its own `include` — so `npm run check` reads the - // one file this repo writes Node globals into and cannot resolve them. - // Nothing here sets `types`, so declaring the package is the whole fix. - // - // Matched to the sandbox's Node 20 rather than to the other templates, - // whose ranges are whatever their scaffolders happened to write. This one - // is chosen, so it describes the runtime the code will execute on. - '@types/node': '^20', - // Removed rather than left to sit unused. SOURCE_PATCHES swaps the - // adapter this scaffolder wired in, and a manifest still naming - // adapter-auto is the ambiguity the swap exists to close: one session - // read this file, read the config, and reported the project as using - // adapter-auto while `@edgeone/sveltekit` sat two lines below. - '@sveltejs/adapter-auto': null, - }, - }, -}; - -/** - * Corrections applied to a scaffolder's source files, by template. - * - * The companion to DEPENDENCY_PINS, for when moving a version back leaves the - * code around it calling an API that version does not have. react-router is - * the case that forced it: the scaffolder's vite.config.ts sets - * `resolve.tsconfigPaths`, Vite 8's built-in tsconfig alias resolution, which - * does not exist in the Vite 7 the adapter requires. - * - * Absent, and not rejected. Vite does not validate `resolve`, so an unknown key - * there is neither an error nor a warning — it is merged in and never read. - * Meanwhile tsconfig.json goes on advertising `~/*` and tsc goes on accepting - * it, so the bake is green, typecheck is green, and the first import written - * through the alias fails the build with `failed to resolve import` and nothing - * anywhere naming the config. Same shape as the adapter mismatch above: costs - * nothing until it costs a deploy. - * - * The replacement is not invented here. It is what this template shipped for - * the whole of the Vite 7 era, until the templates repo moved to 8 and dropped - * the plugin in the same commit. - * - * A patch that no longer applies fails the bake rather than skipping. The - * hazard being fixed is a correction that silently stops happening, and a - * scaffolder rewriting the line underneath it is exactly how that would start. - * - * An entry carrying `contents` instead of `find`/`replace` writes a file the - * scaffolder does not ship, and fails if one turns up under that name — at that - * point the scaffolder has an opinion about it and theirs is the one to read. - */ -const SOURCE_PATCHES = { - // Everything `npm install` does not write. The command under the reference's - // ## Dependencies heading produces a package.json holding only `dependencies` - // and a lockfile, which is most of the value — the versions are what a run - // used to spend an ERESOLVE and two registry probes arriving at — but it is - // not yet a project the platform can start. - // - // The route is baked rather than left to the model because it is the one file - // where the platform's contract is not the obvious thing to write: the chat - // body is `messages`, and three of the four agent references still show a - // singular `message`. A generated project that reads the wrong one answers - // the preview probe and every real request alike with 400. - deepagents: [ - { - file: 'package.json', - find: '{\n "dependencies": {', - replace: '{\n "name": "deepagents-agent",\n "private": true,\n "type": "module",\n "dependencies": {', - }, - { - file: 'edgeone.json', - contents: `${JSON.stringify({ agents: { framework: 'deepagents' } }, null, 2)}\n`, - }, - { - file: '.env.example', - contents: 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n', - }, - { - file: 'agents/chat.ts', - contents: `import { ChatOpenAI } from '@langchain/openai'; -import { createDeepAgent } from 'deepagents'; - -type ChatMessage = { role: 'user' | 'assistant'; content: string }; - -const MODEL_NAME = '@makers/deepseek-v4-flash'; - -// Module-level, so a warm invocation reuses the client instead of rebuilding it -// and its connection pool on every turn. -let model: ChatOpenAI | undefined; -let agent: ReturnType | undefined; - -function getAgent(env: Record) { - model ??= new ChatOpenAI({ - model: MODEL_NAME, - apiKey: env.AI_GATEWAY_API_KEY, - configuration: { baseURL: env.AI_GATEWAY_BASE_URL }, - temperature: 0, - timeout: 300_000, - }); - agent ??= createDeepAgent({ - model, - systemPrompt: 'You are a helpful assistant. Answer in the language you were asked in.', - tools: [], - }); - return agent; -} - -function sseEvent(payload: unknown) { - return \`data: \${JSON.stringify(payload)}\\n\\n\`; -} - -async function* eventStream( - messages: ChatMessage[], - conversationId: string, - env: Record, - signal?: AbortSignal, -) { - try { - const stream = await getAgent(env).stream( - { messages }, - { - streamMode: 'messages', - signal, - // Caps the agent loop. An execution-time option, not a constructor one: - // \`maxTurns\` on createDeepAgent stopped existing and does not error, - // it just is not read. - recursionLimit: 30, - configurable: { thread_id: conversationId }, - }, - ); - for await (const chunk of stream) { - if (signal?.aborted) break; - const [msg] = chunk as any[]; - if (msg?.tool_call_chunks?.length) { - for (const call of msg.tool_call_chunks) { - if (call.name) yield sseEvent({ type: 'tool_call', name: call.name }); - } - } else if (msg?.type === 'tool') { - yield sseEvent({ type: 'tool_result', name: msg.name, content: msg.text?.slice(0, 500) ?? '' }); - } else if (msg?.text) { - yield sseEvent({ type: 'ai_response', content: msg.text }); - } - } - } catch (error) { - // An abort is the user pressing stop, not a failure to report. - if ((error as Error).name !== 'AbortError' && !signal?.aborted) { - yield sseEvent({ type: 'error_message', content: (error as Error).message }); - } - } - yield 'data: [DONE]\\n\\n'; -} - -export async function onRequest(context: any) { - const { request, env, conversation_id: conversationId } = context; - - // \`messages\` and nothing else. A singular \`message\` branch is a second shape - // no client sends and the preview probe never exercises, so a mistake in it - // ships — see makers-agents/references/platform/conversation-id.md. - const messages: ChatMessage[] = (Array.isArray(request?.body?.messages) ? request.body.messages : []) - .filter((m: any) => (m?.role === 'user' || m?.role === 'assistant') - && typeof m?.content === 'string' - && m.content.trim()); - if (messages.length === 0) { - return new Response(JSON.stringify({ error: "'messages' is required" }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const signal = request?.signal as AbortSignal | undefined; - const stream = eventStream(messages, conversationId, env ?? {}, signal); - - return new Response( - new ReadableStream({ - async pull(controller) { - const { value, done } = await stream.next(); - if (done) return controller.close(); - controller.enqueue(new TextEncoder().encode(value)); - }, - cancel: () => void stream.return(undefined), - }), - { - headers: { - 'Content-Type': 'text/event-stream; charset=utf-8', - 'Cache-Control': 'no-cache, no-transform', - Connection: 'keep-alive', - }, - }, - ); -} -`, - }, - ], - // The deepagents set above, one framework over. The graph is the part worth - // baking beyond the lockfile: `compile({ checkpointer, store })` is what binds - // a thread to the platform's memory, and a route that omits it looks correct, - // streams correctly, and forgets every turn. - langgraph: [ - { - file: 'package.json', - find: '{\n "dependencies": {', - replace: '{\n "name": "langgraph-agent",\n "private": true,\n "type": "module",\n "dependencies": {', - }, - { - file: 'edgeone.json', - contents: `${JSON.stringify({ agents: { framework: 'langgraph' } }, null, 2)}\n`, - }, - { - file: '.env.example', - contents: 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n', - }, - { - file: 'agents/chat.ts', - contents: `import { ChatOpenAI } from '@langchain/openai'; -import { END, MessagesAnnotation, START, StateGraph } from '@langchain/langgraph'; -import { ToolNode } from '@langchain/langgraph/prebuilt'; - -const MODEL_NAME = '@makers/deepseek-v4-flash'; - -// Module-level, so a warm invocation reuses the client instead of rebuilding it -// and its connection pool on every turn. The graph is deliberately not cached -// beside it: it compiles against the checkpointer and store this request -// carries, and a graph held across requests would pin the first one's. -let model: ChatOpenAI | undefined; - -function getModel(env: Record) { - model ??= new ChatOpenAI({ - model: MODEL_NAME, - apiKey: env.AI_GATEWAY_API_KEY, - configuration: { baseURL: env.AI_GATEWAY_BASE_URL }, - temperature: 0, - timeout: 300_000, - }); - return model; -} - -function buildGraph(llm: ChatOpenAI, tools: any[], checkpointer: any, store: any) { - // Binding an empty list is not the same as binding none — some providers - // reject the empty array outright. - const modelWithTools = tools.length ? llm.bindTools(tools) : llm; - - async function agentNode(state: typeof MessagesAnnotation.State) { - return { messages: [await modelWithTools.invoke(state.messages)] }; - } - - function shouldContinue(state: typeof MessagesAnnotation.State) { - const last = state.messages[state.messages.length - 1] as any; - return last?.tool_calls?.length ? 'tools' : END; - } - - return new StateGraph(MessagesAnnotation) - .addNode('agent', agentNode) - .addNode('tools', new ToolNode(tools)) - .addEdge(START, 'agent') - .addConditionalEdges('agent', shouldContinue) - .addEdge('tools', 'agent') - .compile({ checkpointer, store }); -} - -function sseEvent(payload: unknown) { - return \`data: \${JSON.stringify(payload)}\\n\\n\`; -} - -async function* eventStream( - graph: any, - message: string, - conversationId: string, - signal?: AbortSignal, -) { - try { - const stream = await graph.stream( - { messages: [{ role: 'user', content: message }] }, - { streamMode: 'messages', signal, configurable: { thread_id: conversationId } }, - ); - for await (const chunk of stream) { - if (signal?.aborted) break; - const [msg] = chunk as any[]; - if (msg?.tool_call_chunks?.length) { - for (const call of msg.tool_call_chunks) { - if (call.name) yield sseEvent({ type: 'tool_call', name: call.name }); - } - } else if (msg?.type === 'tool') { - yield sseEvent({ type: 'tool_result', name: msg.name, content: msg.text?.slice(0, 500) ?? '' }); - } else if (msg?.text) { - yield sseEvent({ type: 'ai_response', content: msg.text }); - } - } - } catch (error) { - // An abort is the user pressing stop, not a failure to report. - if ((error as Error).name !== 'AbortError' && !signal?.aborted) { - yield sseEvent({ type: 'error_message', content: (error as Error).message }); - } - } - yield 'data: [DONE]\\n\\n'; -} - -export async function onRequest(context: any) { - const { request, env, conversation_id: conversationId, store } = context; - - // \`messages\` and nothing else. A singular \`message\` branch is a second shape - // no client sends and the preview probe never exercises, so a mistake in it - // ships — see makers-agents/references/platform/conversation-id.md. - // - // Only the newest turn is forwarded, because the checkpointer below already - // holds this thread's history: replaying the array would append a copy of - // what is already stored and grow the prompt every turn. - const incoming = Array.isArray(request?.body?.messages) ? request.body.messages : []; - const latest = [...incoming].reverse().find( - (m: any) => m?.role === 'user' && typeof m?.content === 'string' && m.content.trim(), - ); - if (!latest) { - return new Response(JSON.stringify({ error: "'messages' is required" }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // The sandbox tools as real LangChain objects. Narrow them with - // \`toLangChainTools(tool, ['web_search'])\`, or pass [] to take them away. - const { tool } = await import('@langchain/core/tools'); - const tools = typeof context.tools?.toLangChainTools === 'function' - ? context.tools.toLangChainTools(tool) - : []; - - const graph = buildGraph( - getModel(env ?? {}), - tools, - store?.langgraphCheckpointer, - store?.langgraphStore, - ); - - const signal = request?.signal as AbortSignal | undefined; - const stream = eventStream(graph, latest.content.trim(), conversationId, signal); - - return new Response( - new ReadableStream({ - async pull(controller) { - const { value, done } = await stream.next(); - if (done) return controller.close(); - controller.enqueue(new TextEncoder().encode(value)); - }, - cancel: () => void stream.return(undefined), - }), - { - headers: { - 'Content-Type': 'text/event-stream; charset=utf-8', - 'Cache-Control': 'no-cache, no-transform', - Connection: 'keep-alive', - }, - }, - ); -} -`, - }, - ], - 'react-router': [ - { - file: 'vite.config.ts', - find: 'import { defineConfig } from "vite";\n', - replace: - 'import { defineConfig } from "vite";\n' - + 'import tsconfigPaths from "vite-tsconfig-paths";\n' - + 'import { edgeoneAdapter } from "@edgeone/react-router";\n', - }, - { - file: 'vite.config.ts', - find: ' plugins: [tailwindcss(), reactRouter()],\n resolve: {\n tsconfigPaths: true,\n },\n', - replace: ' plugins: [tailwindcss(), reactRouter(), edgeoneAdapter(), tsconfigPaths()],\n', - }, - ], - sveltekit: [ - { - file: 'vite.config.ts', - find: "import adapter from '@sveltejs/adapter-auto';\n", - replace: "import adapter from '@edgeone/sveltekit';\n", - }, - // The replaced comment described adapter-auto. What goes in its place is - // the rule that makes this file worth baking: `svelte.config.js` is not - // merged with these options, it is skipped entirely once any argument - // reaches `sveltekit()`. A measured session read the reference, found no - // svelte.config.js to edit, wrote one holding `@edgeone/sveltekit`, and was - // saved only by also emptying this call — had it left the injected - // `paths.base` in place, as the scaffold hint asks, the adapter it just - // installed would have resolved to none and the build would have produced - // output the platform cannot read, without a warning either side of it. - { - file: 'vite.config.ts', - find: '\t\t\t// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.\n' - + '\t\t\t// If your environment is not supported, or you settled on a specific environment, switch out the adapter.\n' - + '\t\t\t// See https://svelte.dev/docs/kit/adapters for more information about adapters.\n', - replace: '\t\t\t// The platform adapter, and the reason this file is the whole of the\n' - + "\t\t\t// SvelteKit config: passing any option to sveltekit() makes a sibling\n" - + '\t\t\t// svelte.config.js dead weight — it is ignored whole, adapter included.\n', - }, - ], - 'tanstack-start': [ - { - file: 'vite.config.ts', - find: "import { defineConfig } from 'vite'\n", - replace: "import { defineConfig } from 'vite'\n" - + "import tsconfigPaths from 'vite-tsconfig-paths'\n" - + "import { edgeoneTanStackStartAdapter } from '@edgeone/tanstack-start'\n", - }, - // Same two repairs react-router needs, for the same two reasons: an adapter - // this framework cannot build without, and a `resolve.tsconfigPaths` that - // only exists in Vite 8 while the adapter's peer range stops at 7. The - // second is latent rather than visible — no file the scaffolder writes uses - // an alias — but tsconfig.json advertises `#/*` and `@/*`, so the first - // import written against one fails to resolve with nothing pointing here. - { - file: 'vite.config.ts', - find: ' resolve: { tsconfigPaths: true },\n' - + ' plugins: [devtools(), tailwindcss(), tanstackStart(), viteReact()],\n', - replace: ' plugins: [\n' - + ' devtools(),\n' - + ' tailwindcss(),\n' - + ' tanstackStart(),\n' - + ' edgeoneTanStackStartAdapter(),\n' - + ' viteReact(),\n' - + ' tsconfigPaths(),\n' - + ' ],\n', - }, - ], - // The one demo in any of these templates that reaches off the machine to - // render. Both +data.ts files fetch brillout.github.io during SSR, so in a - // sandbox that cannot reach it the route this template ships to teach data - // fetching is the route that throws. A measured session read all 18 source - // files, then spent four of its seven edits replacing exactly this with a - // local array before it wrote a line of what was asked for — the demo is - // rewritten every run, so it is cheaper to ship it already rewritten. - // - // Kept rather than deleted: `+data`, `useConfig` and the minimize-before- - // sending note are the Vike-specific parts worth having in front of a model, - // and none of them need the network to make their point. - vike: [ - { - file: 'pages/star-wars/moviesData.ts', - contents: 'import type { MovieDetails } from "./types.js";\n' - + '\n' - + '// Read on the server by the +data.ts files beside it, so the data-fetching\n' - + '// demo renders the same with or without a network.\n' - + 'export const starWarsMovies: MovieDetails[] = [\n' - + ' {\n' - + ' id: "1",\n' - + ' title: "A New Hope",\n' - + ' release_date: "1977-05-25",\n' - + ' director: "George Lucas",\n' - + ' producer: "Gary Kurtz",\n' - + ' },\n' - + ' {\n' - + ' id: "2",\n' - + ' title: "The Empire Strikes Back",\n' - + ' release_date: "1980-05-21",\n' - + ' director: "Irvin Kershner",\n' - + ' producer: "Gary Kurtz",\n' - + ' },\n' - + ' {\n' - + ' id: "3",\n' - + ' title: "Return of the Jedi",\n' - + ' release_date: "1983-05-25",\n' - + ' director: "Richard Marquand",\n' - + ' producer: "Howard Kazanjian",\n' - + ' },\n' - + ' {\n' - + ' id: "4",\n' - + ' title: "The Phantom Menace",\n' - + ' release_date: "1999-05-19",\n' - + ' director: "George Lucas",\n' - + ' producer: "Rick McCallum",\n' - + ' },\n' - + ' {\n' - + ' id: "5",\n' - + ' title: "Attack of the Clones",\n' - + ' release_date: "2002-05-16",\n' - + ' director: "George Lucas",\n' - + ' producer: "Rick McCallum",\n' - + ' },\n' - + ' {\n' - + ' id: "6",\n' - + ' title: "Revenge of the Sith",\n' - + ' release_date: "2005-05-19",\n' - + ' director: "George Lucas",\n' - + ' producer: "Rick McCallum",\n' - + ' },\n' - + '];\n', - }, - // Imported under its own name, not `movies`: the function below declares a - // `const movies` of its own, and a same-named import would be shadowed by - // it from the top of the body — reading it before that line is a TDZ throw, - // not a type error, so nothing would catch it until the page rendered. - { - file: 'pages/star-wars/index/+data.ts', - find: 'import type { Movie, MovieDetails } from "../types.js";\n', - replace: 'import type { Movie, MovieDetails } from "../types.js";\n' - + 'import { starWarsMovies } from "../moviesData.js";\n', - }, - { - file: 'pages/star-wars/index/+data.ts', - find: ' const response = await fetch("https://brillout.github.io/star-wars/api/films.json");\n' - + ' const moviesData = (await response.json()) as MovieDetails[];\n', - replace: ' const moviesData: MovieDetails[] = starWarsMovies;\n', - }, - { - file: 'pages/star-wars/@id/+data.ts', - find: 'import type { MovieDetails } from "../types.js";\n', - replace: 'import type { MovieDetails } from "../types.js";\n' - + 'import { starWarsMovies } from "../moviesData.js";\n', - }, - // The fetch this replaces had no answer for an id that does not exist - // either — it would have parsed GitHub's 404 page and thrown on the way. - // Throwing here says which id, and says it before anything reads .title. - { - file: 'pages/star-wars/@id/+data.ts', - find: ' const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`);\n' - + ' let movie = (await response.json()) as MovieDetails;\n', - replace: ' let movie = starWarsMovies.find(({ id }) => id === pageContext.routeParams.id);\n' - + ' if (!movie) throw new Error(`No Star Wars movie with id ${pageContext.routeParams.id}`);\n', - }, - ], -}; - -async function applySourcePatches(workdir, templateId) { - for (const patch of SOURCE_PATCHES[templateId] ?? []) { - const target = path.join(workdir, patch.file); - - if (patch.contents !== undefined) { - if (await exists(target)) { - throw new Error( - `${patch.file} is now in the scaffolded tree, so this bake would overwrite` - + ' whatever the scaffolder decided it should hold', - ); - } - await mkdir(path.dirname(target), { recursive: true }); - await writeFile(target, patch.contents); - process.stdout.write(`bake:templates — ${templateId}: added ${patch.file}\n`); - continue; - } - - if (!(await exists(target))) { - throw new Error(`${patch.file} is not in the scaffolded tree, so this bake cannot patch it`); - } - const before = await readFile(target, 'utf8'); - if (!before.includes(patch.find)) { - throw new Error( - `${patch.file} no longer contains the text this bake patches, so the correction` - + ` would silently stop being applied. Expected to find:\n${patch.find}`, - ); - } - await writeFile(target, before.replace(patch.find, patch.replace)); - process.stdout.write(`bake:templates — ${templateId}: patched ${patch.file}\n`); - } -} - -/** What the scaffolder's own install actually resolved each package to. */ -async function readLockedVersions(workdir) { - const versions = new Map(); - try { - const lock = JSON.parse(await readFile(path.join(workdir, 'package-lock.json'), 'utf8')); - for (const [key, entry] of Object.entries(lock.packages ?? {})) { - const name = key.startsWith('node_modules/') ? key.slice('node_modules/'.length) : ''; - if (name && !name.includes('/node_modules/') && entry?.version) { - versions.set(name, entry.version); - } - } - } catch { - // No lockfile, so nothing to read a resolved version from. Floating ranges - // stay as they are rather than being pinned to a guess. - } - return versions; -} - -/** - * Close the two gaps a scaffolder leaves in a tree that is about to be frozen: - * ranges that float, and versions that this platform cannot build. - * - * The lockfile is rewritten afterwards rather than edited, because the point of - * the exercise is a lockfile that agrees with its manifest — leaving the one the - * scaffolder wrote in place would reproduce the problem being fixed. - */ -async function alignDependencies(workdir, templateId) { - const manifestFile = path.join(workdir, 'package.json'); - const manifest = JSON.parse(await readFile(manifestFile, 'utf8')); - const locked = await readLockedVersions(workdir); - const pins = DEPENDENCY_PINS[templateId] ?? {}; - const changes = []; - - for (const field of ['dependencies', 'devDependencies']) { - const pinned = pins[field] ?? {}; - if (!manifest[field] && Object.keys(pinned).length === 0) continue; - const block = { ...manifest[field] }; - - for (const [name, range] of Object.entries(block)) { - if (name in pinned || !FLOATING_RANGE.test(range)) continue; - const resolved = locked.get(name); - if (!resolved) continue; - block[name] = `^${resolved}`; - changes.push(`${name} ${range} -> ^${resolved}`); - } - for (const [name, range] of Object.entries(pinned)) { - // A null range drops the package. Only reachable for one a patch above - // has just made unreachable from source, so the install it costs is - // wasted and the name it leaves behind is misleading. - if (range === null) { - if (!(name in block)) continue; - changes.push(`${name} ${block[name]} -> (removed)`); - delete block[name]; - continue; - } - if (block[name] === range) continue; - changes.push(`${name} ${block[name] ?? '(absent)'} -> ${range}`); - block[name] = range; - } - - manifest[field] = Object.fromEntries( - Object.entries(block).sort(([a], [b]) => a.localeCompare(b)), - ); - } - - // The same correction one level up. A scaffolder writes the engine floor the - // dependency it chose demands, so pinning that dependency back to what this - // platform runs strands the floor above it: the template then declares a Node - // it no longer needs, and every install under the sandbox's own version opens - // with an EBADENGINE warning about a requirement nothing in the tree has. - for (const [name, range] of Object.entries(pins.engines ?? {})) { - const current = manifest.engines?.[name]; - if (current === range) continue; - changes.push(`engines.${name} ${current ?? '(absent)'} -> ${range}`); - manifest.engines = { ...manifest.engines, [name]: range }; - } - - if (changes.length === 0) return; - await writeFile(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`); - for (const change of changes) { - process.stdout.write(`bake:templates — ${templateId}: pinned ${change}\n`); - } - - const lockfile = path.join(workdir, 'package-lock.json'); - if (!(await exists(lockfile))) return; - - // Deleted rather than updated, which is what "rewritten" above has to mean. - // npm reads an existing lockfile as the tree to reconcile against, and after - // a pin that moves a major the old one describes a graph the new manifest - // contradicts — react-router 7 against the scaffolder's 8 disagrees on peers - // the whole way down, and npm stops at the conflict rather than resolving - // past it. There is nothing in the old graph this wants to keep. - await rm(lockfile, { force: true }); - - // --ignore-scripts because this resolves a graph rather than building a - // tree. node_modules left with the other build artifacts before this ran, so - // an install hook firing here runs against binaries that are no longer on - // disk: Nuxt's `postinstall: nuxt prepare` exits 127 on the missing `nuxt`, - // and SvelteKit's `prepare` survived the same call only because it happens - // to end in `|| echo ''`. - await run('npm', ['install', '--package-lock-only', '--ignore-scripts', '--no-audit', '--no-fund'], { - cwd: workdir, - maxBuffer: 64 * 1024 * 1024, - }); -} - -/** The single fenced command under a reference's `## Scaffold` heading. */ -function extractScaffoldCommand(markdown) { - return extractFencedCommand(markdown, 'Scaffold'); -} - -/** The single fenced command under one of a reference's headings. */ -function extractFencedCommand(markdown, heading) { - const section = markdown.match( - new RegExp(String.raw`^## ${heading}\s*$([\s\S]*?)(?=^## |\Z)`, 'm'), - )?.[1]; - if (!section) return null; - const blocks = [...section.matchAll(/```(?:bash|sh|shell)\n([\s\S]*?)```/g)]; - // Exactly one, or this script would be choosing on the reference's behalf — - // which is the drift the manifest check exists to prevent. - if (blocks.length !== 1) return null; - const command = blocks[0][1].trim(); - return command.includes('\n') || !RUNS_UNATTENDED.test(command) ? null : command; -} - -/** - * Frameworks that ship no scaffolder, documented outside makers-frameworks. - * - * The agent frameworks are the platform's own reason to exist and had no baked - * tree at all, so every run that named one opened on an empty workspace: a - * measured DeepAgents session spent 145 seconds reading references and - * deliberating before the first package.json existed, which is also the first - * moment the install can start. - * - * There is nothing to scaffold — no `create-deepagents` exists — but the - * expensive half was never the file layout. It was arriving at a set of - * versions that resolve: the same session lost another 46 seconds to an - * ERESOLVE, two registry probes, and a second install. So the `npm install` - * line the reference documents *is* the command, run here once against a - * lockfile, and SOURCE_PATCHES supplies the handful of files npm does not - * write. The reference stays the source of truth either way — the manifest - * records what ran, and a test fails when the two drift apart. - */ -const SCAFFOLDERLESS_TEMPLATES = [ - { id: 'deepagents', ref: 'makers-agents/references/node-frameworks/deepagents.md', heading: 'Dependencies' }, - { id: 'langgraph', ref: 'makers-agents/references/node-frameworks/langgraph.md', heading: 'Dependencies' }, -]; - -/** A reference under makers-frameworks, or a path from the skills root. */ -function referenceFile(ref) { - return ref.includes('/') ? path.join(skillsDir, ref) : path.join(referencesDir, ref); -} - -async function bakeableTemplates() { - const entries = await readdir(referencesDir, { withFileTypes: true }); - const found = []; - for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith('.md')) continue; - const markdown = await readFile(path.join(referencesDir, entry.name), 'utf8'); - const command = extractScaffoldCommand(markdown); - if (command) { - found.push({ id: entry.name.replace(/\.md$/, ''), ref: entry.name, command }); - } - } - for (const { id, ref, heading } of SCAFFOLDERLESS_TEMPLATES) { - const markdown = await readFile(referenceFile(ref), 'utf8'); - const command = extractFencedCommand(markdown, heading); - // Loudly, unlike the scan above: this one is named here by hand, so a - // reference that stopped carrying a single-line command is an edit that - // silently dropped a template rather than a framework we never baked. - if (!command) { - throw new Error(`${ref} has no single-line command under ## ${heading}, so ${id} cannot be baked`); - } - found.push({ id, ref, command }); - } - found.sort((a, b) => a.id.localeCompare(b.id)); - return found; -} - -async function exists(target) { - try { - await stat(target); - return true; - } catch { - return false; - } -} - -async function pruneArtifacts(dir) { - for (const entry of await readdir(dir, { withFileTypes: true })) { - const target = path.join(dir, entry.name); - if (NOT_PART_OF_THE_TEMPLATE.has(entry.name)) { - await rm(target, { recursive: true, force: true }); - } else if (entry.isDirectory()) { - await pruneArtifacts(target); - } else if (entry.name === '.gitignore') { - const stored = path.join(dir, GITIGNORE_STORED_AS); - if (await exists(stored)) { - throw new Error(`${stored} already exists, so the .gitignore beside it cannot be stored`); - } - await rename(target, stored); - } - } -} - -async function measure(dir) { - let files = 0; - let bytes = 0; - async function walk(current) { - for (const entry of await readdir(current, { withFileTypes: true })) { - const target = path.join(current, entry.name); - if (entry.isDirectory()) { - await walk(target); - } else { - files += 1; - bytes += (await stat(target)).size; - } - } - } - await walk(dir); - return { files, bytes }; -} - -/** - * The directory a scaffolder runs in, which is also the name it gives the - * project: every one of them derives `package.json`'s name field from the - * basename, and mkdtemp's random suffix carries capitals that npm rejects - * outright — create-next-app stops with "name can no longer contain capital - * letters" before it writes anything. - * - * `app` rather than the template id, because that is the basename the sandbox - * would have handed the scaffolder anyway: appDir ends in /app. Baking under - * the same name is what keeps the template identical to the tree the run it - * replaces would have produced. - */ -const SCAFFOLD_DIRECTORY_NAME = 'app'; - -async function bake(template) { - const scratch = await mkdtemp(path.join(os.tmpdir(), `bake-${template.id}-`)); - const workdir = path.join(scratch, SCAFFOLD_DIRECTORY_NAME); - await mkdir(workdir, { recursive: true }); - const destination = path.join(templatesDir, template.id); - try { - process.stdout.write(`bake:templates — ${template.id}: ${template.command}\n`); - // The scaffolders want an empty directory and say so; the sandbox gives - // them one, and so does this. - await run('sh', ['-c', template.command], { - cwd: workdir, - maxBuffer: 64 * 1024 * 1024, - env: { ...process.env, CI: '1', ADBLOCK: '1', DISABLE_OPENCOLLECTIVE: '1' }, - }); - - await pruneArtifacts(workdir); - - // "It wrote something" is not the same as "it scaffolded a project", and - // the difference is the failure this guard exists for: `sv create` exits 0 - // after leaving behind a .npmrc and a .gitignore when it cannot reach its - // template, and a two-file template baked from that would have shipped as a - // framework the agent believes it can start a project from. Every scaffolder - // here is an npm one, so a root package.json is the thing that separates a - // project from a pair of dotfiles. - if (!(await exists(path.join(workdir, 'package.json')))) { - throw new Error( - 'the scaffolder exited cleanly without writing a package.json, so it never' - + ' scaffolded anything — check whether it fetches its template over the network', - ); - } - await applySourcePatches(workdir, template.id); - await alignDependencies(workdir, template.id); - - const { files, bytes } = await measure(workdir); - - await rm(destination, { recursive: true, force: true }); - await cp(workdir, destination, { recursive: true }); - - const kb = Math.round(bytes / 1024); - process.stdout.write(`bake:templates — ${template.id}: ${files} files, ${kb}KB\n`); - return { ...template, files, bytes, bakedAt: new Date().toISOString().slice(0, 10) }; - } finally { - await rm(scratch, { recursive: true, force: true }); - } -} - -async function readManifest() { - try { - return JSON.parse(await readFile(manifestPath, 'utf8')); - } catch { - return { templates: [] }; - } -} - -const requested = process.argv.slice(2); -const available = await bakeableTemplates(); - -if (requested.includes('--list')) { - for (const template of available) { - process.stdout.write(`${template.id.padEnd(16)} ${template.command}\n`); - } - process.exit(0); -} - -const selected = requested.length - ? available.filter((template) => requested.includes(template.id)) - : available; - -if (selected.length === 0) { - const names = available.map((template) => template.id).join(', '); - console.error(`bake:templates — nothing to bake. Available: ${names}`); - process.exit(1); -} - -const existing = await readManifest(); -const baked = new Map(existing.templates.map((template) => [template.id, template])); -const failures = []; - -for (const template of selected) { - try { - baked.set(template.id, await bake(template)); - } catch (error) { - // One framework's scaffolder being down, renamed, or newly interactive must - // not throw away the templates that did bake — the manifest is written - // either way, and the summary says what is missing from it. - // - // Its own output is the whole diagnosis, and a scaffolder says why it - // stopped on the stream execFile does not put in `message`. Trimming that - // to the first line once cost an afternoon on a conflicting-directory - // notice that was sitting in stderr the entire time. - failures.push(template.id); - const detail = [error.stderr, error.stdout, error.message] - .find((stream) => typeof stream === 'string' && stream.trim()); - console.error(`bake:templates — ${template.id} failed:\n${(detail || '').trimEnd()}`); - } -} - -await writeFile( - manifestPath, - `${JSON.stringify( - { - bakedWith: { node: process.version }, - templates: [...baked.values()].sort((a, b) => a.id.localeCompare(b.id)), - }, - null, - 2, - )}\n`, -); - -process.stdout.write(`bake:templates — manifest lists ${baked.size} templates\n`); -if (failures.length) { - console.error(`bake:templates — did not bake: ${failures.join(', ')}`); - process.exit(1); -} diff --git a/shared/protocol.ts b/shared/protocol.ts index 4e4439a..cc9136b 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -236,7 +236,26 @@ export type ChatStreamEvent = | { type: 'error'; error?: string } | { type: 'ping'; ts?: number }; +export type SessionPrepMode = 'create' | 'restore'; + +export type SessionPrepStage = + | 'conversation' + | 'sandbox' + | 'agent' + | 'workspace' + | 'preview' + | 'ready'; + +export type SessionPrepStatus = 'running' | 'done' | 'failed'; + +export type SessionPrepData = { + mode: SessionPrepMode; + stage: SessionPrepStage; + status: SessionPrepStatus; +}; + export type ResumeStreamEvent = + | { type: 'session_prep'; data?: SessionPrepData } | { type: 'resume_history'; data?: ResumeData } | { type: 'resume_workspace'; data?: ResumeData } | { type: 'file_changed'; data?: { paths?: string[] } } diff --git a/templates/astro/README.md b/templates/astro/README.md deleted file mode 100644 index 87b813a..0000000 --- a/templates/astro/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Astro Starter Kit: Minimal - -```sh -npm create astro@latest -- --template minimal -``` - -> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! - -## 🚀 Project Structure - -Inside of your Astro project, you'll see the following folders and files: - -```text -/ -├── public/ -├── src/ -│ └── pages/ -│ └── index.astro -└── package.json -``` - -Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. - -There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. - -Any static assets, like images, can be placed in the `public/` directory. - -## 🧞 Commands - -All commands are run from the root of the project, from a terminal: - -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro -- --help` | Get help using the Astro CLI | - -## 👀 Want to learn more? - -Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/templates/astro/_gitignore b/templates/astro/_gitignore deleted file mode 100644 index 16d54bb..0000000 --- a/templates/astro/_gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# build output -dist/ -# generated types -.astro/ - -# dependencies -node_modules/ - -# logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - - -# environment variables -.env -.env.production - -# macOS-specific files -.DS_Store - -# jetbrains setting folder -.idea/ diff --git a/templates/astro/astro.config.mjs b/templates/astro/astro.config.mjs deleted file mode 100644 index e762ba5..0000000 --- a/templates/astro/astro.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -// @ts-check -import { defineConfig } from 'astro/config'; - -// https://astro.build/config -export default defineConfig({}); diff --git a/templates/astro/package-lock.json b/templates/astro/package-lock.json deleted file mode 100644 index d6d8be5..0000000 --- a/templates/astro/package-lock.json +++ /dev/null @@ -1,5585 +0,0 @@ -{ - "name": "app", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "version": "0.0.1", - "dependencies": { - "astro": "^5.18.2" - }, - "engines": { - "node": ">=20.3.0" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", - "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", - "license": "MIT" - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", - "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", - "license": "MIT" - }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.11", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", - "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" - } - }, - "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@capsizecss/unpack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", - "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "license": "MIT" - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", - "cpu": [ - "arm" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", - "cpu": [ - "loong64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", - "cpu": [ - "loong64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", - "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", - "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", - "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", - "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", - "license": "ISC" - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/astro": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.2.tgz", - "integrity": "sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.6", - "@astrojs/markdown-remark": "6.3.11", - "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^4.0.0", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.3.0", - "acorn": "^8.15.0", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.3.1", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.1.1", - "cssesc": "^3.0.0", - "debug": "^4.4.3", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.6.2", - "diff": "^8.0.3", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.7.0", - "esbuild": "^0.27.3", - "estree-walker": "^3.0.3", - "flattie": "^1.1.1", - "fontace": "~0.4.0", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.2.0", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "magic-string": "^0.30.21", - "magicast": "^0.5.1", - "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.1", - "package-manager-detector": "^1.6.0", - "piccolore": "^0.1.3", - "picomatch": "^4.0.3", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.3", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "svgo": "^4.0.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", - "ultrahtml": "^1.6.0", - "unifont": "~0.7.3", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.4", - "vfile": "^6.0.3", - "vite": "^6.4.1", - "vitefu": "^1.1.1", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.3", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.1", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.34.0" - } - }, - "node_modules/astro/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" - } - }, - "node_modules/astro/node_modules/vite": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", - "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/astro/node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cookie-es": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", - "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", - "license": "MIT" - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-select": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", - "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^7.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "nth-check": "^2.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", - "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/defu": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", - "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", - "license": "MIT" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/devalue": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz", - "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", - "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.2" - } - }, - "node_modules/fontkitten": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", - "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", - "license": "MIT", - "dependencies": { - "tiny-inflate": "^1.0.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", - "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.3", - "crossws": "^0.3.5", - "defu": "^6.1.6", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.4", - "radix3": "^1.1.2", - "ufo": "^1.6.3", - "uncrypto": "^0.1.3" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", - "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "source-map-js": "^1.2.1" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", - "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/ofetch": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", - "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.5", - "node-fetch-native": "^1.6.7", - "ufo": "^1.6.1" - } - }, - "node_modules/ohash": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", - "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", - "license": "MIT" - }, - "node_modules/oniguruma-parser": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", - "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", - "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.2", - "regex": "^6.1.0", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", - "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-manager-detector": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", - "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", - "license": "MIT" - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/piccolore": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", - "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", - "license": "ISC" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/property-information": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", - "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", - "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.3.tgz", - "integrity": "sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/sax": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", - "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", - "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/engine-javascript": "3.23.0", - "@shikijs/engine-oniguruma": "3.23.0", - "@shikijs/langs": "3.23.0", - "@shikijs/themes": "3.23.0", - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.8.0.tgz", - "integrity": "sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/svgo": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", - "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^6.0.0", - "css-tree": "^3.0.1", - "css-what": "^7.0.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "1.6.1" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "deprecated": "unmaintained", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", - "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz", - "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==", - "license": "MIT", - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz", - "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==", - "license": "MIT", - "dependencies": { - "css-tree": "^3.1.0", - "ohash": "^2.0.11", - "undici": "^8.0.0" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", - "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^5.0.0", - "destr": "^2.0.5", - "h3": "^1.15.10", - "lru-cache": "^11.2.7", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.5.1", - "ufo": "^1.6.3" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6 || ^7 || ^8", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/functions": "^2.2.12 || ^3.0.0", - "@vercel/kv": "^1 || ^2 || ^3", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/functions": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", - "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", - "license": "MIT" - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", - "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/templates/astro/package.json b/templates/astro/package.json deleted file mode 100644 index 001969d..0000000 --- a/templates/astro/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "app", - "type": "module", - "version": "0.0.1", - "engines": { - "node": ">=20.3.0" - }, - "scripts": { - "dev": "astro dev", - "build": "astro build", - "preview": "astro preview", - "astro": "astro" - }, - "dependencies": { - "astro": "^5.18.2" - }, - "allowScripts": { - "esbuild": true - } -} diff --git a/templates/astro/public/favicon.ico b/templates/astro/public/favicon.ico deleted file mode 100644 index 7f48a94d16071d6c8d06478c7458ab12e675019c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x9_P)Rl-XF(A`bsas&GH{e7U1}Ri zJr5jR8B2*Jd6$=$AqgTM2o2FV$WZ9|#jJ3mmpEs{jB0ps@*Kxv}=RB|IJih8Z&fqwCG`%bN0000#bW%=J zQ=IH#a_&L{B{_6Lu_3m>0bMN%+@aOmN_3G~H^8EGi>+bXO=;-|Z`uFnf==AdP z{Oj-S=ltmI=<4`LcLE*&009F@L_t(|+I`d4ZUZ3@1<*Uo7H^LoCw6-8z4wsbd;b4l zA}zMFtOw2mLX6O5Mgl}(5P=uOM4%=tnuHiuAp%(G<c=npm$Fz%eL - - - diff --git a/templates/astro/src/pages/index.astro b/templates/astro/src/pages/index.astro deleted file mode 100644 index 561196b..0000000 --- a/templates/astro/src/pages/index.astro +++ /dev/null @@ -1,17 +0,0 @@ ---- - ---- - - - - - - - - - Astro - - -

Astro

- - diff --git a/templates/astro/tsconfig.json b/templates/astro/tsconfig.json deleted file mode 100644 index 8bf91d3..0000000 --- a/templates/astro/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "astro/tsconfigs/strict", - "include": [".astro/types.d.ts", "**/*"], - "exclude": ["dist"] -} diff --git a/templates/deepagents/.env.example b/templates/deepagents/.env.example deleted file mode 100644 index 8300bf7..0000000 --- a/templates/deepagents/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -AI_GATEWAY_API_KEY= -AI_GATEWAY_BASE_URL= diff --git a/templates/deepagents/agents/chat.ts b/templates/deepagents/agents/chat.ts deleted file mode 100644 index 8958b4c..0000000 --- a/templates/deepagents/agents/chat.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { ChatOpenAI } from '@langchain/openai'; -import { createDeepAgent } from 'deepagents'; - -type ChatMessage = { role: 'user' | 'assistant'; content: string }; - -const MODEL_NAME = '@makers/deepseek-v4-flash'; - -/** - * The injected gateway base may arrive without a version suffix - * (`https://ai-gateway.edgeone.link`). The OpenAI client appends - * `/chat/completions` to whatever base it is given, so a base missing `/v1` - * resolves to the wrong path and every call comes back 404. Normalize to end - * in exactly one `/v1`. - */ -function normalizeOpenAiGatewayBaseUrl(raw: string) { - const trimmed = raw.trim().replace(/\/+$/, ''); - return /\/v1$/.test(trimmed) ? trimmed : `${trimmed}/v1`; -} - -// Module-level, so a warm invocation reuses the client instead of rebuilding it -// and its connection pool on every turn. -let model: ChatOpenAI | undefined; -let agent: ReturnType | undefined; - -function getAgent(env: Record) { - model ??= new ChatOpenAI({ - model: MODEL_NAME, - apiKey: env.AI_GATEWAY_API_KEY, - configuration: { baseURL: normalizeOpenAiGatewayBaseUrl(env.AI_GATEWAY_BASE_URL || '') }, - temperature: 0, - timeout: 300_000, - }); - agent ??= createDeepAgent({ - model, - systemPrompt: 'You are a helpful assistant. Answer in the language you were asked in.', - tools: [], - }); - return agent; -} - -function sseEvent(payload: unknown) { - return `data: ${JSON.stringify(payload)}\n\n`; -} - -async function* eventStream( - messages: ChatMessage[], - conversationId: string, - env: Record, - signal?: AbortSignal, -) { - try { - const stream = await getAgent(env).stream( - { messages }, - { - streamMode: 'messages', - signal, - // Caps the agent loop. An execution-time option, not a constructor one: - // `maxTurns` on createDeepAgent stopped existing and does not error, - // it just is not read. - recursionLimit: 30, - configurable: { thread_id: conversationId }, - }, - ); - for await (const chunk of stream) { - if (signal?.aborted) break; - const [msg] = chunk as any[]; - if (msg?.tool_call_chunks?.length) { - for (const call of msg.tool_call_chunks) { - if (call.name) yield sseEvent({ type: 'tool_call', name: call.name }); - } - } else if (msg?.type === 'tool') { - yield sseEvent({ type: 'tool_result', name: msg.name, content: msg.text?.slice(0, 500) ?? '' }); - } else if (msg?.text) { - yield sseEvent({ type: 'ai_response', content: msg.text }); - } - } - } catch (error) { - // An abort is the user pressing stop, not a failure to report. - if ((error as Error).name !== 'AbortError' && !signal?.aborted) { - yield sseEvent({ type: 'error_message', content: (error as Error).message }); - } - } - yield 'data: [DONE]\n\n'; -} - -export async function onRequest(context: any) { - const { request, env, conversation_id: conversationId } = context; - - // `messages` and nothing else. A singular `message` branch is a second shape - // no client sends and the preview probe never exercises, so a mistake in it - // ships — see makers-agents/references/platform/conversation-id.md. - const messages: ChatMessage[] = (Array.isArray(request?.body?.messages) ? request.body.messages : []) - .filter((m: any) => (m?.role === 'user' || m?.role === 'assistant') - && typeof m?.content === 'string' - && m.content.trim()); - if (messages.length === 0) { - return new Response(JSON.stringify({ error: "'messages' is required" }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const signal = request?.signal as AbortSignal | undefined; - const stream = eventStream(messages, conversationId, env ?? {}, signal); - - return new Response( - new ReadableStream({ - async pull(controller) { - const { value, done } = await stream.next(); - if (done) return controller.close(); - controller.enqueue(new TextEncoder().encode(value)); - }, - cancel: () => void stream.return(undefined), - }), - { - headers: { - 'Content-Type': 'text/event-stream; charset=utf-8', - 'Cache-Control': 'no-cache, no-transform', - Connection: 'keep-alive', - }, - }, - ); -} diff --git a/templates/deepagents/edgeone.json b/templates/deepagents/edgeone.json deleted file mode 100644 index a735ebc..0000000 --- a/templates/deepagents/edgeone.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "agents": { - "framework": "deepagents", - "externalNodeModules": ["langchain", "langsmith"] - } -} diff --git a/templates/deepagents/package-lock.json b/templates/deepagents/package-lock.json deleted file mode 100644 index 7e5e783..0000000 --- a/templates/deepagents/package-lock.json +++ /dev/null @@ -1,628 +0,0 @@ -{ - "name": "deepagents-agent", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "deepagents-agent", - "dependencies": { - "@langchain/core": "^1.2.9", - "@langchain/langgraph": "^1.4.14", - "@langchain/langgraph-checkpoint": "^1.1.5", - "@langchain/langgraph-sdk": "^1.10.2", - "@langchain/openai": "1.5.8", - "deepagents": "1.13.3", - "langchain": "^1.5.10", - "langsmith": "^0.9.0", - "zod": "^4.5.4" - } - }, - "node_modules/@cfworker/json-schema": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", - "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", - "license": "MIT" - }, - "node_modules/@langchain/core": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.2.9.tgz", - "integrity": "sha512-conzSEj9Zu1AyXJLXsSbgrtxtxinmI1yGqQ5CIJZSoV5rvv+yvQE/vgBnoySpBQ/bl3YPgj2FL/gbDjWykLSfg==", - "license": "MIT", - "dependencies": { - "@cfworker/json-schema": "^4.0.2", - "@standard-schema/spec": "^1.1.0", - "js-tiktoken": "^1.0.12", - "langsmith": ">=0.5.0 <1.0.0", - "mustache": "^4.2.0", - "p-queue": "^6.6.2", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@langchain/langgraph": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.4.14.tgz", - "integrity": "sha512-uWAdRYTllfKCnTrlyovExPJCHJwcf3Wl2LzUlnaqsT7Rmoo3aCeYtq/7MV/Pw4q11motG8pR8bjr6T6V8Pe1gQ==", - "license": "MIT", - "dependencies": { - "@langchain/langgraph-checkpoint": "^1.1.5", - "@langchain/langgraph-sdk": "~1.10.2", - "@langchain/protocol": "^0.0.19", - "@standard-schema/spec": "1.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48", - "zod": "^3.25.32 || ^4.2.0" - } - }, - "node_modules/@langchain/langgraph-checkpoint": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.5.tgz", - "integrity": "sha512-BwDwl5VeTOh6CVuiIPgsUgfK51vTJDMSbFcSCUfjJWsl8/DPdK/mbv+ejxJstkSk/BlSPMP4JfXWcN6jD2ea2Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48" - } - }, - "node_modules/@langchain/langgraph-sdk": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.10.2.tgz", - "integrity": "sha512-86qsfdBZWu1ZgywLN8AThU/jXi9rjPDZPWcTJp4SA1A/L62ypTNoSXbvtiwZt1odokXccYTxK1XWS8tmVdvEmw==", - "license": "MIT", - "dependencies": { - "@langchain/protocol": "^0.0.19", - "@types/json-schema": "^7.0.15", - "p-queue": "^9.0.1", - "p-retry": "^7.1.1" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", - "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.4", - "p-timeout": "^7.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/openai": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.5.8.tgz", - "integrity": "sha512-BKzIgWYSXQ03V9F9u46vC12vZjHy8wyOt8H7VUrTWt6VdwSnnxXmjeEUEIkLjpU/bqVkGzHLXGCSMEHYbDSi5Q==", - "license": "MIT", - "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^6.41.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.2.8" - } - }, - "node_modules/@langchain/protocol": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@langchain/protocol/-/protocol-0.0.19.tgz", - "integrity": "sha512-9hKcRrH7cBX6gfutdfXPoft1OCchHe4FEpALoDJMl5Qu+n/YG5ynZmyu8+8cxORlPwHBoKTxggvXz+76M1yX1Q==", - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/deepagents": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/deepagents/-/deepagents-1.13.3.tgz", - "integrity": "sha512-ApjoznYieCpMRFH7TlxNYU9n1QVQp+IGJyzVnxRV0UFqfalDATI64ddWnrBoaHaLU1qb3bMrqgvKPInjlsPjgA==", - "license": "MIT", - "dependencies": { - "fast-glob": "^3.3.3", - "micromatch": "^4.0.8", - "yaml": "^2.8.2", - "zod": "^4.3.6" - }, - "peerDependencies": { - "@langchain/core": "^1.2.9", - "@langchain/langgraph": "^1.4.10", - "@langchain/langgraph-checkpoint": "^1.1.5", - "@langchain/langgraph-sdk": "^1.9.23", - "langchain": "^1.5.10", - "langsmith": ">=0.7.1 <0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", - "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-network-error": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", - "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/js-tiktoken": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", - "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.5.1" - } - }, - "node_modules/langchain": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.5.10.tgz", - "integrity": "sha512-JaC12C1qyGn985vvjttr4hr8lfFzWhrXp2M1byZJGmNJ2RiIgqnhiYDuLlG/xHDxhKD3onJ5pCuUif/cbdqPhA==", - "license": "MIT", - "dependencies": { - "@langchain/langgraph": "^1.4.10", - "@langchain/langgraph-checkpoint": "^1.1.5", - "langsmith": ">=0.5.0 <1.0.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.2.9" - } - }, - "node_modules/langsmith": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.9.0.tgz", - "integrity": "sha512-tlg/aG7qezAKY6G3fgADSX7PkRj+JKoF3z7QNkCMsAOvwvuzhiwP9Amn1Z+zAIxuKoWuXQdIjtFN0LVmUC1oUQ==", - "license": "MIT", - "dependencies": { - "p-queue": "6.6.2" - }, - "peerDependencies": { - "@opentelemetry/api": "*", - "@opentelemetry/exporter-trace-otlp-proto": "*", - "@opentelemetry/sdk-trace-base": "*", - "openai": "*", - "ws": ">=7" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-proto": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - }, - "openai": { - "optional": true - }, - "ws": { - "optional": true - } - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, - "node_modules/openai": { - "version": "6.49.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz", - "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==", - "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/credential-provider-node": ">=3.972.0 <4", - "@smithy/hash-node": ">=4.3.0 <5", - "@smithy/signature-v4": ">=5.4.0 <6", - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-provider-node": { - "optional": true - }, - "@smithy/hash-node": { - "optional": true - }, - "@smithy/signature-v4": { - "optional": true - }, - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", - "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", - "license": "MIT", - "dependencies": { - "is-network-error": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/zod": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", - "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/templates/deepagents/package.json b/templates/deepagents/package.json deleted file mode 100644 index c3f83df..0000000 --- a/templates/deepagents/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "deepagents-agent", - "private": true, - "type": "module", - "dependencies": { - "@langchain/core": "^1.2.9", - "@langchain/langgraph": "^1.4.14", - "@langchain/langgraph-checkpoint": "^1.1.5", - "@langchain/langgraph-sdk": "^1.10.2", - "@langchain/openai": "1.5.8", - "deepagents": "1.13.3", - "langchain": "^1.5.10", - "langsmith": "^0.9.0", - "zod": "^4.5.4" - } -} diff --git a/templates/langgraph/.env.example b/templates/langgraph/.env.example deleted file mode 100644 index 8300bf7..0000000 --- a/templates/langgraph/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -AI_GATEWAY_API_KEY= -AI_GATEWAY_BASE_URL= diff --git a/templates/langgraph/agents/chat.ts b/templates/langgraph/agents/chat.ts deleted file mode 100644 index 571c148..0000000 --- a/templates/langgraph/agents/chat.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { ChatOpenAI } from '@langchain/openai'; -import { END, MessagesAnnotation, START, StateGraph } from '@langchain/langgraph'; -import { ToolNode } from '@langchain/langgraph/prebuilt'; - -const MODEL_NAME = '@makers/deepseek-v4-flash'; - -/** - * The injected gateway base may arrive without a version suffix - * (`https://ai-gateway.edgeone.link`). The OpenAI client appends - * `/chat/completions` to whatever base it is given, so a base missing `/v1` - * resolves to the wrong path and every call comes back 404. Normalize to end - * in exactly one `/v1`. - */ -function normalizeOpenAiGatewayBaseUrl(raw: string) { - const trimmed = raw.trim().replace(/\/+$/, ''); - return /\/v1$/.test(trimmed) ? trimmed : `${trimmed}/v1`; -} - -// Module-level, so a warm invocation reuses the client instead of rebuilding it -// and its connection pool on every turn. The graph is deliberately not cached -// beside it: it compiles against the checkpointer and store this request -// carries, and a graph held across requests would pin the first one's. -let model: ChatOpenAI | undefined; - -function getModel(env: Record) { - model ??= new ChatOpenAI({ - model: MODEL_NAME, - apiKey: env.AI_GATEWAY_API_KEY, - configuration: { baseURL: normalizeOpenAiGatewayBaseUrl(env.AI_GATEWAY_BASE_URL || '') }, - temperature: 0, - timeout: 300_000, - }); - return model; -} - -function buildGraph(llm: ChatOpenAI, tools: any[], checkpointer: any, store: any) { - // Binding an empty list is not the same as binding none — some providers - // reject the empty array outright. - const modelWithTools = tools.length ? llm.bindTools(tools) : llm; - - async function agentNode(state: typeof MessagesAnnotation.State) { - return { messages: [await modelWithTools.invoke(state.messages)] }; - } - - function shouldContinue(state: typeof MessagesAnnotation.State) { - const last = state.messages[state.messages.length - 1] as any; - return last?.tool_calls?.length ? 'tools' : END; - } - - return new StateGraph(MessagesAnnotation) - .addNode('agent', agentNode) - .addNode('tools', new ToolNode(tools)) - .addEdge(START, 'agent') - .addConditionalEdges('agent', shouldContinue) - .addEdge('tools', 'agent') - .compile({ checkpointer, store }); -} - -function sseEvent(payload: unknown) { - return `data: ${JSON.stringify(payload)}\n\n`; -} - -async function* eventStream( - graph: any, - message: string, - conversationId: string, - signal?: AbortSignal, -) { - try { - const stream = await graph.stream( - { messages: [{ role: 'user', content: message }] }, - { streamMode: 'messages', signal, configurable: { thread_id: conversationId } }, - ); - for await (const chunk of stream) { - if (signal?.aborted) break; - const [msg] = chunk as any[]; - if (msg?.tool_call_chunks?.length) { - for (const call of msg.tool_call_chunks) { - if (call.name) yield sseEvent({ type: 'tool_call', name: call.name }); - } - } else if (msg?.type === 'tool') { - yield sseEvent({ type: 'tool_result', name: msg.name, content: msg.text?.slice(0, 500) ?? '' }); - } else if (msg?.text) { - yield sseEvent({ type: 'ai_response', content: msg.text }); - } - } - } catch (error) { - // An abort is the user pressing stop, not a failure to report. - if ((error as Error).name !== 'AbortError' && !signal?.aborted) { - yield sseEvent({ type: 'error_message', content: (error as Error).message }); - } - } - yield 'data: [DONE]\n\n'; -} - -export async function onRequest(context: any) { - const { request, env, conversation_id: conversationId, store } = context; - - // `messages` and nothing else. A singular `message` branch is a second shape - // no client sends and the preview probe never exercises, so a mistake in it - // ships — see makers-agents/references/platform/conversation-id.md. - // - // Only the newest turn is forwarded, because the checkpointer below already - // holds this thread's history: replaying the array would append a copy of - // what is already stored and grow the prompt every turn. - const incoming = Array.isArray(request?.body?.messages) ? request.body.messages : []; - const latest = [...incoming].reverse().find( - (m: any) => m?.role === 'user' && typeof m?.content === 'string' && m.content.trim(), - ); - if (!latest) { - return new Response(JSON.stringify({ error: "'messages' is required" }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // The sandbox tools as real LangChain objects. Narrow them with - // `toLangChainTools(tool, ['web_search'])`, or pass [] to take them away. - const { tool } = await import('@langchain/core/tools'); - const tools = typeof context.tools?.toLangChainTools === 'function' - ? context.tools.toLangChainTools(tool) - : []; - - const graph = buildGraph( - getModel(env ?? {}), - tools, - store?.langgraphCheckpointer, - store?.langgraphStore, - ); - - const signal = request?.signal as AbortSignal | undefined; - const stream = eventStream(graph, latest.content.trim(), conversationId, signal); - - return new Response( - new ReadableStream({ - async pull(controller) { - const { value, done } = await stream.next(); - if (done) return controller.close(); - controller.enqueue(new TextEncoder().encode(value)); - }, - cancel: () => void stream.return(undefined), - }), - { - headers: { - 'Content-Type': 'text/event-stream; charset=utf-8', - 'Cache-Control': 'no-cache, no-transform', - Connection: 'keep-alive', - }, - }, - ); -} diff --git a/templates/langgraph/edgeone.json b/templates/langgraph/edgeone.json deleted file mode 100644 index 69316b5..0000000 --- a/templates/langgraph/edgeone.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "agents": { - "framework": "langgraph" - } -} diff --git a/templates/langgraph/package-lock.json b/templates/langgraph/package-lock.json deleted file mode 100644 index c389d28..0000000 --- a/templates/langgraph/package-lock.json +++ /dev/null @@ -1,345 +0,0 @@ -{ - "name": "langgraph-agent", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "langgraph-agent", - "dependencies": { - "@langchain/core": "^1.2.9", - "@langchain/langgraph": "^1.4.14", - "@langchain/openai": "1.5.8", - "zod": "^4.5.4" - } - }, - "node_modules/@cfworker/json-schema": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz", - "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", - "license": "MIT" - }, - "node_modules/@langchain/core": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.2.9.tgz", - "integrity": "sha512-conzSEj9Zu1AyXJLXsSbgrtxtxinmI1yGqQ5CIJZSoV5rvv+yvQE/vgBnoySpBQ/bl3YPgj2FL/gbDjWykLSfg==", - "license": "MIT", - "dependencies": { - "@cfworker/json-schema": "^4.0.2", - "@standard-schema/spec": "^1.1.0", - "js-tiktoken": "^1.0.12", - "langsmith": ">=0.5.0 <1.0.0", - "mustache": "^4.2.0", - "p-queue": "^6.6.2", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@langchain/langgraph": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.4.14.tgz", - "integrity": "sha512-uWAdRYTllfKCnTrlyovExPJCHJwcf3Wl2LzUlnaqsT7Rmoo3aCeYtq/7MV/Pw4q11motG8pR8bjr6T6V8Pe1gQ==", - "license": "MIT", - "dependencies": { - "@langchain/langgraph-checkpoint": "^1.1.5", - "@langchain/langgraph-sdk": "~1.10.2", - "@langchain/protocol": "^0.0.19", - "@standard-schema/spec": "1.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48", - "zod": "^3.25.32 || ^4.2.0" - } - }, - "node_modules/@langchain/langgraph-checkpoint": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.1.5.tgz", - "integrity": "sha512-BwDwl5VeTOh6CVuiIPgsUgfK51vTJDMSbFcSCUfjJWsl8/DPdK/mbv+ejxJstkSk/BlSPMP4JfXWcN6jD2ea2Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48" - } - }, - "node_modules/@langchain/langgraph-sdk": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.10.2.tgz", - "integrity": "sha512-86qsfdBZWu1ZgywLN8AThU/jXi9rjPDZPWcTJp4SA1A/L62ypTNoSXbvtiwZt1odokXccYTxK1XWS8tmVdvEmw==", - "license": "MIT", - "dependencies": { - "@langchain/protocol": "^0.0.19", - "@types/json-schema": "^7.0.15", - "p-queue": "^9.0.1", - "p-retry": "^7.1.1" - }, - "peerDependencies": { - "@langchain/core": "^1.1.48", - "react": "^18 || ^19", - "react-dom": "^18 || ^19" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", - "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.4", - "p-timeout": "^7.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/langgraph-sdk/node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@langchain/openai": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-1.5.8.tgz", - "integrity": "sha512-BKzIgWYSXQ03V9F9u46vC12vZjHy8wyOt8H7VUrTWt6VdwSnnxXmjeEUEIkLjpU/bqVkGzHLXGCSMEHYbDSi5Q==", - "license": "MIT", - "dependencies": { - "js-tiktoken": "^1.0.12", - "openai": "^6.41.0", - "zod": "^3.25.76 || ^4" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@langchain/core": "^1.2.8" - } - }, - "node_modules/@langchain/protocol": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@langchain/protocol/-/protocol-0.0.19.tgz", - "integrity": "sha512-9hKcRrH7cBX6gfutdfXPoft1OCchHe4FEpALoDJMl5Qu+n/YG5ynZmyu8+8cxORlPwHBoKTxggvXz+76M1yX1Q==", - "license": "MIT" - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/is-network-error": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", - "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/js-tiktoken": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", - "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.5.1" - } - }, - "node_modules/langsmith": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.10.2.tgz", - "integrity": "sha512-9iqIcEPBMlRT+vvijcjCo4AeHlJHUZjxwbPtDdvQPpGgvo7nYxhsQ7jVd53zXkPstiPfjRhexfnIe0vLltVFmg==", - "license": "MIT", - "dependencies": { - "p-queue": "6.6.2" - }, - "peerDependencies": { - "@opentelemetry/api": "*", - "@opentelemetry/exporter-trace-otlp-proto": "*", - "@opentelemetry/sdk-trace-base": "*", - "openai": "*", - "ws": ">=7" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-proto": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - }, - "openai": { - "optional": true - }, - "ws": { - "optional": true - } - } - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, - "node_modules/openai": { - "version": "6.49.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.49.0.tgz", - "integrity": "sha512-aYCc0C6L864eR6WSYIwQGyXriw/nIyZx0ObvhzOEVuk0zoBDpynjSbrionWI7q65B5H8jJX0DXR9snEzM6bfPg==", - "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/credential-provider-node": ">=3.972.0 <4", - "@smithy/hash-node": ">=4.3.0 <5", - "@smithy/signature-v4": ">=5.4.0 <6", - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-provider-node": { - "optional": true - }, - "@smithy/hash-node": { - "optional": true - }, - "@smithy/signature-v4": { - "optional": true - }, - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", - "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", - "license": "MIT", - "dependencies": { - "is-network-error": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/zod": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", - "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/templates/langgraph/package.json b/templates/langgraph/package.json deleted file mode 100644 index 7905312..0000000 --- a/templates/langgraph/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "langgraph-agent", - "private": true, - "type": "module", - "dependencies": { - "@langchain/core": "^1.2.9", - "@langchain/langgraph": "^1.4.14", - "@langchain/openai": "1.5.8", - "zod": "^4.5.4" - } -} diff --git a/templates/manifest.json b/templates/manifest.json deleted file mode 100644 index 7ad526a..0000000 --- a/templates/manifest.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "bakedWith": { - "node": "v24.19.0" - }, - "templates": [ - { - "id": "astro", - "ref": "astro.md", - "command": "npm create astro@latest . -- --yes --template minimal --install --no-git", - "files": 9, - "bytes": 201500, - "bakedAt": "2026-09-07" - }, - { - "id": "deepagents", - "ref": "makers-agents/references/node-frameworks/deepagents.md", - "command": "npm install deepagents@1.13.3 @langchain/core@^1.2.9 @langchain/langgraph@^1.4.14 @langchain/langgraph-checkpoint@^1.1.5 @langchain/langgraph-sdk@^1.9.23 @langchain/openai@1.5.8 langchain@^1.5.10 langsmith@^0.9.0 zod@^4.3.6", - "files": 5, - "bytes": 26141, - "bakedAt": "2026-09-09" - }, - { - "id": "langgraph", - "ref": "makers-agents/references/node-frameworks/langgraph.md", - "command": "npm install @langchain/langgraph@^1.4.14 @langchain/openai@1.5.8 @langchain/core@^1.2.9 zod@^4.3.6", - "files": 5, - "bytes": 17563, - "bakedAt": "2026-09-09" - }, - { - "id": "nextjs", - "ref": "nextjs.md", - "command": "npx create-next-app@15 . --typescript --tailwind --app --eslint --use-npm --yes", - "files": 18, - "bytes": 256760, - "bakedAt": "2026-09-07" - }, - { - "id": "nuxt", - "ref": "nuxt.md", - "command": "npx nuxi@latest init . --template minimal --packageManager npm --no-gitInit --force", - "files": 9, - "bytes": 395838, - "bakedAt": "2026-09-07" - }, - { - "id": "react-router", - "ref": "react-router.md", - "command": "npx create-react-router@latest . --yes --no-git-init --install", - "files": 17, - "bytes": 206900, - "bakedAt": "2026-09-07" - }, - { - "id": "sveltekit", - "ref": "sveltekit.md", - "command": "npx sv create . --template minimal --types ts --no-add-ons --install npm", - "files": 14, - "bytes": 81481, - "bakedAt": "2026-09-07" - }, - { - "id": "tanstack-start", - "ref": "tanstack-start.md", - "command": "npx @tanstack/cli@latest create . --framework react --non-interactive --no-git --no-intent", - "files": 17, - "bytes": 215479, - "bakedAt": "2026-09-07" - }, - { - "id": "vike", - "ref": "vike.md", - "command": "npm create vike@latest . -- --react --edgeone --skip-git", - "files": 25, - "bytes": 20634, - "bakedAt": "2026-09-08" - }, - { - "id": "vite-spa", - "ref": "vite-spa.md", - "command": "npm create vite@latest . -- --template react-ts", - "files": 18, - "bytes": 53528, - "bakedAt": "2026-09-07" - } - ] -} diff --git a/templates/nextjs/README.md b/templates/nextjs/README.md deleted file mode 100644 index e215bc4..0000000 --- a/templates/nextjs/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/templates/nextjs/_gitignore b/templates/nextjs/_gitignore deleted file mode 100644 index 5ef6a52..0000000 --- a/templates/nextjs/_gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/templates/nextjs/app/favicon.ico b/templates/nextjs/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m diff --git a/templates/nextjs/app/globals.css b/templates/nextjs/app/globals.css deleted file mode 100644 index a2dc41e..0000000 --- a/templates/nextjs/app/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import "tailwindcss"; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/templates/nextjs/app/layout.tsx b/templates/nextjs/app/layout.tsx deleted file mode 100644 index f7fa87e..0000000 --- a/templates/nextjs/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/templates/nextjs/app/page.tsx b/templates/nextjs/app/page.tsx deleted file mode 100644 index 21b686d..0000000 --- a/templates/nextjs/app/page.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( - - ); -} diff --git a/templates/nextjs/eslint.config.mjs b/templates/nextjs/eslint.config.mjs deleted file mode 100644 index 0c44876..0000000 --- a/templates/nextjs/eslint.config.mjs +++ /dev/null @@ -1,40 +0,0 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), - { - ignores: [ - "node_modules/**", - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ], - }, - { - rules: { - // Off because the rule's advice does not hold here, not because it is - // inconvenient. The preview proxy serves this app under a path prefix it - // strips before forwarding, so the framework only ever sees the stripped - // path; next/link intercepts the click and routes on the client against - // that path, landing outside the prefix with nothing left to correct it - // from. A plain anchor asks for a fresh document, which the proxy does - // see and does rewrite, so cross-page navigation here is . - // Leaving the rule on rejects exactly that: next build lints and next dev - // does not, so every linked multi-page app previews green and then fails - // to deploy. - "@next/next/no-html-link-for-pages": "off", - }, - }, -]; - -export default eslintConfig; diff --git a/templates/nextjs/next-env.d.ts b/templates/nextjs/next-env.d.ts deleted file mode 100644 index 830fb59..0000000 --- a/templates/nextjs/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/templates/nextjs/next.config.ts b/templates/nextjs/next.config.ts deleted file mode 100644 index e9ffa30..0000000 --- a/templates/nextjs/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; diff --git a/templates/nextjs/package-lock.json b/templates/nextjs/package-lock.json deleted file mode 100644 index cee3721..0000000 --- a/templates/nextjs/package-lock.json +++ /dev/null @@ -1,6377 +0,0 @@ -{ - "name": "app", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "version": "0.1.0", - "dependencies": { - "next": "15.5.25", - "react": "19.1.0", - "react-dom": "19.1.0" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@tailwindcss/postcss": "^4", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "15.5.25", - "tailwindcss": "^4", - "typescript": "^5" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", - "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", - "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.2", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", - "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", - "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.3" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", - "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.3" - } - }, - "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", - "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", - "license": "Apache-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "dependencies": { - "@img/sharp-wasm32": "0.35.4" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", - "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", - "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", - "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", - "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", - "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", - "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", - "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", - "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", - "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", - "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", - "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.3" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", - "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.3" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", - "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.3" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", - "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.3" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", - "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.3" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", - "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.3" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", - "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", - "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.3" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", - "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.11.3" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", - "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/sharp-wasm32": "0.35.4" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", - "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", - "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", - "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", - "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" - } - }, - "node_modules/@next/env": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.25.tgz", - "integrity": "sha512-42h1lLr07vl4gawALP1hsgRZjHB1xYa58JfUfHwr0f7jG/zhPakh5GHkADHXOC9ZxUvlQFOPIrp7s6qX4DezPQ==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.25.tgz", - "integrity": "sha512-dAzOqZQCAOgIq5yQpsuMBZcwxK0AtzGqHMQpxNWYLQlvf79rsP3SDwdGPNQC4ySaMSihOQXMO/VXubQ3JWW+3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.25.tgz", - "integrity": "sha512-w+RR0v/QuApnWEjRGm1z6gcObKwGMb5YPA7V3bzBEVSBpMFUXprer0tS27UxjUcEnqbhL7Zuzohej79B6rYmBg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.25.tgz", - "integrity": "sha512-QiGGBUSakt8S1H4Lt9Ehsh6Ja87axiBnQQgysOObvCbI7iUfJnRGntF1P64S4/ijuHFnSB8KLsEddkY3nN26uw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.25.tgz", - "integrity": "sha512-ehLos/66zo0d/mJCU5u96a/VDcr01aaUrX0o/i16UdInxz8qPTCDSxGtjk/Lps1sIr1RJFdiX3hxc0fxJo+cPA==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.25.tgz", - "integrity": "sha512-ZVMrqLiJ7DiChgmbkQwFtdhAnUkSH/4p7tB29QY+giATb0Q/XGHNRSKAb/B8XGDHRUaA67NepOW5W8u3ZRJBAA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.25.tgz", - "integrity": "sha512-UOewtDGkTMJTiODrEdeLZ50yGb59xCZSriNpXkfPMxRRgwDkGc7i8mLWqV5076wEdb+Ca/XN7MhJyM3CupyNyQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.25.tgz", - "integrity": "sha512-UBHwA8AhkCZgtRfU1aJpunuAJe/6gZv6jDESQe4p5MjTb5V0YEeJBWCdNqx15Vj3x+5jmauRfeMJSjfQj9HGFQ==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.25.tgz", - "integrity": "sha512-QcFcPRr16djk5IqK5+e8O80eZfgWzIvVBXfitIq0tQ/uc+eyfdoZ0NmKc0cnbIJyfVwREapKuG97YcxWA9gcpA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.25.tgz", - "integrity": "sha512-zREeykps3ndWr9egJgvJKqVkkDuaw6Zrrg23cYBos0ygydFkAWYU4+PaPVwXzP1eAYQJe53ShSK45iDM529BOg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", - "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.3.tgz", - "integrity": "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "postcss": "^8.5.16", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz", - "integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.69.0", - "@typescript-eslint/type-utils": "8.69.0", - "@typescript-eslint/utils": "8.69.0", - "@typescript-eslint/visitor-keys": "8.69.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.69.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", - "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz", - "integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.69.0", - "@typescript-eslint/types": "8.69.0", - "@typescript-eslint/typescript-estree": "8.69.0", - "@typescript-eslint/visitor-keys": "8.69.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz", - "integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.69.0", - "@typescript-eslint/types": "^8.69.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz", - "integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.69.0", - "@typescript-eslint/visitor-keys": "8.69.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz", - "integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz", - "integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.69.0", - "@typescript-eslint/typescript-estree": "8.69.0", - "@typescript-eslint/utils": "8.69.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz", - "integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz", - "integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.69.0", - "@typescript-eslint/tsconfig-utils": "8.69.0", - "@typescript-eslint/types": "8.69.0", - "@typescript-eslint/visitor-keys": "8.69.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz", - "integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.69.0", - "@typescript-eslint/types": "8.69.0", - "@typescript-eslint/typescript-estree": "8.69.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.69.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz", - "integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.69.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", - "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", - "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", - "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", - "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", - "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", - "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", - "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", - "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", - "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", - "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-loong64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", - "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", - "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", - "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", - "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", - "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", - "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", - "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-openharmony-arm64": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", - "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", - "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", - "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", - "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", - "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.13.0.tgz", - "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", - "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "get-intrinsic": "^1.3.0", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-abstract": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", - "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-abstract-get": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", - "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.2", - "is-callable": "^1.2.7", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz", - "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", - "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-abstract-get": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "is-callable": "^1.2.7", - "is-date-object": "^1.1.0", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", - "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.6", - "@eslint/js": "9.39.5", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-next": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.25.tgz", - "integrity": "sha512-hB1oClZzRO3vMAeMmBMch0FHNJ4irH74LvQWDEMHkJtG/Us9+SAsg1f5Pvcw9jz4tc8wSGnvbmF050BRAAIbBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "15.5.25", - "@rushstack/eslint-patch": "^1.10.3", - "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.37.0", - "eslint-plugin-react-hooks": "^5.0.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", - "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.16.1", - "resolve": "^2.0.0-next.6" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@nolyfill/is-core-module": "1.0.39", - "debug": "^4.4.0", - "get-tsconfig": "^4.10.0", - "is-bun-module": "^2.0.0", - "stable-hash": "^0.0.5", - "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-import-resolver-typescript" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*", - "eslint-plugin-import-x": "*" - }, - "peerDependenciesMeta": { - "eslint-plugin-import": { - "optional": true - }, - "eslint-plugin-import-x": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", - "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", - "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "aria-query": "^5.3.2", - "array-includes": "^3.1.8", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "^4.10.0", - "axobject-query": "^4.1.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "hasown": "^2.0.2", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "safe-regex-test": "^1.0.3", - "string.prototype.includes": "^2.0.1" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", - "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", - "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2", - "hasown": "^2.0.4", - "is-callable": "^1.2.7", - "is-document.all": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", - "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-document.all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", - "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/next": { - "version": "15.5.25", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.25.tgz", - "integrity": "sha512-OMWNulIIqKM2ykvC2qMjIt0IoavB4UB2SCs4iXJ6z6847FvyH8jBmBWcvrF5iuhTu8Przh20Fo/aoszIdqx4PA==", - "license": "MIT", - "dependencies": { - "@next/env": "15.5.25", - "@swc/helpers": "0.5.15", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.25", - "@next/swc-darwin-x64": "15.5.25", - "@next/swc-linux-arm64-gnu": "15.5.25", - "@next/swc-linux-arm64-musl": "15.5.25", - "@next/swc-linux-x64-gnu": "15.5.25", - "@next/swc-linux-x64-musl": "15.5.25", - "@next/swc-win32-arm64-msvc": "15.5.25", - "@next/swc-win32-x64-msvc": "15.5.25", - "sharp": "^0.34.3 || ^0.35.4" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-exports-info": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", - "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/node-exports-info/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", - "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "2.0.0-next.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", - "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.2", - "node-exports-info": "^1.6.0", - "object-keys": "^1.1.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", - "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "get-intrinsic": "^1.3.0", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/sharp": { - "version": "0.35.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", - "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.1.0", - "detect-libc": "^2.1.2", - "semver": "^7.8.5" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.4", - "@img/sharp-darwin-x64": "0.35.4", - "@img/sharp-freebsd-wasm32": "0.35.4", - "@img/sharp-libvips-darwin-arm64": "1.3.3", - "@img/sharp-libvips-darwin-x64": "1.3.3", - "@img/sharp-libvips-linux-arm": "1.3.3", - "@img/sharp-libvips-linux-arm64": "1.3.3", - "@img/sharp-libvips-linux-ppc64": "1.3.3", - "@img/sharp-libvips-linux-riscv64": "1.3.3", - "@img/sharp-libvips-linux-s390x": "1.3.3", - "@img/sharp-libvips-linux-x64": "1.3.3", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", - "@img/sharp-libvips-linuxmusl-x64": "1.3.3", - "@img/sharp-linux-arm": "0.35.4", - "@img/sharp-linux-arm64": "0.35.4", - "@img/sharp-linux-ppc64": "0.35.4", - "@img/sharp-linux-riscv64": "0.35.4", - "@img/sharp-linux-s390x": "0.35.4", - "@img/sharp-linux-x64": "0.35.4", - "@img/sharp-linuxmusl-arm64": "0.35.4", - "@img/sharp-linuxmusl-x64": "0.35.4", - "@img/sharp-webcontainers-wasm32": "0.35.4", - "@img/sharp-win32-arm64": "0.35.4", - "@img/sharp-win32-ia32": "0.35.4", - "@img/sharp-win32-x64": "0.35.4" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stable-hash": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", - "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.includes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", - "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", - "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.2", - "get-intrinsic": "^1.3.0", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.4", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", - "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.2", - "es-object-atoms": "^1.1.2", - "has-property-descriptors": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", - "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", - "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.9", - "for-each": "^0.3.5", - "gopd": "^1.2.0", - "is-typed-array": "^1.1.15", - "possible-typed-array-names": "^1.1.0", - "reflect.getprototypeof": "^1.0.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unrs-resolver": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", - "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.4" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.12.2", - "@unrs/resolver-binding-android-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-arm64": "1.12.2", - "@unrs/resolver-binding-darwin-x64": "1.12.2", - "@unrs/resolver-binding-freebsd-x64": "1.12.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", - "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", - "@unrs/resolver-binding-linux-x64-musl": "1.12.2", - "@unrs/resolver-binding-openharmony-arm64": "1.12.2", - "@unrs/resolver-binding-wasm32-wasi": "1.12.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", - "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.9", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/templates/nextjs/package.json b/templates/nextjs/package.json deleted file mode 100644 index 7889d1b..0000000 --- a/templates/nextjs/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "app", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint" - }, - "dependencies": { - "react": "19.1.0", - "react-dom": "19.1.0", - "next": "15.5.25" - }, - "devDependencies": { - "typescript": "^5", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", - "eslint": "^9", - "eslint-config-next": "15.5.25", - "@eslint/eslintrc": "^3" - } -} diff --git a/templates/nextjs/postcss.config.mjs b/templates/nextjs/postcss.config.mjs deleted file mode 100644 index c7bcb4b..0000000 --- a/templates/nextjs/postcss.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -const config = { - plugins: ["@tailwindcss/postcss"], -}; - -export default config; diff --git a/templates/nextjs/public/file.svg b/templates/nextjs/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/templates/nextjs/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/nextjs/public/globe.svg b/templates/nextjs/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/templates/nextjs/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/nextjs/public/next.svg b/templates/nextjs/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/templates/nextjs/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/nextjs/public/vercel.svg b/templates/nextjs/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/templates/nextjs/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/nextjs/public/window.svg b/templates/nextjs/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/templates/nextjs/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/nextjs/tsconfig.json b/templates/nextjs/tsconfig.json deleted file mode 100644 index d8b9323..0000000 --- a/templates/nextjs/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/templates/nuxt/README.md b/templates/nuxt/README.md deleted file mode 100644 index 25b5821..0000000 --- a/templates/nuxt/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Nuxt Minimal Starter - -Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. - -## Setup - -Make sure to install dependencies: - -```bash -# npm -npm install - -# pnpm -pnpm install - -# yarn -yarn install - -# bun -bun install -``` - -## Development Server - -Start the development server on `http://localhost:3000`: - -```bash -# npm -npm run dev - -# pnpm -pnpm dev - -# yarn -yarn dev - -# bun -bun run dev -``` - -## Production - -Build the application for production: - -```bash -# npm -npm run build - -# pnpm -pnpm build - -# yarn -yarn build - -# bun -bun run build -``` - -Locally preview production build: - -```bash -# npm -npm run preview - -# pnpm -pnpm preview - -# yarn -yarn preview - -# bun -bun run preview -``` - -Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. diff --git a/templates/nuxt/_gitignore b/templates/nuxt/_gitignore deleted file mode 100644 index 4a7f73a..0000000 --- a/templates/nuxt/_gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Nuxt dev/build outputs -.output -.data -.nuxt -.nitro -.cache -dist - -# Node dependencies -node_modules - -# Logs -logs -*.log - -# Misc -.DS_Store -.fleet -.idea - -# Local env files -.env -.env.* -!.env.example diff --git a/templates/nuxt/app/app.vue b/templates/nuxt/app/app.vue deleted file mode 100644 index 09f935b..0000000 --- a/templates/nuxt/app/app.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/templates/nuxt/nuxt.config.ts b/templates/nuxt/nuxt.config.ts deleted file mode 100644 index b6baa24..0000000 --- a/templates/nuxt/nuxt.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -// https://nuxt.com/docs/api/configuration/nuxt-config -export default defineNuxtConfig({ - compatibilityDate: '2025-07-15', - devtools: { enabled: true } -}) diff --git a/templates/nuxt/package-lock.json b/templates/nuxt/package-lock.json deleted file mode 100644 index b188cf3..0000000 --- a/templates/nuxt/package-lock.json +++ /dev/null @@ -1,11135 +0,0 @@ -{ - "name": "app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "hasInstallScript": true, - "dependencies": { - "nuxt": "4.4.4", - "vue": "^3.5.42", - "vue-router": "^5.3.1" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", - "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", - "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/helper-replace-supers": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/traverse": "^7.29.7", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", - "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", - "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", - "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", - "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", - "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", - "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", - "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/plugin-syntax-typescript": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bomb.sh/tab": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@bomb.sh/tab/-/tab-0.0.19.tgz", - "integrity": "sha512-dTRfo9Q9B+lbLG3JCu8a/AGQSfD2XXcFcnakQzVjSOX+VvR/s9zpsH8TlqV3iHqazniRn1Ypwd1hcRlXcu/4BA==", - "license": "MIT", - "bin": { - "tab": "dist/bin/cli.mjs" - }, - "peerDependencies": { - "cac": "^6.7.14", - "citty": "^0.1.6 || ^0.2.0", - "commander": "^13.1.0 || ^14.0.0 || ^15.0.0" - }, - "peerDependenciesMeta": { - "cac": { - "optional": true - }, - "citty": { - "optional": true - }, - "commander": { - "optional": true - } - } - }, - "node_modules/@clack/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", - "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", - "license": "MIT", - "dependencies": { - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@clack/prompts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", - "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", - "license": "MIT", - "dependencies": { - "@clack/core": "1.4.3", - "fast-string-width": "^3.0.2", - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@cloudflare/kv-asset-handler": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", - "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", - "license": "MIT OR Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@colordx/core": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.8.0.tgz", - "integrity": "sha512-cG0QJAO6VkaRUlIb0zOzX9gfJgs1pjoOL9gZ/PK1kfvBs0GCkADu5oQh6gPxXgQnZ3gV575h+lQRC0NlMDTclA==", - "license": "MIT" - }, - "node_modules/@dxup/nuxt": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@dxup/nuxt/-/nuxt-0.4.1.tgz", - "integrity": "sha512-gtYffW6OfWNvoLW+XD3Mx/K8uUq08PMGLYJoDxc92EzZAWqR0FhcR5iaLm5r/OxyGTKz+P5f5Y7Aoir9+SjYaw==", - "license": "MIT", - "dependencies": { - "@dxup/unimport": "^0.1.2", - "@nuxt/kit": "^4.4.2", - "chokidar": "^5.0.0", - "pathe": "^2.0.3", - "tinyglobby": "^0.2.16" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@dxup/unimport": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@dxup/unimport/-/unimport-0.1.2.tgz", - "integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==", - "license": "MIT" - }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@ioredis/commands": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", - "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1" - } - }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "license": "MIT" - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", - "license": "BSD-3-Clause", - "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", - "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nuxt/cli": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@nuxt/cli/-/cli-3.37.0.tgz", - "integrity": "sha512-Zj9NwHjEBzVrgezsgMFjpMhNqwNgROk9DzNi/dyfk1mCbXIRigV11br2xOl0Satek30bmv7oZAfMtCnDe6Ip0Q==", - "license": "MIT", - "dependencies": { - "@bomb.sh/tab": "^0.0.19", - "@clack/prompts": "^1.7.0", - "c12": "^3.3.4", - "citty": "^0.2.2", - "confbox": "^0.2.4", - "consola": "^3.4.2", - "debug": "^4.4.3", - "defu": "^6.1.7", - "exsolve": "^1.1.0", - "fuse.js": "^7.4.2", - "fzf": "^0.5.2", - "giget": "^3.3.0", - "jiti": "^2.7.0", - "listhen": "^1.10.0", - "nypm": "^0.6.8", - "ofetch": "^1.5.1", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^2.1.0", - "pkg-types": "^2.3.1", - "scule": "^1.3.0", - "semver": "^7.8.5", - "srvx": "^0.11.22", - "std-env": "^4.2.0", - "tinyclip": "^0.1.15", - "tinyexec": "^1.2.4", - "ufo": "^1.6.4", - "youch": "^4.1.1" - }, - "bin": { - "nuxi": "bin/nuxi.mjs", - "nuxi-ng": "bin/nuxi.mjs", - "nuxt": "bin/nuxi.mjs", - "nuxt-cli": "bin/nuxi.mjs" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - }, - "peerDependencies": { - "@nuxt/schema": "^4.4.6" - }, - "peerDependenciesMeta": { - "@nuxt/schema": { - "optional": true - } - } - }, - "node_modules/@nuxt/devalue": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nuxt/devalue/-/devalue-2.0.2.tgz", - "integrity": "sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==", - "license": "MIT" - }, - "node_modules/@nuxt/devtools": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@nuxt/devtools/-/devtools-3.4.2.tgz", - "integrity": "sha512-Nidg0/zB710cyK89ltTkv66hMfPlIPlKA+Yb67bIrAkNm5PoTwQ1goHdFWHIAGZ9nTadcVTzATwMwa56nvl/Wg==", - "license": "MIT", - "dependencies": { - "@nuxt/devtools-kit": "3.4.2", - "@nuxt/devtools-wizard": "3.4.2", - "@nuxt/kit": "^4.5.1", - "@vue/devtools-core": "^8.2.1", - "@vue/devtools-kit": "^8.2.1", - "birpc": "^4.0.0", - "consola": "^3.4.2", - "destr": "^2.0.5", - "error-stack-parser-es": "^2.0.1", - "execa": "^8.0.1", - "fast-npm-meta": "^2.2.0", - "get-port-please": "^3.2.0", - "hookable": "^6.1.1", - "image-meta": "^0.2.2", - "is-installed-globally": "^1.0.0", - "launch-editor": "^2.14.1", - "local-pkg": "^1.2.1", - "magicast": "^0.5.4", - "nypm": "^0.6.9", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^2.1.0", - "pkg-types": "^2.3.1", - "semver": "^7.8.5", - "simple-git": "^3.36.0", - "sirv": "^3.0.2", - "structured-clone-es": "^2.0.1", - "tinyglobby": "^0.2.17", - "unstorage": "^1.17.5", - "vite-plugin-inspect": "^11.4.1", - "vite-plugin-vue-tracer": "^1.4.0", - "which": "^6.0.1", - "ws": "^8.21.1" - }, - "bin": { - "devtools": "cli.mjs" - }, - "peerDependencies": { - "@vitejs/devtools": "*", - "vite": ">=6.0" - }, - "peerDependenciesMeta": { - "@vitejs/devtools": { - "optional": true - } - } - }, - "node_modules/@nuxt/devtools-kit": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@nuxt/devtools-kit/-/devtools-kit-3.4.2.tgz", - "integrity": "sha512-TPPbmH9xrExagYxfCe6JgtbHmgchfgDdA656Yws18rn8I/oCvB+gcw3kTWBBp2sC9ipZPXsgbsW/yZnVnGNBiA==", - "license": "MIT", - "dependencies": { - "@nuxt/kit": "^4.5.1", - "execa": "^8.0.1" - }, - "peerDependencies": { - "vite": ">=6.0" - } - }, - "node_modules/@nuxt/devtools-wizard": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@nuxt/devtools-wizard/-/devtools-wizard-3.4.2.tgz", - "integrity": "sha512-U2G8fw7KW1rdECyJDho5N14z8hSllpTk430KG1QPLjrv5w3pDksZ+YcpITd0u762Vw3gq3Arpg65Bk6U4ua6aQ==", - "license": "MIT", - "dependencies": { - "@clack/prompts": "^1.7.0", - "consola": "^3.4.2", - "diff": "^8.0.4", - "execa": "^8.0.1", - "magicast": "^0.5.4", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "semver": "^7.8.5" - }, - "bin": { - "devtools-wizard": "cli.mjs" - } - }, - "node_modules/@nuxt/kit": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.5.2.tgz", - "integrity": "sha512-l66LU9DcJYjmNwqwAj2I5UGRrUbnG2DOKGChnN70zIGtn0eq/z87gi/FRgha6eMb9/FmB1PFHgtx6PWVml1C2Q==", - "license": "MIT", - "dependencies": { - "c12": "^3.3.4", - "consola": "^3.4.2", - "defu": "^6.1.7", - "destr": "^2.0.5", - "errx": "^0.1.2", - "exsolve": "^1.1.1", - "ignore": "^7.0.6", - "jiti": "^2.7.0", - "klona": "^2.0.6", - "mlly": "^1.8.2", - "nostics": "^1.2.0", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "rc9": "^3.0.1", - "scule": "^1.3.0", - "tinyglobby": "^0.2.17", - "ufo": "^1.6.4", - "unctx": "^3.0.0", - "untyped": "^2.0.0", - "verkit": "^0.3.1" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/@nuxt/nitro-server": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/nitro-server/-/nitro-server-4.4.4.tgz", - "integrity": "sha512-jMZPf+vJ2/IF5TZc+c/1c6O6p94pklVLvrexCu9FYZFK3H9oqYUlzBfYRd2kL5tdRTkIOpxTjfcgB1oc62UOhw==", - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-typescript": "^7.28.6", - "@nuxt/devalue": "^2.0.2", - "@nuxt/kit": "4.4.4", - "@unhead/vue": "^2.1.13", - "@vue/shared": "^3.5.33", - "consola": "^3.4.2", - "defu": "^6.1.7", - "destr": "^2.0.5", - "devalue": "^5.7.1", - "errx": "^0.1.0", - "escape-string-regexp": "^5.0.0", - "exsolve": "^1.0.8", - "h3": "^1.15.11", - "impound": "^1.1.5", - "klona": "^2.0.6", - "mocked-exports": "^0.1.1", - "nitropack": "^2.13.4", - "nypm": "^0.6.6", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "rou3": "^0.8.1", - "std-env": "^4.1.0", - "ufo": "^1.6.4", - "unctx": "^2.5.0", - "unstorage": "^1.17.5", - "vue": "^3.5.33", - "vue-bundle-renderer": "^2.2.0", - "vue-devtools-stub": "^0.1.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@babel/plugin-proposal-decorators": "^7.25.0", - "@rollup/plugin-babel": "^6.0.0 || ^7.0.0", - "nuxt": "^4.4.4" - }, - "peerDependenciesMeta": { - "@babel/plugin-proposal-decorators": { - "optional": true - }, - "@rollup/plugin-babel": { - "optional": true - } - } - }, - "node_modules/@nuxt/nitro-server/node_modules/@nuxt/kit": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.4.tgz", - "integrity": "sha512-oy4fAeMkyz7gelnalDQLPm8QZRN+c5c/Eh/M6oFgPx86jnA8m6xeOlONpJN2dk0GhcJwJYuN/kmzBffZ93WXPQ==", - "license": "MIT", - "dependencies": { - "c12": "^3.3.4", - "consola": "^3.4.2", - "defu": "^6.1.7", - "destr": "^2.0.5", - "errx": "^0.1.0", - "exsolve": "^1.0.8", - "ignore": "^7.0.5", - "jiti": "^2.6.1", - "klona": "^2.0.6", - "mlly": "^1.8.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "rc9": "^3.0.1", - "scule": "^1.3.0", - "semver": "^7.7.4", - "tinyglobby": "^0.2.16", - "ufo": "^1.6.4", - "unctx": "^2.5.0", - "untyped": "^2.0.0" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/@nuxt/nitro-server/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/@nuxt/nitro-server/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@nuxt/nitro-server/node_modules/unctx": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", - "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21", - "unplugin": "^2.3.11" - } - }, - "node_modules/@nuxt/nitro-server/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/@nuxt/telemetry": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@nuxt/telemetry/-/telemetry-2.9.1.tgz", - "integrity": "sha512-WYQs6GvebU278YXhbGlLA8pUO3ox+juHAiKCB3SEH0lq2PDRwX465tHRtpVIqsHv+lGK/n99FC7qqyBt9+p3oQ==", - "license": "MIT", - "dependencies": { - "citty": "^0.2.2", - "consola": "^3.4.2", - "rc9": "^3.0.1", - "std-env": "^4.2.0" - }, - "bin": { - "nuxt-telemetry": "bin/nuxt-telemetry.mjs" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "@nuxt/kit": ">=3.0.0" - } - }, - "node_modules/@nuxt/vite-builder": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/vite-builder/-/vite-builder-4.4.4.tgz", - "integrity": "sha512-SNyxEYVeTo3d26tt5rxS550VOFLyXx1UBqhZJexWhk42HgHa3d115LWZx+4e+FJf75SYZ1B/KTrkVeeOhfNBMw==", - "license": "MIT", - "dependencies": { - "@nuxt/kit": "4.4.4", - "@rollup/plugin-replace": "^6.0.3", - "@vitejs/plugin-vue": "^6.0.6", - "@vitejs/plugin-vue-jsx": "^5.1.5", - "autoprefixer": "^10.5.0", - "consola": "^3.4.2", - "cssnano": "^7.1.7", - "defu": "^6.1.7", - "escape-string-regexp": "^5.0.0", - "exsolve": "^1.0.8", - "get-port-please": "^3.2.0", - "jiti": "^2.6.1", - "knitwork": "^1.3.0", - "magic-string": "^0.30.21", - "mlly": "^1.8.2", - "mocked-exports": "^0.1.1", - "nypm": "^0.6.6", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "postcss": "^8.5.12", - "seroval": "^1.5.2", - "std-env": "^4.1.0", - "ufo": "^1.6.4", - "unenv": "^2.0.0-rc.24", - "vite": "^7.3.2", - "vite-node": "^5.3.0", - "vite-plugin-checker": "^0.13.0", - "vue-bundle-renderer": "^2.2.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@babel/plugin-proposal-decorators": "^7.25.0", - "@babel/plugin-syntax-jsx": "^7.25.0", - "nuxt": "4.4.4", - "rolldown": "^1.0.0-beta.38", - "rollup-plugin-visualizer": "^6.0.0 || ^7.0.1", - "vue": "^3.3.4" - }, - "peerDependenciesMeta": { - "@babel/plugin-proposal-decorators": { - "optional": true - }, - "@babel/plugin-syntax-jsx": { - "optional": true - }, - "rolldown": { - "optional": true - }, - "rollup-plugin-visualizer": { - "optional": true - } - } - }, - "node_modules/@nuxt/vite-builder/node_modules/@nuxt/kit": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.4.tgz", - "integrity": "sha512-oy4fAeMkyz7gelnalDQLPm8QZRN+c5c/Eh/M6oFgPx86jnA8m6xeOlONpJN2dk0GhcJwJYuN/kmzBffZ93WXPQ==", - "license": "MIT", - "dependencies": { - "c12": "^3.3.4", - "consola": "^3.4.2", - "defu": "^6.1.7", - "destr": "^2.0.5", - "errx": "^0.1.0", - "exsolve": "^1.0.8", - "ignore": "^7.0.5", - "jiti": "^2.6.1", - "klona": "^2.0.6", - "mlly": "^1.8.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "rc9": "^3.0.1", - "scule": "^1.3.0", - "semver": "^7.7.4", - "tinyglobby": "^0.2.16", - "ufo": "^1.6.4", - "unctx": "^2.5.0", - "untyped": "^2.0.0" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/@nuxt/vite-builder/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/@nuxt/vite-builder/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@nuxt/vite-builder/node_modules/unctx": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", - "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21", - "unplugin": "^2.3.11" - } - }, - "node_modules/@nuxt/vite-builder/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/@nuxt/vite-builder/node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/@oxc-minify/binding-android-arm-eabi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.128.0.tgz", - "integrity": "sha512-EwdDhZLRmXxSnfy0v9gdOru7TutM8ItRg1Xv8e2B4boWMnHlFCIH38JfwgQnenbkF8SVTwVJtDCkmwEzN4q3xA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-android-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.128.0.tgz", - "integrity": "sha512-kwJ8YxWTzty8hD36jXxKiB+Po/ecmHZvT1xAYklkATbr0A4NUqV32sV+3Wfm8TecdA6jX34/mc+4CKK2+Hha2Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-darwin-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.128.0.tgz", - "integrity": "sha512-WBV8j5EZ7/3rvFbiJ8LxowmobR/XH+l2iRzkE7zRYLD5VC+TvZayYGrVGGDXQvXm6cGED0B1NweByTmeT4lpGQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-darwin-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.128.0.tgz", - "integrity": "sha512-U4k1CSBsY1uf6yHE+vCNJp0mHzjsUUXgOZXMyhRN3sE2ovBDT9Gl8oACmLWPjg0R68jwP+1vhnNPsSqpTEOycg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-freebsd-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.128.0.tgz", - "integrity": "sha512-NT1GtcWpX4sOuU5dMdSNpdXJRpk9BGAHHnKc42IUId8E+jEhZUrg9vqIRIlspZG5O9Y7FjO2r6GBK93bpyIIUg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-arm-gnueabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.128.0.tgz", - "integrity": "sha512-OskPMYMH2KtkqvRMULF2/+55hFo/qmRz2p/g7Cp7XNiqdjZ/DvQDiVbME63rVoX3dYjgS15DolGbo54mHTyA9w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-arm-musleabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.128.0.tgz", - "integrity": "sha512-fKUY7Y1vb8CYlGnS5FzqTeeM5zQz1Fleyaqz/T9iNHYAYNJ0Os9iT0rACLfAVCQKP9yOqPSwZ80xgZdVVGD61w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-arm64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.128.0.tgz", - "integrity": "sha512-T+CQQZ3BoWY/TxQk9LZsXZYj3madR/5tCErV6wzphTYZJfVjvKmQxnxMaT+TKE40Jha6+iGgwzxwcYWJfltULQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-arm64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.128.0.tgz", - "integrity": "sha512-F6RkJ90S1Xt25Mk7/wPUmddsE4RZ7Nei+HlEa2FAjfhpoaTciOwV6E/Gtp7wPIYbwft7UfhMYwuEuZiZQytVWw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-ppc64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.128.0.tgz", - "integrity": "sha512-0HP2FBGMlquLjShIIJvS4cebc6sdRRYL04GtxVpg96MtpejrkHYI2gQWcezsTUaGgg+eNRsuv2tdZPENu5+iWA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-riscv64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.128.0.tgz", - "integrity": "sha512-2j6Bd340IZqZbu4KUI28z87Ao9aHhq56HH1Qz5/+EdE732ajFYIoDF3z+QcxHXY0CFOG/Ur1ZOKTBEIWQ6BYIw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-riscv64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.128.0.tgz", - "integrity": "sha512-z5HSppdxNwB6//3Eo7mDWbTrLeyuTKvL/iLXaKEgocrJg1MhZLbRR7P5ore9gKvS4lF4EtEpA24xzilFxQK0iw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-s390x-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.128.0.tgz", - "integrity": "sha512-9rxYqH7P8NiYqRlLxlnNjJSF8BYADOmihM5ZHVkmlE4tqjHkoLNevdAyAP2ZBkL8QJflm1WGOXFWmFnWA54EvA==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-x64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.128.0.tgz", - "integrity": "sha512-sy5+4Oamw6Ly5gUNUIDQ7346Lryt7AhqjKhOtWl5dzYZnTIwwoI0V2DeIl3bR/vU8D629ZMYQOqhquRtSyBUOA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-linux-x64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.128.0.tgz", - "integrity": "sha512-59Cxvjppy09TsaB15gr6rA9Bf87rm9t0bD1EW9dCZsdxWElnAC+TvWZ7v9dFUIeYeZUkhAAMPtpdqa3Y9CI2zA==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-openharmony-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-openharmony-arm64/-/binding-openharmony-arm64-0.128.0.tgz", - "integrity": "sha512-XGa03zmiYpD7Kf1aXy6vjgkjfaCR90qH0TzGplnUXo6FF6gNe6sH9Zgneo9kxOyYt8CKKzXYD4VudT/nDTXq8Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-wasm32-wasi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.128.0.tgz", - "integrity": "sha512-W+fK3cWhu/cUgx3NIAmDYcAyJs01aULlr3E3n/ZN79Q1/CX+FS+yWfwt/IysIi4FhpVL7z58azbJHDzhEx4X4g==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-win32-arm64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.128.0.tgz", - "integrity": "sha512-pwMZd27FF+j4tHLYKtu4QBl6KI0gkt6xTNGLffs8VlH5vfDPHUvLo/AS6y66tdEjQ3chhs8OGg1mAFhPoQldDw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-win32-ia32-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.128.0.tgz", - "integrity": "sha512-GskPdx/Fsn3ttkJbzxh51LYhla4N4p1sMufJKgf6PHupt5RukBaHI/GKM/2ni6ObxUI0b9UK37fROdV+5ekpMQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-minify/binding-win32-x64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.128.0.tgz", - "integrity": "sha512-m8oakspZCbCod3WuY0U9DvwQlhMYaU31bK+Way1Rb+JGs455WLtkebEie/luSuN5DeF+aZyRH/zt1AY4weKQQg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.148.0.tgz", - "integrity": "sha512-pHASv9g5pASxb7akHERZNSkrEqPhFaUix98o7d9hbTpolnnFWl7UiRrcMhCsV1+iVO4/cJwKsbKRJTFNs2tdBQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.148.0.tgz", - "integrity": "sha512-sg/6Ez0KdAygsu0POELux9wN1Po2CP93WY8eNl4DBKIGprsd4QSHBXOb471Pu9i2OCD5sLkISSb2agZEhVn2Zw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.148.0.tgz", - "integrity": "sha512-yiSJmzGUvCUaJT8X3j40gVcX+ckuHQMuiOtF8DvzTs5+JtB/7XuHFPp4M+vv5u+HlBtDUd4Ks5pyHpWz8mfnkg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.148.0.tgz", - "integrity": "sha512-6ZeklaamrMy4H2JmhvcJg6iip59tYILtuLaILxyAHT3l5FDxnI5ihVievAft5ZmAbqtlWHErOi1OpJK8gy1wcA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.148.0.tgz", - "integrity": "sha512-vFsPx+a/qFECPnz/H8nC6x6MDvnWscLTCo/5muojEF54ERUq1kdgbvnWo95YnkhjF9sTIcG/uDxQBh1gffaufQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.148.0.tgz", - "integrity": "sha512-eOr3M+6iGbbxNL4PSS0VtsyQ2eOUxSBh00BqO22SbolDimPSYsBuLr/LCrZBkiqW2BoabhR6V4R8jrRAay7hjg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.148.0.tgz", - "integrity": "sha512-58ZKDw0mQRbCNfrd2IDyV4o8T7enzGERJn41BH2tjrZVGyiKiFzcfDicuB7Zcpb/1xIOrObovr8Dja6lZi8dLw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.148.0.tgz", - "integrity": "sha512-Fnu95O4eZ5i++GPvIzBEZ8y4ddTLR+D9paYa8JRaRk6ZK7nHQiWP5xtrhcPQsXqgat1d7sU/d5rbbI0p1FTHSQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.148.0.tgz", - "integrity": "sha512-3CQy/BMdx7N7H3qrcPxUL+a2CwUZodUcf6oq8iJuNZ9C6Ol1aq3mcWzsgySJ7CHFLvpX21ZDPp1r1X0QLbu/AQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.148.0.tgz", - "integrity": "sha512-9LkaYvfiF8hMOw900csAvkf1oxE8XlmMeGowu5BcastSSwV8mKvKRMNU7HsV+ycyj1dQD8pX5qgOw8ja6SJacg==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.148.0.tgz", - "integrity": "sha512-2GBiM9h26dR4WJfhoMvnFMnFLf7m/kYs4UMqjvrOfQG4BV1nuTJDH22Zc2MQr3INZF7nSKYQ6xlhD3hQ7A6gug==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.148.0.tgz", - "integrity": "sha512-uPqZexvKJmEgq4mAu36qe2xTfXZE7oyik1R7KtZ5tl8qKlq1U1fIqTFRUEBZqRGvforoTrGIpatRzcoPKO66RA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.148.0.tgz", - "integrity": "sha512-9oUHvnTbp7ZraFsTC8PN6XhdhPSSxZumYvixWl7Smi353gEULvK6yV0sXNVrdFMHQeaDKFCi8TgDhNK7/A+Y+Q==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.148.0.tgz", - "integrity": "sha512-2qhDSJwKzbSZzF7lDqqk8sr/yXsmwr3PeUa4/nazIF+zFAYz1gVPEfC34GQtGxzJUUmklaYAL63368LEfrMeyw==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.148.0.tgz", - "integrity": "sha512-qQoPDZUFV0bh9xA09XydmkjMBpgc1ukJuhMvzQ9QeVmFaHTS9W5TE5CoLmSl3QQyUP9OuHO3x/WPZTIIZPWR3Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.148.0.tgz", - "integrity": "sha512-1UGbaQWEXUCLqAmaR5kwRDjx/R4S5LQKZkM9CHmaHkuKhriOF32aRLfS0jCRNE2yGQJLMEA1z9UucbBVqjXnDw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.128.0.tgz", - "integrity": "sha512-L38ojghJYHmgiz6fJd7jwLB/ESDBpB02NdFxh+smqVM6P2anCEvHn0jhaSrt5eVNR1Ak8+moOeftUlofeyvniA==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.148.0.tgz", - "integrity": "sha512-pWKdzRDNG2+NK4h/V6U/CYERcfYD6u28h5IB/VJVsrZaD3muvE58tUj22lieL5vLZ+XFi1GPv9YXckZbJZ9BLA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.148.0.tgz", - "integrity": "sha512-i3p4x+mvwtjcE1J5HM6V7ggsbXiznExN/4MkNyOy3dfXrVV3bnkSfmZxvo6/84qCVX4ShkpNE1SKt9biIF31GQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.148.0.tgz", - "integrity": "sha512-Ye6vB7VQulghWYkYkECOBYFRVEizz4XyRTUAv+t8BuyurhKU7uD0P9eowL+mKG5Mf8MSYx+DI3Cm8SKZvYG7bQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", - "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/oxc-project" - } - }, - "node_modules/@oxc-transform/binding-android-arm-eabi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm-eabi/-/binding-android-arm-eabi-0.128.0.tgz", - "integrity": "sha512-qVO4izEs88ZSo7KOK4P+O5nAXXJmkSadInvFjGkhVnm2R2Wr8trU/GLhjAK0S0u8Qv9bkXspNhgpECk+CTQ/ew==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-android-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.128.0.tgz", - "integrity": "sha512-F3RXlbCzIgkpRWlz1PEguDZl5NzZRmbeHKTFTQWFnK6mIdw2EkWihPVv9+CIcO80c7+sF/YRGOBaji6hwUDhtQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-darwin-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.128.0.tgz", - "integrity": "sha512-xj63gIzQ67LDYHCOWXSHgfx4LbPVz1ck0G3y0eR6mbgYk3CwwylbhWi/CaDC6BWsHwoLQryeYjHB5XBCR0EPMQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-darwin-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.128.0.tgz", - "integrity": "sha512-YQkvFqNqpwEt197RjREAOWeRANalPtCD+ayZlx4IjTQ6IOYZEP83B9/++gTQisHV3r8E7dU8UqJKeSS1cHlTQg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-freebsd-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.128.0.tgz", - "integrity": "sha512-Jvd3Ximb3x3o0+xRBB5lq63JlzxhJN787IsBjn0PEnmuocYQj+tJ5BB4n9xPIG27GXwg3ycckQPO/RsWeEcBPg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-arm-gnueabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.128.0.tgz", - "integrity": "sha512-TaRKWeGnAJNIdCa5+m0I8/SksBgkLX94iH40qy3chvLuaIOGAmOViUStYx8geXBzO9P99V7En8nHXLoqCONBRQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-arm-musleabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.128.0.tgz", - "integrity": "sha512-7TMrtA5/3SCvS+yMPrGnri5T4ZhIoCbjwKWV6Kn8d3v+vx7MpEmNkfe+CdF3rb5LlnuxeDMPwr1E2ntya0b8HQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-arm64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.128.0.tgz", - "integrity": "sha512-lMQEa1jLBNm1N+5uvyj9zX9urVY4xKkLnhO8/4CtSGdXX+mExqsVawyQPAZqbtq1fLQ0yt1QYJ9YuM0+fiSJTQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-arm64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.128.0.tgz", - "integrity": "sha512-dPSjyd0gQ9dE3mpdJi0BHNJaqQz4V7mVW6Fbs6jRSiGnrxwGfXdMJFInXoJ49B3k5Zhfa9Is9Ixp6St7c6ouCA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-ppc64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.128.0.tgz", - "integrity": "sha512-YNa9XAotPKvAXFJrHC7kBsHMVg0HOB4vRiKuYUjzFsfLkxTbuztKHTKG/gW5kjp7dBw+TNFofTaVCVZgOnHXPQ==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-riscv64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.128.0.tgz", - "integrity": "sha512-jjSiG9H8ya/U3igW5DdIBFIDwhffF7Vbc7th2tcHV73eg0DQz75n36a9RmQ1/0aS9vknUuNtY6SODr8/gmuzsQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-riscv64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.128.0.tgz", - "integrity": "sha512-FVUr/XNT7BfQA4XVMel/HTCJi5mQyEitslgX42ztYPnCFMRFG1sQQKgnlLJdl7qifuyxpvKLR1f7h7HEuwWw1Q==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-s390x-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.128.0.tgz", - "integrity": "sha512-caJnVw5PG8v339zAyHgA7p34xXa3A4Kc9VyrDgsT1znr51qacaUv4BRlgRi0qkqxRWXYNVFfsbU2g0t1qS7E9w==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-x64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.128.0.tgz", - "integrity": "sha512-zkQKjsHEUX3ckQBcZTtHE/7pgFMkWQp6y/4t7N8eT3j8wnoL+vapv7l4ISjgx1/EePRJN1HErYXmriz7tPVKRg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-linux-x64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.128.0.tgz", - "integrity": "sha512-NjYtwl9ijp34iisHxYBvE7nii1Ac0QPP3doHv8MQHhDA3zjUcDCROuBNybfaEYCxnJ1aF+cAPqsyeopnAGsyuQ==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-openharmony-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-openharmony-arm64/-/binding-openharmony-arm64-0.128.0.tgz", - "integrity": "sha512-itsi0tVkVdrYphSppdFChLq9tD0pvbRRS3EV8NQYKZ/NWHMoxzjlf9TFA/ZZYV113juYo1Dq3glVX48knhBeFQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-wasm32-wasi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.128.0.tgz", - "integrity": "sha512-elzjX2gy1jcseeFaKtbk/6T2FPTpGNx0IpeD0iyk6cahWN7wD6eHY5u7th1X85cYbRq9rqniS+xYIxN3StthWg==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-win32-arm64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.128.0.tgz", - "integrity": "sha512-p5LmbI66dk2dziJSUzjQ24gOWeI6pJpXcOC6famloRtKCq54V5/KegsztFgZZCtYFEAEqFgcfspFHrV+CcKWcg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-win32-ia32-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.128.0.tgz", - "integrity": "sha512-CMU3Yn05rXeLw7GyVlDB3bbp2iV14yt3VWyF0pNuMK9NVgOmUkXgFLe5SOcX9rEm64TRJjOMEghtE5+r0GtqIQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-transform/binding-win32-x64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.128.0.tgz", - "integrity": "sha512-Vck5AdNH2JPYMQWVDxvX5PbDFfqVG+tCOgKJzAC/S9bgbD3qcMjN5Dx6FOmEbwY3hZm//fzOsY4tErofoiK/aQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@parcel/watcher-wasm": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.6.0.tgz", - "integrity": "sha512-dtjbDxKSDPQ8AmA+pS4OFaHE1FKrjtGpLGBxw85uKFkRorjNbvDM/aFPgqosu40wprbp1xw2ZSxIKqghCUHe2w==", - "bundleDependencies": [ - "napi-wasm" - ], - "license": "MIT", - "dependencies": { - "is-glob": "^4.0.3", - "napi-wasm": "^1.1.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { - "version": "1.1.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "license": "MIT" - }, - "node_modules/@poppinss/colors": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", - "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", - "license": "MIT", - "dependencies": { - "kleur": "^4.1.5" - } - }, - "node_modules/@poppinss/dumper": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.7.0.tgz", - "integrity": "sha512-0UTYalzk2t6S4rA2uHOz5bSSW2CHdv4vggJI6Alg90yvl0UgXs6XSXpH96OH+bRkX4J/06djv29pqXJ0lq5Kag==", - "license": "MIT", - "dependencies": { - "@poppinss/colors": "^4.1.5", - "@sindresorhus/is": "^7.0.2", - "supports-color": "^10.0.0" - } - }, - "node_modules/@poppinss/exception": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", - "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", - "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", - "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", - "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", - "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", - "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", - "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", - "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", - "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", - "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", - "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", - "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", - "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", - "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", - "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", - "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "license": "MIT" - }, - "node_modules/@rollup/plugin-alias": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-6.0.0.tgz", - "integrity": "sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==", - "license": "MIT", - "engines": { - "node": ">=20.19.0" - }, - "peerDependencies": { - "rollup": ">=4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "29.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", - "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "fdir": "^6.2.0", - "is-reference": "1.2.1", - "magic-string": "^0.30.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@rollup/plugin-inject": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", - "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-inject/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@rollup/plugin-json": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", - "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.1.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", - "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", - "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-replace/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", - "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", - "license": "MIT", - "dependencies": { - "serialize-javascript": "^7.0.3", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", - "cpu": [ - "arm" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", - "cpu": [ - "loong64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", - "cpu": [ - "loong64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@simple-git/args-pathspec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz", - "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==", - "license": "MIT" - }, - "node_modules/@simple-git/argv-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz", - "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==", - "license": "MIT", - "dependencies": { - "@simple-git/args-pathspec": "^1.0.3" - } - }, - "node_modules/@sindresorhus/is": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", - "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@speed-highlight/core": { - "version": "1.2.24", - "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.24.tgz", - "integrity": "sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==", - "license": "CC0-1.0" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "license": "MIT" - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "license": "MIT" - }, - "node_modules/@unhead/vue": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.17.tgz", - "integrity": "sha512-pnC8x9HLV3qQXdvWfylUEU25uhfCAy3ly9nmpQz84j9py818DRfU8jOsQ5wjdWtxyU1vX/fW2udfm4jtxUK8Bg==", - "license": "MIT", - "dependencies": { - "hookable": "^6.0.1", - "unhead": "2.1.17" - }, - "funding": { - "url": "https://github.com/sponsors/harlan-zw" - }, - "peerDependencies": { - "vue": ">=3.5.18" - } - }, - "node_modules/@vercel/nft": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.11.0.tgz", - "integrity": "sha512-m1QFg+U+3yPOnP1xSYJ73UIRxLOXdts1JOhiOiyPYqEsALgrXFFINvgUaD6R6iNvaBFAjHllBCbkfx4FuOdpaA==", - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^13.0.0", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.4", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@vitejs/plugin-vue": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", - "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.1" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vitejs/plugin-vue-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.6.tgz", - "integrity": "sha512-YXvi4as2clxt6DFw5+a0tTA97ntiQXm/raR8ofNj3aNwwdlVGTiG2gp7EvfZW17P50acL/9bP0ccF4XnqNmlgA==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-syntax-typescript": "^7.29.7", - "@babel/plugin-transform-typescript": "^7.29.7", - "@rolldown/pluginutils": "^1.0.1", - "@vue/babel-plugin-jsx": "^2.0.1" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "vue": "^3.0.0" - } - }, - "node_modules/@vue-macros/common": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz", - "integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==", - "license": "MIT", - "dependencies": { - "@vue/compiler-sfc": "^3.5.22", - "ast-kit": "^2.1.2", - "local-pkg": "^1.1.2", - "magic-string-ast": "^1.0.2", - "unplugin-utils": "^0.3.0" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/vue-macros" - }, - "peerDependencies": { - "vue": "^2.7.0 || ^3.2.25" - }, - "peerDependenciesMeta": { - "vue": { - "optional": true - } - } - }, - "node_modules/@vue/babel-helper-vue-transform-on": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz", - "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==", - "license": "MIT" - }, - "node_modules/@vue/babel-plugin-jsx": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz", - "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", - "@vue/babel-helper-vue-transform-on": "2.0.1", - "@vue/babel-plugin-resolve-type": "2.0.1", - "@vue/shared": "^3.5.22" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - } - } - }, - "node_modules/@vue/babel-plugin-resolve-type": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz", - "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/parser": "^7.28.4", - "@vue/compiler-sfc": "^3.5.22" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz", - "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@vue/shared": "3.5.42", - "entities": "^7.0.1", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz", - "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.42", - "@vue/shared": "3.5.42" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz", - "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@vue/compiler-core": "3.5.42", - "@vue/compiler-dom": "3.5.42", - "@vue/compiler-ssr": "3.5.42", - "@vue/shared": "3.5.42", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.21", - "postcss": "^8.5.19", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz", - "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.42", - "@vue/shared": "3.5.42" - } - }, - "node_modules/@vue/devtools-api": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", - "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^8.2.1" - } - }, - "node_modules/@vue/devtools-core": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.2.1.tgz", - "integrity": "sha512-s/VfAY9oDTb/kFEWmy461jaFde2MIV1RO/gi1vwM+PAZBZ/Pc2Ndu3BNBdZUze8QDUuyYvElbEEGA83syjJfzA==", - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^8.2.1", - "@vue/devtools-shared": "^8.2.1" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", - "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", - "license": "MIT", - "dependencies": { - "@vue/devtools-shared": "^8.2.1", - "birpc": "^2.6.1", - "hookable": "^5.5.3", - "perfect-debounce": "^2.0.0" - } - }, - "node_modules/@vue/devtools-kit/node_modules/birpc": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", - "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vue/devtools-kit/node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "license": "MIT" - }, - "node_modules/@vue/devtools-shared": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", - "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", - "license": "MIT" - }, - "node_modules/@vue/reactivity": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz", - "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.42" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz", - "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.42", - "@vue/shared": "3.5.42" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz", - "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.42", - "@vue/runtime-core": "3.5.42", - "@vue/shared": "3.5.42", - "csstype": "^3.2.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz", - "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.42", - "@vue/runtime-dom": "3.5.42", - "@vue/shared": "3.5.42" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz", - "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==", - "license": "MIT" - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz", - "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/archiver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", - "license": "MIT", - "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/archiver-utils/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/archiver-utils/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/archiver-utils/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ast-kit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", - "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.5", - "pathe": "^2.0.3" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/ast-walker-scope": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.9.0.tgz", - "integrity": "sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.2", - "@babel/types": "^7.29.0", - "ast-kit": "^2.2.0" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/async-sema": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.5.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.5.tgz", - "integrity": "sha512-uiRYvQYe/nNSzBJ7OUnd2/TZVsAdob3blml44teEpee9Cc1f4rGZFewO+JT3Wo8mgFOSzNqes4FHZn/Qz8WOuw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.9", - "caniuse-lite": "^1.0.30001810", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/b4a": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", - "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/bare-events": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", - "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz", - "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.28.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-path": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", - "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", - "license": "Apache-2.0" - }, - "node_modules/bare-stream": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", - "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.8.1", - "streamx": "^2.25.0", - "teex": "^1.0.1" - }, - "peerDependencies": { - "bare-abort-controller": "*", - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - }, - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", - "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", - "license": "Apache-2.0", - "dependencies": { - "bare-path": "^3.0.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.21", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", - "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/birpc": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.2.0.tgz", - "integrity": "sha512-KxgKcZPfrtzJDDALHPguGpGJUrzdgpymyiQQgzFjWreHMOpWrnFNVREr5J48x2DBh8ZVioscrV1SBkDipGiX+Q==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", - "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.20", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.420", - "node-releases": "^2.0.54", - "update-browserslist-db": "^1.3.2" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/c12": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", - "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", - "license": "MIT", - "dependencies": { - "chokidar": "^5.0.0", - "confbox": "^0.2.4", - "defu": "^6.1.6", - "dotenv": "^17.3.1", - "exsolve": "^1.0.8", - "giget": "^3.2.0", - "jiti": "^2.6.1", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^2.1.0", - "pkg-types": "^2.3.0", - "rc9": "^3.0.1" - }, - "peerDependencies": { - "magicast": "*" - }, - "peerDependenciesMeta": { - "magicast": { - "optional": true - } - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/citty": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", - "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", - "license": "MIT" - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", - "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "license": "MIT" - }, - "node_modules/compatx": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/compatx/-/compatx-0.2.0.tgz", - "integrity": "sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==", - "license": "MIT" - }, - "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/compress-commons/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie-es": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", - "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/croner": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", - "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", - "funding": [ - { - "type": "other", - "url": "https://paypal.me/hexagonpp" - }, - { - "type": "github", - "url": "https://github.com/sponsors/hexagon" - } - ], - "license": "MIT", - "engines": { - "node": ">=18.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-declaration-sorter": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.4.0.tgz", - "integrity": "sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==", - "license": "ISC", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-select": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", - "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^7.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "nth-check": "^2.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", - "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.9.tgz", - "integrity": "sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==", - "license": "MIT", - "dependencies": { - "cssnano-preset-default": "^7.0.17", - "lilconfig": "^3.1.3" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/cssnano-preset-default": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.17.tgz", - "integrity": "sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "css-declaration-sorter": "^7.2.0", - "cssnano-utils": "^5.0.3", - "postcss-calc": "^10.1.1", - "postcss-colormin": "^7.0.10", - "postcss-convert-values": "^7.0.12", - "postcss-discard-comments": "^7.0.8", - "postcss-discard-duplicates": "^7.0.4", - "postcss-discard-empty": "^7.0.3", - "postcss-discard-overridden": "^7.0.3", - "postcss-merge-longhand": "^7.0.7", - "postcss-merge-rules": "^7.0.11", - "postcss-minify-font-values": "^7.0.3", - "postcss-minify-gradients": "^7.0.5", - "postcss-minify-params": "^7.0.9", - "postcss-minify-selectors": "^7.1.2", - "postcss-normalize-charset": "^7.0.3", - "postcss-normalize-display-values": "^7.0.3", - "postcss-normalize-positions": "^7.0.4", - "postcss-normalize-repeat-style": "^7.0.4", - "postcss-normalize-string": "^7.0.3", - "postcss-normalize-timing-functions": "^7.0.3", - "postcss-normalize-unicode": "^7.0.9", - "postcss-normalize-url": "^7.0.3", - "postcss-normalize-whitespace": "^7.0.3", - "postcss-ordered-values": "^7.0.4", - "postcss-reduce-initial": "^7.0.9", - "postcss-reduce-transforms": "^7.0.3", - "postcss-svgo": "^7.1.3", - "postcss-unique-selectors": "^7.0.7" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/cssnano-utils": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.3.tgz", - "integrity": "sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==", - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/db0": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/db0/-/db0-0.3.4.tgz", - "integrity": "sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==", - "license": "MIT", - "peerDependencies": { - "@electric-sql/pglite": "*", - "@libsql/client": "*", - "better-sqlite3": "*", - "drizzle-orm": "*", - "mysql2": "*", - "sqlite3": "*" - }, - "peerDependenciesMeta": { - "@electric-sql/pglite": { - "optional": true - }, - "@libsql/client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "drizzle-orm": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", - "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", - "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defu": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", - "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", - "license": "MIT" - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devalue": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz", - "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==", - "license": "MIT" - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-prop": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.2.0.tgz", - "integrity": "sha512-BTJ9aZYL3vCfZlZOBLy9v8TUqWGQ0pzFnygKwFZt5udj6viBoFIBviKPUoZLDCPn1FoXffv6McQFDenrm5Krfw==", - "license": "MIT", - "dependencies": { - "type-fest": "^5.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dotenv": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", - "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.422", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", - "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-stack-parser-es": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-2.0.1.tgz", - "integrity": "sha512-J36ntO+rMQVRuR/umlmxmfLi4TpWwtmTnHoJoXTiC2xDNazs0VDPKcX6pbhZMDd2HFtH9isMBJXMdbki+A++Pg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/errx": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/errx/-/errx-0.1.2.tgz", - "integrity": "sha512-chfpPHmCerdo/rXr/nNvPZRkV4WwDRwzwnsJ0Uzz3tVi8Z41tDctRjduYy1138ii77AFlts1qvWtX3g/Acg91Q==", - "license": "MIT" - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", - "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exsolve": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", - "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", - "license": "MIT" - }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-npm-meta": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/fast-npm-meta/-/fast-npm-meta-2.2.0.tgz", - "integrity": "sha512-99jPl8JkCSCa4VlboNU1XuL98ijm74Pm9CGo6H4BoMVoVh1uhguQcvwLgXDT8Vkl2qj/UEQ0J9gD8beHjTFk1w==", - "license": "MIT", - "dependencies": { - "cac": "^7.0.0" - }, - "bin": { - "fast-npm-meta": "dist/cli.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/fast-npm-meta/node_modules/cac": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", - "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", - "license": "MIT", - "engines": { - "node": ">=20.19.0" - } - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, - "node_modules/fastq": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", - "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/fuse.js": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.5.0.tgz", - "integrity": "sha512-sQtrEfA+ez/3G0cCZecF70oqpCRttCexYUG4mUrtWL49ULUzUyxokt5kyqwtKzj1270RaKih+hcP3qLcumccow==", - "license": "Apache-2.0", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/krisk" - } - }, - "node_modules/fzf": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", - "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", - "license": "BSD-3-Clause" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-port-please": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", - "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", - "license": "MIT" - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/giget": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", - "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", - "license": "MIT", - "bin": { - "giget": "dist/cli.mjs" - } - }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/global-directory": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "license": "MIT", - "dependencies": { - "ini": "4.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.4.tgz", - "integrity": "sha512-c8B/VNLmxRcmqqenRA9t+9IyOjf9+V6lTxPaUJLqOCONdQkWZ0ETYgX0qbtJqPsgCNusT9MZ5Jeidw8Eb9tn2g==", - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "fast-glob": "^3.3.3", - "ignore": "^7.0.5", - "is-path-inside": "^4.0.0", - "micromatch": "^4.0.8", - "slash": "^5.1.0", - "unicorn-magic": "^0.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/gzip-size": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz", - "integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==", - "license": "MIT", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/h3": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", - "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.3", - "crossws": "^0.3.5", - "defu": "^6.1.6", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.4", - "radix3": "^1.1.2", - "ufo": "^1.6.3", - "uncrypto": "^0.1.3" - } - }, - "node_modules/h3/node_modules/cookie-es": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", - "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", - "license": "MIT" - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hookable": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz", - "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==", - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-shutdown": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", - "integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==", - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/httpxy": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.5.5.tgz", - "integrity": "sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==", - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", - "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-meta": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/image-meta/-/image-meta-0.2.2.tgz", - "integrity": "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==", - "license": "MIT" - }, - "node_modules/impound": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/impound/-/impound-1.2.0.tgz", - "integrity": "sha512-hbFh2WURN+XQ364SbblzubhEEj/B3qZCe80l/EHXPWM6/kZP2mvOllxwRBBWNxe4gLbqXZL0fxOZwoDCSlJZQw==", - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "es-module-lexer": "^2.0.0", - "pathe": "^2.0.3", - "unplugin": "^3.0.0", - "unplugin-utils": "^0.3.1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ioredis": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", - "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "1.10.0", - "cluster-key-slot": "1.1.1", - "debug": "4.4.3", - "denque": "2.1.0", - "redis-errors": "1.2.0", - "redis-parser": "3.0.0", - "standard-as-callback": "2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-in-ssh": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", - "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-installed-globally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", - "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", - "license": "MIT", - "dependencies": { - "global-directory": "^4.0.1", - "is-path-inside": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "license": "MIT" - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", - "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=20" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/knitwork": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/knitwork/-/knitwork-1.3.0.tgz", - "integrity": "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==", - "license": "MIT" - }, - "node_modules/launch-editor": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", - "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.8.4" - } - }, - "node_modules/lazystream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/lazystream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/lightningcss": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", - "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "license": "MPL-2.0", - "peer": true, - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.33.0", - "lightningcss-darwin-arm64": "1.33.0", - "lightningcss-darwin-x64": "1.33.0", - "lightningcss-freebsd-x64": "1.33.0", - "lightningcss-linux-arm-gnueabihf": "1.33.0", - "lightningcss-linux-arm64-gnu": "1.33.0", - "lightningcss-linux-arm64-musl": "1.33.0", - "lightningcss-linux-x64-gnu": "1.33.0", - "lightningcss-linux-x64-musl": "1.33.0", - "lightningcss-win32-arm64-msvc": "1.33.0", - "lightningcss-win32-x64-msvc": "1.33.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", - "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", - "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", - "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", - "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", - "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", - "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", - "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", - "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", - "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", - "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", - "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/listhen": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.10.1.tgz", - "integrity": "sha512-6nt/86SkqUQSLW1ofz8MxC6RhRMqOl3ONISe6qqvJ3xj09aJWQx6DhgSZpugs3PX4PXdOas/WD6A9jx6J2N19A==", - "license": "MIT", - "dependencies": { - "@parcel/watcher-wasm": "^2.5.6", - "citty": "^0.2.2", - "consola": "^3.4.2", - "crossws": "^0.4.10", - "defu": "^6.1.7", - "get-port-please": "^3.2.0", - "h3": "^1.15.11", - "http-shutdown": "^1.2.2", - "jiti": "^2.7.0", - "node-forge": "^1.4.0", - "pathe": "^2.0.3", - "std-env": "^4.2.0", - "tinyclip": "^0.1.15", - "ufo": "^1.6.4", - "untun": "^0.2.2", - "uqr": "^0.1.3" - }, - "bin": { - "listen": "bin/listhen.mjs", - "listhen": "bin/listhen.mjs" - }, - "peerDependencies": { - "@parcel/watcher": "^2.5.6" - }, - "peerDependenciesMeta": { - "@parcel/watcher": { - "optional": true - } - } - }, - "node_modules/listhen/node_modules/crossws": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.4.12.tgz", - "integrity": "sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==", - "license": "MIT", - "peerDependencies": { - "srvx": ">=0.11.5" - }, - "peerDependenciesMeta": { - "srvx": { - "optional": true - } - } - }, - "node_modules/local-pkg": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", - "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "license": "MIT" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-regexp": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.10.0.tgz", - "integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==", - "license": "MIT", - "dependencies": { - "estree-walker": "^3.0.3", - "magic-string": "^0.30.12", - "mlly": "^1.7.2", - "regexp-tree": "^0.1.27", - "type-level-regexp": "~0.1.17", - "ufo": "^1.5.4", - "unplugin": "^2.0.0" - } - }, - "node_modules/magic-regexp/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/magic-regexp/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magic-regexp/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/magic-string": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", - "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magic-string-ast": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", - "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.19" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/magic-string-ast/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", - "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "source-map-js": "^1.2.1" - } - }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", - "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", - "funding": [ - "https://github.com/sponsors/broofa" - ], - "license": "MIT", - "bin": { - "mime": "bin/cli.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", - "license": "MIT", - "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" - } - }, - "node_modules/mlly/node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT" - }, - "node_modules/mlly/node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/mocked-exports": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/mocked-exports/-/mocked-exports-0.1.1.tgz", - "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==", - "license": "MIT" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/muggle-string": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", - "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nanotar": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.3.0.tgz", - "integrity": "sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg==", - "license": "MIT" - }, - "node_modules/nitropack": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.13.4.tgz", - "integrity": "sha512-tX7bT6zxNeMwkc6hxHiZeUoTOjVrcjoh1Z3cmxOlodIqjl4HISgqfGOmkWSayky3Nv9Z5+KQH52F8nmXJY5AAA==", - "license": "MIT", - "dependencies": { - "@cloudflare/kv-asset-handler": "^0.4.2", - "@rollup/plugin-alias": "^6.0.0", - "@rollup/plugin-commonjs": "^29.0.2", - "@rollup/plugin-inject": "^5.0.5", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-replace": "^6.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@vercel/nft": "^1.5.0", - "archiver": "^7.0.1", - "c12": "^3.3.4", - "chokidar": "^5.0.0", - "citty": "^0.2.2", - "compatx": "^0.2.0", - "confbox": "^0.2.4", - "consola": "^3.4.2", - "cookie-es": "^2.0.1", - "croner": "^10.0.1", - "crossws": "^0.3.5", - "db0": "^0.3.4", - "defu": "^6.1.7", - "destr": "^2.0.5", - "dot-prop": "^10.1.0", - "esbuild": "^0.28.0", - "escape-string-regexp": "^5.0.0", - "etag": "^1.8.1", - "exsolve": "^1.0.8", - "globby": "^16.2.0", - "gzip-size": "^7.0.0", - "h3": "^1.15.11", - "hookable": "^5.5.3", - "httpxy": "^0.5.1", - "ioredis": "^5.10.1", - "jiti": "^2.6.1", - "klona": "^2.0.6", - "knitwork": "^1.3.0", - "listhen": "^1.9.1", - "magic-string": "^0.30.21", - "magicast": "^0.5.2", - "mime": "^4.1.0", - "mlly": "^1.8.2", - "node-fetch-native": "^1.6.7", - "node-mock-http": "^1.0.4", - "ofetch": "^1.5.1", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^2.1.0", - "pkg-types": "^2.3.1", - "pretty-bytes": "^7.1.0", - "radix3": "^1.1.2", - "rollup": "^4.60.2", - "rollup-plugin-visualizer": "^7.0.1", - "scule": "^1.3.0", - "semver": "^7.7.4", - "serve-placeholder": "^2.0.2", - "serve-static": "^2.2.1", - "source-map": "^0.7.6", - "std-env": "^4.1.0", - "ufo": "^1.6.4", - "ultrahtml": "^1.6.0", - "uncrypto": "^0.1.3", - "unctx": "^2.5.0", - "unenv": "2.0.0-rc.24", - "unimport": "^6.2.0", - "unplugin-utils": "^0.3.1", - "unstorage": "^1.17.5", - "untyped": "^2.0.0", - "unwasm": "^0.5.3", - "youch": "^4.1.1", - "youch-core": "^0.3.3" - }, - "bin": { - "nitro": "dist/cli/index.mjs", - "nitropack": "dist/cli/index.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "xml2js": "^0.6.2" - }, - "peerDependenciesMeta": { - "xml2js": { - "optional": true - } - } - }, - "node_modules/nitropack/node_modules/cookie-es": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.1.tgz", - "integrity": "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==", - "license": "MIT" - }, - "node_modules/nitropack/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/nitropack/node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "license": "MIT" - }, - "node_modules/nitropack/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/nitropack/node_modules/unctx": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", - "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21", - "unplugin": "^2.3.11" - } - }, - "node_modules/nitropack/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-mock-http": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", - "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.54", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", - "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nostics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.2.0.tgz", - "integrity": "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==", - "license": "MIT" - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nuxt": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.4.4.tgz", - "integrity": "sha512-r9E3PYo+uJazltAmjm0TwFW3MQ++Wd//2uRZgCyqkt7VSAVJ5KnRRwUF7JktK/NZbLYAUDiV3tgqE9ZYbHbymA==", - "license": "MIT", - "dependencies": { - "@dxup/nuxt": "^0.4.1", - "@nuxt/cli": "^3.35.1", - "@nuxt/devtools": "^3.2.4", - "@nuxt/kit": "4.4.4", - "@nuxt/nitro-server": "4.4.4", - "@nuxt/schema": "4.4.4", - "@nuxt/telemetry": "^2.8.0", - "@nuxt/vite-builder": "4.4.4", - "@unhead/vue": "^2.1.13", - "@vue/shared": "^3.5.33", - "chokidar": "^5.0.0", - "compatx": "^0.2.0", - "consola": "^3.4.2", - "cookie-es": "^2.0.1", - "defu": "^6.1.7", - "devalue": "^5.7.1", - "errx": "^0.1.0", - "escape-string-regexp": "^5.0.0", - "exsolve": "^1.0.8", - "hookable": "^6.1.1", - "ignore": "^7.0.5", - "impound": "^1.1.5", - "jiti": "^2.6.1", - "klona": "^2.0.6", - "knitwork": "^1.3.0", - "magic-string": "^0.30.21", - "mlly": "^1.8.2", - "nanotar": "^0.3.0", - "nypm": "^0.6.6", - "ofetch": "^1.5.1", - "ohash": "^2.0.11", - "on-change": "^6.0.2", - "oxc-minify": "^0.128.0", - "oxc-parser": "^0.128.0", - "oxc-transform": "^0.128.0", - "oxc-walker": "^0.7.0", - "pathe": "^2.0.3", - "perfect-debounce": "^2.1.0", - "picomatch": "^4.0.4", - "pkg-types": "^2.3.1", - "rou3": "^0.8.1", - "scule": "^1.3.0", - "semver": "^7.7.4", - "std-env": "^4.1.0", - "tinyglobby": "^0.2.16", - "ufo": "^1.6.4", - "ultrahtml": "^1.6.0", - "uncrypto": "^0.1.3", - "unctx": "^2.5.0", - "unimport": "^6.2.0", - "unplugin": "^3.0.0", - "unrouting": "^0.1.7", - "untyped": "^2.0.0", - "vue": "^3.5.33", - "vue-router": "^5.0.6" - }, - "bin": { - "nuxi": "bin/nuxt.mjs", - "nuxt": "bin/nuxt.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@parcel/watcher": "^2.1.0", - "@types/node": ">=18.12.0" - }, - "peerDependenciesMeta": { - "@parcel/watcher": { - "optional": true - }, - "@types/node": { - "optional": true - } - } - }, - "node_modules/nuxt/node_modules/@nuxt/kit": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.4.tgz", - "integrity": "sha512-oy4fAeMkyz7gelnalDQLPm8QZRN+c5c/Eh/M6oFgPx86jnA8m6xeOlONpJN2dk0GhcJwJYuN/kmzBffZ93WXPQ==", - "license": "MIT", - "dependencies": { - "c12": "^3.3.4", - "consola": "^3.4.2", - "defu": "^6.1.7", - "destr": "^2.0.5", - "errx": "^0.1.0", - "exsolve": "^1.0.8", - "ignore": "^7.0.5", - "jiti": "^2.6.1", - "klona": "^2.0.6", - "mlly": "^1.8.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "rc9": "^3.0.1", - "scule": "^1.3.0", - "semver": "^7.7.4", - "tinyglobby": "^0.2.16", - "ufo": "^1.6.4", - "unctx": "^2.5.0", - "untyped": "^2.0.0" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/nuxt/node_modules/@nuxt/schema": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-4.4.4.tgz", - "integrity": "sha512-X70+lDZ4Wtp38l18/zFlKOZO5fd0uWQ60nrr1gxTNua8sqOxqVeZpLWTBmor7lFfJsXPPclsaFjcstyXYqXgpg==", - "license": "MIT", - "dependencies": { - "@vue/shared": "^3.5.33", - "defu": "^6.1.7", - "pathe": "^2.0.3", - "pkg-types": "^2.3.1", - "std-env": "^4.1.0" - }, - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.128.0.tgz", - "integrity": "sha512-aca6ZvzmCBUGOANQRiRQRZuRKYI3ENhcit6GisnknOOmcezfQc7xJ4dxlPU7MV7mOvrC7RNR1u3LAD7xyaiCxA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.128.0.tgz", - "integrity": "sha512-BbeDmuohoJ7Rz/it5wnkj69i/OsCPS3Z51nLEzwO/Y6YshtC4JU+15oNwhY8v4LRKRYclRc7ggOikwrsJ/eOEQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.128.0.tgz", - "integrity": "sha512-tRUHPt80417QmvNpoSslJT1VY8NUbWdrWR+L14Zn+RbOTcaqB8E6PYE/ZGN8jjWBzqporiA/H4MfO50ew/NCNA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.128.0.tgz", - "integrity": "sha512-rWI2Hb1Nt3U/vKsjyNvZzDC8i/l144U20DKjhzaTmwIhIiSRGeroPWWiImwypmKLqrw8GuIixbWJkpGWLbkzrQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.128.0.tgz", - "integrity": "sha512-hhpdVMaNCLgQxjgNPeeFzSeJMmZPc5lKfv0NGSI3egZq9EdnEGqeC8JsYsQjK7PoQgbvZ17xlj0SO5ziH5Obkg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.128.0.tgz", - "integrity": "sha512-093zNw0zZ/e/obML+rhlSdmnzR0mVZluPcAkxunEc5E3F0yBVsFn24Y1ILfsEte11Ud041qn/gp2OJ1jxNqUng==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.128.0.tgz", - "integrity": "sha512-fq7DmKmfC+dvD97IXrgbph6Jzwe0EDu+PYMofmzZ6fv5X1k9vtaqLpDGMuICO9MmUnyKAQmVl+wIv2RNy4Dz8g==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.128.0.tgz", - "integrity": "sha512-Xvm48jJah8TlIrURIjNOP/gNiGe6aKvCB+r06VliflFo8Kq7VOLE8PxtgShJzZIqubrgdMdYfvuPPozn7F6MbQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.128.0.tgz", - "integrity": "sha512-M7iwBGmYJTx+pKOYFjI0buop4gJvlmcVzFGaXPt21DKpQkbQZG1f63Yg7LloIYT/t9yLxCw0Lhfx/RFlAlMSjA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.128.0.tgz", - "integrity": "sha512-21LGNIZb1Pcfk5/EGsqabrxv4yqQOWis1407JJrClS7XpFCrbvr74YAB1V+m54cYbwvO6UWwQqS4WecxiyfCRg==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.128.0.tgz", - "integrity": "sha512-gyHjOTFpg9bTTYjxPmQirvufb89+VdZwVfcMtAUyPr6F5H8ZswvCQshK4qOW+Q+2Xyb33hduRgY/eFHJQjU/vQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.128.0.tgz", - "integrity": "sha512-X6Q2oKUrP5GyDd2xniuEBLk6aFQCZ97W2+aVXGgJXdjx5t4/oFuA9ri0wLOUrBIX+qdSuK581snMBio4z910eA==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.128.0.tgz", - "integrity": "sha512-BdzTmqxfxoYkpgokoLaSnOX6T+R3/goL42klre2tnG+kHbG2TXS0VN+P5BPofH1axdKOHy5ei4ENZrjmCOt2lA==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.128.0.tgz", - "integrity": "sha512-OO1nW2Q7sSYYvJZpDHdvyFSdRaVcQqRijZSSmWVMqFxPYy8cEF45zJ9fcdIYuzIT3jYq6YRhEFm/VMWNWhE22Q==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.128.0.tgz", - "integrity": "sha512-4NehAe404MRdoZVS9DW8C5XbJwbXIc/KfVlYdpi5vE4081zc9Y0YzKVqyOYj/Puye7/Do+ohaONBFWlEHYl9hw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.128.0.tgz", - "integrity": "sha512-kVbqgW9xLL8bh8oc7aYOJilRKXE5G33+tE0jan+duo/9OriaFRpijcCwT2waWs2oqYROYq0GlE7/p3ywoshVeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.128.0.tgz", - "integrity": "sha512-xgvO35GyHBtjlQ5AEpaYr7Rll1rvY7zqIhT6ty8E3ezBW2J1SFLjIDEvI/tcgDg6oaseDAqVcM+jU1HuCekgZw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.128.0.tgz", - "integrity": "sha512-OY+3eM2SN72prHKRB22mPz8o5A/7dJ+f5DFLBVvggyZhEaNDAH9IB+ElMjmOkOIwf5MDCUAowCK7pAncNxzpBA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.128.0.tgz", - "integrity": "sha512-NE9ny+cPUCCObXa0IKLfj0tCdPd7pe/dz9ZpkxpUOymB3miNeMPybdlYYTBSGJUalMWeBM85/4JcCErCNTqOXw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/nuxt/node_modules/@oxc-project/types": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", - "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/nuxt/node_modules/cookie-es": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.1.tgz", - "integrity": "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==", - "license": "MIT" - }, - "node_modules/nuxt/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/nuxt/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/nuxt/node_modules/oxc-parser": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.128.0.tgz", - "integrity": "sha512-XkOw3eiIxAgQ19WRew/Bq9wc5Ga/guaWIzDBzq80z1PyuDNGvWBpPby9k6YGwV8A8uMw+Nlq3xqlzuDYmUFYUw==", - "license": "MIT", - "dependencies": { - "@oxc-project/types": "^0.128.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.128.0", - "@oxc-parser/binding-android-arm64": "0.128.0", - "@oxc-parser/binding-darwin-arm64": "0.128.0", - "@oxc-parser/binding-darwin-x64": "0.128.0", - "@oxc-parser/binding-freebsd-x64": "0.128.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.128.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.128.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.128.0", - "@oxc-parser/binding-linux-arm64-musl": "0.128.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.128.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.128.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.128.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.128.0", - "@oxc-parser/binding-linux-x64-gnu": "0.128.0", - "@oxc-parser/binding-linux-x64-musl": "0.128.0", - "@oxc-parser/binding-openharmony-arm64": "0.128.0", - "@oxc-parser/binding-wasm32-wasi": "0.128.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.128.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.128.0", - "@oxc-parser/binding-win32-x64-msvc": "0.128.0" - } - }, - "node_modules/nuxt/node_modules/unctx": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", - "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21", - "unplugin": "^2.3.11" - } - }, - "node_modules/nuxt/node_modules/unctx/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/nypm": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.9.tgz", - "integrity": "sha512-zxlE2yvSWZWmHcNdT3+5zV2lrCogeE9YOklHrR3dFjqutq5wO7GFDYLFDRXLsYnJzwvy/im9fYoxePvS0VTW0w==", - "license": "MIT", - "dependencies": { - "citty": "^0.2.2", - "pathe": "^2.0.3", - "tinyexec": "^1.2.4" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/ofetch": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", - "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.5", - "node-fetch-native": "^1.6.7", - "ufo": "^1.6.1" - } - }, - "node_modules/ohash": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", - "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", - "license": "MIT" - }, - "node_modules/on-change": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/on-change/-/on-change-6.0.2.tgz", - "integrity": "sha512-08+12qcOVEA0fS9g/VxKS27HaT94nRutUT77J2dr8zv/unzXopvhBuF8tNLWsoLQ5IgrQ6eptGeGqUYat82U1w==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/on-change?sponsor=1" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/open/-/open-11.0.2.tgz", - "integrity": "sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==", - "license": "MIT", - "dependencies": { - "default-browser": "^5.5.1", - "define-lazy-prop": "^3.0.0", - "is-in-ssh": "^1.0.0", - "is-inside-container": "^1.0.0", - "powershell-utils": "^0.2.1", - "wsl-utils": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/oxc-minify": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/oxc-minify/-/oxc-minify-0.128.0.tgz", - "integrity": "sha512-VIXQO2W886aB+N17yV55Sack6aCpbUqtuNAYhNcPV6dFiWIZ5+kwOjvvw36igWwoljfjWhasu99CQ5wtvPJDYg==", - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-minify/binding-android-arm-eabi": "0.128.0", - "@oxc-minify/binding-android-arm64": "0.128.0", - "@oxc-minify/binding-darwin-arm64": "0.128.0", - "@oxc-minify/binding-darwin-x64": "0.128.0", - "@oxc-minify/binding-freebsd-x64": "0.128.0", - "@oxc-minify/binding-linux-arm-gnueabihf": "0.128.0", - "@oxc-minify/binding-linux-arm-musleabihf": "0.128.0", - "@oxc-minify/binding-linux-arm64-gnu": "0.128.0", - "@oxc-minify/binding-linux-arm64-musl": "0.128.0", - "@oxc-minify/binding-linux-ppc64-gnu": "0.128.0", - "@oxc-minify/binding-linux-riscv64-gnu": "0.128.0", - "@oxc-minify/binding-linux-riscv64-musl": "0.128.0", - "@oxc-minify/binding-linux-s390x-gnu": "0.128.0", - "@oxc-minify/binding-linux-x64-gnu": "0.128.0", - "@oxc-minify/binding-linux-x64-musl": "0.128.0", - "@oxc-minify/binding-openharmony-arm64": "0.128.0", - "@oxc-minify/binding-wasm32-wasi": "0.128.0", - "@oxc-minify/binding-win32-arm64-msvc": "0.128.0", - "@oxc-minify/binding-win32-ia32-msvc": "0.128.0", - "@oxc-minify/binding-win32-x64-msvc": "0.128.0" - } - }, - "node_modules/oxc-parser": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.148.0.tgz", - "integrity": "sha512-syxUKHeUll89RIABQADcI7sikYrwyssvA6gj4phSSIPezKVM8yMaLAiLLSc7fmzVvrwybfFGFbW5zme9sX87rg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@oxc-project/types": "^0.148.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/oxc-project" - }, - "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.148.0", - "@oxc-parser/binding-android-arm64": "0.148.0", - "@oxc-parser/binding-darwin-arm64": "0.148.0", - "@oxc-parser/binding-darwin-x64": "0.148.0", - "@oxc-parser/binding-freebsd-x64": "0.148.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.148.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.148.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.148.0", - "@oxc-parser/binding-linux-arm64-musl": "0.148.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.148.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.148.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.148.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.148.0", - "@oxc-parser/binding-linux-x64-gnu": "0.148.0", - "@oxc-parser/binding-linux-x64-musl": "0.148.0", - "@oxc-parser/binding-openharmony-arm64": "0.148.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.148.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.148.0", - "@oxc-parser/binding-win32-x64-msvc": "0.148.0" - } - }, - "node_modules/oxc-transform": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/oxc-transform/-/oxc-transform-0.128.0.tgz", - "integrity": "sha512-8DfEHlmUiLOHlCK9DGX+d5tORc1xwPPvoRSHSJCYgLHyGjKp4PvfBrvgi59DkEW0SMOWfO8GL9t+R7vdKtupbg==", - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-transform/binding-android-arm-eabi": "0.128.0", - "@oxc-transform/binding-android-arm64": "0.128.0", - "@oxc-transform/binding-darwin-arm64": "0.128.0", - "@oxc-transform/binding-darwin-x64": "0.128.0", - "@oxc-transform/binding-freebsd-x64": "0.128.0", - "@oxc-transform/binding-linux-arm-gnueabihf": "0.128.0", - "@oxc-transform/binding-linux-arm-musleabihf": "0.128.0", - "@oxc-transform/binding-linux-arm64-gnu": "0.128.0", - "@oxc-transform/binding-linux-arm64-musl": "0.128.0", - "@oxc-transform/binding-linux-ppc64-gnu": "0.128.0", - "@oxc-transform/binding-linux-riscv64-gnu": "0.128.0", - "@oxc-transform/binding-linux-riscv64-musl": "0.128.0", - "@oxc-transform/binding-linux-s390x-gnu": "0.128.0", - "@oxc-transform/binding-linux-x64-gnu": "0.128.0", - "@oxc-transform/binding-linux-x64-musl": "0.128.0", - "@oxc-transform/binding-openharmony-arm64": "0.128.0", - "@oxc-transform/binding-wasm32-wasi": "0.128.0", - "@oxc-transform/binding-win32-arm64-msvc": "0.128.0", - "@oxc-transform/binding-win32-ia32-msvc": "0.128.0", - "@oxc-transform/binding-win32-x64-msvc": "0.128.0" - } - }, - "node_modules/oxc-walker": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-0.7.0.tgz", - "integrity": "sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==", - "license": "MIT", - "dependencies": { - "magic-regexp": "^0.10.0" - }, - "peerDependencies": { - "oxc-parser": ">=0.98.0" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/perfect-debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", - "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-types": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.2.tgz", - "integrity": "sha512-v0sVXzj7oPGysr543YYZLYbcJNJsKikSsp/fFzoxQ12ewY3ZZr7oCPC8y7OlmxfYB3QPvriXmuPD8KZggE1vqg==", - "license": "MIT", - "dependencies": { - "confbox": "^0.3.0", - "exsolve": "^1.1.1", - "pathe": "^2.0.3" - } - }, - "node_modules/pkg-types/node_modules/confbox": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", - "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", - "license": "MIT" - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-calc": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", - "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12 || ^20.9 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.4.38" - } - }, - "node_modules/postcss-colormin": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.10.tgz", - "integrity": "sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==", - "license": "MIT", - "dependencies": { - "@colordx/core": "^5.4.3", - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-convert-values": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.12.tgz", - "integrity": "sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-discard-comments": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.8.tgz", - "integrity": "sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.4.tgz", - "integrity": "sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==", - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-discard-empty": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.3.tgz", - "integrity": "sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==", - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.3.tgz", - "integrity": "sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==", - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.7.tgz", - "integrity": "sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^7.0.11" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-merge-rules": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.11.tgz", - "integrity": "sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^5.0.3", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.3.tgz", - "integrity": "sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.5.tgz", - "integrity": "sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==", - "license": "MIT", - "dependencies": { - "@colordx/core": "^5.4.3", - "cssnano-utils": "^5.0.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-minify-params": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.9.tgz", - "integrity": "sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "cssnano-utils": "^5.0.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.1.2.tgz", - "integrity": "sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-api": "^3.0.0", - "cssesc": "^3.0.0", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.3.tgz", - "integrity": "sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==", - "license": "MIT", - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.3.tgz", - "integrity": "sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.4.tgz", - "integrity": "sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.4.tgz", - "integrity": "sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-string": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.3.tgz", - "integrity": "sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.3.tgz", - "integrity": "sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.9.tgz", - "integrity": "sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-url": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.3.tgz", - "integrity": "sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.3.tgz", - "integrity": "sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-ordered-values": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.4.tgz", - "integrity": "sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==", - "license": "MIT", - "dependencies": { - "cssnano-utils": "^5.0.3", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.9.tgz", - "integrity": "sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.3.tgz", - "integrity": "sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.6.tgz", - "integrity": "sha512-7qASPzhKF2l2KLboRZux8CCTRMdGiV08vWmyKzPz22qZ7ZjQBOeY7rNzNoCLSUiftJ7HUq0GERHmxw/t0dCdMw==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.3.tgz", - "integrity": "sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^4.0.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >= 18" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.7.tgz", - "integrity": "sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/powershell-utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", - "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-bytes": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-7.1.3.tgz", - "integrity": "sha512-U2KZ675nqba6XSBppSjU2pRgc9Ffx9+uUtd/RTWYmOawVkZuUbBYuwdtFZQmLv+yMgQ2TQxWWuOpyNje67J9tg==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proper-lockfile/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/quansync": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", - "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/rc9": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.1.0.tgz", - "integrity": "sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==", - "license": "MIT", - "dependencies": { - "defu": "^6.1.7", - "destr": "^2.0.5" - } - }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "license": "MIT", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rolldown": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", - "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", - "license": "MIT", - "peer": true, - "dependencies": { - "@oxc-project/types": "=0.148.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.7", - "@rolldown/binding-android-arm64": "1.2.7", - "@rolldown/binding-darwin-arm64": "1.2.7", - "@rolldown/binding-darwin-x64": "1.2.7", - "@rolldown/binding-freebsd-x64": "1.2.7", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", - "@rolldown/binding-linux-arm64-gnu": "1.2.7", - "@rolldown/binding-linux-arm64-musl": "1.2.7", - "@rolldown/binding-linux-ppc64-gnu": "1.2.7", - "@rolldown/binding-linux-s390x-gnu": "1.2.7", - "@rolldown/binding-linux-x64-gnu": "1.2.7", - "@rolldown/binding-linux-x64-musl": "1.2.7", - "@rolldown/binding-openharmony-arm64": "1.2.7", - "@rolldown/binding-win32-arm64-msvc": "1.2.7", - "@rolldown/binding-win32-x64-msvc": "1.2.7" - } - }, - "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-visualizer": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-7.1.1.tgz", - "integrity": "sha512-ThaGiHTU8XW02OkK80TrTHATraJmM9OAduU4otal+7gyXLpYEtmGBLfx5kW+EHvvLwn03YGW2NnwKUIqsYlJAA==", - "license": "MIT", - "dependencies": { - "open": "^11.0.0", - "picomatch": "^4.0.5", - "source-map": "^0.8.0", - "yargs": "^18.1.0" - }, - "bin": { - "rollup-plugin-visualizer": "dist/bin/cli.js" - }, - "engines": { - "node": ">=22" - }, - "peerDependencies": { - "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", - "rollup": "2.x || 3.x || 4.x" - }, - "peerDependenciesMeta": { - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/rollup-plugin-visualizer/node_modules/source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0.tgz", - "integrity": "sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/rou3": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.8.1.tgz", - "integrity": "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==", - "license": "MIT" - }, - "node_modules/run-applescript": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", - "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", - "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scule": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", - "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serialize-javascript": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.1.1.tgz", - "integrity": "sha512-k3CMsaIvvdSwm8oLB4MXSl0wH2/cwlH7xGcnRd2DaeRmBkbzYmyT8j0tsX60DwD1eRwHTpNpH8ljKu9oUT1MeQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/seroval": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.4.tgz", - "integrity": "sha512-LErWMNS2RRFdu2RMA5u/PA59/IWs0XsikyEXGQ2/36iEWFrdG0ABmg17E17cikrv76891kOAMq3TkTFXpwAHXw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/serve-placeholder": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/serve-placeholder/-/serve-placeholder-2.0.2.tgz", - "integrity": "sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==", - "license": "MIT", - "dependencies": { - "defu": "^6.1.4" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", - "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-git": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz", - "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==", - "license": "MIT", - "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "@simple-git/args-pathspec": "^1.0.3", - "@simple-git/argv-parser": "^1.1.0", - "debug": "^4.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" - } - }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/smob": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", - "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/srvx": { - "version": "0.11.22", - "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.22.tgz", - "integrity": "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==", - "license": "MIT", - "bin": { - "srvx": "bin/srvx.mjs" - }, - "engines": { - "node": ">=20.16.0" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/std-env": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", - "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", - "license": "MIT" - }, - "node_modules/streamx": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", - "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", - "license": "MIT", - "dependencies": { - "events-universal": "^1.0.0", - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", - "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-4.0.0.tgz", - "integrity": "sha512-PaqAvfUZKBwc/SLmNZtHmzK+v19Z4O4eS3cKPeGvbIv/U3pnyEq4Tuw3/4v/FwfM8VQaEawsyCcOQ0P+kpwWWw==", - "license": "MIT", - "dependencies": { - "js-tokens": "^10.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "license": "MIT" - }, - "node_modules/structured-clone-es": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/structured-clone-es/-/structured-clone-es-2.0.1.tgz", - "integrity": "sha512-10ZL5r77LhknxlP1FBiCW+VdnuWOEFLdSS2SKtjyEV+L4qP1hUEIMIZW94LC3jKxRmT/Dj7KkD1mqh/IY6lhKQ==", - "license": "ISC" - }, - "node_modules/stylehacks": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.11.tgz", - "integrity": "sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==", - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.2", - "postcss-selector-parser": "^7.1.1" - }, - "engines": { - "node": "^18.12.0 || ^20.9.0 || >=22.0" - }, - "peerDependencies": { - "postcss": "^8.5.13" - } - }, - "node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svgo": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", - "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^6.0.0", - "css-tree": "^3.0.1", - "css-what": "^7.0.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "1.6.1" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/tagged-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", - "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tar": { - "version": "7.5.22", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", - "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar-stream": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", - "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "bare-fs": "^4.5.5", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/teex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", - "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", - "license": "MIT", - "dependencies": { - "streamx": "^2.12.5" - } - }, - "node_modules/terser": { - "version": "5.51.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", - "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, - "node_modules/text-decoder": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", - "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tinyclip": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", - "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", - "license": "MIT", - "engines": { - "node": "^16.14.0 || >= 17.3.0" - } - }, - "node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/type-fest": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.9.0.tgz", - "integrity": "sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==", - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-level-regexp": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz", - "integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==", - "license": "MIT" - }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", - "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/unctx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/unctx/-/unctx-3.0.1.tgz", - "integrity": "sha512-5RAt2etv7g362RXyd33R82gm9u/kbtQlpoaOs9Bgm4E32GRMJ0xjbZyvdxUTmiuHk3V1IQb1aUNrp5IWY+JaWw==", - "license": "MIT", - "peerDependencies": { - "magic-string": ">=0.30.21", - "oxc-parser": ">=0.140.0", - "rolldown": "^1.1.5", - "unplugin": "^3.3.0" - }, - "peerDependenciesMeta": { - "magic-string": { - "optional": true - }, - "oxc-parser": { - "optional": true - }, - "rolldown": { - "optional": true - }, - "unplugin": { - "optional": true - } - } - }, - "node_modules/unenv": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", - "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", - "license": "MIT", - "dependencies": { - "pathe": "^2.0.3" - } - }, - "node_modules/unhead": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.17.tgz", - "integrity": "sha512-HLMKXOszRhAPBrr6VlqCeVeJq2kbC4kXwzGLEZvvojPLWNYTJw22xG7Bfwhsvs31+IBet3Wl8ADg9dwYdyphfQ==", - "license": "MIT", - "dependencies": { - "hookable": "^6.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/harlan-zw" - } - }, - "node_modules/unicorn-magic": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", - "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unimport": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/unimport/-/unimport-6.4.0.tgz", - "integrity": "sha512-JJOOuNMFq8b4ZPBKwQUxEcba4MplskDzYI1Lvrf8rJfWphZTWvPNXWa493qsPngHUmub89w6C7j+SeLWTE/UIQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.18.0", - "escape-string-regexp": "^5.0.0", - "estree-walker": "^3.0.3", - "local-pkg": "^1.2.1", - "magic-string": "^1.1.0", - "mlly": "^1.8.2", - "pathe": "^2.0.3", - "picomatch": "^4.0.5", - "pkg-types": "^2.3.1", - "scule": "^1.3.0", - "strip-literal": "^4.0.0", - "tinyglobby": "^0.2.17", - "unplugin": "^3.3.0", - "unplugin-utils": "^0.3.2" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "oxc-parser": "*", - "rolldown": "^1.0.0" - }, - "peerDependenciesMeta": { - "oxc-parser": { - "optional": true - }, - "rolldown": { - "optional": true - } - } - }, - "node_modules/unimport/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/unplugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", - "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "picomatch": "^4.0.4", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@farmfe/core": "*", - "@rspack/core": "*", - "bun-types-no-globals": "*", - "esbuild": "*", - "rolldown": "*", - "rollup": "*", - "unloader": "*", - "vite": "*", - "webpack": "*" - }, - "peerDependenciesMeta": { - "@farmfe/core": { - "optional": true - }, - "@rspack/core": { - "optional": true - }, - "bun-types-no-globals": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - }, - "unloader": { - "optional": true - }, - "vite": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/unplugin-utils": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", - "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", - "license": "MIT", - "dependencies": { - "pathe": "^2.0.3", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/unrouting": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/unrouting/-/unrouting-0.1.7.tgz", - "integrity": "sha512-+0hfD+CVWtD636rc5Fn9VEjjTEDhdqgMpbwAuVoUmydSHDaMNiFW93SJG4LV++RoGSEAyvQN5uABAscYpDphpQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^5.0.0", - "ufo": "^1.6.3" - } - }, - "node_modules/unstorage": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", - "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^5.0.0", - "destr": "^2.0.5", - "h3": "^1.15.10", - "lru-cache": "^11.2.7", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.5.1", - "ufo": "^1.6.3" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6 || ^7 || ^8", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/functions": "^2.2.12 || ^3.0.0", - "@vercel/kv": "^1 || ^2 || ^3", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/functions": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/unstorage/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/untun": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/untun/-/untun-0.2.2.tgz", - "integrity": "sha512-+NnOJcSiEtYsVgJmXUzQbJeRAFXJC4yPJYuh6kF9B0Rm6zunXcs/3GZOTllyocSbUDIxD6Bj7e/4ATw7sph1Sw==", - "license": "MIT", - "bin": { - "untun": "dist/cli.mjs" - } - }, - "node_modules/untyped": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/untyped/-/untyped-2.0.0.tgz", - "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==", - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "defu": "^6.1.4", - "jiti": "^2.4.2", - "knitwork": "^1.2.0", - "scule": "^1.3.0" - }, - "bin": { - "untyped": "dist/cli.mjs" - } - }, - "node_modules/untyped/node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/unwasm": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/unwasm/-/unwasm-0.5.3.tgz", - "integrity": "sha512-keBgTSfp3r6+s9ZcSma+0chwxQdmLbB5+dAD9vjtB21UTMYuKAxHXCU1K2CbCtnP09EaWeRvACnXk0EJtUx+hw==", - "license": "MIT", - "dependencies": { - "exsolve": "^1.0.8", - "knitwork": "^1.3.0", - "magic-string": "^0.30.21", - "mlly": "^1.8.0", - "pathe": "^2.0.3", - "pkg-types": "^2.3.0" - } - }, - "node_modules/unwasm/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", - "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uqr": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.3.tgz", - "integrity": "sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/verkit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.3.2.tgz", - "integrity": "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==", - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/sxzz" - } - }, - "node_modules/vite": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", - "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.26", - "rolldown": "~1.2.4", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0 || ^0.5.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-dev-rpc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-2.0.0.tgz", - "integrity": "sha512-yKwbTwdHKSD2k/aGqyWpPHepo45OQc8lH3/6IfT4ZqeKE26ooKvi4WIEKzqWav8v+9Is8u1k8q54hvOmqASazA==", - "license": "MIT", - "dependencies": { - "birpc": "^4.0.0", - "vite-hot-client": "^2.2.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 || ^8.0.0" - } - }, - "node_modules/vite-hot-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.2.0.tgz", - "integrity": "sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0" - } - }, - "node_modules/vite-node": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.3.0.tgz", - "integrity": "sha512-8f20COPYJujc3OKPX6OuyBy3ZIv2det4eRRU4GY1y2MjbeGSUmPjedxg1b72KnTagCofwvZ65ThzjxDW2AtQFQ==", - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "es-module-lexer": "^2.0.0", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "vite": "^7.3.1" - }, - "bin": { - "vite-node": "dist/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://opencollective.com/antfu" - } - }, - "node_modules/vite-node/node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-plugin-checker": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.13.0.tgz", - "integrity": "sha512-14EkOZmfinVZNxRmg2uCNDwtqGc/33lU/UEJansHgu27+ad+r6mMBf1Xtnq57jGZWiO/xzwtiEKPYsganw7ZFQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "chokidar": "^4.0.3", - "npm-run-path": "^6.0.0", - "picocolors": "^1.1.1", - "picomatch": "^4.0.4", - "proper-lockfile": "^4.1.2", - "tiny-invariant": "^1.3.3", - "tinyglobby": "^0.2.15", - "vscode-uri": "^3.1.0" - }, - "engines": { - "node": ">=16.11" - }, - "peerDependencies": { - "@biomejs/biome": ">=1.7", - "eslint": ">=9.39.4", - "meow": "^13.2.0 || ^14.0.0", - "optionator": "^0.9.4", - "oxlint": ">=1", - "stylelint": ">=16.26.1", - "typescript": "*", - "vite": ">=5.4.21", - "vls": "*", - "vti": "*", - "vue-tsc": "~2.2.10 || ^3.0.0" - }, - "peerDependenciesMeta": { - "@biomejs/biome": { - "optional": true - }, - "eslint": { - "optional": true - }, - "meow": { - "optional": true - }, - "optionator": { - "optional": true - }, - "oxlint": { - "optional": true - }, - "stylelint": { - "optional": true - }, - "typescript": { - "optional": true - }, - "vls": { - "optional": true - }, - "vti": { - "optional": true - }, - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/vite-plugin-checker/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/vite-plugin-checker/node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vite-plugin-checker/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vite-plugin-checker/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/vite-plugin-checker/node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vite-plugin-inspect": { - "version": "11.4.1", - "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.4.1.tgz", - "integrity": "sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==", - "license": "MIT", - "dependencies": { - "ansis": "^4.3.0", - "error-stack-parser-es": "^1.0.5", - "obug": "^2.1.1", - "ohash": "^2.0.11", - "open": "^11.0.0", - "perfect-debounce": "^2.1.0", - "sirv": "^3.0.2", - "unplugin-utils": "^0.3.1", - "vite-dev-rpc": "^2.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" - }, - "peerDependenciesMeta": { - "@nuxt/kit": { - "optional": true - } - } - }, - "node_modules/vite-plugin-inspect/node_modules/error-stack-parser-es": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", - "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/vite-plugin-vue-tracer": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/vite-plugin-vue-tracer/-/vite-plugin-vue-tracer-1.5.0.tgz", - "integrity": "sha512-G2aQ676fLBPnYhRi5lsARoL4hbtc/sx6fVzO7p44AB+1xQXo2UuiRgv7K26UdijrNzmuQprmJ121n3rdjBk1CA==", - "license": "MIT", - "dependencies": { - "estree-walker": "^3.0.3", - "exsolve": "^1.1.1", - "magic-string": "^1.1.0", - "pathe": "^2.0.3", - "source-map-js": "^1.2.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", - "vue": "^3.5.0" - } - }, - "node_modules/vite-plugin-vue-tracer/node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/vscode-uri": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", - "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", - "license": "MIT" - }, - "node_modules/vue": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz", - "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.42", - "@vue/compiler-sfc": "3.5.42", - "@vue/runtime-dom": "3.5.42", - "@vue/server-renderer": "3.5.42", - "@vue/shared": "3.5.42" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue-bundle-renderer": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-2.3.2.tgz", - "integrity": "sha512-BtazFw0lm3N/ZE4+tre2g8G+c5wolfizu+e5jxZAcao0gcy5KJei9Yopl1svDato2poeFldVLfmLMk57Sg8rLg==", - "license": "MIT", - "dependencies": { - "ufo": "^1.6.4" - } - }, - "node_modules/vue-devtools-stub": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz", - "integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==", - "license": "MIT" - }, - "node_modules/vue-router": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.3.1.tgz", - "integrity": "sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==", - "license": "MIT", - "dependencies": { - "@vue-macros/common": "^3.1.3", - "@vue/devtools-api": "^8.1.5", - "ast-walker-scope": "^0.9.0", - "chokidar": "^5.0.0", - "confbox": "^0.2.4", - "local-pkg": "^1.2.1", - "magic-string": "^0.30.21", - "mlly": "^1.8.2", - "muggle-string": "^0.4.1", - "nostics": "^1.1.4", - "pathe": "^2.0.3", - "picomatch": "^4.0.5", - "scule": "^1.3.0", - "tinyglobby": "^0.2.17", - "unplugin": "^3.3.0", - "unplugin-utils": "^0.3.2" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "@pinia/colada": ">=0.21.2", - "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", - "pinia": "^3.0.4 || ^4.0.2", - "vite": "^7.3.0 || ^8.0.0", - "vue": "^3.5.34 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@pinia/colada": { - "optional": true - }, - "@vue/compiler-sfc": { - "optional": true - }, - "pinia": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/vue-router/node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "license": "MIT" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", - "license": "ISC", - "dependencies": { - "isexe": "^4.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/wsl-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz", - "integrity": "sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==", - "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0", - "powershell-utils": "^0.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wsl-utils/node_modules/powershell-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", - "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", - "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^8.2.1", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/youch": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.1.tgz", - "integrity": "sha512-mxW3qiSnl+GRxXsaUMzv2Mbada1Y8CDltET9UxejDQe6DBYlSekghl5U5K0ReAikcHDi0G1vKZEmmo/NWAGKLA==", - "license": "MIT", - "dependencies": { - "@poppinss/colors": "^4.1.6", - "@poppinss/dumper": "^0.7.0", - "@speed-highlight/core": "^1.2.14", - "cookie-es": "^3.0.1", - "youch-core": "^0.3.3" - } - }, - "node_modules/youch-core": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", - "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", - "license": "MIT", - "dependencies": { - "@poppinss/exception": "^1.2.2", - "error-stack-parser-es": "^1.0.5" - } - }, - "node_modules/youch-core/node_modules/error-stack-parser-es": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", - "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/zip-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", - "license": "MIT", - "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", - "readable-stream": "^4.0.0" - }, - "engines": { - "node": ">= 14" - } - } - } -} diff --git a/templates/nuxt/package.json b/templates/nuxt/package.json deleted file mode 100644 index dd897fe..0000000 --- a/templates/nuxt/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "app", - "type": "module", - "private": true, - "scripts": { - "build": "nuxt build", - "dev": "nuxt dev", - "generate": "nuxt generate", - "preview": "nuxt preview", - "postinstall": "nuxt prepare" - }, - "dependencies": { - "nuxt": "4.4.4", - "vue": "^3.5.42", - "vue-router": "^5.3.1" - } -} diff --git a/templates/nuxt/public/favicon.ico b/templates/nuxt/public/favicon.ico deleted file mode 100644 index 18993ad91cfd43e03b074dd0b5cc3f37ab38e49c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeHLOKuuL5PjK%MHWVi6lD zOGiREbCw`xmFozJ^aNatJY>w+g ze6a2@u~m#^BZm@8wco9#Crlli0uLb^3E$t2-WIc^#(?t)*@`UpuofJ(Uyh@F>b3Ph z$D^m8Xq~pTkGJ4Q`Q2)te3mgkWYZ^Ijq|hkiP^9`De={bQQ%heZC$QU2UpP(-tbl8 zPWD2abEew;oat@w`uP3J^YpsgT%~jT(Dk%oU}sa$7|n6hBjDj`+I;RX(>)%lm_7N{+B7Mu%H?422lE%MBJH!!YTN2oT7xr>>N-8OF$C&qU^ z>vLsa{$0X%q1fjOe3P1mCv#lN{xQ4_*HCSAZjTb1`}mlc+9rl8$B3OP%VT@mch_~G z7Y+4b{r>9e=M+7vSI;BgB?ryZDY4m>&wcHSn81VH1N~`0gvwH{ z8dv#hG|OK`>1;j7tM#B)Z7zDN?{6=dUal}$e [ - { rel: "preconnect", href: "https://fonts.googleapis.com" }, - { - rel: "preconnect", - href: "https://fonts.gstatic.com", - crossOrigin: "anonymous", - }, - { - rel: "stylesheet", - href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", - }, -]; - -export function Layout({ children }: { children: React.ReactNode }) { - return ( - - - - - - - - - {children} - - - - - ); -} - -export default function App() { - return ; -} - -export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { - let message = "Oops!"; - let details = "An unexpected error occurred."; - let stack: string | undefined; - - if (isRouteErrorResponse(error)) { - message = error.status === 404 ? "404" : "Error"; - details = - error.status === 404 - ? "The requested page could not be found." - : error.statusText || details; - } else if (import.meta.env.DEV && error && error instanceof Error) { - details = error.message; - stack = error.stack; - } - - return ( -
-

{message}

-

{details}

- {stack && ( -
-          {stack}
-        
- )} -
- ); -} diff --git a/templates/react-router/app/routes.ts b/templates/react-router/app/routes.ts deleted file mode 100644 index 102b402..0000000 --- a/templates/react-router/app/routes.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { type RouteConfig, index } from "@react-router/dev/routes"; - -export default [index("routes/home.tsx")] satisfies RouteConfig; diff --git a/templates/react-router/app/routes/home.tsx b/templates/react-router/app/routes/home.tsx deleted file mode 100644 index 398e47c..0000000 --- a/templates/react-router/app/routes/home.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Route } from "./+types/home"; -import { Welcome } from "../welcome/welcome"; - -export function meta({}: Route.MetaArgs) { - return [ - { title: "New React Router App" }, - { name: "description", content: "Welcome to React Router!" }, - ]; -} - -export default function Home() { - return ; -} diff --git a/templates/react-router/app/welcome/logo-dark.svg b/templates/react-router/app/welcome/logo-dark.svg deleted file mode 100644 index dd82028..0000000 --- a/templates/react-router/app/welcome/logo-dark.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/react-router/app/welcome/logo-light.svg b/templates/react-router/app/welcome/logo-light.svg deleted file mode 100644 index 7328492..0000000 --- a/templates/react-router/app/welcome/logo-light.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/react-router/app/welcome/welcome.tsx b/templates/react-router/app/welcome/welcome.tsx deleted file mode 100644 index 8ac6e1d..0000000 --- a/templates/react-router/app/welcome/welcome.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import logoDark from "./logo-dark.svg"; -import logoLight from "./logo-light.svg"; - -export function Welcome() { - return ( -
- -
- ); -} - -const resources = [ - { - href: "https://reactrouter.com/docs", - text: "React Router Docs", - icon: ( - - - - ), - }, - { - href: "https://rmx.as/discord", - text: "Join Discord", - icon: ( - - - - ), - }, -]; diff --git a/templates/react-router/package-lock.json b/templates/react-router/package-lock.json deleted file mode 100644 index 48a08b9..0000000 --- a/templates/react-router/package-lock.json +++ /dev/null @@ -1,5001 +0,0 @@ -{ - "name": "app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "dependencies": { - "@react-router/node": "^7.18.1", - "@react-router/serve": "^7.18.1", - "isbot": "^5.1.36", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router": "^7.18.1" - }, - "devDependencies": { - "@edgeone/react-router": "^1.1.10", - "@react-router/dev": "^7.18.1", - "@tailwindcss/vite": "^4.2.2", - "@types/node": "^22", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "tailwindcss": "^4.2.2", - "typescript": "^5.9.3", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.1.4" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", - "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", - "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/helper-replace-supers": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/traverse": "^7.29.7", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", - "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", - "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", - "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", - "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", - "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", - "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", - "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", - "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/plugin-syntax-typescript": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", - "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "@babel/plugin-syntax-jsx": "^7.29.7", - "@babel/plugin-transform-modules-commonjs": "^7.29.7", - "@babel/plugin-transform-typescript": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@edgeone/react-router": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@edgeone/react-router/-/react-router-1.1.10.tgz", - "integrity": "sha512-ZRYYH67qsg6r/iKo0LX1YMgq2epQVEwKU8BHOmobIJYq1hDv0e/sBFzd5SceLLC1t3GNM34/IQyJosBTI/eOag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@edgeone/vite-core": "1.1.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@react-router/dev": "^7.0.0", - "react-router": "^7.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/@edgeone/vite-core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@edgeone/vite-core/-/vite-core-1.1.0.tgz", - "integrity": "sha512-bGRwzxNRBe7yMmuwllGnKHt3Om9Ej7DgbeOCs9ji+CsbTiVX0VyvNjX8prJew1Lxf2HtmKo/qIuTkPkeD8wNWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vercel/nft": "^1.3.0", - "esbuild": "^0.20.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mjackson/node-fetch-server": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz", - "integrity": "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==", - "license": "MIT" - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@react-router/dev": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@react-router/dev/-/dev-7.18.3.tgz", - "integrity": "sha512-smLBdktEcLw1BgjaeWZG+TDRpmK9Mry4DzgNv4q556/Kq9qDo9Lfxu9Gp4/4BGOctFQG2UuyvxOnOhz0tEyzWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.7", - "@babel/generator": "^7.27.5", - "@babel/parser": "^7.27.7", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/preset-typescript": "^7.27.1", - "@babel/traverse": "^7.27.7", - "@babel/types": "^7.27.7", - "@react-router/node": "7.18.3", - "@remix-run/node-fetch-server": "^0.13.0", - "arg": "^5.0.1", - "babel-dead-code-elimination": "^1.0.6", - "chokidar": "^4.0.0", - "dedent": "^1.5.3", - "es-module-lexer": "^1.3.1", - "exit-hook": "2.2.1", - "isbot": "^5.1.11", - "jsesc": "3.0.2", - "lodash": "^4.17.21", - "p-map": "^7.0.3", - "pathe": "^1.1.2", - "picocolors": "^1.1.1", - "pkg-types": "^2.3.0", - "prettier": "^3.6.2", - "react-refresh": "^0.14.0", - "semver": "^7.3.7", - "tinyglobby": "^0.2.14", - "valibot": "^1.2.0", - "vite-node": "^3.2.2" - }, - "bin": { - "react-router": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@react-router/serve": "^7.18.3", - "@vitejs/plugin-rsc": "~0.5.21", - "react-router": "^7.18.3", - "react-server-dom-webpack": "^19.2.3", - "typescript": "^5.1.0 || ^6.0.0", - "vite": "^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "wrangler": "^3.28.2 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@react-router/serve": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "react-server-dom-webpack": { - "optional": true - }, - "typescript": { - "optional": true - }, - "wrangler": { - "optional": true - } - } - }, - "node_modules/@react-router/express": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@react-router/express/-/express-7.18.3.tgz", - "integrity": "sha512-dSN8CJSii74X5vnajGgmyJaFgtugrA3cH0/VW5Q0hvT6LAn6yB+2f7V9Tsv/tGdTc6m95XlE3shnsVTbSEH1Lg==", - "license": "MIT", - "dependencies": { - "@react-router/node": "7.18.3" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "express": "^4.17.1 || ^5", - "react-router": "7.18.3", - "typescript": "^5.1.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/node": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@react-router/node/-/node-7.18.3.tgz", - "integrity": "sha512-wIBFSsmp+uA/F2MEHN1BxFoWAhh9rdIH/Zd39KYyLhZSeb35YlRi8ARtOMDYj7EqhhZfkoetf/SEmYywK3nUkA==", - "license": "MIT", - "dependencies": { - "@mjackson/node-fetch-server": "^0.2.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.18.3", - "typescript": "^5.1.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@react-router/serve": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@react-router/serve/-/serve-7.18.3.tgz", - "integrity": "sha512-5zcahVd0QAo5Ew66UkG7bmkk3ixLD+R2Q5wxfUKJUVUaKa/N9vy/MtkuuxYYw5D6s+jcIv7X5fQ8GkjuUExc7Q==", - "license": "MIT", - "dependencies": { - "@mjackson/node-fetch-server": "^0.2.0", - "@react-router/express": "7.18.3", - "@react-router/node": "7.18.3", - "compression": "^1.8.1", - "express": "^4.19.2", - "get-port": "5.1.1", - "morgan": "^1.10.1", - "source-map-support": "^0.5.21" - }, - "bin": { - "react-router-serve": "bin.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react-router": "7.18.3" - } - }, - "node_modules/@remix-run/node-fetch-server": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/@remix-run/node-fetch-server/-/node-fetch-server-0.13.3.tgz", - "integrity": "sha512-UfjOXed/DQteaM5VyTfqTeGpHwyL2J5aoRGY6cydip4tt1ehNNeSwuXCC7AEGE0RWBs/7bgKxYkL/B/+UDe4AA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", - "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "tailwindcss": "4.3.3" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@vercel/nft": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.11.0.tgz", - "integrity": "sha512-m1QFg+U+3yPOnP1xSYJ73UIRxLOXdts1JOhiOiyPYqEsALgrXFFINvgUaD6R6iNvaBFAjHllBCbkfx4FuOdpaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^13.0.0", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.4", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/async-sema": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-dead-code-elimination": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", - "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.21", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", - "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.6", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", - "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/browserslist": { - "version": "4.28.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", - "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.20", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.420", - "node-releases": "^2.0.54", - "update-browserslist-db": "^1.3.2" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", - "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.422", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", - "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", - "dev": true, - "license": "ISC" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/exit-hook": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.15.1", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/exsolve": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", - "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT" - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/isbot": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.2.2.tgz", - "integrity": "sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==", - "license": "Unlicense", - "engines": { - "node": ">=18" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/morgan": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.12.0.tgz", - "integrity": "sha512-OHpTRQwn2ezasILW8iKe+Yww1XsfWsZIpUOLF7RDb2g5GwO3trPaRwi7+8BDiJ7HFx2Kg2mfUdCBcVhwYlOz2g==", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.4.1", - "on-headers": "~1.1.0" - }, - "engines": { - "node": ">= 0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.54", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", - "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/p-map": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.7.tgz", - "integrity": "sha512-VaWRu2i4FJNRtiRWCuuQRgfQ1B7a6+gMSrO+3j0EQi/k0ULfS9kosRxGoiqwzIjZTDI02tGfk5mXXltLg6QtfQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-types": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.2.tgz", - "integrity": "sha512-v0sVXzj7oPGysr543YYZLYbcJNJsKikSsp/fFzoxQ12ewY3ZZr7oCPC8y7OlmxfYB3QPvriXmuPD8KZggE1vqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.3.0", - "exsolve": "^1.1.1", - "pathe": "^2.0.3" - } - }, - "node_modules/pkg-types/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prettier": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", - "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", - "license": "BSD-3-Clause", - "dependencies": { - "es-define-property": "^1.0.1", - "side-channel": "^1.1.1" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.3.tgz", - "integrity": "sha512-gyXgtdr5uACJ5b1Q4udzjVV+tb/rlHIMJKuJ0e89R4Kzgz47z/rgP0dIKxktqIEUhDHluGTPJJH/wRha7CyqsA==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router/node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "7.5.22", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", - "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "deprecated": "unmaintained", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", - "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/valibot": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz", - "integrity": "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "typescript": ">=5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "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" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-node/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/templates/react-router/package.json b/templates/react-router/package.json deleted file mode 100644 index 701ee49..0000000 --- a/templates/react-router/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "app", - "private": true, - "type": "module", - "scripts": { - "build": "react-router build", - "dev": "react-router dev", - "start": "react-router-serve ./build/server/index.js", - "typecheck": "react-router typegen && tsc" - }, - "dependencies": { - "@react-router/node": "^7.18.1", - "@react-router/serve": "^7.18.1", - "isbot": "^5.1.36", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router": "^7.18.1" - }, - "devDependencies": { - "@edgeone/react-router": "^1.1.10", - "@react-router/dev": "^7.18.1", - "@tailwindcss/vite": "^4.2.2", - "@types/node": "^22", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "tailwindcss": "^4.2.2", - "typescript": "^5.9.3", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.1.4" - } -} diff --git a/templates/react-router/public/favicon.ico b/templates/react-router/public/favicon.ico deleted file mode 100644 index 5dbdfcddcb14182535f6d32d1c900681321b1aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI33v3ic7{|AFEmuJ-;v>ep_G*NPi6KM`qNryCe1PIJ8siIN1WZ(7qVa)RVtmC% z)Ch?tN+afMKm;5@rvorJk zcXnoOc4q51HBQnQH_jn!cAg&XI1?PlX>Kl^k8qq0;zkha`kY$Fxt#=KNJAE9CMdpW zqr4#g8`nTw191(+H4xW8Tmyru2I^3=J1G3emPxkPXA=3{vvuvse_WWSshqaqls^-m zgB7q8&Vk*aYRe?sn$n53dGH#%3y%^vxv{pL*-h0Z4bmb_(k6{FL7HWIz(V*HT#IcS z-wE{)+0x1U!RUPt3gB97%p}@oHxF4|6S*+Yw=_tLtxZ~`S=z6J?O^AfU>7qOX`JNBbV&8+bO0%@fhQitKIJ^O^ zpgIa__qD_y07t@DFlBJ)8SP_#^j{6jpaXt{U%=dx!qu=4u7^21lWEYHPPY5U3TcoQ zX_7W+lvZi>TapNk_X>k-KO%MC9iZp>1E`N34gHKd9tK&){jq2~7OsJ>!G0FzxQFw6G zm&Vb(2#-T|rM|n3>uAsG_hnbvUKFf3#ay@u4uTzia~NY%XgCHfx4^To4BDU@)HlV? z@EN=g^ymETa1sQK{kRwyE4Ax8?wT&GvaG@ASO}{&a17&^v`y z!oPdiSiia^oov(Z)QhG2&|FgE{M9_4hJROGbnj>#$~ZF$-G^|zPj*QApltKe?;u;uKHJ~-V!=VLkg7Kgct)l7u39f@%VG8e3f$N-B zAu3a4%ZGf)r+jPAYCSLt73m_J3}p>}6Tx0j(wg4vvKhP!DzgiWANiE;Ppvp}P2W@m z-VbYn+NXFF?6ngef5CfY6ZwKnWvNV4z6s^~yMXw2i5mv}jC$6$46g?G|CPAu{W5qF zDobS=zb2ILX9D827g*NtGe5w;>frjanY{f)hrBP_2ehBt1?`~ypvg_Ot4x1V+43P@Ve8>qd)9NX_jWdLo`Zfy zoeam9)@Dpym{4m@+LNxXBPjPKA7{3a&H+~xQvr>C_A;7=JrfK~$M2pCh>|xLz>W6SCs4qC|#V`)# z)0C|?$o>jzh<|-cpf

K7osU{Xp5PG4-K+L2G=)c3f&}H&M3wo7TlO_UJjQ-Oq&_ zjAc9=nNIYz{c3zxOiS5UfcE1}8#iI4@uy;$Q7>}u`j+OU0N<*Ezx$k{x_27+{s2Eg z`^=rhtIzCm!_UcJ?Db~Lh-=_))PT3{Q0{Mwdq;0>ZL%l3+;B&4!&xm#%HYAK|;b456Iv&&f$VQHf` z>$*K9w8T+paVwc7fLfMlhQ4)*zL_SG{~v4QR;IuX-(oRtYAhWOlh`NLoX0k$RUYMi z2Y!bqpdN}wz8q`-%>&Le@q|jFw92ErW-hma-le?S z-@OZt2EEUm4wLsuEMkt4zlyy29_3S50JAcQHTtgTC{P~%-mvCTzrjXOc|{}N`Cz`W zSj7CrXfa7lcsU0J(0uSX6G`54t^7}+OLM0n(|g4waOQ}bd3%!XLh?NX9|8G_|06Ie zD5F1)w5I~!et7lA{G^;uf7aqT`KE&2qx9|~O;s6t!gb`+zVLJyT2T)l*8l(j diff --git a/templates/react-router/react-router.config.ts b/templates/react-router/react-router.config.ts deleted file mode 100644 index 6ff16f9..0000000 --- a/templates/react-router/react-router.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Config } from "@react-router/dev/config"; - -export default { - // Config options... - // Server-side render by default, to enable SPA mode set this to `false` - ssr: true, -} satisfies Config; diff --git a/templates/react-router/tsconfig.json b/templates/react-router/tsconfig.json deleted file mode 100644 index cbe49c7..0000000 --- a/templates/react-router/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "include": [ - "**/*", - "**/.server/**/*", - "**/.client/**/*", - ".react-router/types/**/*" - ], - "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], - "paths": { - "~/*": ["./app/*"] - }, - "esModuleInterop": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true - } -} diff --git a/templates/react-router/vite.config.ts b/templates/react-router/vite.config.ts deleted file mode 100644 index ea8394c..0000000 --- a/templates/react-router/vite.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { reactRouter } from "@react-router/dev/vite"; -import tailwindcss from "@tailwindcss/vite"; -import { defineConfig } from "vite"; -import tsconfigPaths from "vite-tsconfig-paths"; -import { edgeoneAdapter } from "@edgeone/react-router"; - -export default defineConfig({ - plugins: [tailwindcss(), reactRouter(), edgeoneAdapter(), tsconfigPaths()], -}); diff --git a/templates/sveltekit/.npmrc b/templates/sveltekit/.npmrc deleted file mode 100644 index b6f27f1..0000000 --- a/templates/sveltekit/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/templates/sveltekit/README.md b/templates/sveltekit/README.md deleted file mode 100644 index 2478171..0000000 --- a/templates/sveltekit/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# sv - -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). - -## Creating a project - -If you're seeing this, you've probably already done this step. Congrats! - -```sh -# create a new project -npx sv create my-app -``` - -To recreate this project with the same configuration: - -```sh -# recreate this project -npx sv@0.17.0 create --template minimal --types ts --install npm . -``` - -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```sh -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Building - -To create a production version of your app: - -```sh -npm run build -``` - -You can preview the production build with `npm run preview`. - -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/templates/sveltekit/_gitignore b/templates/sveltekit/_gitignore deleted file mode 100644 index 3b462cb..0000000 --- a/templates/sveltekit/_gitignore +++ /dev/null @@ -1,23 +0,0 @@ -node_modules - -# Output -.output -.vercel -.netlify -.wrangler -/.svelte-kit -/build - -# OS -.DS_Store -Thumbs.db - -# Env -.env -.env.* -!.env.example -!.env.test - -# Vite -vite.config.js.timestamp-* -vite.config.ts.timestamp-* diff --git a/templates/sveltekit/package-lock.json b/templates/sveltekit/package-lock.json deleted file mode 100644 index eb3a932..0000000 --- a/templates/sveltekit/package-lock.json +++ /dev/null @@ -1,2233 +0,0 @@ -{ - "name": "app", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "version": "0.0.1", - "devDependencies": { - "@edgeone/sveltekit": "^1.1.1", - "@sveltejs/kit": "^2.63.0", - "@sveltejs/vite-plugin-svelte": "^7.1.2", - "@types/node": "^20", - "svelte": "^5.56.1", - "svelte-check": "^4.6.0", - "typescript": "^6.0.3", - "vite": "^8.0.16" - } - }, - "node_modules/@edgeone/sveltekit": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@edgeone/sveltekit/-/sveltekit-1.1.1.tgz", - "integrity": "sha512-ttiF/so1hsmYpQz4MWqk79O+Fy1vhO9jvnnlAwljKGfeKskXj1tFg6FCf9RVjGCPbCc+AYVg6XoMZIILwkBdJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/kit": "^2.4.0", - "@vercel/nft": "^0.30.0" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.4.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", - "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/oxc-project" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.29", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", - "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm-eabi": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", - "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", - "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", - "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", - "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", - "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", - "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", - "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", - "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", - "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", - "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", - "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", - "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", - "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", - "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", - "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.13.tgz", - "integrity": "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8.9.0" - } - }, - "node_modules/@sveltejs/kit": { - "version": "2.70.3", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.70.3.tgz", - "integrity": "sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@sveltejs/acorn-typescript": "^1.0.9", - "@types/cookie": "^0.6.0", - "acorn": "^8.16.0", - "cookie": "^0.6.0", - "devalue": "^5.8.1", - "esm-env": "^1.2.2", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "mrmime": "^2.0.0", - "set-cookie-parser": "^3.0.0", - "sirv": "^3.0.0" - }, - "bin": { - "svelte-kit": "svelte-kit.js" - }, - "engines": { - "node": ">=18.13" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": "^5.3.3 || ^6.0.0", - "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@sveltejs/load-config": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.3.tgz", - "integrity": "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.3.0.tgz", - "integrity": "sha512-QbRoJyD92e9R0ufeQIWRHrCC0ObcqSv/aBDdrQMoU+sypav3cDx5wytdQ6GLdXjEMO6xjrXGzfkUygng8JMv0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "deepmerge": "^4.3.1", - "magic-string": "^1.0.0", - "obug": "^2.1.0", - "vitefu": "^1.1.2" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "svelte": "^5.46.4", - "vite": "^8.0.0-beta.7 || ^8.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte/node_modules/magic-string": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", - "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", - "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@vercel/nft": { - "version": "0.30.4", - "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.30.4.tgz", - "integrity": "sha512-wE6eAGSXScra60N2l6jWvNtVK0m+sh873CpfZW4KI2v8EHuUQp+mSEi4T+IcdPCSEDgCdAS/7bizbhQlkjzrSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.2", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", - "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/aria-query": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", - "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/async-sema": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", - "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devalue": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz", - "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==", - "dev": true, - "license": "MIT" - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/esm-env": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/esrap": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.3.7.tgz", - "integrity": "sha512-n2nf7fZR3c9yXf0BPEuHuXqT+KW0SJVj4cN5FMEkpCZ3scLjOQWpiccyCxVzCC2q1wubTghuEGzngJY/7Ah0Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "peerDependencies": { - "@typescript-eslint/types": "^8.2.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/types": { - "optional": true - } - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-reference": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", - "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.6" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lightningcss": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", - "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.33.0", - "lightningcss-darwin-arm64": "1.33.0", - "lightningcss-darwin-x64": "1.33.0", - "lightningcss-freebsd-x64": "1.33.0", - "lightningcss-linux-arm-gnueabihf": "1.33.0", - "lightningcss-linux-arm64-gnu": "1.33.0", - "lightningcss-linux-arm64-musl": "1.33.0", - "lightningcss-linux-x64-gnu": "1.33.0", - "lightningcss-linux-x64-musl": "1.33.0", - "lightningcss-win32-arm64-msvc": "1.33.0", - "lightningcss-win32-x64-msvc": "1.33.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", - "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", - "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", - "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", - "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", - "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", - "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", - "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", - "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", - "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", - "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", - "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-character": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rolldown": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", - "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.148.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm-eabi": "1.2.7", - "@rolldown/binding-android-arm64": "1.2.7", - "@rolldown/binding-darwin-arm64": "1.2.7", - "@rolldown/binding-darwin-x64": "1.2.7", - "@rolldown/binding-freebsd-x64": "1.2.7", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", - "@rolldown/binding-linux-arm64-gnu": "1.2.7", - "@rolldown/binding-linux-arm64-musl": "1.2.7", - "@rolldown/binding-linux-ppc64-gnu": "1.2.7", - "@rolldown/binding-linux-s390x-gnu": "1.2.7", - "@rolldown/binding-linux-x64-gnu": "1.2.7", - "@rolldown/binding-linux-x64-musl": "1.2.7", - "@rolldown/binding-openharmony-arm64": "1.2.7", - "@rolldown/binding-win32-arm64-msvc": "1.2.7", - "@rolldown/binding-win32-x64-msvc": "1.2.7" - } - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", - "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", - "dev": true, - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sirv": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", - "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@polka/url": "^1.0.0-next.24", - "mrmime": "^2.0.0", - "totalist": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/svelte": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.57.0.tgz", - "integrity": "sha512-NdbDn7fl4be1ViUG0oq/lvG6OZy3oENolV2ONjiqqsfVoeAfzaQAKUcEX3MrQod/Bebv1PgwET9rfXhgn9s4Kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.10", - "@types/estree": "^1.0.5", - "acorn": "^8.12.1", - "aria-query": "5.3.1", - "axobject-query": "^4.1.0", - "clsx": "^2.1.1", - "devalue": "^5.8.1", - "esm-env": "^1.2.1", - "esrap": "^2.2.12", - "is-reference": "^3.0.3", - "locate-character": "^3.0.0", - "magic-string": "^0.30.11", - "zimmerframe": "^1.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/svelte-check": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.6.tgz", - "integrity": "sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "@sveltejs/load-config": "^0.2.3", - "chokidar": "^4.0.1", - "fdir": "^6.2.0", - "picocolors": "^1.0.0", - "sade": "^1.7.4" - }, - "bin": { - "svelte-check": "bin/svelte-check" - }, - "engines": { - "node": ">= 18.0.0" - }, - "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "typescript": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/tar": { - "version": "7.5.22", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", - "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/totalist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", - "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.26", - "rolldown": "~1.2.4", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0 || ^0.5.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "dev": true, - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/zimmerframe": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.5.tgz", - "integrity": "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/templates/sveltekit/package.json b/templates/sveltekit/package.json deleted file mode 100644 index d198d17..0000000 --- a/templates/sveltekit/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "app", - "private": true, - "version": "0.0.1", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" - }, - "devDependencies": { - "@edgeone/sveltekit": "^1.1.1", - "@sveltejs/kit": "^2.63.0", - "@sveltejs/vite-plugin-svelte": "^7.1.2", - "@types/node": "^20", - "svelte": "^5.56.1", - "svelte-check": "^4.6.0", - "typescript": "^6.0.3", - "vite": "^8.0.16" - } -} diff --git a/templates/sveltekit/src/app.d.ts b/templates/sveltekit/src/app.d.ts deleted file mode 100644 index da08e6d..0000000 --- a/templates/sveltekit/src/app.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// See https://svelte.dev/docs/kit/types#app.d.ts -// for information about these interfaces -declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } -} - -export {}; diff --git a/templates/sveltekit/src/app.html b/templates/sveltekit/src/app.html deleted file mode 100644 index 6a2bb58..0000000 --- a/templates/sveltekit/src/app.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - %sveltekit.head% - - -

%sveltekit.body%
- - diff --git a/templates/sveltekit/src/lib/assets/favicon.svg b/templates/sveltekit/src/lib/assets/favicon.svg deleted file mode 100644 index cc5dc66..0000000 --- a/templates/sveltekit/src/lib/assets/favicon.svg +++ /dev/null @@ -1 +0,0 @@ -svelte-logo \ No newline at end of file diff --git a/templates/sveltekit/src/lib/index.ts b/templates/sveltekit/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/templates/sveltekit/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/templates/sveltekit/src/routes/+layout.svelte b/templates/sveltekit/src/routes/+layout.svelte deleted file mode 100644 index 9cebde5..0000000 --- a/templates/sveltekit/src/routes/+layout.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -{@render children()} diff --git a/templates/sveltekit/src/routes/+page.svelte b/templates/sveltekit/src/routes/+page.svelte deleted file mode 100644 index cc88df0..0000000 --- a/templates/sveltekit/src/routes/+page.svelte +++ /dev/null @@ -1,2 +0,0 @@ -

Welcome to SvelteKit

-

Visit svelte.dev/docs/kit to read the documentation

diff --git a/templates/sveltekit/static/robots.txt b/templates/sveltekit/static/robots.txt deleted file mode 100644 index b6dd667..0000000 --- a/templates/sveltekit/static/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# allow crawling everything by default -User-agent: * -Disallow: diff --git a/templates/sveltekit/tsconfig.json b/templates/sveltekit/tsconfig.json deleted file mode 100644 index 2c2ed3c..0000000 --- a/templates/sveltekit/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "rewriteRelativeImportExtensions": true, - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } - // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias - // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files - // - // To make changes to top-level options such as include and exclude, we recommend extending - // the generated config; see https://svelte.dev/docs/kit/configuration#typescript -} diff --git a/templates/sveltekit/vite.config.ts b/templates/sveltekit/vite.config.ts deleted file mode 100644 index 36b853e..0000000 --- a/templates/sveltekit/vite.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import adapter from '@edgeone/sveltekit'; -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - plugins: [ - sveltekit({ - compilerOptions: { - // Force runes mode for the project, except for libraries. Can be removed in svelte 6. - runes: ({ filename }) => - filename.split(/[/\\]/).includes('node_modules') ? undefined : true - }, - - // The platform adapter, and the reason this file is the whole of the - // SvelteKit config: passing any option to sveltekit() makes a sibling - // svelte.config.js dead weight — it is ignored whole, adapter included. - adapter: adapter() - }) - ] -}); diff --git a/templates/tanstack-start/.cta.json b/templates/tanstack-start/.cta.json deleted file mode 100644 index a88b492..0000000 --- a/templates/tanstack-start/.cta.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "projectName": "app", - "mode": "file-router", - "typescript": true, - "tailwind": true, - "packageManager": "npm", - "git": false, - "install": true, - "intent": false, - "addOnOptions": {}, - "projectPreset": "default", - "includeExamples": true, - "routerOnly": false, - "version": 1, - "framework": "react", - "chosenAddOns": [] -} \ No newline at end of file diff --git a/templates/tanstack-start/README.md b/templates/tanstack-start/README.md deleted file mode 100644 index 1585cbf..0000000 --- a/templates/tanstack-start/README.md +++ /dev/null @@ -1,187 +0,0 @@ -Welcome to your new TanStack Start app! - -# Getting Started - -To run this application: - -```bash -npm install -npm run dev -``` - -# Building For Production - -To build this application for production: - -```bash -npm run build -``` - -## Styling - -This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. - -### Removing Tailwind CSS - -If you prefer not to use Tailwind CSS: - -1. Remove the demo pages in `src/routes/demo/` -2. Replace the Tailwind import in `src/styles.css` with your own styles -3. Remove `tailwindcss()` from the plugins array in `vite.config.ts` -4. Remove `@tailwindcss/vite` and `tailwindcss` from `package.json` - - - -## Routing - -This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`. - -### Adding A Route - -To add a new route to your application just add a new file in the `./src/routes` directory. - -TanStack will automatically generate the content of the route file for you. - -Now that you have two routes you can use a `Link` component to navigate between them. - -### Adding Links - -To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. - -```tsx -import { Link } from "@tanstack/react-router"; -``` - -Then anywhere in your JSX you can use it like so: - -```tsx -About -``` - -This will create a link that will navigate to the `/about` route. - -More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). - -### Using A Layout - -In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you render `{children}` in the `shellComponent`. - -Here is an example layout that includes a header: - -```tsx -import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' - -export const Route = createRootRoute({ - head: () => ({ - meta: [ - { charSet: 'utf-8' }, - { name: 'viewport', content: 'width=device-width, initial-scale=1' }, - { title: 'My App' }, - ], - }), - shellComponent: ({ children }) => ( - - - - - -
- -
- {children} - - - - ), -}) -``` - -More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). - -## Server Functions - -TanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components. - -```tsx -import { createServerFn } from '@tanstack/react-start' - -const getServerTime = createServerFn({ - method: 'GET', -}).handler(async () => { - return new Date().toISOString() -}) - -// Use in a component -function MyComponent() { - const [time, setTime] = useState('') - - useEffect(() => { - getServerTime().then(setTime) - }, []) - - return
Server time: {time}
-} -``` - -## API Routes - -You can create API routes by using the `server` property in your route definitions: - -```tsx -import { createFileRoute } from '@tanstack/react-router' -import { json } from '@tanstack/react-start' - -export const Route = createFileRoute('/api/hello')({ - server: { - handlers: { - GET: () => json({ message: 'Hello, World!' }), - }, - }, -}) -``` - -## Data Fetching - -There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. - -For example: - -```tsx -import { createFileRoute } from '@tanstack/react-router' - -export const Route = createFileRoute('/people')({ - loader: async () => { - const response = await fetch('https://swapi.dev/api/people') - return response.json() - }, - component: PeopleComponent, -}) - -function PeopleComponent() { - const data = Route.useLoaderData() - return ( -
    - {data.results.map((person) => ( -
  • {person.name}
  • - ))} -
- ) -} -``` - -Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters). - - -# Demo files - -Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. - - -# Learn More - -You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). - -For TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start). diff --git a/templates/tanstack-start/_gitignore b/templates/tanstack-start/_gitignore deleted file mode 100644 index 8b25bb5..0000000 --- a/templates/tanstack-start/_gitignore +++ /dev/null @@ -1,13 +0,0 @@ -node_modules -.DS_Store -dist -dist-ssr -*.local -.env -.nitro -.tanstack -.wrangler -.output -.vinxi -__unconfig* -todos.json diff --git a/templates/tanstack-start/package-lock.json b/templates/tanstack-start/package-lock.json deleted file mode 100644 index 5db3e59..0000000 --- a/templates/tanstack-start/package-lock.json +++ /dev/null @@ -1,5304 +0,0 @@ -{ - "name": "app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "dependencies": { - "@edgeone/tanstack-start": "^1.1.0", - "@tailwindcss/vite": "^4.1.18", - "@tanstack/react-devtools": "^0.10.12", - "@tanstack/react-router": "^1.170.33", - "@tanstack/react-router-devtools": "^1.167.1", - "@tanstack/react-start": "^1.168.50", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tailwindcss": "^4.1.18" - }, - "devDependencies": { - "@tailwindcss/typography": "^0.5.16", - "@tanstack/devtools-vite": "^0.8.5", - "@tanstack/router-cli": "^1.132.0", - "@types/node": "^22.10.2", - "@types/react": "^19.2.0", - "@types/react-dom": "^19.2.0", - "@vitejs/plugin-react": "^5.2.0", - "typescript": "^6.0.2", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.1.4" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", - "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", - "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@edgeone/tanstack-start": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@edgeone/tanstack-start/-/tanstack-start-1.1.0.tgz", - "integrity": "sha512-aOL4mDAIOsgdjVcWDVPUBWtln6j4yedDkh/yzeiMz7EpX7hOp4BxNvEcZN+9w5Z1FiyeXrCwO4HxxdrRNHWNdQ==", - "license": "MIT", - "dependencies": { - "@edgeone/vite-core": "1.1.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@tanstack/react-start": "^1.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/@edgeone/vite-core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@edgeone/vite-core/-/vite-core-1.1.0.tgz", - "integrity": "sha512-bGRwzxNRBe7yMmuwllGnKHt3Om9Ej7DgbeOCs9ji+CsbTiVX0VyvNjX8prJew1Lxf2HtmKo/qIuTkPkeD8wNWw==", - "license": "MIT", - "dependencies": { - "@vercel/nft": "^1.3.0", - "esbuild": "^0.20.0" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", - "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.3", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", - "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz", - "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==", - "license": "BSD-3-Clause", - "dependencies": { - "consola": "^3.2.3", - "detect-libc": "^2.0.0", - "https-proxy-agent": "^7.0.5", - "node-fetch": "^2.6.7", - "nopt": "^8.0.0", - "semver": "^7.5.3", - "tar": "^7.4.0" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", - "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" - } - }, - "node_modules/@neodrag/core": { - "version": "3.0.0-next.11", - "resolved": "https://registry.npmjs.org/@neodrag/core/-/core-3.0.0-next.11.tgz", - "integrity": "sha512-3WQWxyrbxiaK9zS5JU2wJsW2gpoQlZBXVghduBh61JpqaeE0T0cte8R0qYK2RuJo3J2TYQYqxO19CpG/C1i5eg==", - "license": "MIT" - }, - "node_modules/@neodrag/solid": { - "version": "3.0.0-next.11", - "resolved": "https://registry.npmjs.org/@neodrag/solid/-/solid-3.0.0-next.11.tgz", - "integrity": "sha512-vCBIn/pimjWMQ6vhTS2/O1XNAwzVtc4eUhdbQ91WykbZWWqQ5NocDXt/1OdYrEkeRzJcpCv8wEz5PnMkgKP81Q==", - "license": "MIT", - "peerDependencies": { - "@neodrag/core": "3.0.0-next.11", - "solid-js": "^1.0.0" - } - }, - "node_modules/@oozcitak/dom": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz", - "integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==", - "license": "MIT", - "dependencies": { - "@oozcitak/infra": "^2.0.2", - "@oozcitak/url": "^3.0.0", - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/infra": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-2.0.2.tgz", - "integrity": "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==", - "license": "MIT", - "dependencies": { - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-3.0.0.tgz", - "integrity": "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==", - "license": "MIT", - "dependencies": { - "@oozcitak/infra": "^2.0.2", - "@oozcitak/util": "^10.0.0" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oozcitak/util": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-10.0.0.tgz", - "integrity": "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==", - "license": "MIT", - "engines": { - "node": ">=20.0" - } - }, - "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.120.0.tgz", - "integrity": "sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.120.0.tgz", - "integrity": "sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.120.0.tgz", - "integrity": "sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.120.0.tgz", - "integrity": "sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.120.0.tgz", - "integrity": "sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.120.0.tgz", - "integrity": "sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.120.0.tgz", - "integrity": "sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.120.0.tgz", - "integrity": "sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.120.0.tgz", - "integrity": "sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.120.0.tgz", - "integrity": "sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.120.0.tgz", - "integrity": "sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.120.0.tgz", - "integrity": "sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.120.0.tgz", - "integrity": "sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.120.0.tgz", - "integrity": "sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.120.0.tgz", - "integrity": "sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.120.0.tgz", - "integrity": "sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.120.0.tgz", - "integrity": "sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.120.0.tgz", - "integrity": "sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.120.0.tgz", - "integrity": "sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.120.0.tgz", - "integrity": "sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.120.0.tgz", - "integrity": "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", - "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", - "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", - "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", - "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", - "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", - "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", - "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", - "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", - "cpu": [ - "arm" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", - "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", - "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", - "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", - "cpu": [ - "loong64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", - "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", - "cpu": [ - "loong64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", - "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", - "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", - "cpu": [ - "ppc64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", - "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", - "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", - "cpu": [ - "riscv64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", - "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", - "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", - "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", - "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", - "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", - "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", - "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", - "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", - "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@solid-primitives/event-listener": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@solid-primitives/event-listener/-/event-listener-2.4.6.tgz", - "integrity": "sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw==", - "license": "MIT", - "dependencies": { - "@solid-primitives/utils": "^6.4.1" - }, - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@solid-primitives/keyboard": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/@solid-primitives/keyboard/-/keyboard-1.3.7.tgz", - "integrity": "sha512-558RPNYnXx4nGh537DSqAn4xMrC8iFipl/5+xzgzWoTNFst4RnUN3BOLmtDjJ0UGGoQXVMALYR3bNOHM0xnt1Q==", - "license": "MIT", - "dependencies": { - "@solid-primitives/event-listener": "^2.4.6", - "@solid-primitives/rootless": "^1.5.4", - "@solid-primitives/utils": "^6.4.1" - }, - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@solid-primitives/resize-observer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@solid-primitives/resize-observer/-/resize-observer-2.2.0.tgz", - "integrity": "sha512-9Fuu/EWBeGj+atGHRJp70HKhdfalmpjwxY8a32NZixdLNmfCJ45AfhLQNr6uOzETbbiMx4iCKlTrJ8KZCHC2Ww==", - "license": "MIT", - "dependencies": { - "@solid-primitives/event-listener": "^2.4.6", - "@solid-primitives/rootless": "^1.5.4", - "@solid-primitives/static-store": "^0.1.4", - "@solid-primitives/utils": "^6.4.1" - }, - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@solid-primitives/rootless": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@solid-primitives/rootless/-/rootless-1.5.4.tgz", - "integrity": "sha512-TOIZa1VUfVJ+9nkCcRajw3U4t9vBOP1HxX1WHNTbXq32mXwlqTvUnC4CRIilohcryBkT9u2ZkhUDSHRTaGp55g==", - "license": "MIT", - "dependencies": { - "@solid-primitives/utils": "^6.4.1" - }, - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@solid-primitives/static-store": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@solid-primitives/static-store/-/static-store-0.1.4.tgz", - "integrity": "sha512-LgtVaVBtB7EbmS4+M0b8xY5Iq6pUWXBsIC4VgtrFKDGDdyCaDt88sHk0fUlx1Enxm/XZnZyLXJABRoa39RjJqA==", - "license": "MIT", - "dependencies": { - "@solid-primitives/utils": "^6.4.1" - }, - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@solid-primitives/utils": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@solid-primitives/utils/-/utils-6.4.1.tgz", - "integrity": "sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg==", - "license": "MIT", - "peerDependencies": { - "solid-js": "^1.6.12" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/typography": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz", - "integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "6.0.10" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", - "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "tailwindcss": "4.3.3" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tanstack/devtools": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@tanstack/devtools/-/devtools-0.14.2.tgz", - "integrity": "sha512-8FVVmDU+x3iEwrl5rtbIhud8gwp9eSXPlhs36SCV59yAtXmheFFvLqLAUXpfIU79sZVBg3LLTy2VlTIXaWbaBw==", - "license": "MIT", - "dependencies": { - "@neodrag/core": "3.0.0-next.11", - "@neodrag/solid": "3.0.0-next.11", - "@solid-primitives/event-listener": "^2.4.3", - "@solid-primitives/keyboard": "^1.3.3", - "@solid-primitives/resize-observer": "^2.1.3", - "@tanstack/devtools-client": "0.0.8", - "@tanstack/devtools-event-bus": "0.4.3", - "@tanstack/devtools-ui": "0.7.1", - "clsx": "^2.1.1", - "goober": "^2.1.16", - "solid-js": "^1.9.9" - }, - "bin": { - "intent": "bin/intent.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "solid-js": ">=1.9.7" - } - }, - "node_modules/@tanstack/devtools-bundler-core": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-bundler-core/-/devtools-bundler-core-0.1.3.tgz", - "integrity": "sha512-F0tlxIyfFqXkZ1mJP1EjtkiSeJA+ztXY2AYOHf7r4goCIEAOp86N9PFJ/yv8vu1TnmxGS6vKsZCS3Kyls9xQQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tanstack/devtools-client": "0.0.8", - "@tanstack/devtools-event-bus": "0.4.3", - "chalk": "^5.6.2", - "launch-editor": "^2.14.1", - "magic-string": "^0.30.0", - "oxc-parser": "^0.120.0", - "picomatch": "^4.0.5" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/devtools-client": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-client/-/devtools-client-0.0.8.tgz", - "integrity": "sha512-cG3iZkGWCwN330bLBKa8+9r4Of2AXNoz2zUqcsy/4XsD3105ghVBx78cGyvJj9fSclNomPxoqAnDGXXhg1WLvA==", - "license": "MIT", - "dependencies": { - "@tanstack/devtools-event-client": "^0.5.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/devtools-event-bus": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-bus/-/devtools-event-bus-0.4.3.tgz", - "integrity": "sha512-NeegBt5/n2E5q4DbrXHqECBq42+kDi6JBOp8/+RNqkIE+P4hJpbM36kGyjnGQFMWOoku31qhMyX9/48VuTTdmg==", - "license": "MIT", - "dependencies": { - "ws": "^8.18.3" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/devtools-event-client": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.5.0.tgz", - "integrity": "sha512-H+OH3zC6Vhu/K0NaVfQKknEKawc/+2PT+D3SB3Ox0V8SiMlTo0abbmH2rH0721R2aNYbjdMXA1oENOd8E2UVoA==", - "license": "MIT", - "bin": { - "intent": "bin/intent.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/devtools-ui": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-ui/-/devtools-ui-0.7.1.tgz", - "integrity": "sha512-3xQ/ezZ2qVNszhjpCN2N3jn7uHc2J1PMgcyjHzH4XZBt9xAQyMMcPNoR2cd7rzReyxWoJpZUWiDBmOJiCtLj9A==", - "license": "MIT", - "dependencies": { - "clsx": "^2.1.1", - "dayjs": "^1.11.19", - "goober": "^2.1.16", - "solid-js": "^1.9.9" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "solid-js": ">=1.9.7" - } - }, - "node_modules/@tanstack/devtools-vite": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-vite/-/devtools-vite-0.8.5.tgz", - "integrity": "sha512-xaifCEmiwwzizlbp973oISXNsnmuU/BugLa66gryAZaPJJ/qo1kJd2DxY5X960eHwmyIS3SN0KYrL3/aRhucmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tanstack/devtools-bundler-core": "0.1.3", - "@tanstack/devtools-client": "0.0.8", - "@tanstack/devtools-event-bus": "0.4.3", - "chalk": "^5.6.2" - }, - "bin": { - "intent": "bin/intent.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@tanstack/history": { - "version": "1.162.2", - "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.162.2.tgz", - "integrity": "sha512-Lemp3DJbzNqcin/nZpWxycDaEqySDbnIshDbyHJMMCapD4ZQMe57szRpBXOfzfP6fyWAtHNrLrcBUyANJ6Vlow==", - "license": "MIT", - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-devtools": { - "version": "0.10.12", - "resolved": "https://registry.npmjs.org/@tanstack/react-devtools/-/react-devtools-0.10.12.tgz", - "integrity": "sha512-dgoz7TFm97Izo/D34z91PD0h+ufk+eBmoN9OgRHJlj/c7Ol5xpIP7bqLBNByjgt5paRE2eSu5AQHV92Ul+G6iw==", - "license": "MIT", - "dependencies": { - "@tanstack/devtools": "0.14.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@types/react": ">=16.8", - "@types/react-dom": ">=16.8", - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/@tanstack/react-router": { - "version": "1.170.33", - "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.170.33.tgz", - "integrity": "sha512-iNnI98vH3kO/V4dy6YM0CInhqwWBddU0G5wZK5jiMvr3HsK2avDQSRx3RY/y6v+6zQAqb2kD6hUPHIenrJBTSw==", - "license": "MIT", - "dependencies": { - "@tanstack/history": "1.162.2", - "@tanstack/react-store": "^0.9.3", - "@tanstack/router-core": "1.171.28", - "isbot": "^5.1.22" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - } - }, - "node_modules/@tanstack/react-router-devtools": { - "version": "1.167.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-router-devtools/-/react-router-devtools-1.167.1.tgz", - "integrity": "sha512-pjfGrmjj4d7naEPM7oshqFfwBoxDPNo/UxltlHH5ePbHsJ+plBhd+JaAewm1ueYOjZ0js9hckjWWDYXpCrSfKw==", - "license": "MIT", - "dependencies": { - "@tanstack/router-devtools-core": "1.168.1" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@tanstack/react-router": "^1.170.19", - "@tanstack/router-core": "^1.171.16", - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - }, - "peerDependenciesMeta": { - "@tanstack/router-core": { - "optional": true - } - } - }, - "node_modules/@tanstack/react-start": { - "version": "1.168.50", - "resolved": "https://registry.npmjs.org/@tanstack/react-start/-/react-start-1.168.50.tgz", - "integrity": "sha512-QtC9gSEQBE3Nnx24ZpbhSO+agNR9Wv/fkW03yORFLtDxMenqNYISrsBPEH2GpRElGofATaLWkCShlUwilaYksw==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.33", - "@tanstack/react-start-client": "1.168.31", - "@tanstack/react-start-rsc": "0.1.49", - "@tanstack/react-start-server": "1.167.38", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-client-core": "1.170.28", - "@tanstack/start-plugin-core": "1.171.40", - "@tanstack/start-server-core": "1.169.32", - "pathe": "^2.0.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rsbuild/core": "^2.0.0", - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0", - "vite": ">=7.0.0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@tanstack/react-start-client": { - "version": "1.168.31", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-client/-/react-start-client-1.168.31.tgz", - "integrity": "sha512-zpvlHd4Bq75tCc86Yz5GYWrO/6pWu4KL4q2q9RkBLAZE56epBuSvRBVZaMclaN9/pXIuW8R73fejKrSVNtJSbw==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.33", - "@tanstack/router-core": "1.171.28", - "@tanstack/start-client-core": "1.170.28" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - } - }, - "node_modules/@tanstack/react-start-rsc": { - "version": "0.1.49", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-rsc/-/react-start-rsc-0.1.49.tgz", - "integrity": "sha512-2c/k2pT2gCvEpymug9sBqJ8klKGNnazxqbfdEMQi9rI5FXoB1W6TBt3XEQa3V5FvTxk7p7xKmgOqVhzR+bDKdw==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.33", - "@tanstack/router-core": "1.171.28", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-client-core": "1.170.28", - "@tanstack/start-fn-stubs": "1.162.0", - "@tanstack/start-plugin-core": "1.171.40", - "@tanstack/start-storage-context": "1.167.30", - "pathe": "^2.0.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rspack/core": ">=2.0.0-0", - "@vitejs/plugin-rsc": ">=0.5.30", - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0", - "react-server-dom-rspack": ">=0.0.2" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "@vitejs/plugin-rsc": { - "optional": true - }, - "react-server-dom-rspack": { - "optional": true - } - } - }, - "node_modules/@tanstack/react-start-server": { - "version": "1.167.38", - "resolved": "https://registry.npmjs.org/@tanstack/react-start-server/-/react-start-server-1.167.38.tgz", - "integrity": "sha512-7Txo84AYNlSW0kLfd4HDccD5mgI/Atc0z29AvRMafS5KOjEb2j9tyOGDI0KRmTMAg/3zk9EIgBe8w7yDrysMEg==", - "license": "MIT", - "dependencies": { - "@tanstack/react-router": "1.170.33", - "@tanstack/router-core": "1.171.28", - "@tanstack/start-server-core": "1.169.32" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=18.0.0 || >=19.0.0", - "react-dom": ">=18.0.0 || >=19.0.0" - } - }, - "node_modules/@tanstack/react-store": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.9.3.tgz", - "integrity": "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==", - "license": "MIT", - "dependencies": { - "@tanstack/store": "0.9.3", - "use-sync-external-store": "^1.6.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@tanstack/router-cli": { - "version": "1.167.34", - "resolved": "https://registry.npmjs.org/@tanstack/router-cli/-/router-cli-1.167.34.tgz", - "integrity": "sha512-E4d+j3mpekq26GL/4PAi9fT0nX09ABLfCHH7E1v4p8UfBVR7cOkfn7PfCpdvYEsK1qtsAm0YyBwZ1NstTZ1VUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tanstack/router-generator": "1.167.34", - "chokidar": "^5.0.0", - "yargs": "^17.7.2" - }, - "bin": { - "tsr": "bin/tsr.cjs" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/router-core": { - "version": "1.171.28", - "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.171.28.tgz", - "integrity": "sha512-PvPWSklhw6i9b0rzScVh0btQsK5u/gBYN3mBHyDzhC/U4LrB3WzPXPkUunQUKvQOGXCp16UEb7Htc/ITGm5DkQ==", - "license": "MIT", - "dependencies": { - "@tanstack/history": "1.162.2", - "cookie-es": "^3.0.0", - "seroval": "^1.6.2", - "seroval-plugins": "^1.6.2" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/router-devtools-core": { - "version": "1.168.1", - "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.168.1.tgz", - "integrity": "sha512-qr4voa4cpSMwQvS3867xkU3AB3MtJbTuovKIy+btjJ/Faju6er9w0nDylmD+005Mk/3YKw9/iueZJl2JAB7JOA==", - "license": "MIT", - "dependencies": { - "clsx": "^2.1.1", - "goober": "^2.1.16" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@tanstack/router-core": "^1.171.16", - "csstype": "^3.0.10" - }, - "peerDependenciesMeta": { - "csstype": { - "optional": true - } - } - }, - "node_modules/@tanstack/router-generator": { - "version": "1.167.34", - "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.167.34.tgz", - "integrity": "sha512-sbCQrpYd2MGQXVXltUyPNLQFl924kLXBYp6Gq4keb1GPuDKfxH+yfcI3Qv9RQxI3ZErjgDZE7sOhRDXIf7xzxQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.5", - "@tanstack/router-core": "1.171.28", - "@tanstack/router-utils": "1.162.2", - "@tanstack/virtual-file-routes": "1.162.0", - "jiti": "^2.7.0", - "magic-string": "^0.30.21", - "prettier": "^3.5.0", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/router-plugin": { - "version": "1.168.36", - "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.168.36.tgz", - "integrity": "sha512-A/Ama19yfgbgC3QZgQGwC+wceK8cYiqqFgRjTuNxhPCrrrdUrP1SKnCKvLvHISkx6xgLnDDMl2wJyP9PMgrjYg==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "@tanstack/router-core": "1.171.28", - "@tanstack/router-generator": "1.167.34", - "@tanstack/router-utils": "1.162.2", - "chokidar": "^5.0.0", - "unplugin": "^3.0.0", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rsbuild/core": ">=1.0.2 || ^2.0.0", - "@tanstack/react-router": "^1.170.33", - "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", - "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", - "webpack": ">=5.92.0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - }, - "@tanstack/react-router": { - "optional": true - }, - "vite": { - "optional": true - }, - "vite-plugin-solid": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/@tanstack/router-utils": { - "version": "1.162.2", - "resolved": "https://registry.npmjs.org/@tanstack/router-utils/-/router-utils-1.162.2.tgz", - "integrity": "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==", - "license": "MIT", - "dependencies": { - "@babel/generator": "^7.28.5", - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "ansis": "^4.1.0", - "babel-dead-code-elimination": "^1.0.12", - "diff": "^8.0.2", - "pathe": "^2.0.3", - "tinyglobby": "^0.2.15" - }, - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-client-core": { - "version": "1.170.28", - "resolved": "https://registry.npmjs.org/@tanstack/start-client-core/-/start-client-core-1.170.28.tgz", - "integrity": "sha512-WbMnrvYzsE4TtqotX0HeRnBI8wyMbFbg/uLzcY23Omk5BiduwlyIWEnBZsac3AjdK8C0BP1XTafp2p4kwhhNeA==", - "license": "MIT", - "dependencies": { - "@tanstack/router-core": "1.171.28", - "@tanstack/start-fn-stubs": "1.162.0", - "@tanstack/start-storage-context": "1.167.30", - "seroval": "^1.6.2" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-fn-stubs": { - "version": "1.162.0", - "resolved": "https://registry.npmjs.org/@tanstack/start-fn-stubs/-/start-fn-stubs-1.162.0.tgz", - "integrity": "sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==", - "license": "MIT", - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-plugin-core": { - "version": "1.171.40", - "resolved": "https://registry.npmjs.org/@tanstack/start-plugin-core/-/start-plugin-core-1.171.40.tgz", - "integrity": "sha512-AWKf7Lr14qd+G24K5AJTHsxKrgzmYnk0a521O80ZMAkVWXeNZZ8A2L4qyNTL+eVk9i5vssMhPoSgulJqPEvUHg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "7.27.1", - "@babel/core": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "@tanstack/router-core": "1.171.28", - "@tanstack/router-generator": "1.167.34", - "@tanstack/router-plugin": "1.168.36", - "@tanstack/router-utils": "1.162.2", - "@tanstack/start-server-core": "1.169.32", - "exsolve": "^1.0.7", - "lightningcss": "^1.32.0", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "seroval": "^1.6.2", - "source-map": "^0.7.6", - "srvx": "^0.11.9", - "tinyglobby": "^0.2.15", - "ufo": "^1.5.4", - "vitefu": "^1.1.1", - "xmlbuilder2": "^4.0.3", - "zod": "^4.4.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@rsbuild/core": "^2.0.0", - "vite": ">=7.0.0" - }, - "peerDependenciesMeta": { - "@rsbuild/core": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@tanstack/start-server-core": { - "version": "1.169.32", - "resolved": "https://registry.npmjs.org/@tanstack/start-server-core/-/start-server-core-1.169.32.tgz", - "integrity": "sha512-/r10WTBAncF1llVqxc7r+a5Y3Lkd685gMbFCl4udV+9022bW40or3K4mmNI5q+LceZgI/HFdHF4+j54wikFrNw==", - "license": "MIT", - "dependencies": { - "@tanstack/history": "1.162.2", - "@tanstack/router-core": "1.171.28", - "@tanstack/start-client-core": "1.170.28", - "@tanstack/start-storage-context": "1.167.30", - "fetchdts": "^0.1.6", - "h3-v2": "npm:h3@2.0.1-rc.20", - "seroval": "^1.6.2" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/start-storage-context": { - "version": "1.167.30", - "resolved": "https://registry.npmjs.org/@tanstack/start-storage-context/-/start-storage-context-1.167.30.tgz", - "integrity": "sha512-NR4AmF7PjnOyQ47pMc9pC1IpOKGmLMjTOKkGzRzipluzLE8gfEsrapbef/rSRGAYyGU0oHDGm3Fz4itFnK1eUQ==", - "license": "MIT", - "dependencies": { - "@tanstack/router-core": "1.171.28" - }, - "engines": { - "node": ">=22.12.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/store": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.9.3.tgz", - "integrity": "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/virtual-file-routes": { - "version": "1.162.0", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.162.0.tgz", - "integrity": "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==", - "license": "MIT", - "engines": { - "node": ">=20.19" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.20.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", - "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@vercel/nft": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.11.0.tgz", - "integrity": "sha512-m1QFg+U+3yPOnP1xSYJ73UIRxLOXdts1JOhiOiyPYqEsALgrXFFINvgUaD6R6iNvaBFAjHllBCbkfx4FuOdpaA==", - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^2.0.0", - "@rollup/pluginutils": "^5.1.3", - "acorn": "^8.6.0", - "acorn-import-attributes": "^1.9.5", - "async-sema": "^3.1.1", - "bindings": "^1.4.0", - "estree-walker": "2.0.2", - "glob": "^13.0.0", - "graceful-fs": "^4.2.9", - "node-gyp-build": "^4.2.2", - "picomatch": "^4.0.4", - "resolve-from": "^5.0.0" - }, - "bin": { - "nft": "out/cli.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz", - "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/async-sema": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", - "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", - "license": "MIT" - }, - "node_modules/babel-dead-code-elimination": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.12.tgz", - "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.21", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", - "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/browserslist": { - "version": "4.28.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", - "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.20", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.420", - "node-releases": "^2.0.54", - "update-browserslist-db": "^1.3.2" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie-es": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-3.1.1.tgz", - "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==", - "license": "MIT" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/dayjs": { - "version": "1.11.23", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", - "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.422", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", - "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/exsolve": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", - "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fetchdts": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/fetchdts/-/fetchdts-0.1.7.tgz", - "integrity": "sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==", - "license": "MIT" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, - "node_modules/goober": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.19.tgz", - "integrity": "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==", - "license": "MIT", - "peerDependencies": { - "csstype": "^3.0.10" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/h3-v2": { - "name": "h3", - "version": "2.0.1-rc.20", - "resolved": "https://registry.npmjs.org/h3/-/h3-2.0.1-rc.20.tgz", - "integrity": "sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==", - "license": "MIT", - "dependencies": { - "rou3": "^0.8.1", - "srvx": "^0.11.13" - }, - "bin": { - "h3": "bin/h3.mjs" - }, - "engines": { - "node": ">=20.11.1" - }, - "peerDependencies": { - "crossws": "^0.4.1" - }, - "peerDependenciesMeta": { - "crossws": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isbot": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.2.2.tgz", - "integrity": "sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==", - "license": "Unlicense", - "engines": { - "node": ">=18" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", - "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/launch-editor": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", - "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.8.4" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "2.0.54", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", - "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "license": "ISC", - "dependencies": { - "abbrev": "^3.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/oxc-parser": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.120.0.tgz", - "integrity": "sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "^0.120.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/sponsors/Boshen" - }, - "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.120.0", - "@oxc-parser/binding-android-arm64": "0.120.0", - "@oxc-parser/binding-darwin-arm64": "0.120.0", - "@oxc-parser/binding-darwin-x64": "0.120.0", - "@oxc-parser/binding-freebsd-x64": "0.120.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.120.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.120.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.120.0", - "@oxc-parser/binding-linux-arm64-musl": "0.120.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.120.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.120.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.120.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.120.0", - "@oxc-parser/binding-linux-x64-gnu": "0.120.0", - "@oxc-parser/binding-linux-x64-musl": "0.120.0", - "@oxc-parser/binding-openharmony-arm64": "0.120.0", - "@oxc-parser/binding-wasm32-wasi": "0.120.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.120.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.120.0", - "@oxc-parser/binding-win32-x64-msvc": "0.120.0" - } - }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prettier": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", - "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup": { - "version": "4.63.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", - "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.1", - "@rollup/rollup-android-arm64": "4.63.1", - "@rollup/rollup-darwin-arm64": "4.63.1", - "@rollup/rollup-darwin-x64": "4.63.1", - "@rollup/rollup-freebsd-arm64": "4.63.1", - "@rollup/rollup-freebsd-x64": "4.63.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", - "@rollup/rollup-linux-arm-musleabihf": "4.63.1", - "@rollup/rollup-linux-arm64-gnu": "4.63.1", - "@rollup/rollup-linux-arm64-musl": "4.63.1", - "@rollup/rollup-linux-loong64-gnu": "4.63.1", - "@rollup/rollup-linux-loong64-musl": "4.63.1", - "@rollup/rollup-linux-ppc64-gnu": "4.63.1", - "@rollup/rollup-linux-ppc64-musl": "4.63.1", - "@rollup/rollup-linux-riscv64-gnu": "4.63.1", - "@rollup/rollup-linux-riscv64-musl": "4.63.1", - "@rollup/rollup-linux-s390x-gnu": "4.63.1", - "@rollup/rollup-linux-x64-gnu": "4.63.1", - "@rollup/rollup-linux-x64-musl": "4.63.1", - "@rollup/rollup-openbsd-x64": "4.63.1", - "@rollup/rollup-openharmony-arm64": "4.63.1", - "@rollup/rollup-win32-arm64-msvc": "4.63.1", - "@rollup/rollup-win32-ia32-msvc": "4.63.1", - "@rollup/rollup-win32-x64-gnu": "4.63.1", - "@rollup/rollup-win32-x64-msvc": "4.63.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rou3": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.8.1.tgz", - "integrity": "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/seroval": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.6.5.tgz", - "integrity": "sha512-sNG8dL93a6zoDfB/+fw8d6Ovg01ydncHmPefPS54oS/3lk96EAFFWBcH79NrrHuP3Zzcb/w1lXZ5jfRabL2lHw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/seroval-plugins": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.6.5.tgz", - "integrity": "sha512-YJIec8RyWI5ZZzZF9Sj2G1gM7p2ua4f8O2Fi/qYDwR7/fQd1dhEM6mBOl9s9DZ9hyJo5SN5jKRhtsV1pkV7Qng==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "seroval": "^1.0" - } - }, - "node_modules/shell-quote": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", - "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/solid-js": { - "version": "1.9.15", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.15.tgz", - "integrity": "sha512-EeiY2xfpZJqPLjXspVEKjAII4yv8NyG//NxZ3IpOFHdUNnnTyL0uJOeS9LWGvA7cFCz5y94cjFwYlmw5Luncsg==", - "license": "MIT", - "dependencies": { - "csstype": "^3.1.0", - "seroval": "~1.5.4", - "seroval-plugins": "~1.5.4" - } - }, - "node_modules/solid-js/node_modules/seroval": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.6.tgz", - "integrity": "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/solid-js/node_modules/seroval-plugins": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.6.tgz", - "integrity": "sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "seroval": "^1.0" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/srvx": { - "version": "0.11.22", - "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.22.tgz", - "integrity": "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==", - "license": "MIT", - "bin": { - "srvx": "bin/srvx.mjs" - }, - "engines": { - "node": ">=20.16.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "7.5.22", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", - "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/unplugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz", - "integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "picomatch": "^4.0.4", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@farmfe/core": "*", - "@rspack/core": "*", - "bun-types-no-globals": "*", - "esbuild": "*", - "rolldown": "*", - "rollup": "*", - "unloader": "*", - "vite": "*", - "webpack": "*" - }, - "peerDependenciesMeta": { - "@farmfe/core": { - "optional": true - }, - "@rspack/core": { - "optional": true - }, - "bun-types-no-globals": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "rolldown": { - "optional": true - }, - "rollup": { - "optional": true - }, - "unloader": { - "optional": true - }, - "vite": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/update-browserslist-db": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", - "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite-tsconfig-paths/node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "deprecated": "unmaintained", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vite-tsconfig-paths/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "extraneous": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "license": "MIT" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlbuilder2": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz", - "integrity": "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==", - "license": "MIT", - "dependencies": { - "@oozcitak/dom": "^2.0.2", - "@oozcitak/infra": "^2.0.2", - "@oozcitak/util": "^10.0.0", - "js-yaml": "^4.1.1" - }, - "engines": { - "node": ">=20.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", - "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/zod": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", - "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/templates/tanstack-start/package.json b/templates/tanstack-start/package.json deleted file mode 100644 index 1eb0473..0000000 --- a/templates/tanstack-start/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "app", - "private": true, - "type": "module", - "imports": { - "#/*": "./src/*" - }, - "scripts": { - "dev": "vite dev --port 3000", - "generate-routes": "tsr generate", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "@edgeone/tanstack-start": "^1.1.0", - "@tailwindcss/vite": "^4.1.18", - "@tanstack/react-devtools": "^0.10.12", - "@tanstack/react-router": "^1.170.33", - "@tanstack/react-router-devtools": "^1.167.1", - "@tanstack/react-start": "^1.168.50", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tailwindcss": "^4.1.18" - }, - "devDependencies": { - "@tailwindcss/typography": "^0.5.16", - "@tanstack/devtools-vite": "^0.8.5", - "@tanstack/router-cli": "^1.132.0", - "@types/node": "^22.10.2", - "@types/react": "^19.2.0", - "@types/react-dom": "^19.2.0", - "@vitejs/plugin-react": "^5.2.0", - "typescript": "^6.0.2", - "vite": "^7.0.0", - "vite-tsconfig-paths": "^5.1.4" - }, - "pnpm": { - "onlyBuiltDependencies": [ - "esbuild", - "lightningcss" - ] - } -} diff --git a/templates/tanstack-start/src/components/Footer.tsx b/templates/tanstack-start/src/components/Footer.tsx deleted file mode 100644 index c8bfd17..0000000 --- a/templates/tanstack-start/src/components/Footer.tsx +++ /dev/null @@ -1,44 +0,0 @@ -export default function Footer() { - const year = new Date().getFullYear() - - return ( - - ) -} diff --git a/templates/tanstack-start/src/components/Header.tsx b/templates/tanstack-start/src/components/Header.tsx deleted file mode 100644 index 4b558fb..0000000 --- a/templates/tanstack-start/src/components/Header.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Link } from '@tanstack/react-router' -import ThemeToggle from './ThemeToggle' - -export default function Header() { - return ( -
- -
- ) -} diff --git a/templates/tanstack-start/src/components/ThemeToggle.tsx b/templates/tanstack-start/src/components/ThemeToggle.tsx deleted file mode 100644 index 081ebe2..0000000 --- a/templates/tanstack-start/src/components/ThemeToggle.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useEffect, useState } from 'react' - -type ThemeMode = 'light' | 'dark' | 'auto' - -function getInitialMode(): ThemeMode { - if (typeof window === 'undefined') { - return 'auto' - } - - const stored = window.localStorage.getItem('theme') - if (stored === 'light' || stored === 'dark' || stored === 'auto') { - return stored - } - - return 'auto' -} - -function applyThemeMode(mode: ThemeMode) { - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches - const resolved = mode === 'auto' ? (prefersDark ? 'dark' : 'light') : mode - - document.documentElement.classList.remove('light', 'dark') - document.documentElement.classList.add(resolved) - - if (mode === 'auto') { - document.documentElement.removeAttribute('data-theme') - } else { - document.documentElement.setAttribute('data-theme', mode) - } - - document.documentElement.style.colorScheme = resolved -} - -export default function ThemeToggle() { - const [mode, setMode] = useState('auto') - - useEffect(() => { - const initialMode = getInitialMode() - setMode(initialMode) - applyThemeMode(initialMode) - }, []) - - useEffect(() => { - if (mode !== 'auto') { - return - } - - const media = window.matchMedia('(prefers-color-scheme: dark)') - const onChange = () => applyThemeMode('auto') - - media.addEventListener('change', onChange) - return () => { - media.removeEventListener('change', onChange) - } - }, [mode]) - - function toggleMode() { - const nextMode: ThemeMode = - mode === 'light' ? 'dark' : mode === 'dark' ? 'auto' : 'light' - setMode(nextMode) - applyThemeMode(nextMode) - window.localStorage.setItem('theme', nextMode) - } - - const label = - mode === 'auto' - ? 'Theme mode: auto (system). Click to switch to light mode.' - : `Theme mode: ${mode}. Click to switch mode.` - - return ( - - ) -} diff --git a/templates/tanstack-start/src/routeTree.gen.ts b/templates/tanstack-start/src/routeTree.gen.ts deleted file mode 100644 index 12bc916..0000000 --- a/templates/tanstack-start/src/routeTree.gen.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -import { Route as rootRouteImport } from './routes/__root' -import { Route as IndexRouteImport } from './routes/index' -import { Route as AboutRouteImport } from './routes/about' - -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const AboutRoute = AboutRouteImport.update({ - id: '/about', - path: '/about', - getParentRoute: () => rootRouteImport, -} as any) - -export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/about': typeof AboutRoute -} -export interface FileRoutesByTo { - '/': typeof IndexRoute - '/about': typeof AboutRoute -} -export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/about': typeof AboutRoute -} -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/about' - fileRoutesByTo: FileRoutesByTo - to: '/' | '/about' - id: '__root__' | '/' | '/about' - fileRoutesById: FileRoutesById -} -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - AboutRoute: typeof AboutRoute -} - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } - } -} - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - AboutRoute: AboutRoute, -} -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() diff --git a/templates/tanstack-start/src/router.tsx b/templates/tanstack-start/src/router.tsx deleted file mode 100644 index e7b1c4d..0000000 --- a/templates/tanstack-start/src/router.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { createRouter as createTanStackRouter } from '@tanstack/react-router' -import { routeTree } from './routeTree.gen' - -export function getRouter() { - const router = createTanStackRouter({ - routeTree, - scrollRestoration: true, - defaultPreload: 'intent', - defaultPreloadStaleTime: 0, - }) - - return router -} - -declare module '@tanstack/react-router' { - interface Register { - router: ReturnType - } -} diff --git a/templates/tanstack-start/src/routes/__root.tsx b/templates/tanstack-start/src/routes/__root.tsx deleted file mode 100644 index 3f7f8c2..0000000 --- a/templates/tanstack-start/src/routes/__root.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' -import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' -import { TanStackDevtools } from '@tanstack/react-devtools' -import Footer from '../components/Footer' -import Header from '../components/Header' - -import appCss from '../styles.css?url' - -const THEME_INIT_SCRIPT = `(function(){try{var stored=window.localStorage.getItem('theme');var mode=(stored==='light'||stored==='dark'||stored==='auto')?stored:'auto';var prefersDark=window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='auto'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);if(mode==='auto'){root.removeAttribute('data-theme')}else{root.setAttribute('data-theme',mode)}root.style.colorScheme=resolved;}catch(e){}})();` - -export const Route = createRootRoute({ - head: () => ({ - meta: [ - { - charSet: 'utf-8', - }, - { - name: 'viewport', - content: 'width=device-width, initial-scale=1', - }, - { - title: 'TanStack Start Starter', - }, - ], - links: [ - { - rel: 'stylesheet', - href: appCss, - }, - ], - }), - shellComponent: RootDocument, -}) - -function RootDocument({ children }: { children: React.ReactNode }) { - return ( - - - - - diff --git a/templates/vite-spa/package.json b/templates/vite-spa/package.json deleted file mode 100644 index 83f29b5..0000000 --- a/templates/vite-spa/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "app", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "oxlint", - "preview": "vite preview" - }, - "dependencies": { - "react": "^19.2.8", - "react-dom": "^19.2.8" - }, - "devDependencies": { - "@types/node": "^24.13.3", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", - "@vitejs/plugin-react": "^6.1.0", - "oxlint": "^1.79.0", - "typescript": "~6.0.2", - "vite": "^8.2.2" - } -} diff --git a/templates/vite-spa/public/favicon.svg b/templates/vite-spa/public/favicon.svg deleted file mode 100644 index 6893eb1..0000000 --- a/templates/vite-spa/public/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/vite-spa/public/icons.svg b/templates/vite-spa/public/icons.svg deleted file mode 100644 index e952219..0000000 --- a/templates/vite-spa/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/vite-spa/src/App.css b/templates/vite-spa/src/App.css deleted file mode 100644 index f90339d..0000000 --- a/templates/vite-spa/src/App.css +++ /dev/null @@ -1,184 +0,0 @@ -.counter { - font-size: 16px; - padding: 5px 10px; - border-radius: 5px; - color: var(--accent); - background: var(--accent-bg); - border: 2px solid transparent; - transition: border-color 0.3s; - margin-bottom: 24px; - - &:hover { - border-color: var(--accent-border); - } - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } -} - -.hero { - position: relative; - - .base, - .framework, - .vite { - inset-inline: 0; - margin: 0 auto; - } - - .base { - width: 170px; - position: relative; - z-index: 0; - } - - .framework, - .vite { - position: absolute; - } - - .framework { - z-index: 1; - top: 34px; - height: 28px; - transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) - scale(1.4); - } - - .vite { - z-index: 0; - top: 107px; - height: 26px; - width: auto; - transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) - scale(0.8); - } -} - -#center { - display: flex; - flex-direction: column; - gap: 25px; - place-content: center; - place-items: center; - flex-grow: 1; - - @media (max-width: 1024px) { - padding: 32px 20px 24px; - gap: 18px; - } -} - -#next-steps { - display: flex; - border-top: 1px solid var(--border); - text-align: left; - - & > div { - flex: 1 1 0; - padding: 32px; - @media (max-width: 1024px) { - padding: 24px 20px; - } - } - - .icon { - margin-bottom: 16px; - width: 22px; - height: 22px; - } - - @media (max-width: 1024px) { - flex-direction: column; - text-align: center; - } -} - -#docs { - border-right: 1px solid var(--border); - - @media (max-width: 1024px) { - border-right: none; - border-bottom: 1px solid var(--border); - } -} - -#next-steps ul { - list-style: none; - padding: 0; - display: flex; - gap: 8px; - margin: 32px 0 0; - - .logo { - height: 18px; - } - - a { - color: var(--text-h); - font-size: 16px; - border-radius: 6px; - background: var(--social-bg); - display: flex; - padding: 6px 12px; - align-items: center; - gap: 8px; - text-decoration: none; - transition: box-shadow 0.3s; - - &:hover { - box-shadow: var(--shadow); - } - .button-icon { - height: 18px; - width: 18px; - } - } - - @media (max-width: 1024px) { - margin-top: 20px; - flex-wrap: wrap; - justify-content: center; - - li { - flex: 1 1 calc(50% - 8px); - } - - a { - width: 100%; - justify-content: center; - box-sizing: border-box; - } - } -} - -#spacer { - height: 88px; - border-top: 1px solid var(--border); - @media (max-width: 1024px) { - height: 48px; - } -} - -.ticks { - position: relative; - width: 100%; - - &::before, - &::after { - content: ''; - position: absolute; - top: -4.5px; - border: 5px solid transparent; - } - - &::before { - left: 0; - border-left-color: var(--border); - } - &::after { - right: 0; - border-right-color: var(--border); - } -} diff --git a/templates/vite-spa/src/App.tsx b/templates/vite-spa/src/App.tsx deleted file mode 100644 index 9ca8e8d..0000000 --- a/templates/vite-spa/src/App.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useState } from 'react' -import heroImg from './assets/hero.png' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) -} - -export default App diff --git a/templates/vite-spa/src/assets/hero.png b/templates/vite-spa/src/assets/hero.png deleted file mode 100644 index 02251f4b956c55af2d76fd0788124d7eee2b45eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13057 zcmV+cGycqpP)V|)f$;Qooc7=_G zlYe)HToTQIc!$)^+J1M1y0*T%w!p~7%ux`!eRhO?c80XDxKQ*R^lUUMnA>6NT^?feoZ8xxvP32D&s-9ow zqjcM}eesrC)NeDmsf)*P7wJ|K!&xP%Zy4iI8lF)Tv2!reW)tCzg_1=PmOwd1SQfxa z8;58t!=z~Ba7CYlNWVG>he8aRPY|+-JmozNhn!#9i#77Aa_Edt$ijyCWL#=~I>~2X zZNrQ8I0=D+NWD4pq=7~(i zhfThMNw|G>g^y9pGzxX7ZSApl@tIxFcs{p#MX{Ax&XZT+cR#U+OWc@S)pkIuI}dzu zH?^Q=<(y&Vq-oxSLfc0Zmq81bjZWf}RnssBaD6}2g-XJHLcN_|*IOu>m|x$nbm(?E zyNy!Zp=RroS;?Vg*kmoJYBi!n5{_^@rA!)=t#a^;N$8GL!*DsQb}`yvEuX!G@||An znOfUZAevPrkV_qjl|<~3QRZzG&h@C9Y5z zqpNH4xqbF_InIPh)kX}Vn^5kyed|mOuq+2>M;v~KO37a#yrEn3XDqtOl=rc6_KZ!; zreo)DFVB4|>1Zd(bvMI%8uM;3!)YMYu&cG?(PE!B~y@3yKBMt|R zAf=I16tFwPsl)!jDqvYkLHaAQ+f@W1m6F5aZvwhm4JL z{_l)@b;)mDSzle2gyFP5-r1x-5X{G}ot%VyWP@vEW80!Q=f%RTfpg>B*TA^pyWYUQ z<=xPtz}WcZ!;rFl4m1D&FFHv?K~#9!?A%+fn=lXt;9!Fc#kQ;zk~gZFsH z8e5iu@c_pzX&qb8&Dum*oXwB+fm6l6gFfC|o*wgEiy6tw~&co z9Vd_4)P%wP-KwQW7|lN-znGK#?N+j24U=$982myIBM+vsiKsc*@4-rwJxuAaHKna6 zT3wi!C~a4ZKH03qU}_1bKyx0&$CaK7_%Z+Kl$)fF5^op zZApQF2TvDav!s|krTjw-8US6ep z%!VmX4luub+fseQz_D9ATJQ?iQQwD}TZz{-yo#l12a%+7bT@E(X-hyaVS-5vuXc#^ zx^w;L21;NphGVoj*{s3f4dme0y2LC=G1-7THd`#z?;tuC{^9k(dM{Rf2GOxg7Jzho z7nSZHl7?M9kdalX`)YgoKEfiae5+;$(OGeN1eqxrv!ZCVKyH>xiyNqfe8xzY8*7)H zQls8KMp)F4D>ED;idMOU^^WhVF@q>ZSmeB0y~qC~|DB648hr%Sh|*T(4q|w2l?m2+ zvBVw3@7+Mz?^Yc#+se6KM;a<=(W-I>k)$-qL2V*t}VaW`;?P4)WqI%maIDq8!oUcSYAD`}wWjkSyAVsnF65#2zQ zZ>(K*TlS(E#4y$4Zq+e^_&}d)q20hCe3!LfLYP%nQpLJ~gM6a1hJlz3)aS<9C9me| zAcmJ#>tOwBy{HoP0Sm1&_(E+S@6 zgBIFUoei8zJmdpiq8q5=OY7t@`)JWxn_&GvKVr=Zdb_pEL_j|=?f;WK^U9Q0efd#K z9q7SfJTl4pmA$jsZ5oK8@O9#!I3Cv-kL)<8SalSsp#dcpvJ}Nz#G6FC0%9|7Fi#8; zGDJXtj!&GljT3*HE@0EE>G8Se&d)*nkqe}-?`3vPl&UqK?xG z!3XJ4M-x`EuQjhBbu?ik-)rmIt=DF_N?TVMP)8Gjn)TZ2V%H|zENbeix}kOxd@0}Q z>)HuH6Ean!uS#~4g2Ne2WsMGel|h%j9*W_quQheG^JqmKhc*RYzp0wKlGjBq2VzY_ zgOv8WC1+%W=W)k)Yp_`8kfE=uiiwOZTXi8Uj9YGr$f@yJcJ;#&-Nq~sJ7anE(@;QN z=~br%7%7`isKStX|7!1?L(apl^QvPKlrHV4S+6tNVQ*R1iGdC~WMNE1$a+=rpQmcB z>wxiLIBvOnm;u*;9Y!kJdy(T4lk|8>JAm(&wEsFIF1$_*{>2ZNd$V6DS=SfrGxAv0 zzKe377JI`&o9Ljr+VnS*EwehA{f&{cKZF(6*MG5!p5MvrFA3ll{fmRG*L@6^cb;o^ z3Wm8c?Sc6$`>~VEWw(c$Y?nRO;2Q$=ulpqPtM^=1IZx;@xK0PgO7rKQ^WHVLwtgUT z%|JF{^f(VH)wLKQ%dYiu2RmchBdxL0-M?wxxul_z*{h6ZZ`>-k(vizs((vW8Lt6Z6 zY;Dt?@JWyN`O`f;&d1Mb?e%9oyRK1ql?EE5XB2(W)|D1~Rx35$H6@6)$F?)7V|zEO zI}fu0-0}8W5=6sg$fPnZ~7=tTudl?Ecb@pxbo)vni%gP-?hL|%*?62C;x6?@E`VRnJv z?fTb;k4x;TS7Cu-z%J}uy}e-pwpLQ17Q@4DC+FCdAmNKklG$`I_pyw7E{fYmw~{Fj zi?6KcVy=Wrel)EB_DWO|0CKmI|13!gBV?X`Ozp7x>?6jr`>Qz=^4ea35!$*f}) zS$i+x_k+@P2q1RFUH^ZTTk7=n?cjfR>hTq3l3SY~#w+I8SSutXGyhw;Ws~=zMQ%Vc z>$On~47Ut?P*_!TOQ&PFmLAyJieB2X4_Fd_!WxI-AY`q1Lc-oK?+qcOTzlQ?@~x@OT}*9jTVNfl@3rGvZpWI=eKg>T zZb@6YWz)J=IhP7CF|c?G62vMEG%#U}?#86$0jR4sG~i(jRd#jmn`7b(O#?N;3a;1t zhXLssmUwGhp79luw#(*V8WL0|8+E z6=YZ_O@er~$LrD_PYGc(kJgB=;yw#+Z3X6LDUZ(NcwN=B-hjdiHm!JFar%m{(5bEW z@@_VEtG$5;`EJZ|OkJ@l&G9n((w@uNFwmU%bG|s#TbcJJos!{e+bjCjrCq_}LcN!UFgKtgg7siV*7# z!}1whTRRi*-avJPu->C}Z8EiuK$#886+H_#_!btv+rsiBbv2jAJvJ+O0{#}y(%L3H zfjU-kq_-L@2XrL*ae{{qYJkD{@dw%*bkh2P&YS-0!Xt!PRz7KHV0+~j(t9W8lAVWR zt@B*DgURgEz4>WuN>o?_iKcw$?k{||Pg7{Q2o4|VmJ)mg?{VQJA<}zEr^YAAS zgGm5RT4T3p)U;yz-tfBO^kw8?IoG!IVmc+Z3m#}AOQ?5MRa>)OcU!$N^_+yK6ayn? zK>~WK0!#ysuj^oNLakm)Zvu+J)OSubX^kv!c*xgdIvs;kln!rgG4*uZ;w0mQQO4XD zO9P{GNdv!=cQ(CAL{S(%KtuV^zC&Q{%g)PoXnp^gn^>c*`E>$hLYg2HjnbVGtWLa{7zHdG1jT@B{|Dm16 z7K2(jsfG+m*Zxof)iXxu+!H5Mo-0$pkyV3VV4B@Qms46M zuBxGRV@HxU7Wwx-6CB zaU*HO<_qn$5GH>&@?nRy1{z zkik!sLfWQ)r#75)vVwCBU*r_)Q6mp?!j85{#Xqse)ApRdE$V0%I0*~e(_{)5H)`Mk z#rExC>yjhZxuL@|+#v4#<Axw$+VpV zuT;!2Vww$je$DpAW`$FX_Ab|Ip%$;&T$-lW8jS~B$>G}rd>eQG+$h9lQx4Mx0w={m zx9?T6VU`>sR}XClkAhHEShOUe8awiq zmizhL+}5UKs3}6~It7vBTig9dfQ2Q8coo+Miiaw7n~>4ybv2Ptt0^^=VqX(t*Yya9 zr`FxxFX8(v*H=+uJ#JJWIB2A(==HDYx~^zZ2nu?2`}|Wsa*f3h3ixc+U|FDtAG$Y! z*lc_7se5Oso-Cgqe0){{!8H4g$3<8!R<6JOurD;((({c$1(pwb>(#TT!sge@4>r2@ zVL7>U`0`nsWAYErezk4(Z!gMI2?UTo{J3Ajo(u4)KYIRd>BRcG4BoS3G0EXyEp@tw z%P7__?A^a>Q&AKL@ayDO9D*Qkc!NHnO9l}kpp_6hXbMppYL(X1L?njdFT|-h2<_$; zAtDZ!1Rf%|yb!qbWKd}%0b`LzBeyNy43|QO(&h2mxQLUL)|0%agVOW)6TV!&Ip^Ls z`PG2cygM8)IecQx=Fc+nqYRo4hS^^-nM_&-y8?EJXUczP=DIw(GkTJdpEdh<_STs{ z|A)4n1GKdE=Wu!!nYoZHcUQ4S&R;oDOKX2lrkdF(mK>hz<$Pp>igjOcvoRIjlN=W8 zu8Gx5(roqn8$>gEE5vy{GiGeW8Tq{vnf3hS-V=$tZkQuftUVuU8o6k&dn=Yg3)6MOIH>nlK^-2+C6BZITr~1@So?NvG#TwL)|~=1YXGMTLpS<)ziK_CSOabe z=cB#5)yz|@0i9dSo?*CX)}UP=s6)B+F@~Em(u@Q(I9J9i_V{LmMu8BfXYMh~*oPP+ z!3~xTv|(>|=n6ZOtT~C@V!z!w%18*8T2t6}U2S##rC)mekBql&VsBX;$~ByGE$oA9 z`0Wzq8p?R{4)$l*on;!cLa}Dh^Xe?owiQZt9nH1fxxh$pN9K%CtOw?u3>85L7rr!d zXs)l{TZ{xXP&U8exz?9cv~dNNibOmt*K4I$?RxqIBZ0(?Mg-9FS{*9Bc49Qc1`=sIF-rye`aNT1G@4NwXcnyc@+bw_mTsR>5< zF<2;X0QesG_pw|TonqVBhRtfqI>ty(SIu&VOXd0CrLlfp+;WH7HYjhqnu^oAY!9cB z=B6#R?Rfz9BP`dJ=@v_?70s3HxQPk+{6Y+lM85f2NF^00*^OcM0~?JOZfR9ZPYF+# zYSs}(_BUYV8{n@2a1hD^SV41bwmi2uztR;PeBgF1F-`9>`zoNss-@3LaF2sjl~>OaaVmp7PNp+UT`6@}gR%uzqHDVeEZ14{Yt?n%JeQm+t(1_u zSc}oj^{b;+rlS|ME%+LjzSI&xu0Bblxo$MJ-J$kJ?Qu_XUXh}*@*-x@ny|}wVM%Lg z3tNB`yvr*}N?ClGL;H2cglcvErIccU3(eP7>@~4nOIcI~-`P8tSQnx=jI&{9)!1}l z;gQ%_h>ZlPSV@o@Azq1R$C6ja5!^ZGh;YRhhxs58qJWo9@Bceac&yy(pET1hnn`~7@}2L0&dfPKYs$ih7m2}R!25!(hxqA(!UIw; zK4+~Jowy3=RNC6nE=ncU{LH5?*9@W24lacJlvCZXB$CYtE@>c+~H zkV=(5I&gb{xn2!~f&fs2NQgAL6`p|kyt6kpWk}iVlqIp(H;ig`{_U9yxs1jzu^ETM z7~)Rg8C-NueqTYP&U8l{DY=Y47cR zOR@U%$KQV{mkRF|4)z9Y^t3K`@p>duY&QLUFeh6VoV`a`$U@)(z!-N*5Cj<11$EZW&hJLX83TO{lJYP74rlDZQPkm@t<=U^I)x@|UnHHkdQlh?!ltZwl92rE;;^ zZuIappj4dhld1}kttYYV-j|KF1Kus zWBnzttD^00%LFK(wrwNragFub6xiV8QE2rm<`&fcR4SLFcdtLxVuN!Aal-g6dE4%k zARZ}|xeo;K{0yf7@9aua%2j5o)CPcIOc6uLHFJOcgtB5owlcNAwyAHc0QB0Dts?c@ zUemG~j_E&W7R%+x-IO4FJl8e&*2Blmp1S#RA|)geVrxvP)NHdYuxi~g&Etn?QdNK8ZDKZ?QFLU?zh30G|t9G>a_X4zk}Ygw<^$7K!GIn(Io$>(d4ODJQ2XSd%jpK zm7>ptl$a3GyB}5-%p4>Q*p#VL^B{yQMuFCM^#l#+N!Ne z5_PrJWB=@Iy+t)H`g1lX`{bm($KE5I?0c(JEYm#t{F}j!xtsbob0{xu@0TB_*>G7w0ICn zr#VoBktqHZ~XxhiKD*lcG|b;H*|Ny3P^8ceV`sfBRfrhwZ!T+MFZ!F1Bt{q$8d9i6o?~ zODj^POr}&ivSa^R^YFIq7o0giLBKCycH_aU`F6)O6JX%nPTwh~Q`eq6*0iE#Srj2^ z*_hN3%*b83zfafy60@Cp3{J({RlSaEn&E?mrxRNC9GQ7#+f=s! z0KBf-9Ny_v2VbE%aB|Di)5kNJ^t&C`4D(>t7zYUWUFtbxt+Oq=!@O7BU)}>d*R72o zFF)3jQD_lLe4is&xzyJYC1-c{8TX$RU>&>P$%)ufpez0XSAukmh!xcekg`s$c<>-q zI#zn^JU0zzF}V60)o$_gY}PQH>b2M9&8fRZa#OauglPb zeQ@pMm&=!vNgos4CluQjLMV!pfkmxK+35bi^k&=k>9h02?l+u+m0agG;(h2|Jslc-llvtEwn~*w3bx7qnvZACG<8}AGeaDVvcHbKd2>3G^ zSFPULUn-?Pmo^-_`mLZr??uNH`2=I&yajlrF{DtUxMy#Nu}z=3y7qbUA;5`)hibMR zhXL@@uKyV0-2&A@t@!xyrBnMJl&^o@Gx$&5_q6?D=ji5grd-~=?dlg;ur(_V0wjh! zA=JV^C1m+DDkOsgr<%O9ZQFg!0}pD(#PSz4Dr_EyS5$`)VIAv);4n-SFP~YtC7sH= z7&*MfpH;gd*FHbkmD#)hVxb6xjc9~`t?_{=JS+@ip_cTicXxG<=7m9& zPX+Z8IC*GSAXuGCrZDHgR$r%jyk-fctis2Kx4HvZ|B~8uC@o)m^>Hy-O!&TKA?$&n zkP2Xc54w~!=z2?^NafyL*L0V9cbYrugHBBUj`xVyZmGFR&kvk#>1J*Z~i zNTz}?IAdJ$gkqd2!Gw(%LzE!O5s4C7q4%T~e_P{+z=DNDKrG**p=U`d5yg^vp`;Zn zsU=8gd0a9s4s0FPJePWR9eH5=+O^Kks&kC-iblNqTh2&Pw*^(4384f+D8N|fewZu_ zg2ejQ)ov;ztz;NQl7yj;A`(!H!XQu_$sqY9h_IrH*}_%1{L&_YLDvO?%R5Z-t+ClW z_qERbL?HKUZ!nt+!E9S`uoh^5A|DaIHe*_gf1`E_Vq+}{&T@t$EGhMnRjJ4z2w_W8 zp+qjs7as22^&S3wY1?+}^j-I=RcCE>#|39)g(lU7v_8;?=qK(9D8-*pPdiy)P3lIblG`+?%ea| zYoD3dopYt!tKgFicfNmNi(EWE=E4hC6(r|PYtanqJlmt57YOVrr2^tfrG(eG9C##X zu&1t@%L$RIvpj!wUA z8i>Pqot#_+Cnp6L2XPcZy1ar|9MnY+7eNvK1E)@Tr#2KsXq1*>)uUCozT7L##ok?o zhA6ofP4E|b*9tAfG?uf$#}>TIR&1A!yslP8}i7w-EzW(x#9VEvx18k%Tn=-$VV zkOtUr0b2!w3t>h?#8AZl^Az*(6KCGlD;4j~yx};`#2gN1_gv=%7KVzecIRakN{f*4 zeaI>yH;-o4OGhvGTU)(quWI)-q?V*(sVesSMv|wMUQ3hLEt=lBB$KZ9TyHr>)f7o%) zPYeU<3P)*P10*7vE)nA5#{c=6-E-_>r_u4e3i!I2+UksELwDqwMeBZ9FSP$;^Ajro z_@M#_Ss$?ejoB@!wN|kbGKs(0zLo%0QpQXW#t;oC$B0MZYZ&Ej?8~fNhcCVvPo3vo zFn0WWZaPliF^8_}yzb`*f@yg0uWv6HgNI)xa=pO%Ck(C<=-60l#uD3(wXP~c7!NoX z0&^6=N`zcc90F#qt@=Rn@r!3(*1v(Tl{B!m?Mc7yIA+nEHpY{YWr$=)F7rhR1P}(v zt{YhY#;jsW6G>#xhP*B`OCk|Pf+NN;ju1rxa*HAgoGq*rvqw&xe~;t1JA31$s?GBb z*g7&@cbKo4n<`>)!UlIAgR6q&))B0KYU8r66GbFj?8Guw4E%&}Qi_lT003LtoIZei zwD~=XZmeo+yZ2Pq3KYCF-R&11^p= z@H%s+=G`}wrbJ{()Mh71#2SP3Zy3m>l1n?0N-N1Q;z6?oSxr-G(H5m4EO>~&;}VKi zfY}3w+9z>vp#d)hVuu`)vG_aaH%3b=WKMnSu&c31;<3O;bz2iD=w+o4#oBb36 z5ZCF*Gu?zjZIR0S>_%pHY2$k8D^n7Sz_K8tCDeXM+dO<#LSg%h6`~dnVG1N@T7v&e z%wEd1!k{^zfz_1BTW{!$!B%g)J^2b87!9Y>>100X1SgT7s0z$o>^lAA=Gp_cC1(h=*5Tmf8z&LGJJ>$|K^~s`z9*OWz5MFUr?>Bi?_PGBB)#psD5?>n+q{o_ zz7~ez&;t#h8l$jwGPCC&xq2YetXYQT+0F3j(`xmNGf8dj#an|p#I*pvI*kwW4iuB> z+q3_7xB8y;pLzHG-S%+UHQA zvqp;$kmGJY>lLsN4C~&TcvAS1SErTcwcw0r@wngk zShAUA1M9b#g}^pL-zH7Q#z^&j#r9F8BTVfkR&qF<=e35goTu7c|GN)0mokj4m0%~0 zXJ8j4Hc_l;HJ&uU*Iw`8d_EscJ``s0tk9mkKo^&#TYXm-EoAzTQObxa@^u~g2t#T) zJz|rE!I_?i4dCJC=B8(_pZ{YR>|V?0iCcnU;E@$239^x?SYCfNaMHN;CtHIS_zHN9 zTkQc1v@O35okiFtq5_u+5FkY55ap@pi)O?}x0D1c*qB0KpYR}>Ul+B0Vmr}Z@+%mJ|As}sis_=ROPbov@*2thpE&?!V#Qgu$snYvCZ zrkhmkMU+fSf-s8(L37fPr&M*jRs{{THb!aXQu|P9l_-vJhHvLzMGH zE?1U0H_+PmNABp9`|KzkGfrrZ%XvdGo6*<{d5m9~L7 z_^`M;X6xDo=m6LY6RfvJEvsTK1!u8d2HPx|$S}p;sRy!I zWL55Yxu~_B`OP@~(q6&W3#)~I&+MGL%GWR$#udC151^wsswhqlii;rP9jJpiI7o&Z zAb})=HY7?4HA|re3ns`%$)FuvKCFWjhb~?IE)F6dF2K5}poj-NK6Gf;hw$t3=1txY zoxQxZWrQU6K!%|~!m?~Bnw-6Rr!F3BZ{u5!LqnZTDON}Coj9^@&le)V!NYrVwS~B% zEL+>Sr@}qGwGvu|HrOo|gSt__ezN^&%~{*)a=rf7y1HujUcr`zZB<4#l@T#eN)si} z)lZA<{=tKx8E%c9>A(##6}_p+~EZpKsl5a4pj`E*;_-6`ysiv zffA!7=MT1vCz}-m4~tjVey1b2KSR4OEtLd-(_DdUqYZ74LaDkhH?KFh?%WAOP2WbX zp@zT+Dx|5_f%JQiAGvVw!oh+g3e50u!aPfMxdC=E)XB{F5IcEZhePIM- zph6Y`$Oy?JBL<8Ex(SqEhLeQ@XcrdA>a?rx+_~HLA;l14)WmmpH}_w?Pg#HBZs0eS zwypwAW?M-x+3AU-(GGWSJ=ngxUEcEZ5OsX(Qlt!MQ zn^(`S{GHkAv(8@D`EAfSYig%Cxv?z!{=w^F#y)5_d7FuKZH7qlR-#5B0bt806%D0I zT7VdVP_?q*%Rq8UR;JkD4i^RXowt+E%#V2U>TfDqzZSDZ+dR!a#T3I>-z_$q9@k|m zy5~A*m~&JWP@E7a=pc}4kVHTc4h&R;Li7d@f`|hKMLkbb^uhOakNr3&FLjlm~i5NBM< zFaYI{;cpiHCNRdE0dg*>qIm(_t?#$h=(SCw?h3rJV2*ER8{O4^3#=dO)KwklZkoqU zS8i5c%YL*y*4;FY#D=XmkQnYj%LH)?02~gSJH`Qp1XY64g>%c_K$xseI&|e)7vRoL zAqRba$G@%fSGA7X7hQk%_3NVOYVS+$leU_!&6*5uN)8#5ZBz_6ASCA;azYS-Rt@ki zg2NWz(=;t}SC(~Ibl63$5C8FPmhXqb^)5#jaJ~I{Ex3xZ!+2h8$}}h_g@Be>HZ;72 z6#y#>AY3^skuVKF#0WxFBQ()5d5_nWb?c6c>EeMM|Mh+*&wEpPyxHCq{R-Gdr-`hN zF=1sxl&mBoK+#qRLl9#CEN|Fg8>nbmsTg3a1;#M9enQ$RgWk}kp#-5wh=EF&1tl%mJln2V^8o%Qv(*=zEuO7y z=m*8?xpUn-*@h5Cl_3BK3joiGkyaScK+>|MWdMRWm@RT!Q1piAlv5hL@B6>3&GI8) zP!xBc6}ZNIpJLL%2a8Y!+(<=f%WX>_uWVxlga9!D*oYt$l0cxRDMvqfU;Kq_mLK5k z)dvqYcgLa_Lz?3HyeF)@$%$&6lI?r4I>6W#M*<)vq{?&Oqrx``d`mhpVPr> z#q078F6gw_X<=?KR>8%^t%@wbITvNMu!hKiTSkCTJkw>1!e*Y{%31#_yMf=LW7{RJ zYoC^w$6%3cBtVG5)x#{Hg6IVTh9XEcM{gQwXk!R^y95^f-hZ`d{aVa+xW1EO4wDV4 zB?JgD7*?qkvc|$nIykTvNl2x0j3Q!MXoLL^)~}d7jcYf(H8D~c+?$pKL(px>Z3`eb z04RzS6_AgFT6Pn#iZAg$Sl_j8#;6ShF%&(Fag#E2asU@@LaN;=b=Wf7sgPKhfzhBM zC@eFL8^MrnA*9&Khe*Ab@CC9*uyJGXyi(;y2>lQLJZt;ShtJi?3Yf_t`F+$hY!+Q2Ndsx=U+bjTiAy7djLji>7k%k`$9&--f<*BNA3Hy&ZrHH|4 zG5H&9cB?O#zI1_OOf0Ce%mDfQxdtp3vU%(iY6yji3iISS61XLv#z|!zI_sZqza@B+ zyu9st5-h+`H7QUKx9}3w@oU@EO}&cEzG?fu!!bLO->%zkcg;i9^j`S~=WKMnDi1f= P00000NkvXXu0mjft=yBf diff --git a/templates/vite-spa/src/assets/react.svg b/templates/vite-spa/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/templates/vite-spa/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/templates/vite-spa/src/assets/vite.svg b/templates/vite-spa/src/assets/vite.svg deleted file mode 100644 index 5101b67..0000000 --- a/templates/vite-spa/src/assets/vite.svg +++ /dev/null @@ -1 +0,0 @@ -Vite diff --git a/templates/vite-spa/src/index.css b/templates/vite-spa/src/index.css deleted file mode 100644 index 5fb3313..0000000 --- a/templates/vite-spa/src/index.css +++ /dev/null @@ -1,111 +0,0 @@ -:root { - --text: #6b6375; - --text-h: #08060d; - --bg: #fff; - --border: #e5e4e7; - --code-bg: #f4f3ec; - --accent: #aa3bff; - --accent-bg: rgba(170, 59, 255, 0.1); - --accent-border: rgba(170, 59, 255, 0.5); - --social-bg: rgba(244, 243, 236, 0.5); - --shadow: - rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; - - --sans: system-ui, 'Segoe UI', Roboto, sans-serif; - --heading: system-ui, 'Segoe UI', Roboto, sans-serif; - --mono: ui-monospace, Consolas, monospace; - - font: 18px/145% var(--sans); - letter-spacing: 0.18px; - color-scheme: light dark; - color: var(--text); - background: var(--bg); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - @media (max-width: 1024px) { - font-size: 16px; - } -} - -@media (prefers-color-scheme: dark) { - :root { - --text: #9ca3af; - --text-h: #f3f4f6; - --bg: #16171d; - --border: #2e303a; - --code-bg: #1f2028; - --accent: #c084fc; - --accent-bg: rgba(192, 132, 252, 0.15); - --accent-border: rgba(192, 132, 252, 0.5); - --social-bg: rgba(47, 48, 58, 0.5); - --shadow: - rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; - } - - #social .button-icon { - filter: invert(1) brightness(2); - } -} - -#root { - width: 1126px; - max-width: 100%; - margin: 0 auto; - text-align: center; - border-inline: 1px solid var(--border); - min-height: 100svh; - display: flex; - flex-direction: column; - box-sizing: border-box; -} - -body { - margin: 0; -} - -h1, -h2 { - font-family: var(--heading); - font-weight: 500; - color: var(--text-h); -} - -h1 { - font-size: 56px; - letter-spacing: -1.68px; - margin: 32px 0; - @media (max-width: 1024px) { - font-size: 36px; - margin: 20px 0; - } -} -h2 { - font-size: 24px; - line-height: 118%; - letter-spacing: -0.24px; - margin: 0 0 8px; - @media (max-width: 1024px) { - font-size: 20px; - } -} -p { - margin: 0; -} - -code, -.counter { - font-family: var(--mono); - display: inline-flex; - border-radius: 4px; - color: var(--text-h); -} - -code { - font-size: 15px; - line-height: 135%; - padding: 4px 8px; - background: var(--code-bg); -} diff --git a/templates/vite-spa/src/main.tsx b/templates/vite-spa/src/main.tsx deleted file mode 100644 index bef5202..0000000 --- a/templates/vite-spa/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' - -createRoot(document.getElementById('root')!).render( - - - , -) diff --git a/templates/vite-spa/tsconfig.app.json b/templates/vite-spa/tsconfig.app.json deleted file mode 100644 index 6830b6f..0000000 --- a/templates/vite-spa/tsconfig.app.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "es2023", - "lib": ["ES2023", "DOM"], - "module": "esnext", - "types": ["vite/client"], - "allowArbitraryExtensions": true, - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} diff --git a/templates/vite-spa/tsconfig.json b/templates/vite-spa/tsconfig.json deleted file mode 100644 index 1ffef60..0000000 --- a/templates/vite-spa/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/templates/vite-spa/tsconfig.node.json b/templates/vite-spa/tsconfig.node.json deleted file mode 100644 index 8455dcb..0000000 --- a/templates/vite-spa/tsconfig.node.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "es2023", - "lib": ["ES2023"], - "types": ["node"], - "skipLibCheck": true, - - /* Bundler mode */ - "module": "nodenext", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "moduleDetection": "force", - "noEmit": true, - - /* Linting */ - "noUnusedLocals": true, - "noUnusedParameters": true, - "erasableSyntaxOnly": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} diff --git a/templates/vite-spa/vite.config.ts b/templates/vite-spa/vite.config.ts deleted file mode 100644 index 9982072..0000000 --- a/templates/vite-spa/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import react from '@vitejs/plugin-react' -import { defineConfig } from 'vite' - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], -}) diff --git a/tests/architecture.test.ts b/tests/architecture.test.ts index 05ff998..cde2eaa 100644 --- a/tests/architecture.test.ts +++ b/tests/architecture.test.ts @@ -111,6 +111,8 @@ test('retired session-truth modules stay gone', async () => { 'agents/_lib/chat-tasks.ts', 'agents/_lib/shared.ts', 'agents/_lib/pipelines', + 'agents/_lib/project/templates.ts', + 'scripts/bake-templates.mjs', 'agents/session-model.ts', 'shared/makers-dev.ts', 'shared/makers-deploy.ts', @@ -128,6 +130,7 @@ test('session kernel and makers CLI live under agents/_lib', async () => { 'agents/_lib/session/transcript.ts', 'agents/transcript.ts', 'agents/_lib/session/live.ts', + 'agents/_lib/session/prepare.ts', 'agents/_lib/session/projection.ts', 'agents/_lib/makers/session.ts', 'agents/_lib/makers/cli-dev.ts', diff --git a/tests/preview-path.test.ts b/tests/preview-path.test.ts index 2670ec3..1f4216f 100644 --- a/tests/preview-path.test.ts +++ b/tests/preview-path.test.ts @@ -547,7 +547,7 @@ test('the host starts dest with the workspace and keeps it watching files', asyn assert.match(chat, /const startHostPreview = async/); assert.match(chat, /if \(state\.created\) \{\s*\n\s*void startHostPreview\('\[preview\] workspace ready:'\)/); - assert.match(chat, /onWorkspaceReady: \(\) => \{\s*\n\s*void startHostPreview\('\[preview\] after scaffold:'\)/); + assert.doesNotMatch(chat, /onWorkspaceReady/); assert.match(chat, /state\.created && !state\.previewUrl/); assert.match(chat, /await persistWorkspace\(context, conversationId, state\)/); assert.match(chat, /let previewVerified = Boolean\(state\.previewUrl\)/); @@ -557,7 +557,7 @@ test('the host starts dest with the workspace and keeps it watching files', asyn /previewTouched && Boolean\(state\.previewUrl\)/, 'host preview must not wait for the model to have launched dest', ); - assert.match(assemble, /onWorkspaceReady\?\.\(\)/); + assert.doesNotMatch(assemble, /onWorkspaceReady/); assert.match(resume, /const shouldStartPreview = !generationActive && hasFileItems/); assert.doesNotMatch(resume, /&& hadPreview/); assert.match(preview, /revision === undefined \|\| nextPreview\.restarted/); diff --git a/tests/project-templates.test.ts b/tests/project-templates.test.ts deleted file mode 100644 index 703d90f..0000000 --- a/tests/project-templates.test.ts +++ /dev/null @@ -1,644 +0,0 @@ -import assert from 'node:assert/strict'; -import { execFile } from 'node:child_process'; -import { readdir, readFile, mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; -import { existsSync } from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; -import test from 'node:test'; -import { promisify } from 'node:util'; -import { - applyProjectTemplate, - listProjectTemplates, - resolveProjectTemplate, - withPreviewAssetPrefix, -} from '../agents/_lib/project/templates.ts'; -import { describeScaffold } from '../agents/_lib/tools/project-tools.ts'; -import { PREVIEW_ASSET_PREFIX_ENV } from '../agents/_lib/constants.ts'; -import { NPM_WARMUP_BASE } from '../agents/_lib/makers/npm-install.ts'; -import { projectState } from './helpers/fixtures.ts'; - -const execFileAsync = promisify(execFile); - -const SKILLS_DIR = '.claude/skills/edgeone-makers-tools/references'; -const REFERENCES_DIR = `${SKILLS_DIR}/makers-frameworks/references`; - -/** - * A ref names a file in the framework references, or a path from the skills - * root when the framework is documented under a different skill — the agent - * frameworks belong to makers-agents, which has no scaffolders to describe. - */ -function referenceFile(ref: string) { - return ref.includes('/') ? path.join(SKILLS_DIR, ref) : path.join(REFERENCES_DIR, ref); -} - -/** Files only: a recursive readdir counts the directories it walks as entries. */ -async function committedFiles(id: string) { - const entries = await readdir(path.join('templates', id), { - recursive: true, - withFileTypes: true, - }); - return entries - .filter((entry) => entry.isFile()) - .map((entry) => path.relative( - path.join('templates', id), - path.join(entry.parentPath, entry.name), - )); -} - -/** - * A sandbox whose paths are relative to one root, matching what the platform - * presents — and whose commands.run raises on a non-zero exit rather than - * reporting one, which is what the real one does. - * - * /tmp is the exception it has to model: the extractor is written there and run - * from appDir, so the fake maps absolute paths to a scratch directory of its - * own instead of under the project root. - */ -async function templateFixture() { - const root = await mkdtemp(path.join(os.tmpdir(), 'makers-template-')); - const state = projectState('projects/demo'); - const abs = (target: string) => ( - path.isAbsolute(target) ? path.join(root, '__abs__', target) : path.join(root, target) - ); - - // The warmup this exercises ends in `nohup npm install &`, and with the real - // npm on PATH that is what these tests were running: a background install - // into a temp directory, still writing to it while the fixture tore it down. - // Stubbing npm keeps the script itself real — the guards, the pid file, the - // stamp all still run — while the one command that must not is inert. - const stubBin = path.join(root, '__bin__'); - await mkdir(stubBin, { recursive: true }); - await writeFile(path.join(stubBin, 'npm'), '#!/bin/sh\nexit 0\n', { mode: 0o755 }); - - const calls = { writes: [] as string[], commands: [] as string[] }; - const context = { - sandbox: { - files: { - makeDir: async (target: string) => { await mkdir(abs(target), { recursive: true }); }, - exists: async (target: string) => existsSync(abs(target)), - write: async (target: string, content: string) => { - calls.writes.push(target); - await mkdir(path.dirname(abs(target)), { recursive: true }); - await writeFile(abs(target), content); - }, - }, - commands: { - run: async (command: string, options: { cwd?: string } = {}) => { - calls.commands.push(command); - const cwd = abs(options.cwd || '.'); - await mkdir(cwd, { recursive: true }); - // The script is addressed by its absolute /tmp path, which this - // fixture relocates; rewrite it the same way the fake write did. - const rewritten = command.replaceAll(/(?<=^|\s)\/tmp\/\S+/g, (match) => abs(match)); - const { stdout, stderr } = await execFileAsync('sh', ['-c', rewritten], { - cwd, - env: { ...process.env, PATH: `${stubBin}:${process.env.PATH}` }, - }); - return { exitCode: 0, stdout, stderr }; - }, - }, - }, - }; - - return { - context, - state, - calls, - readBytes: (relative: string) => readFile(path.join(root, relative)), - exists: (relative: string) => existsSync(path.join(root, relative)), - // The warmup detaches, so its last few writes can still land after the - // command returns; retries are cheaper than reaching into it to wait. - cleanup: () => rm(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 25 }), - }; -} - -// The bug this guards shipped twice and is invisible in review: a chat route -// that reads a singular `message` streams a clean 200 for the preview probe and -// answers every real request 400, because the array is the only body a chat UI -// sends. Baking the route was the fix; asserting it here is what keeps a later -// edit from quietly undoing it. -test('every baked agent route reads the messages array and nothing else', async () => { - const agents = []; - for (const { id } of await listProjectTemplates()) { - if ((await committedFiles(id)).includes(path.join('agents', 'chat.ts'))) agents.push(id); - } - assert.ok(agents.length >= 2, `expected the baked agent templates, found ${agents.join(', ') || 'none'}`); - - for (const id of agents) { - const route = await readFile(path.join('templates', id, 'agents', 'chat.ts'), 'utf8'); - assert.match(route, /body\?\.messages/, `templates/${id} does not read body.messages`); - assert.match( - route, - /normalizeOpenAiGatewayBaseUrl/, - `templates/${id} must normalize AI_GATEWAY_BASE_URL to end in /v1`, - ); - assert.doesNotMatch( - route.replace(/^\s*\/\/.*$/gm, ''), - /\bbody[^\n]*\.message\b(?!s)/, - `templates/${id} has a singular message branch no client reaches`, - ); - } -}); - -test('the manifest only lists templates whose files are actually committed', async () => { - const templates = await listProjectTemplates(); - assert.ok(templates.length > 0, 'no baked templates; run npm run bake:templates'); - - for (const template of templates) { - assert.ok( - (await committedFiles(template.id)).includes('package.json'), - `templates/${template.id} has no package.json, so it is not a project`, - ); - } -}); - -// Baked trees only reach a deployed run because edgeone.json names them, and -// the CLI globs that list with dot:false hardcoded — so `templates/**` alone -// silently left four dotfiles, .npmrc among them, out of every bundle. The -// second pattern buys back dotfiles but not a dot-directory's contents, which -// nothing here could carry, and a template arriving short is invisible until -// something it needed at install time is missing. -test('every committed template file is one edgeone.json can carry', async () => { - const config = JSON.parse(await readFile('edgeone.json', 'utf8')); - assert.deepEqual( - config.agents?.includeFiles, - ['templates/**', 'templates/**/.*'], - 'templates reach the agent bundle through these two patterns and nothing else', - ); - - for (const template of await listProjectTemplates()) { - for (const file of await committedFiles(template.id)) { - const buried = file - .split(path.sep) - .slice(0, -1) - .find((segment) => segment.startsWith('.')); - assert.ok( - !buried, - `templates/${template.id}/${file} sits under ${buried}/, which no ` - + 'includeFiles pattern can reach; bake it to a name without the dot', - ); - } - } -}); - -// The command is the reference's to state and the manifest only records which -// one produced the tree. When a skill sync changes a scaffolder, the baked tree -// is stale and nothing else would say so — the agent would keep serving the old -// framework version from a template nobody re-baked. -test('every baked template still matches the scaffold command its reference documents', async () => { - for (const template of await listProjectTemplates()) { - const markdown = await readFile(referenceFile(template.ref), 'utf8'); - assert.ok( - markdown.includes(template.command), - `${template.ref} no longer documents "${template.command}" — re-bake with ` - + `node scripts/bake-templates.mjs ${template.id}`, - ); - } -}); - -// An adapter in package.json and nowhere else is the shape that broke: the -// model has to find the config, work out the wiring, and edit the one file -// carrying the injected preview prefix. SvelteKit made the cost concrete — -// its reference described a svelte.config.js the scaffolder stopped writing, -// and adding one there while leaving the prefix in vite.config.ts resolves to -// no adapter at all, silently, with a build that still reports success. -test('an adapter a template installs is also wired into its config', async () => { - for (const template of await listProjectTemplates()) { - const files = await committedFiles(template.id); - const manifest = JSON.parse( - await readFile(path.join('templates', template.id, 'package.json'), 'utf8'), - ); - const adapters = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }) - .filter((name) => name.startsWith('@edgeone/')); - if (adapters.length === 0) continue; - - const sources = await Promise.all( - files - .filter((file) => /\.(?:ts|js|mjs|cjs|tsx)$/.test(file)) - .map((file) => readFile(path.join('templates', template.id, file), 'utf8')), - ); - for (const adapter of adapters) { - assert.ok( - sources.some((source) => source.includes(adapter)), - `templates/${template.id} installs ${adapter} but no source file imports it, ` - + 'so wiring it is left to the model — add a SOURCE_PATCHES entry and re-bake', - ); - } - } -}); - -// The other half of the same rule. SvelteKit reads exactly one config, and it -// prefers the Vite one: any option passed to sveltekit() makes a sibling -// svelte.config.js unreachable in full. A template shipping both would leave -// whichever one lost as a decoy for the next model to edit. -test('the sveltekit template keeps its config in one place', async () => { - const files = await committedFiles('sveltekit'); - assert.ok( - !files.some((file) => /^svelte\.config\.(?:js|ts|mjs)$/.test(file)), - 'templates/sveltekit commits a svelte.config.js that its vite.config.ts already overrides', - ); - const config = await readFile('templates/sveltekit/vite.config.ts', 'utf8'); - assert.match(config, /sveltekit\(\{/, 'sveltekit() takes no options, so the adapter cannot be here'); - assert.doesNotMatch(config, /@sveltejs\/adapter-auto/); -}); - -// A demo that renders by calling out to the internet is a demo that throws in -// a sandbox with no route to it, on the one route the template ships to teach -// data fetching. Vike had the only one: both star-wars +data.ts files pulled -// from brillout.github.io on every server render, and a measured session read -// all 18 source files and then spent four of its seven edits replacing that -// with a local array before writing any of what was actually asked for. -// -// Source files only. A README example is read, not run. -test('no baked template renders by reaching off the machine', async () => { - for (const template of await listProjectTemplates()) { - for (const file of await committedFiles(template.id)) { - if (!/\.(?:ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/.test(file)) continue; - const source = await readFile(path.join('templates', template.id, file), 'utf8'); - const reached = source.match(/fetch\(\s*['"`]https?:\/\/[^'"`]*/); - assert.ok( - !reached, - `templates/${template.id}/${file} renders by calling ${reached?.[0]}…`, - ); - } - } -}); - -test('a framework resolves to its template however the user spelled it', async () => { - for (const spelling of ['nextjs', 'Next.js', 'NEXT', 'next js']) { - assert.equal( - (await resolveProjectTemplate(spelling))?.id, - 'nextjs', - `"${spelling}" should reach the Next.js template`, - ); - } - assert.equal((await resolveProjectTemplate('vite'))?.id, 'vite-spa'); - assert.equal((await resolveProjectTemplate('React'))?.id, 'vite-spa'); - for (const spelling of ['TanStack Start', 'tanstack-start', 'tanstack']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'tanstack-start', spelling); - } - assert.equal((await resolveProjectTemplate('Vike'))?.id, 'vike'); - for (const spelling of ['Nuxt', 'nuxt.js', 'Nuxt 4']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'nuxt', spelling); - } - for (const spelling of ['Astro', 'astro.js']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'astro', spelling); - } - for (const spelling of ['SvelteKit', 'svelte-kit', 'Svelte']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'sveltekit', spelling); - } - for (const spelling of ['React Router', 'react-router', 'Remix']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'react-router', spelling); - } - for (const spelling of ['DeepAgents', 'deep-agents', 'deep agents']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'deepagents', spelling); - } - for (const spelling of ['LangGraph', 'lang-graph', 'langgraph']) { - assert.equal((await resolveProjectTemplate(spelling))?.id, 'langgraph', spelling); - } -}); - -// Scaffolders increasingly write agent and editor config, and under templates/ -// it stops describing the generated project and becomes live configuration for -// this repository — the AGENTS.md create-next-app writes is the same rule block -// this repo carries at its root. `.agents` also sits in this repo's .gitignore, -// so a template holding one arrives at a fresh clone short of its manifest, -// which is what makes this a correctness rule and not a tidiness one. -test('no template carries agent or editor configuration into this repo', async () => { - const live = ['.agents', '.claude', '.cursor', '.vscode', '.idea', 'AGENTS.md', 'CLAUDE.md']; - - for (const template of await listProjectTemplates()) { - for (const file of await committedFiles(template.id)) { - const offending = file.split(path.sep).find((segment) => live.includes(segment)); - assert.equal( - offending, - undefined, - `templates/${template.id}/${file} ships ${offending}, which this repo would act on`, - ); - } - } -}); - -// Resolution is an accelerator, never an allowlist: everything it cannot place -// has to fall through to the scaffolder path rather than be refused or, worse, -// be handed the nearest template. -test('an unknown, unbaked, or absent framework resolves to nothing', async () => { - for (const framework of [undefined, '', ' ', 'cobol', 'jquery']) { - assert.equal(await resolveProjectTemplate(framework), undefined, String(framework)); - } - // Vue is the trap this guards: the baked Vite tree is the React one, so a Vue - // request must reach its own scaffolder instead of a React app. - assert.equal(await resolveProjectTemplate('vue'), undefined); -}); - -// Every template in the manifest rather than one of them, so a framework baked -// later is covered by having been baked rather than by someone remembering to -// add it here. -test('applying a template writes its files and starts the install in one command', async () => { - for (const template of await listProjectTemplates()) { - const fixture = await templateFixture(); - try { - const applied = await applyProjectTemplate(fixture.context, fixture.state, template); - - assert.equal(applied.id, template.id); - assert.equal(applied.files, template.files, `${template.id} wrote the wrong file count`); - assert.equal( - fixture.exists('projects/demo/app/package.json'), - true, - `${template.id} arrived without a package.json`, - ); - - // One write and one command, whatever the file count: the scaffold is the - // first tool of every turn and the install is what the turn is waiting on. - assert.equal(fixture.calls.writes.length, 1, template.id); - assert.equal(fixture.calls.commands.length, 1, template.id); - assert.match(fixture.calls.commands[0], new RegExp(NPM_WARMUP_BASE.replace('/', '\\/'))); - } finally { - await fixture.cleanup(); - } - } -}); - -// The tree carries a favicon and a PNG, and a payload that stored everything as -// a UTF-8 string would deliver them re-encoded and silently corrupt. -test('binary files survive the trip into the workspace byte for byte', async () => { - const binaries = [ - ['vite-spa', 'src/assets/hero.png'], - ['nextjs', 'app/favicon.ico'], - ] as const; - - for (const [id, relative] of binaries) { - const fixture = await templateFixture(); - try { - await applyProjectTemplate( - fixture.context, - fixture.state, - (await resolveProjectTemplate(id))!, - ); - - const source = await readFile(path.join('templates', id, relative)); - assert.ok(source.length > 0, `${id}/${relative} is empty in the baked template`); - assert.ok( - source.equals(await fixture.readBytes(`projects/demo/app/${relative}`)), - `${id}/${relative} did not arrive byte for byte`, - ); - } finally { - await fixture.cleanup(); - } - } -}); - -// A .gitignore committed inside templates/ is a live ignore file for its own -// directory: the Next.js one lists next-env.d.ts, so the template was a file -// short of the manifest — but only on a fresh clone, where nothing untracked -// was lying around to hide it. The bake script stores it under another name and -// the runtime restores it, and this is the only thing holding those two ends -// together. -test('a template ships its .gitignore under a name git will not act on', async () => { - for (const template of await listProjectTemplates()) { - const committed = await committedFiles(template.id); - assert.ok( - !committed.some((entry) => path.basename(entry) === '.gitignore'), - `templates/${template.id} commits a real .gitignore, which hides part of itself from git`, - ); - assert.equal( - committed.length, - template.files, - `templates/${template.id} has ${committed.length} committed files, manifest says ${template.files}`, - ); - } - - const fixture = await templateFixture(); - try { - await applyProjectTemplate( - fixture.context, - fixture.state, - (await resolveProjectTemplate('nextjs'))!, - ); - assert.equal(fixture.exists('projects/demo/app/.gitignore'), true, 'restored on the way in'); - assert.equal(fixture.exists('projects/demo/app/_gitignore'), false, 'and not under both names'); - // The file this whole rename exists for. - assert.equal(fixture.exists('projects/demo/app/next-env.d.ts'), true); - } finally { - await fixture.cleanup(); - } -}); - -// The official trees never mention the host's prefix variable, so without this -// the model loads makers-frameworks just to rewrite one line of next.config — -// and often the rest of the file with it. Every baked config the function -// knows about has to come out already reading the env var, and a file that -// already does must be left alone so a second apply cannot stack the option. -test('a baked framework config is given the preview prefix before it is written', async () => { - const env = `process.env.${PREVIEW_ASSET_PREFIX_ENV}`; - const cases: Array<{ file: string; source: string; needle: string }> = [ - { - file: 'next.config.ts', - source: 'const nextConfig: NextConfig = {\n /* config options here */\n};\n', - needle: `assetPrefix: ${env},`, - }, - { - file: 'vite.config.ts', - source: 'export default defineConfig({\n plugins: [react()],\n})\n', - needle: `base: ${env},`, - }, - { - file: 'astro.config.mjs', - source: 'export default defineConfig({});\n', - needle: `base: ${env},`, - }, - { - file: 'nuxt.config.ts', - source: "export default defineNuxtConfig({\n compatibilityDate: '2025-07-15',\n})\n", - needle: `app: { baseURL: ${env} },`, - }, - // SvelteKit types this one as `"" | \`/${string}\`` rather than as a string, - // so the environment variable needs an assertion TypeScript can read — and - // svelte-check reaches it, because SvelteKit's generated tsconfig puts - // vite.config.ts in its own include list. - { - file: 'vite.config.ts', - source: 'export default defineConfig({\n plugins: [\n sveltekit({\n adapter: adapter(),\n }),\n ],\n});\n', - needle: '...(' + env + ' ? { paths: { base: ' + env + ' as `/${string}` } } : {}),', - }, - // The same option in a file where `as` would be a syntax error. Both are - // still checked — the SvelteKit tsconfig turns on checkJs — so the - // assertion has to follow the language, not the framework. - { - file: 'svelte.config.js', - source: 'const config = {\n kit: {\n adapter: adapter(),\n },\n};\n', - needle: `...(${env} ? { paths: { base: ${env} } } : {}),`, - }, - // Not an asset option at all, and the only config here that is not: React - // Router's dev adapter restores the prefix Vite strips, so `base` alone - // leaves every route unmatched. The basename is what the router reads. - { - file: 'react-router.config.ts', - source: 'export default {\n ssr: true,\n} satisfies Config;\n', - needle: `basename: ${env} ?? "/",`, - }, - ]; - - for (const { file, source, needle } of cases) { - const adapted = withPreviewAssetPrefix(file, source); - assert.ok(adapted, `${file} was not adapted`); - assert.match(adapted!, new RegExp(needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); - assert.equal( - withPreviewAssetPrefix(file, adapted!), - undefined, - `${file} must not be adapted twice`, - ); - } - - assert.equal(withPreviewAssetPrefix('package.json', '{}\n'), undefined); - assert.equal( - withPreviewAssetPrefix('next.config.ts', `const nextConfig = { assetPrefix: ${env} };\n`), - undefined, - ); - - for (const template of await listProjectTemplates()) { - const files = await readdir(path.join('templates', template.id), { - recursive: true, - withFileTypes: true, - }); - const configs = files - .filter((entry) => ( - entry.isFile() - && /^(?:next|vite|astro|nuxt|svelte|react-router)\.config\.(?:ts|js|mjs)$/.test(entry.name) - )) - .map((entry) => ({ - relative: path.relative( - path.join('templates', template.id), - path.join(entry.parentPath, entry.name), - ).replaceAll(path.sep, '/'), - absolute: path.join(entry.parentPath, entry.name), - })); - - for (const config of configs) { - const source = await readFile(config.absolute, 'utf8'); - const adapted = withPreviewAssetPrefix(config.relative, source); - assert.ok( - adapted, - `templates/${template.id}/${config.relative} was not given a prefix option`, - ); - assert.match(adapted!, new RegExp(PREVIEW_ASSET_PREFIX_ENV)); - } - } - - const fixture = await templateFixture(); - try { - await applyProjectTemplate( - fixture.context, - fixture.state, - (await resolveProjectTemplate('nextjs'))!, - ); - const written = (await fixture.readBytes('projects/demo/app/next.config.ts')).toString('utf8'); - assert.match(written, new RegExp(`assetPrefix: process\\.env\\.${PREVIEW_ASSET_PREFIX_ENV}`)); - // The rest of the scaffolder file is still there; this is an insertion. - assert.match(written, /\/\* config options here \*\//); - } finally { - await fixture.cleanup(); - } -}); - -test('the extractor leaves nothing of itself behind in /tmp', async () => { - const fixture = await templateFixture(); - try { - await applyProjectTemplate( - fixture.context, - fixture.state, - (await resolveProjectTemplate('nextjs'))!, - ); - const script = fixture.calls.writes[0]; - assert.match(script, /^\/tmp\/eo-template-.*\.cjs$/); - assert.match(fixture.calls.commands[0], new RegExp(`rm -f ${script}`)); - } finally { - await fixture.cleanup(); - } -}); - -// Both branches used to set installHint in one object literal, so whichever was -// spread last silently won — and the one that lost was the only one the model -// sees on the path this change created. -test('the workspace report answers "should I install" exactly once', () => { - const state = projectState('projects/demo'); - const fromTemplate = describeScaffold(state, { - created: true, - dependenciesInstalled: false, - template: { id: 'nextjs', files: 20, adapted: false }, - }); - assert.equal(fromTemplate.templateApplied, 'nextjs'); - assert.match(String(fromTemplate.installHint), /already running/); - assert.match(String(fromTemplate.scaffolderHint), /Do not run a scaffold command/); - assert.match(String(fromTemplate.scaffolderHint), /already in the framework config/); - - // A populated tree outranks it: that is the case where an install does not - // fit on the disk beside what is already there. - const alreadyInstalled = describeScaffold(state, { - created: true, - dependenciesInstalled: true, - template: { id: 'nextjs', files: 20, adapted: false }, - }); - assert.match(String(alreadyInstalled.installHint), /Do not run npm install/); - - const plain = describeScaffold(state, { created: true, dependenciesInstalled: false }); - assert.equal(plain.installHint, undefined); - assert.equal(plain.templateApplied, undefined); -}); - -test('an adapted manifest is reported so the model wires it instead of installing it', () => { - const report = describeScaffold(projectState('projects/demo'), { - created: true, - dependenciesInstalled: false, - template: { id: 'astro', files: 12, adapted: true }, - }); - assert.match(String(report.adapterHint), /already on its way/); - assert.match(String(report.adapterHint), /makers-frameworks/); -}); - -// The prompt that exposed this asked for an AI chat assistant, which names an -// app and no framework. Nothing matched, so the baked chat agent went unused -// for the one request it was baked for. -test('a request that names an app rather than a framework still reaches a baked tree', async () => { - for (const named of ['chat', 'chatbot', 'agent', 'ai-agent', 'AI chat assistant']) { - assert.equal((await resolveProjectTemplate(named))?.id, 'deepagents', named); - } - assert.equal((await resolveProjectTemplate('langgraph'))?.id, 'langgraph'); - - // The agent frameworks nobody baked have to keep missing: their own - // reference is a better start than a tree built around another runtime. - for (const unbaked of ['crewai', 'openai-agents-sdk', 'claude-agent-sdk']) { - assert.equal(await resolveProjectTemplate(unbaked), undefined, unbaked); - } -}); - -test('a workspace left empty names the trees it could have been filled from', () => { - const state = projectState('projects/demo'); - const missed = describeScaffold(state, { - created: true, - dependenciesInstalled: false, - available: ['deepagents', 'nextjs'], - }); - - // Only this result reaches the model, so the recovery has to be in it. A - // silent miss reads as "no template exists for this" rather than "you did - // not ask for one", and the first reading is the one that hand-writes a - // project beside a baked one. - assert.deepEqual(missed.templatesAvailable, ['deepagents', 'nextjs']); - assert.match(String(missed.templatesHint), /deepagents, nextjs/); - assert.match(String(missed.templatesHint), /call ensure_project_scaffold again/); - assert.equal(missed.templateApplied, undefined); - - // A tree that did land is still reported on its own terms, without the - // retry advice that only applies to an empty workspace. - const applied = describeScaffold(state, { - created: true, - dependenciesInstalled: false, - template: { id: 'deepagents', files: 5, adapted: false }, - }); - assert.equal(applied.templateApplied, 'deepagents'); - assert.equal(applied.templatesAvailable, undefined); - assert.equal(applied.templatesHint, undefined); - assert.match(String(applied.scaffolderHint), /agents\/chat\.ts/); - assert.match(String(applied.scaffolderHint), /do not create agents\/chat\/index\.ts/); -}); diff --git a/tests/prompt-single-source.test.ts b/tests/prompt-single-source.test.ts index 7c6f11d..81eff56 100644 --- a/tests/prompt-single-source.test.ts +++ b/tests/prompt-single-source.test.ts @@ -215,7 +215,7 @@ test('the prompt keeps the sandbox corrections the skills cannot know about', () test('the prompt keeps its tool contracts and workspace boundary', () => { const prompt = renderPrompt(); assert.ok(prompt.includes(state.appDir), 'prompt must name the writable project directory'); - assert.match(prompt, /ensure_project_scaffold as the first tool/); + assert.match(prompt, /load_makers_skill as the first tool/); assert.match(prompt, /write_project_file accepts exactly one file per call/); assert.match(prompt, /The host starts the sandbox preview/); assert.match(prompt, /Run edgeone makers deploy only when the user explicitly asks/); @@ -224,8 +224,8 @@ test('the prompt keeps its tool contracts and workspace boundary', () => { }); test('the prompt reflects whether the workspace already exists', () => { - assert.match(renderPrompt(true), /workspace may not have been prepared yet/); - assert.match(renderPrompt(false), /already prepared a project workspace/); + assert.match(renderPrompt(true), /workspace is empty and ready for you to write files/); + assert.match(renderPrompt(false), /already has a project workspace with files in it/); }); test('the system prompt is the same on every turn of a conversation', () => { @@ -395,23 +395,15 @@ test('the official scaffolder replaces the search for an official template', () // survive that: the one that gets the framework name into the first tool call, // which is the only moment the host can still act on it, and the one that stops // the run putting a scaffolder into a directory no longer empty enough for it. -test('a workspace prepared from a template is not scaffolded a second time', () => { +test('the host already prepared an empty workspace, so the first tool is a reference load', () => { const prompt = renderPrompt(); - assert.match(prompt, /Pass framework to that call whenever the request names one/); - assert.match(prompt, /Omit it for a plain HTML\/CSS\/JS page/); - assert.match(prompt, /A templateApplied in the ensure_project_scaffold result/); - assert.match(prompt, /Skip the rest of this step and go to step 3/); - // The step it exempts still has to read as conditional, or the two contradict. - assert.match(prompt, /When the request names a framework and no template was applied/); - // Scaffold and assetPrefix are why the frameworks skill was loaded after a - // template landed. Both are already done, so the load has to be optional. - assert.match(prompt, /[Dd]o not load makers-frameworks just to read the Scaffold command/); - assert.match(prompt, /the prefix option is already in the framework config/); - assert.match(prompt, /Load makers-storage, makers-agents, or makers-cloud-functions only when/); - // And naming a framework here would put the choice of template in the prompt - // rather than in the manifest, which is the drift this file exists to catch. - assert.doesNotMatch(prompt, /templateApplied[^.]*(?:Next|Vite|Nuxt|Astro)/); + assert.match(prompt, /host has already prepared an empty project directory/); + assert.match(prompt, /The workspace has no files yet/); + assert.match(prompt, /load_makers_skill is the first tool of a new project/); + assert.doesNotMatch(prompt, /ensure_project_scaffold/); + assert.doesNotMatch(prompt, /templateApplied/); + assert.match(prompt, /When the request names a framework, the reference loaded in step 1 gives its scaffold command under Scaffold/); }); // The failure mode of sourcing the command from the references: read as a list diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index c98accc..86f8daf 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -32,7 +32,7 @@ test('session is GET restore; turns go through /prompt and /deploy', async () => assert.match(deploy, /onRequestPost/); assert.match(deploy, /kind: 'deploy'/); assert.match(tasks, /export async function\* iterateLiveChatTaskEvents/); - assert.match(client, /fetch\('\/session',[\s\S]*?method: 'GET'/); + assert.match(client, /fetch\(`\/session/); assert.match(client, /fetch\('\/prompt',[\s\S]*?method: 'POST'/); assert.match(client, /fetch\('\/deploy',[\s\S]*?method: 'POST'/); assert.doesNotMatch(client, /fetch\('\/session-model'/); @@ -60,9 +60,10 @@ test('initial session restore is one progressive SSE request that can attach a l assert.match(route, /createProjectResumeStreamResponse/); assert.match(pipeline, /type: 'resume_history'/); assert.match(pipeline, /type: 'resume_workspace'/); + assert.match(pipeline, /sessionPrepSse/); assert.match(pipeline, /iterateLiveChatTaskEvents/); assert.doesNotMatch(pipeline, /streamUrl: `\/chat\?runId=/); - assert.match(client, /fetch\('\/session',[\s\S]*?method: 'GET'/); + assert.match(client, /fetch\(`\/session/); }); test('the session tab reads the raw JSONL transcript and does not project it', async () => { diff --git a/tests/scaffold.test.ts b/tests/scaffold.test.ts index 1215e8a..51f7ba7 100644 --- a/tests/scaffold.test.ts +++ b/tests/scaffold.test.ts @@ -6,20 +6,11 @@ import os from 'node:os'; import path from 'node:path'; import { promisify } from 'node:util'; import test from 'node:test'; -import { - ensureProjectScaffold, - repairNestedAppDirLayout, -} from '../agents/_lib/project/scaffold.ts'; +import { repairNestedAppDirLayout } from '../agents/_lib/project/scaffold.ts'; import { projectState } from './helpers/fixtures.ts'; const execFileAsync = promisify(execFile); -/** - * The workspace as the sandbox presents it: project paths are relative to one - * root, which is what makes the nested-layout marker `appDir/appDir` mean - * anything. An absolute appDir would make that test match the app directory - * itself and the repair would fire on every healthy project. - */ async function scaffoldFixture(files: Record = {}) { const root = await mkdtemp(path.join(os.tmpdir(), 'makers-scaffold-')); const state = projectState('projects/demo'); @@ -48,11 +39,6 @@ async function scaffoldFixture(files: Record = {}) { }, }, commands: { - // Raises on a non-zero exit rather than reporting one, which is what - // the platform does: `commands.run` rejects with SANDBOX_UNKNOWN_ERROR - // and the exit status in the message. A fake that returned an exitCode - // instead made every `exitCode !== 0` branch in this file look tested - // while none of them could ever run in production. run: async (command: string, options: { cwd?: string } = {}) => { calls.commands.push(command); const { stdout, stderr } = await execFileAsync('sh', ['-c', command], { @@ -73,127 +59,6 @@ async function scaffoldFixture(files: Record = {}) { }; } -test('an empty workspace scaffolds and reports itself as new', async () => { - const fixture = await scaffoldFixture(); - try { - const outcome = await ensureProjectScaffold(fixture.context, fixture.state); - - assert.equal(outcome.created, true); - assert.equal(outcome.dependenciesInstalled, false); - assert.equal(outcome.template, undefined); - // Left empty because this call named no framework, and the baked trees - // come back with it: that is the only way the caller learns the workspace - // could have been filled. Without it a chat-assistant request resolved to - // nothing and the model wrote a project by hand beside a baked chat agent. - assert.ok(outcome.available?.includes('deepagents'), 'the baked trees went unreported'); - - assert.equal(fixture.exists('projects/demo/app'), true); - } finally { - await fixture.cleanup(); - } -}); - -// One conversation maps to one long-lived project, so a second turn must not -// read as a fresh one. -test('a workspace with files is reused rather than reported as new', async () => { - const fixture = await scaffoldFixture({ - 'projects/demo/app/package.json': '{"name":"demo"}', - }); - try { - assert.deepEqual(await ensureProjectScaffold(fixture.context, fixture.state), { - created: false, - dependenciesInstalled: false, - }); - } finally { - await fixture.cleanup(); - } -}); - -// The listing prunes node_modules, so this is the only thing standing between -// the model and an `npm install` over a tree that is already installed. On a -// 1.1G sandbox that install does not fit: it fills the disk, dies partway, and -// leaves the tree it overwrote unusable. -test('a workspace whose dependencies are installed says so', async () => { - const fixture = await scaffoldFixture({ - 'projects/demo/app/package.json': '{"name":"demo"}', - 'projects/demo/app/node_modules/.bin/next': '#!/bin/sh\n', - }); - try { - const outcome = await ensureProjectScaffold(fixture.context, fixture.state); - - assert.deepEqual(outcome, { created: false, dependenciesInstalled: true }); - } finally { - await fixture.cleanup(); - } -}); - -// The state that has to read as "not installed": npm empties .bin early and -// refills it at the end, so a tree left behind by an install that ran out of -// disk is a directory full of packages with nothing runnable in it. -test('a half-installed tree does not count as installed', async () => { - const fixture = await scaffoldFixture({ - 'projects/demo/app/package.json': '{"name":"demo"}', - 'projects/demo/app/node_modules/next/package.json': '{"name":"next"}', - }); - try { - const outcome = await ensureProjectScaffold(fixture.context, fixture.state); - - assert.equal(outcome.dependenciesInstalled, false); - } finally { - await fixture.cleanup(); - } -}); - -// node_modules is pruned from the listing because a populated tree is hundreds -// of thousands of paths, and the marker must not leak into it either: a -// workspace holding nothing but dependencies is still an empty project. -test('neither the dependency tree nor its marker counts as a project file', async () => { - const fixture = await scaffoldFixture({ - 'projects/demo/app/node_modules/.bin/next': '#!/bin/sh\n', - 'projects/demo/app/node_modules/next/package.json': '{"name":"next"}', - }); - try { - const outcome = await ensureProjectScaffold(fixture.context, fixture.state); - - assert.equal(outcome.created, true); - assert.equal(outcome.dependenciesInstalled, true); - // The point of the case: dependencies alone leave it an empty project, so - // nothing was laid down over them. `available` also comes back here — this - // call named no framework — and is asserted where it is the subject, not - // pinned to the baked list, which every new template would change. - assert.equal(outcome.template, undefined); - } finally { - await fixture.cleanup(); - } -}); - -// The scaffold is the first tool of every turn, so each round trip it makes is -// paid on every turn. One was buying nothing: a recursive create of a directory -// the next call created anyway. The repair's existence probe is not in that -// category — it is what keeps the repair command from running at all, which is -// the cheaper and the safer of the two. -test('the scaffold makes one directory and only probes for the nested layout', async () => { - const fixture = await scaffoldFixture(); - try { - await ensureProjectScaffold(fixture.context, fixture.state); - - assert.equal(fixture.calls.makeDir, 1); - assert.equal(fixture.calls.exists, 1); - assert.equal( - fixture.calls.commands.some((command) => command.includes('NESTED=')), - false, - 'a project with no nested layout should never run the repair script', - ); - } finally { - await fixture.cleanup(); - } -}); - -// What took the failure to production: the repair is opportunistic, but a -// sandbox that raises on it took the whole turn down with it — and because it -// raises instead of reporting an exit code, the function's own `exitCode !== 0` -// branch could not absorb anything. This is the first tool of every -// conversation, so that failure was the first thing every user saw. test('a sandbox that fails the repair command does not fail the turn', async () => { const fixture = await scaffoldFixture({ 'projects/demo/app/projects/demo/app/package.json': '{"name":"nested"}', @@ -211,14 +76,11 @@ test('a sandbox that fails the repair command does not fail the turn', async () try { assert.equal(await repairNestedAppDirLayout(fixture.context, fixture.state), false); - assert.equal((await ensureProjectScaffold(fixture.context, fixture.state)).created, true); } finally { await fixture.cleanup(); } }); -// Models used to pass appDir-prefixed paths to write_project_file, which joined -// appDir again. The repair is what brings that tree back to the project root. test('a nested app directory is lifted back to the project root', async () => { const fixture = await scaffoldFixture({ 'projects/demo/app/projects/demo/app/package.json': '{"name":"nested"}', @@ -247,8 +109,6 @@ test('a healthy project is left untouched by the repair', async () => { } }); -// The guard that matters most: a nested directory that is not a project must -// not have its contents hoisted over the real one. test('a nested directory with no project in it is not lifted', async () => { const fixture = await scaffoldFixture({ 'projects/demo/app/package.json': '{"name":"real"}', diff --git a/tests/session-prep.test.ts b/tests/session-prep.test.ts new file mode 100644 index 0000000..ee56073 --- /dev/null +++ b/tests/session-prep.test.ts @@ -0,0 +1,188 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import test from 'node:test'; +import { createMemoryBlobStore } from '../agents/_lib/session/store.ts'; +import { + iterateConversationPrep, + iterateSandboxAndAgentPrep, + persistConversationPreferences, + prepareSandboxWorkspace, + sessionPrepSse, +} from '../agents/_lib/session/prepare.ts'; +import { getConversationRecord } from '../agents/_lib/session/store.ts'; +import { sseEvent } from '../agents/_lib/runtime/sse.ts'; +import type { AgentContext } from '../agents/_lib/runtime/context.ts'; + +function fakeContext() { + const blobStore = createMemoryBlobStore(); + const made: string[] = []; + return { + context: { + blobStore, + sandbox: { + extendTimeout: async () => {}, + files: { + makeDir: async (target: string) => { + made.push(target); + }, + exists: async () => false, + }, + }, + } as unknown as AgentContext, + made, + }; +} + +test('session_prep events name the stage and status, not a user-facing sentence', () => { + const payload = sessionPrepSse('create', 'sandbox', 'running'); + assert.match(payload, /"type":"session_prep"/); + assert.match(payload, /"mode":"create"/); + assert.match(payload, /"stage":"sandbox"/); + assert.match(payload, /"status":"running"/); + assert.equal(payload, sseEvent({ + type: 'session_prep', + data: { mode: 'create', stage: 'sandbox', status: 'running' }, + })); +}); + +test('conversation prep persists model and language on a brand-new cid', async () => { + const { context } = fakeContext(); + await persistConversationPreferences(context, 'cid-new', { + model: 'kimi-k2.6', + language: 'zh', + }); + const record = await getConversationRecord(context, 'cid-new'); + assert.equal(record.modelPreference, 'kimi-k2.6'); + assert.equal(record.languagePreference, 'zh'); + assert.equal(record.projectState.created, false); + assert.match(record.projectState.appDir, /projects\/cid-new\/app/); +}); + +test('sandbox prep extends the timeout and creates the empty app directory', async () => { + const fixture = fakeContext(); + const state = await prepareSandboxWorkspace(fixture.context, 'cid-sandbox'); + assert.deepEqual(fixture.made, [state.sessionDir, state.appDir]); + assert.equal(state.created, false); +}); + +test('conversation prep streams before the sandbox and agent start', async () => { + const fixture = fakeContext(); + const events: string[] = []; + for await (const chunk of iterateConversationPrep(fixture.context, 'cid-flow', { + mode: 'create', + model: 'kimi-k2.6', + language: 'en', + })) { + events.push(chunk); + } + + const stages = events.map((chunk) => { + const line = chunk.replace(/^data: /, '').trim(); + return JSON.parse(line) as { + type: string; + data?: { stage?: string; status?: string; mode?: string }; + }; + }); + assert.deepEqual( + stages.map((event) => `${event.data?.stage}:${event.data?.status}`), + ['conversation:running', 'conversation:done'], + ); + assert.equal(stages[0]?.data?.mode, 'create'); + const record = await getConversationRecord(fixture.context, 'cid-flow'); + assert.equal(record.languagePreference, 'en'); + assert.equal(record.modelPreference, 'kimi-k2.6'); +}); + +test('sandbox activation and CLI warmup run together and both finish', async () => { + const fixture = fakeContext(); + const events: string[] = []; + for await (const chunk of iterateSandboxAndAgentPrep(fixture.context, 'cid-parallel', { + mode: 'create', + isNewProject: true, + })) { + events.push(chunk); + } + + const stages = events.map((chunk) => { + const line = chunk.replace(/^data: /, '').trim(); + return JSON.parse(line) as { + data?: { stage?: string; status?: string }; + }; + }); + const labels = stages.map((event) => `${event.data?.stage}:${event.data?.status}`); + assert.ok(labels.includes('sandbox:running')); + assert.ok(labels.includes('sandbox:done')); + assert.ok(labels.includes('agent:running')); + assert.ok(labels.includes('agent:done') || labels.includes('agent:failed')); + assert.ok( + labels.indexOf('sandbox:running') < labels.indexOf('sandbox:done'), + 'sandbox must finish after it starts', + ); + const agentDone = labels.findIndex((label) => label === 'agent:done' || label === 'agent:failed'); + assert.ok(labels.indexOf('agent:running') < agentDone); + assert.ok(fixture.made.length >= 2, 'sandbox directories are created during the parallel warmup'); +}); + +test('warmLiveQuery reuses a live process and recycles one that never starts a turn', async () => { + const live = await readFile('agents/_lib/session/live.ts', 'utf8'); + assert.match(live, /export async function warmLiveQuery/); + assert.match(live, /liveQueries\.get\(options\.conversationId\)/); + assert.match(live, /scheduleIdleClose/); + assert.match(live, /LIVE_QUERY_IDLE_MS/); + assert.match(live, /WARM_LIVE_QUERY_BUDGET_MS/); + assert.doesNotMatch(live, /SCAFFOLD_TOOL_NAME/); + assert.doesNotMatch(live, /onWorkspaceReady/); +}); + +test('GET /session streams prep stages before history on restore, and only prep on create', async () => { + const resume = await readFile('agents/_lib/session/resume.ts', 'utf8'); + assert.match(resume, /iterateConversationPrep/); + assert.match(resume, /mode === 'create'/); + assert.match(resume, /iterateSandboxAndAgentPrep/); + assert.match(resume, /sessionPrepSse\(mode, 'ready', 'done'\)/); + assert.match(resume, /type: 'resume_history'/); + const createBranch = resume.slice(resume.indexOf("if (mode === 'create')")); + const historyCall = resume.indexOf('loadProjectResumeHistory'); + const warmupInCreate = createBranch.indexOf('iterateSandboxAndAgentPrep'); + assert.ok(warmupInCreate >= 0); + assert.ok(historyCall > resume.indexOf('iterateConversationPrep')); + const readyInCreate = createBranch.indexOf("sessionPrepSse(mode, 'ready', 'done')"); + assert.ok(readyInCreate > warmupInCreate); + const prepare = await readFile('agents/_lib/session/prepare.ts', 'utf8'); + assert.match(prepare, /mergeSseGenerators\(\[\s*iterateSandboxPrepEvents/); + assert.match(prepare, /iterateAgentWarmupEvents/); +}); + +test('the prompt no longer tells the model to prepare the environment', async () => { + const prompt = await readFile('agents/_lib/prompt.ts', 'utf8'); + assert.doesNotMatch(prompt, /ensure_project_scaffold/); + assert.match(prompt, /load_makers_skill as the first tool/); + assert.match(prompt, /host has already prepared an empty/); +}); + +test('frontend copy names each session prep stage in both languages', async () => { + const i18n = await readFile('app/i18n.ts', 'utf8'); + for (const stage of ['conversation', 'sandbox', 'agent', 'workspace', 'preview', 'ready']) { + assert.match(i18n, new RegExp(`${stage}: '`)); + } + assert.match(i18n, /正在创建会话/); + assert.match(i18n, /Creating the conversation/); + assert.match(i18n, /正在唤醒编码代理/); + assert.match(i18n, /Waking the coding agent/); +}); + +test('the workspace consumes session_prep instead of draining the stream', async () => { + const [api, liveTurn, resume] = await Promise.all([ + readFile('app/features/workspace/workspace-api.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-session-resume.ts', 'utf8'), + ]); + assert.match(api, /params\.set\('model'/); + assert.match(api, /params\.set\('language'/); + assert.match(api, /params\.set\('mode'/); + assert.match(liveTurn, /mode: 'create'/); + assert.match(liveTurn, /sessionPrepToChatEvents/); + assert.doesNotMatch(liveTurn, /consumeEventStream\(resumeResponse, \(\) => \{\}\)/); + assert.match(resume, /mode: 'restore'/); + assert.match(resume, /setPrepStage\(event\.data\.stage\)/); +}); diff --git a/tests/tool-activity.test.ts b/tests/tool-activity.test.ts index 2a44d2f..5b2c683 100644 --- a/tests/tool-activity.test.ts +++ b/tests/tool-activity.test.ts @@ -23,6 +23,15 @@ test('direct Makers CLI dev and deploy commands have distinct actions', () => { assert.equal(dev.action, 'Create preview'); }); +test('environment prep rows keep the stage as the target', () => { + const presentation = presentToolActivity({ + name: 'environment', + inputSummary: '正在启动沙箱…', + }); + assert.equal(presentation.action, 'Environment Preparing'); + assert.equal(presentation.target, '正在启动沙箱…'); +}); + test('npm run build is a run command', () => { const build = presentToolActivity({ name: 'mcp__edgeone-sandbox__commands', From a23c2bec3312911200804fe711eb1fe333c78a0a Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 16:29:37 +0800 Subject: [PATCH 12/26] feat(workspace): collect the Models API key without stopping generation The host shows the BYOK card as soon as an AI project is detected, writes .env without a coding turn, and drops the request_gateway_credentials tool. --- agents/_lib/project/gateway.ts | 140 +++------- agents/_lib/project/preview.ts | 48 ++-- agents/_lib/prompt.ts | 10 +- agents/_lib/session/gateway-apply.ts | 126 +++++++++ agents/_lib/session/live-workspace.ts | 29 ++ agents/_lib/session/resume.ts | 2 + agents/_lib/session/task.ts | 3 + agents/_lib/tools/assemble.ts | 19 +- agents/_lib/tools/commands-wrap.ts | 43 ++- agents/_lib/tools/makers-skills.ts | 15 +- agents/_lib/tools/project-tools.ts | 18 +- agents/_lib/turn/chat.ts | 48 +--- agents/_lib/turn/checkpoint.ts | 1 - agents/_lib/turn/deploy.ts | 6 +- agents/prompt.ts | 11 +- app/components/agent-conversation.tsx | 24 +- .../components/session-prep-loading.tsx | 58 ++++ app/features/workspace/hooks/use-live-turn.ts | 178 ++++++------ .../workspace/hooks/use-session-resume.ts | 24 +- .../workspace/hooks/use-workspace-state.ts | 12 + .../workspace/session-prep-progress.ts | 14 + app/features/workspace/workspace-api.ts | 21 +- app/features/workspace/workspace-screen.tsx | 50 ++-- app/i18n.ts | 28 +- app/styles/conversation.css | 34 +++ app/styles/workspace.css | 74 +++++ shared/protocol.ts | 2 + shared/user-facing-reply.ts | 14 +- tests/deploy-task.test.ts | 6 +- tests/gateway-prompt.test.ts | 257 ++++++++++++------ tests/prompt-single-source.test.ts | 4 +- tests/route-consolidation.test.ts | 2 +- tests/session-prep.test.ts | 39 ++- tests/user-facing-reply.test.ts | 13 +- 34 files changed, 939 insertions(+), 434 deletions(-) create mode 100644 agents/_lib/session/gateway-apply.ts create mode 100644 agents/_lib/session/live-workspace.ts create mode 100644 app/features/workspace/components/session-prep-loading.tsx create mode 100644 app/features/workspace/session-prep-progress.ts diff --git a/agents/_lib/project/gateway.ts b/agents/_lib/project/gateway.ts index 0ad2682..c0d53f7 100644 --- a/agents/_lib/project/gateway.ts +++ b/agents/_lib/project/gateway.ts @@ -1,17 +1,14 @@ /** * Models API key collection for a generated AI project. * - * `.env.example` declares the names. The agent checks before preview or deploy - * and asks the user when `.env` has no key. The host shows the input card; the - * next user turn carries the key (masked in the transcript) or a skip, and - * this module writes `.env` so the CLI can load it. + * `.env.example` declares the names. The host shows the input card as soon as + * it sees an AI project. Generation and preview keep going; submitting a key + * writes `.env` without opening a coding-agent turn. */ -import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; import { persistWorkspace, setGatewayPending, setGatewaySkipped } from './workspace-store.ts'; import { requireSandbox, type AgentContext, type SandboxCapable } from '../runtime/context.ts'; -import type { ClaudeMcpTool, ProjectState, StreamSend } from '../types.ts'; -import { stringifyToolResult } from '../utils/text.ts'; +import type { ProjectState, StreamSend } from '../types.ts'; import { getFileTree } from './fs.ts'; import { AGENT_GATEWAY_ENV_KEYS } from '../makers/declarations.ts'; @@ -29,20 +26,13 @@ export function gatewayBaseUrlForAgentFramework(framework?: string | null) { return framework === 'claude-agent-sdk' ? AI_GATEWAY_ORIGIN : DEFAULT_AI_GATEWAY_BASE_URL; } -export const REQUEST_GATEWAY_CREDENTIALS_TOOL = 'request_gateway_credentials'; - export const GATEWAY_CREDENTIALS_PAUSE_MESSAGE = [ 'AI_GATEWAY_API_KEY is not set in the project .env.', 'The user has been shown the API key input card.', - 'End this turn now. Do not run preview or deploy, and do not call this again.', - 'A later turn will continue after they provide a key or skip.', + 'Do not run edgeone makers deploy until they provide a key or skip.', + 'A missing key is not a preview failure, but a live publish still needs the card answered.', ].join(' '); -export function isRequestGatewayCredentialsTool(name: string) { - return name === REQUEST_GATEWAY_CREDENTIALS_TOOL - || name.endsWith(`__${REQUEST_GATEWAY_CREDENTIALS_TOOL}`); -} - export function declaredGatewayKeys(content: string): string[] { return AGENT_GATEWAY_ENV_KEYS.filter((key) => ( new RegExp(`^\\s*(?:export\\s+)?${key}\\s*=`, 'm').test(content) @@ -185,20 +175,49 @@ export type GatewayPromptOptions = { send?: StreamSend; }; +function emitGatewayNeeded(send: StreamSend | undefined) { + send?.({ + type: 'gateway_credentials', + data: { + status: 'needed', + keys: [...AGENT_GATEWAY_ENV_KEYS], + }, + }); +} + +function emitGatewayResolved(send: StreamSend | undefined, skipped = false) { + send?.({ + type: 'gateway_credentials', + data: { + status: 'resolved', + ...(skipped ? { skipped: true } : {}), + }, + }); +} + export async function askUserForGatewayCredentials( context: AgentContext, state: ProjectState, options: GatewayPromptOptions = {}, ) { + if (state.gatewaySkipped) return; + if (await sandboxGatewayKeyIsSet(context, state)) return; + if (state.gatewayPromptPending) { + emitGatewayNeeded(options.send); + return; + } setGatewayPending(state, true); await persistGatewayState(context, options.conversationId || '', state); - options.send?.({ - type: 'gateway_credentials', - data: { - status: 'needed', - keys: [...AGENT_GATEWAY_ENV_KEYS], - }, - }); + emitGatewayNeeded(options.send); +} + +export function writeSuggestsAiGatewayProject(relPath: string, content: string) { + const path = relPath.replace(/^\.?\//, ''); + if (path === 'agents' || path.startsWith('agents/')) return true; + if (path === '.env.example' || path.endsWith('/.env.example')) { + return declaredGatewayKeys(content).length > 0; + } + return false; } export async function shouldPauseForGatewayCredentials( @@ -244,6 +263,7 @@ export async function applyUserGatewayDecision( if (decision.skip) { setGatewaySkipped(state, true); await persistGatewayState(context, conversationId, state); + emitGatewayResolved(send, true); return {}; } @@ -261,78 +281,6 @@ export async function applyUserGatewayDecision( setGatewaySkipped(state, false); await persistGatewayState(context, conversationId, state); await publishFileTreeAfterEnvWrite(context, state, send); + emitGatewayResolved(send); return values; } - -export function buildRequestGatewayCredentialsTool(options: { - context: AgentContext; - state: ProjectState; - conversationId?: string; - send?: StreamSend; -}): ClaudeMcpTool { - const { context, state, conversationId, send } = options; - return defineClaudeTool( - REQUEST_GATEWAY_CREDENTIALS_TOOL, - [ - 'Before preview or deploy of an AI project, check whether .env has a non-empty AI_GATEWAY_API_KEY.', - 'If the key is already set, not required, or the user already skipped, continue with preview or deploy.', - 'If the key is missing, this shows the user the API key input card and you must end the turn.', - 'Do not run edgeone makers dest or deploy after this tool says the user has been asked.', - ].join(' '), - {}, - async () => { - if (state.gatewaySkipped) { - return { - content: [{ - type: 'text' as const, - text: stringifyToolResult({ - needed: true, - configured: false, - skipped: true, - instruction: 'The user already skipped the API key. Continue preview or deploy without writing .env. A missing key is not a preview or deploy failure.', - }), - }], - }; - } - - const needed = await projectNeedsGatewayKey(context, state); - if (!needed) { - return { - content: [{ - type: 'text' as const, - text: stringifyToolResult({ - needed: false, - instruction: 'This project does not need a Models API key. Continue.', - }), - }], - }; - } - - if (await sandboxGatewayKeyIsSet(context, state)) { - return { - content: [{ - type: 'text' as const, - text: stringifyToolResult({ - needed: true, - configured: true, - instruction: 'AI_GATEWAY_API_KEY is already set. Continue preview or deploy. Do not quote the value.', - }), - }], - }; - } - - await askUserForGatewayCredentials(context, state, { conversationId, send }); - return { - content: [{ - type: 'text' as const, - text: stringifyToolResult({ - needed: true, - configured: false, - askedUser: true, - instruction: GATEWAY_CREDENTIALS_PAUSE_MESSAGE, - }), - }], - }; - }, - ) as ClaudeMcpTool; -} diff --git a/agents/_lib/project/preview.ts b/agents/_lib/project/preview.ts index dc4447d..7947506 100644 --- a/agents/_lib/project/preview.ts +++ b/agents/_lib/project/preview.ts @@ -115,33 +115,35 @@ export function rewritePreviewAccessToken(existingUrl: string, token: string) { export async function startPreviewServer( context: AgentContext, state: ProjectState, - options: { verifyRoutes?: boolean } = {}, + options: { verifyRoutes?: boolean; forceRestart?: boolean } = {}, ) { const verifyRoutes = options.verifyRoutes !== false; await assertMakersProjectCompatible(context, state); const projectName = resolveMakersProjectName(context, state); const area = resolveConversationPublishArea(state); const launchCommand = buildMakersDevLaunchCommand(MAKERS_DEV_PORT, projectName, { area }); - let forceRestart = false; + let forceRestart = options.forceRestart === true; // makers-dev watches project files. On resume, keep a healthy process rather // than starting a second CLI instance on the same port. - const warm = await runCommandCapturingExit( - context, - probePreviewReadyCommand(), - { timeout: 5 }, - ); - if (warm.exitCode === 0) { - try { - if (verifyRoutes) { - await assertGeneratedRoutesReady(context, state); + if (!forceRestart) { + const warm = await runCommandCapturingExit( + context, + probePreviewReadyCommand(), + { timeout: 5 }, + ); + if (warm.exitCode === 0) { + try { + if (verifyRoutes) { + await assertGeneratedRoutesReady(context, state); + } + return previewServerInfo(launchCommand); + } catch (error) { + // A warm port that fails to answer is a stale server, not a preview. One + // that answers wrongly is a code bug the restart would only delay. + if (!previewFailureWarrantsRestart(error)) throw error; + forceRestart = true; } - return previewServerInfo(launchCommand); - } catch (error) { - // A warm port that fails to answer is a stale server, not a preview. One - // that answers wrongly is a code bug the restart would only delay. - if (!previewFailureWarrantsRestart(error)) throw error; - forceRestart = true; } } @@ -456,6 +458,18 @@ export async function publishRunningPreview( }; } +export async function isPreviewServerReady( + context: AgentContext, + readyPath = PREVIEW_PATH_PREFIX, +) { + const result = await runCommandCapturingExit( + context, + probePreviewReadyCommand(readyPath), + { timeout: 5 }, + ); + return result.exitCode === 0; +} + export async function assertPreviewServerReady( context: AgentContext, readyPath = PREVIEW_PATH_PREFIX, diff --git a/agents/_lib/prompt.ts b/agents/_lib/prompt.ts index b6b9991..73dcf7a 100644 --- a/agents/_lib/prompt.ts +++ b/agents/_lib/prompt.ts @@ -134,8 +134,8 @@ function buildSandboxPreview(appDir: string, makersProjectName: string, area: st `Only when the user explicitly asks for a live deployment, run edgeone makers deploy --json once through commands with cwd=${appDir}. This conversation publishes to ${quotedProjectName} with --area ${publishArea}. The host supplies credentials, pins the project this conversation publishes to, allows the long timeout, parses the final JSON line, and renders the result in its own deployment card.`, 'Never pass -n, invent a project name, or retry a failed deploy under a different one: the name identifies the user\'s site, and a deploy under a name you chose publishes somewhere nobody can find again. A deployment never replaces the right-hand preview, so do not tell the user their live site opened there.', 'Declare AI_GATEWAY_API_KEY= and AI_GATEWAY_BASE_URL= in .env.example when the project calls a model. Never write a .env file yourself, and never write an actual API key or gateway URL value into source. Generated agents read them from context.env.', - 'Before preview or deploy of an AI project — one that declares those keys in .env.example, or that has an agents/ directory — call request_gateway_credentials. If the result says the key is already configured, not required, or previously skipped, continue. If it says the user has been asked, stop this turn: do not start a preview or run edgeone makers deploy, and do not call the tool again. The host shows the input card. Your last user-facing sentence must ask them to enter the key or skip; do not say the preview is ready.', - 'The user may type a key in the composer in natural language, for example "我的 apikey 是 …,配置好并重新预览", or submit the input card. The host extracts it, writes .env, and the message you see is a masked API Key line — or a skip. After a provided key the host has written .env; after a skip, preview and deploy must still run — a missing key is not a preview or deploy failure. Chat in the generated app may not answer until a key is added later. Never write .env yourself and never quote an API key value, from a file or from the user.', + 'The host collects a Models API key for generated AI projects as soon as it sees one. If you load makers-agents or write agents/ files, the host shows the input card while you keep working. Do not stop this turn, do not wait for the key, and do not say the preview is blocked. Continue writing files and let the host start preview. A missing key is not a preview or deploy failure — chat in the generated app may not answer until a key is added. Never write .env yourself and never quote an API key value, from a file or from the user.', + 'The user may type a key in the composer in natural language, for example "我的 apikey 是 …,配置好并重新预览". The host extracts it, writes .env, and the message you see is a masked API Key line. Never write .env yourself and never quote an API key value.', 'The host writes AI_GATEWAY_BASE_URL already shaped for OpenAI-compatible clients. Use that value through the generated env helper; never probe, enumerate, or retry alternate gateway paths, and never concatenate /v1/chat/completions onto the base.', ]; } @@ -216,14 +216,14 @@ function buildNewProjectWorkflow(appDir: string) { '3. After the required references are loaded, write the project with write_project_file, one complete file per call and in dependency order. When a scaffolder ran, keep what it produced and use these calls to adapt it — the platform declarations and the entry route — rather than rewriting files it already got right. If agents/chat.ts is already in the workspace, edit that file; do not also write agents/chat/index.ts — both mount POST /chat. Otherwise write configuration and dependencies first, then styles and small modules, then the entry HTML, then any platform function or agent directories. Dependencies come before agent code specifically: the platform declarations an agent project needs are derived from the packages it declares, so a dependency file that arrives later cannot inform them.', `4. The host starts npm install in the background the moment package.json is written. When you run npm install yourself, that command waits for the background install and reports its result — it does not install twice. Run npm install inside ${appDir} only when the project has a package.json with dependencies that are not yet on disk (cd ${appDir} && npm install by default; Python packages are declared in the project's requirements file and installed by the platform). Do not invent nested ${appDir}/${appDir} paths.`, 'Take every dependency name and version range from the reference you loaded for that framework, and copy its dependency block as written. Versions recalled from memory are the usual cause of peer-dependency conflicts and engine mismatches, and each one costs a rewrite plus a reinstall. If a reference pins a version or caps a range, keep the pin instead of widening it to latest.', - '5. Check gateway credentials as the preview section requires, then stop. The host starts the sandbox preview. Do not curl/fetch/code_interpreter the public URL and do not start a preview server.', + '5. The host starts the sandbox preview. Do not curl/fetch/code_interpreter the public URL and do not start a preview server.', ]; } function buildExistingProjectWorkflow(appDir: string) { return [ `When ${appDir} already contains project files, load only the specific Makers references required by the change with load_makers_skill, inspect only the project files directly related to the request, then make the smallest complete change needed.`, - 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command, then check gateway credentials as the preview section requires. The host starts the sandbox preview.', + 'For bug reports, do not investigate platform internals, generated .edgeone files, running processes, ports, or external AI gateway behavior. Use at most one focused reproduction command before editing; after the edit, use at most one focused verification command. The host starts the sandbox preview.', ]; } @@ -277,7 +277,7 @@ const FINAL_REPLY = [ // page back every time, and reported the feature working. An HTML body from a // POST to a streaming endpoint is the static site answering in its place. 'An HTML document is not a verified endpoint. When a probe of a project API answers with a page instead of the response that endpoint defines, the request never reached the handler at all — that is a failure to report, not a result to read a meaning into, and never grounds for saying the feature works.', - 'After code changes, check gateway credentials as the preview section requires. The host starts the sandbox preview. Do not synthesize preview URLs. Run edgeone makers deploy only when the user explicitly asks to publish a live Makers URL.', + 'After code changes, the host starts the sandbox preview. Do not synthesize preview URLs. Run edgeone makers deploy only when the user explicitly asks to publish a live Makers URL.', 'Do not include preview buttons, preview links, preview URLs, or sandboxDebugUrl in the final response. The sandbox preview is shown only in the right preview panel.', 'A live deployment is the exception: when edgeone makers deploy succeeds, state that the site is live and write its complete URL, query string included, on its own line in the final response. That address is the deliverable and the user has to be able to copy it out of the conversation.', 'Do not take screenshots.', diff --git a/agents/_lib/session/gateway-apply.ts b/agents/_lib/session/gateway-apply.ts new file mode 100644 index 0000000..bd004a3 --- /dev/null +++ b/agents/_lib/session/gateway-apply.ts @@ -0,0 +1,126 @@ +import type { AgentContext } from '../runtime/context.ts'; +import { applyUserGatewayDecision } from '../project/gateway.ts'; +import { + isPreviewServerReady, + publishRunningPreview, + startPreviewServer, +} from '../project/preview.ts'; +import { persistWorkspace } from '../project/workspace-store.ts'; +import { prepareProjectWorkspace } from '../project/workspace.ts'; +import { getConversationId } from './task.ts'; +import { getLiveWorkspace } from './live-workspace.ts'; +import type { ProjectState, StreamSend } from '../types.ts'; + +function jsonResponse(body: Record, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' }, + }); +} + +async function restartOrStartPreview( + context: AgentContext, + conversationId: string, + state: ProjectState, + send: StreamSend | undefined, + options: { forceRestart: boolean }, +) { + await startPreviewServer(context, state, { + verifyRoutes: false, + forceRestart: options.forceRestart, + }); + const preview = await publishRunningPreview(context, state, { routesAlreadyVerified: true }); + await persistWorkspace(context, conversationId, state); + const payload = { + ...preview, + restarted: options.forceRestart, + }; + send?.({ + type: 'preview_ready', + data: { + preview: payload, + download: { url: '/download', filename: 'source.zip' }, + }, + }); + return payload; +} + +/** + * Write a Models API key or skip without opening a coding-agent turn. + * A live generation keeps running; its SSE gets `gateway_credentials: resolved`. + */ +export async function applyGatewayDecisionAndRespond( + context: AgentContext, + decision: { apiKey?: string; skip?: boolean }, +) { + const conversationId = getConversationId(context); + if (!conversationId) { + return jsonResponse({ + ok: false, + error: 'Missing conversationId. The project workspace cannot be prepared.', + }, 400); + } + + const apiKey = (decision.apiKey || '').trim(); + if (!decision.skip && !apiKey) { + return jsonResponse({ + ok: false, + error: 'Provide an API key or skip.', + }, 400); + } + + try { + const live = getLiveWorkspace(conversationId); + const send = live?.send; + const state = live?.state ?? await prepareProjectWorkspace(context, conversationId, send); + const values = await applyUserGatewayDecision( + context, + state, + conversationId, + { + ...(apiKey ? { apiKey } : {}), + ...(decision.skip ? { skip: true } : {}), + }, + send, + ); + + let preview: Awaited> | undefined; + const destRunning = Boolean(state.previewUrl) || await isPreviewServerReady(context).catch(() => false); + try { + if (live) { + if (!decision.skip && destRunning) { + preview = await restartOrStartPreview(context, conversationId, state, send, { + forceRestart: true, + }); + } + } else if (state.created) { + preview = await restartOrStartPreview(context, conversationId, state, send, { + forceRestart: destRunning && !decision.skip, + }); + } + } catch (error) { + console.warn( + '[gateway] preview after apply failed', + error instanceof Error ? error.message : error, + ); + } + + return jsonResponse({ + ok: true, + conversation_id: conversationId, + applied: true, + live: Boolean(live), + skipped: Boolean(decision.skip), + configured: Boolean(values.AI_GATEWAY_API_KEY), + ...(preview ? { + preview, + download: { url: '/download', filename: 'source.zip' }, + } : {}), + }); + } catch (error) { + return jsonResponse({ + ok: false, + error: error instanceof Error ? error.message : 'Failed to apply the API key.', + }, 500); + } +} diff --git a/agents/_lib/session/live-workspace.ts b/agents/_lib/session/live-workspace.ts new file mode 100644 index 0000000..ed18614 --- /dev/null +++ b/agents/_lib/session/live-workspace.ts @@ -0,0 +1,29 @@ +import type { ProjectState, StreamSend } from '../types.ts'; + +type LiveWorkspaceBinding = { + state: ProjectState; + send?: StreamSend; +}; + +const bindings = new Map(); + +export function bindLiveWorkspace( + conversationId: string, + state: ProjectState, + send?: StreamSend, +) { + const id = conversationId.trim(); + if (!id) return; + bindings.set(id, { state, send }); +} + +export function unbindLiveWorkspace(conversationId: string) { + const id = conversationId.trim(); + if (!id) return; + bindings.delete(id); +} + +export function getLiveWorkspace(conversationId: string): LiveWorkspaceBinding | undefined { + const id = conversationId.trim(); + return id ? bindings.get(id) : undefined; +} diff --git a/agents/_lib/session/resume.ts b/agents/_lib/session/resume.ts index b60c4a7..c70ee8f 100644 --- a/agents/_lib/session/resume.ts +++ b/agents/_lib/session/resume.ts @@ -130,6 +130,7 @@ async function loadProjectResumeHistory(context: AgentContext, conversationId: s model, language: language || undefined, gatewayNeeded: state.gatewayPromptPending === true, + gatewaySkipped: state.gatewaySkipped === true, }; } @@ -271,6 +272,7 @@ async function runWorkspaceRestoreBody(context: AgentContext, conversationId: st deployment: state.deployment, files: { root: state.appDir, items }, gatewayNeeded: state.gatewayPromptPending === true, + gatewaySkipped: state.gatewaySkipped === true, ...(hasFileItems ? { download: { url: '/download', filename: 'source.zip' } } : {}), }; } diff --git a/agents/_lib/session/task.ts b/agents/_lib/session/task.ts index 6e3bb69..99d462e 100644 --- a/agents/_lib/session/task.ts +++ b/agents/_lib/session/task.ts @@ -14,6 +14,7 @@ import { createSSEResponse, sseEvent } from '../runtime/sse.ts'; import { resolveConversationId } from '../runtime/request.ts'; import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; import type { ChatStreamEvent } from '../../../shared/protocol.ts'; +import { unbindLiveWorkspace } from './live-workspace.ts'; type SequencedEvent = { sequence: number; @@ -260,6 +261,8 @@ async function executeLiveTask(context: AgentContext, liveTask: LiveChatTask) { publish(liveTask, { type: 'error', error }); finalEvent = { type: 'error', error }; } + } finally { + unbindLiveWorkspace(liveTask.conversationId); } const current = liveTask.task; diff --git a/agents/_lib/tools/assemble.ts b/agents/_lib/tools/assemble.ts index cc519e0..b525f24 100644 --- a/agents/_lib/tools/assemble.ts +++ b/agents/_lib/tools/assemble.ts @@ -1,9 +1,5 @@ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk'; import { SANDBOX_MCP_SERVER_NAME } from '../constants.ts'; -import { - buildRequestGatewayCredentialsTool, - REQUEST_GATEWAY_CREDENTIALS_TOOL, -} from '../project/gateway.ts'; import type { AgentContext } from '../runtime/context.ts'; import type { ClaudeMcpTool, @@ -77,6 +73,12 @@ export function assembleAgentTools(session: LiveSessionHandle) { && !isGenericProjectWriteToolName(name) && (webSearchAvailable || !isWebSearchToolName(name)); + const gatewayPrompt = { + conversationId: session.conversationId, + get send() { + return session.getCallbacks().send; + }, + }; const writeProjectFileTool = buildWriteProjectFileTool( context, session.getState(), @@ -85,6 +87,7 @@ export function assembleAgentTools(session: LiveSessionHandle) { session.flags.filesWritten = true; await session.getCallbacks().onProjectFilesChanged?.({ path: written, content }); }, + gatewayPrompt, ); const sandboxTools = wrapWebSearchTool(wrapSandboxTools( (edgeoneMcp.tools as ClaudeMcpTool[]).filter((tool) => offerSandboxTool(tool.name)), @@ -112,9 +115,7 @@ export function assembleAgentTools(session: LiveSessionHandle) { )); const mcpTools = [ ...sandboxTools, - buildLoadMakersSkillTool(), - writeProjectFileTool, - buildRequestGatewayCredentialsTool({ + buildLoadMakersSkillTool({ context, get state() { return session.getState(); @@ -123,13 +124,13 @@ export function assembleAgentTools(session: LiveSessionHandle) { get send() { return session.getCallbacks().send; }, - } as any), + }), + writeProjectFileTool, ]; const mcpAllowedTools = [ ...edgeoneMcp.allowedTools.filter(offerSandboxTool), `mcp__${mcpServerName}__load_makers_skill`, `mcp__${mcpServerName}__write_project_file`, - `mcp__${mcpServerName}__${REQUEST_GATEWAY_CREDENTIALS_TOOL}`, 'Skill', ]; diff --git a/agents/_lib/tools/commands-wrap.ts b/agents/_lib/tools/commands-wrap.ts index 5bf5b76..f99c0cc 100644 --- a/agents/_lib/tools/commands-wrap.ts +++ b/agents/_lib/tools/commands-wrap.ts @@ -1,7 +1,11 @@ import type { ClaudeMcpTool } from '../types.ts'; import { startPreviewServer } from '../project/preview.ts'; import { assertMakersProjectCompatible } from '../makers/compat/run.ts'; -import { pauseForGatewayCredentialsIfNeeded } from '../project/gateway.ts'; +import { + askUserForGatewayCredentials, + pauseForGatewayCredentialsIfNeeded, + shouldPauseForGatewayCredentials, +} from '../project/gateway.ts'; import { buildEdgeoneVersionCheckCommand, forbiddenSandboxCommandReason, @@ -89,19 +93,30 @@ export function wrapSandboxTools( | undefined; if (lifecycle && isMakersCommand) { try { - const pause = await pauseForGatewayCredentialsIfNeeded( - lifecycle.context, - lifecycle.state, - { - conversationId: lifecycle.conversationId, - send: lifecycle.send, - }, - ); - if (pause) { - return { - content: [{ type: 'text' as const, text: pause }], - isError: true, - }; + if (isDeploymentCommand) { + const pause = await pauseForGatewayCredentialsIfNeeded( + lifecycle.context, + lifecycle.state, + { + conversationId: lifecycle.conversationId, + send: lifecycle.send, + }, + ); + if (pause) { + return { + content: [{ type: 'text' as const, text: pause }], + isError: true, + }; + } + } else if (await shouldPauseForGatewayCredentials(lifecycle.context, lifecycle.state)) { + await askUserForGatewayCredentials( + lifecycle.context, + lifecycle.state, + { + conversationId: lifecycle.conversationId, + send: lifecycle.send, + }, + ); } await assertMakersProjectCompatible(lifecycle.context, lifecycle.state); makers = await prepareMakersCommand(args, command, lifecycle); diff --git a/agents/_lib/tools/makers-skills.ts b/agents/_lib/tools/makers-skills.ts index ebbe50f..87b4514 100644 --- a/agents/_lib/tools/makers-skills.ts +++ b/agents/_lib/tools/makers-skills.ts @@ -2,7 +2,9 @@ import { readdir, readFile } from 'node:fs/promises'; import path from 'node:path'; import { tool as defineClaudeTool } from '@anthropic-ai/claude-agent-sdk'; import { z } from 'zod'; -import type { ClaudeMcpTool } from '../types.ts'; +import { askUserForGatewayCredentials, type GatewayPromptOptions } from '../project/gateway.ts'; +import type { AgentContext } from '../runtime/context.ts'; +import type { ClaudeMcpTool, ProjectState } from '../types.ts'; export const MAKERS_REFERENCE_SKILL_NAMES = [ 'makers-agents', @@ -126,7 +128,10 @@ function formatUnknownReference( ].join('\n'); } -export function buildLoadMakersSkillTool() { +export function buildLoadMakersSkillTool(gateway?: { + context: AgentContext; + state: ProjectState; +} & GatewayPromptOptions) { return defineClaudeTool( 'load_makers_skill', [ @@ -150,6 +155,12 @@ export function buildLoadMakersSkillTool() { try { const skill = makersReferenceSkillSchema.parse(input.skill); const ref = typeof input.ref === 'string' ? input.ref.trim() : ''; + if (skill === 'makers-agents' && gateway) { + await askUserForGatewayCredentials(gateway.context, gateway.state, { + conversationId: gateway.conversationId, + send: gateway.send, + }).catch(() => undefined); + } if (!ref) { const [content, refs] = await Promise.all([ diff --git a/agents/_lib/tools/project-tools.ts b/agents/_lib/tools/project-tools.ts index fa0ce39..1fff7ca 100644 --- a/agents/_lib/tools/project-tools.ts +++ b/agents/_lib/tools/project-tools.ts @@ -7,6 +7,11 @@ import { ensureMakersAgentDeclarations, ensureMakersFrameworkAdapter, } from '../makers/declarations.ts'; +import { + askUserForGatewayCredentials, + writeSuggestsAiGatewayProject, + type GatewayPromptOptions, +} from '../project/gateway.ts'; import type { ClaudeMcpTool, ProjectState } from '../types.ts'; import { getBlockedProjectWriteReason, toAppRelPath } from '../utils/paths.ts'; import { stringifyToolResult } from '../utils/text.ts'; @@ -24,6 +29,7 @@ export function buildWriteProjectFileTool( // The content is handed back so the pipeline can push it straight to the // frontend, which then renders the file without a /file round trip. onResult?: (result: { written: string; content: string }) => void | Promise, + gateway?: GatewayPromptOptions, ) { return defineClaudeTool( 'write_project_file', @@ -55,9 +61,9 @@ export function buildWriteProjectFileTool( await onResult?.({ written: relPath, content: file.content }); // An agents/ project needs agents.framework and .env.example declared, // and meeting that at the preview gate instead costs the user a failed - // attempt. Values for those keys are collected in a later user turn, - // not written here. Best effort: the lint remains the authority, so a - // failure here costs the old behaviour and nothing more. + // attempt. The host collects values for those keys on its own card. + // Best effort: the lint remains the authority, so a failure here costs + // the old behaviour and nothing more. let adapterAdded = false; const declared = relPath.startsWith('agents/') ? await ensureMakersAgentDeclarations(context, state).catch(() => []) @@ -84,6 +90,12 @@ export function buildWriteProjectFileTool( for (const declaration of declared) { await onResult?.({ written: declaration.path, content: declaration.content }); } + if ( + writeSuggestsAiGatewayProject(relPath, file.content) + || declared.some((declaration) => writeSuggestsAiGatewayProject(declaration.path, declaration.content)) + ) { + await askUserForGatewayCredentials(context, state, gateway).catch(() => undefined); + } return { content: [{ type: 'text' as const, diff --git a/agents/_lib/turn/chat.ts b/agents/_lib/turn/chat.ts index 91ce97f..94a4bec 100644 --- a/agents/_lib/turn/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -19,7 +19,6 @@ import { toAppRelPath } from '../utils/paths.ts'; import { sanitizeAssistantText } from '../../../shared/timeline.ts'; import { resolveConversationId, resolveRequestSiteDomain } from '../runtime/request.ts'; import { - GATEWAY_CREDENTIALS_USER_REPLY, compactUserFacingReply, createFileTreePushController, createProjectCheckpointController, @@ -33,12 +32,10 @@ import { withLiveDeploymentUrl, buildRequirementConclusionFallback, } from './checkpoint.ts'; +import { bindLiveWorkspace } from '../session/live-workspace.ts'; import { createTurnLifecycle } from './lifecycle.ts'; import { prepareProjectWorkspace } from '../project/workspace.ts'; -import { - applyUserGatewayDecision, - isRequestGatewayCredentialsTool, -} from '../project/gateway.ts'; +import { applyUserGatewayDecision } from '../project/gateway.ts'; import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; import { runAutoFixTurn } from './auto-fix.ts'; import { sendTurnResult } from './result.ts'; @@ -110,7 +107,7 @@ export async function runChatPipeline( send, ); } - const hiddenToolUseIds = new Set(); + bindLiveWorkspace(conversationId, state, send); const activityTurnId = options.turnId || String(context?.run_id || `${Date.now()}-${Math.random().toString(36).slice(2)}`); @@ -129,16 +126,6 @@ export async function runChatPipeline( const finalizeTurn = turn.finalize; const forwardProgress = (event: AgentProgressEvent) => { - if (event.type === 'tool_use') { - const name = event.data?.name || ''; - if (isRequestGatewayCredentialsTool(name)) { - hiddenToolUseIds.add(event.data?.id || ''); - return; - } - } - if (event.type === 'tool_result' && hiddenToolUseIds.has(event.data?.id || '')) { - return; - } if (event.type === 'text_segment') { const text = state.previewUrl ? stripReturnedPreviewLinks(event.data?.text || '', state.previewUrl) @@ -255,35 +242,6 @@ export async function runChatPipeline( return; } - if (state.gatewayPromptPending) { - const pauseReply = GATEWAY_CREDENTIALS_USER_REPLY[replyLocale]; - send({ - type: 'gateway_credentials', - data: { status: 'needed' }, - }); - send({ - type: 'agent', - data: { - ok: true, - reply: pauseReply, - }, - }); - - if (modelResult.projectTouched) { - await fileTreePush.flush('Failed to read the file list.'); - } - await finalizeTurn(pauseReply, 'completed', { - withSnapshot: false, - }); - sendTurnResult(send, slimResult(conversationId, { - ok: true, - reply: pauseReply, - })); - if (modelResult.projectTouched) { - void checkpoint.flush(); - } - return; - } const sanitizedModelOutput = modelResult.success && modelResult.output ? sanitizeAssistantText(modelResult.output) : ''; diff --git a/agents/_lib/turn/checkpoint.ts b/agents/_lib/turn/checkpoint.ts index 939fbe6..850229b 100644 --- a/agents/_lib/turn/checkpoint.ts +++ b/agents/_lib/turn/checkpoint.ts @@ -4,7 +4,6 @@ import { runSandboxCommand } from '../project/commands.ts'; import type { FileTreeItem, ProjectState, StreamSend } from '../types.ts'; export { compactUserFacingReply, - GATEWAY_CREDENTIALS_USER_REPLY, replyLocaleFor, resolveFinishedTurn, STOPPED_TURN_REPLY, diff --git a/agents/_lib/turn/deploy.ts b/agents/_lib/turn/deploy.ts index d71f1f0..2824e3f 100644 --- a/agents/_lib/turn/deploy.ts +++ b/agents/_lib/turn/deploy.ts @@ -43,6 +43,7 @@ import { } from './checkpoint.ts'; import { createTurnLifecycle } from './lifecycle.ts'; import { prepareProjectWorkspace } from '../project/workspace.ts'; +import { bindLiveWorkspace } from '../session/live-workspace.ts'; import { sendTurnResult } from './result.ts'; /** Used when an API caller asks to publish without wording the request itself. */ @@ -83,14 +84,14 @@ const COPY = { noProject: '还没有可部署的项目,请先生成一个项目。', success: '已发布到线上。', failedPrefix: '部署失败:', - needGateway: '请先在下方填写 Models API Key,填写后我会继续部署。', + needGateway: '请先添加 API 密钥,添加后我会继续部署。', }, en: { missingConversation: 'Missing conversationId, so this project cannot be deployed.', noProject: 'There is no project to deploy yet. Generate one first.', success: 'The project is live.', failedPrefix: 'Deploy failed: ', - needGateway: 'Enter a Models API Key below. I will continue the deploy after that.', + needGateway: 'Add an API key below. I will continue the deploy after that.', }, } as const; @@ -228,6 +229,7 @@ export async function runDeployPipeline( return; } + bindLiveWorkspace(conversationId, state, send); const inboundGateway = resolveGatewayUserTurn(request, options.apiKey); if (inboundGateway.apiKey || options.gatewaySkip) { await applyUserGatewayDecision( diff --git a/agents/prompt.ts b/agents/prompt.ts index 7b30ff8..9c9dc8e 100644 --- a/agents/prompt.ts +++ b/agents/prompt.ts @@ -1,4 +1,5 @@ import type { AgentContext } from './_lib/runtime/context.ts'; +import { applyGatewayDecisionAndRespond } from './_lib/session/gateway-apply.ts'; import { createChatTaskAndStreamResponse } from './_lib/session/task.ts'; import { resolveRequestedModel } from './_lib/models.ts'; import { getRequestBody } from './_lib/runtime/request.ts'; @@ -7,6 +8,14 @@ import { getRequestBody } from './_lib/runtime/request.ts'; export async function onRequestPost(context: AgentContext) { const body = getRequestBody(context); const message = String(body.message || '').trim(); + const apiKey = String(body.apiKey || '').trim(); + const gatewaySkip = body.gatewaySkip === true; + if (!message && (apiKey || gatewaySkip)) { + return applyGatewayDecisionAndRespond(context, { + ...(apiKey ? { apiKey } : {}), + ...(gatewaySkip ? { skip: true } : {}), + }); + } if (!message) { return new Response(JSON.stringify({ ok: false, @@ -18,14 +27,12 @@ export async function onRequestPost(context: AgentContext) { } try { - const apiKey = String(body.apiKey || '').trim(); return await createChatTaskAndStreamResponse(context, message, { kind: 'prompt', turnId: String(body.turnId || '').trim() || undefined, model: resolveRequestedModel(context, body.model), language: String(body.language || '').trim() || undefined, ...(apiKey ? { apiKey } : {}), - ...(body.gatewaySkip === true ? { gatewaySkip: true } : {}), }); } catch (error) { return new Response(JSON.stringify({ diff --git a/app/components/agent-conversation.tsx b/app/components/agent-conversation.tsx index 47bc015..12e27bb 100644 --- a/app/components/agent-conversation.tsx +++ b/app/components/agent-conversation.tsx @@ -54,6 +54,7 @@ export type DeployOfferCopy = { export type GatewayPromptCopy = { title: string; + description?: string; docs: string; docsUrl: string; apiKey: string; @@ -310,9 +311,12 @@ export function AgentConversation({ onDeployOffer, onDismissDeployOffer, gatewayPrompt, + gatewayChip, + gatewaySaved, gatewayBusy, onGatewaySubmit, onGatewaySkip, + onGatewayReopen, }: { messages: ConversationMessage[]; input: string; @@ -330,9 +334,12 @@ export function AgentConversation({ onDeployOffer?: () => void; onDismissDeployOffer?: () => void; gatewayPrompt?: GatewayPromptCopy | null; + gatewayChip?: string | null; + gatewaySaved?: string | null; gatewayBusy?: boolean; onGatewaySubmit?: (values: { apiKey: string }) => void; onGatewaySkip?: () => void; + onGatewayReopen?: () => void; }) { const [gatewayApiKey, setGatewayApiKey] = useState(''); const gatewayInputRef = useRef(null); @@ -343,8 +350,6 @@ export function AgentConversation({ setGatewayApiKey(''); return; } - // The card mounts only after the assistant turn has finished, so focus - // can land immediately instead of waiting on a disabled input. const node = gatewayInputRef.current; if (!node || node.disabled) return; node.focus(); @@ -406,6 +411,9 @@ export function AgentConversation({ >

{gatewayPrompt.title}

+ {gatewayPrompt.description && ( +

{gatewayPrompt.description}

+ )}

)} + {!gatewayPrompt && gatewayChip && ( + + )} + {!gatewayPrompt && !gatewayChip && gatewaySaved && ( +

{gatewaySaved}

+ )} {deployOffer && (
{deployOffer.prompt} diff --git a/app/features/workspace/components/session-prep-loading.tsx b/app/features/workspace/components/session-prep-loading.tsx new file mode 100644 index 0000000..b420c05 --- /dev/null +++ b/app/features/workspace/components/session-prep-loading.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import type { SessionPrepStage } from '@/app/types/workspace'; +import { prepStageRange } from '../session-prep-progress'; + +function usePrepProgress(stage: SessionPrepStage | null) { + const { floor, ceiling, durationMs } = prepStageRange(stage); + const [value, setValue] = useState(floor); + + useEffect(() => { + setValue((current) => Math.max(current, floor)); + if (durationMs <= 0 || ceiling <= floor) { + setValue(ceiling); + return; + } + + const startedAt = Date.now(); + let frame = 0; + const tick = () => { + const t = Math.min(1, (Date.now() - startedAt) / durationMs); + const eased = 1 - (1 - t) ** 2; + setValue((current) => Math.max(current, floor + (ceiling - floor) * eased)); + if (t < 1) frame = requestAnimationFrame(tick); + }; + frame = requestAnimationFrame(tick); + return () => cancelAnimationFrame(frame); + }, [stage, floor, ceiling, durationMs]); + + return value; +} + +export function SessionPrepLoading(options: { + stage: SessionPrepStage | null; + title: string; + stageLabel: string; +}) { + const progress = usePrepProgress(options.stage); + + return ( +
+
+

{options.title}

+
+ +
+

{options.stageLabel}

+
+
+ ); +} diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index 336695f..794d953 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -20,12 +20,12 @@ import type { ChatMessage, ChatResponse, ChatStreamEvent, - SessionPrepData, SessionPrepStage, SessionStreamEvent, } from '@/app/types/workspace'; import { consumeEventStream } from '../sse'; import { + applyGatewayDecision, openSessionStream, startDeployTurn, startPromptTurn, @@ -44,49 +44,6 @@ type LiveCopy = { agentFlowEnded: string; }; -type PrepStageCopy = Record; - -function sessionPrepToChatEvents( - data: SessionPrepData, - labels: PrepStageCopy, -): ChatStreamEvent[] { - if (data.stage === 'ready') { - return (['conversation', 'sandbox', 'agent'] as const).map((stage) => ({ - type: 'tool_result' as const, - data: { - id: `session-prep-${stage}`, - ok: true, - status: 'completed' as const, - endedAt: Date.now(), - }, - })); - } - - const id = `session-prep-${data.stage}`; - const label = labels[data.stage] || data.stage; - if (data.status === 'running') { - return [{ - type: 'tool_use', - data: { - id, - name: 'environment', - inputSummary: label, - startedAt: Date.now(), - }, - }]; - } - - return [{ - type: 'tool_result', - data: { - id, - ok: data.status === 'done', - status: data.status === 'failed' ? 'failed' : 'completed', - endedAt: Date.now(), - }, - }]; -} - export function useLiveTurn(options: { language: Locale; model: string; @@ -94,9 +51,6 @@ export function useLiveTurn(options: { response: LiveCopy; workspace: { deployRequest: string; - gatewayPromptApiKey: string; - gatewayPromptSkip: string; - prepStages: PrepStageCopy; }; }; workspace: WorkspaceStateApi; @@ -125,6 +79,8 @@ export function useLiveTurn(options: { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [loading, setLoading] = useState(false); + const [sessionPreparing, setSessionPreparing] = useState(false); + const [prepStage, setPrepStage] = useState(null); const messagesRef = useRef([]); const modelRef = useRef(model); const chatAbortControllerRef = useRef(null); @@ -264,11 +220,20 @@ export function useLiveTurn(options: { if (event.type === 'gateway_credentials') { if (event.data?.status === 'needed') { workspace.setGatewayNeeded(true); + workspace.setGatewayDeferred(false); workspace.setGatewayBusy(false); } if (event.data?.status === 'resolved') { workspace.setGatewayNeeded(false); workspace.setGatewayBusy(false); + if (event.data.skipped) { + workspace.setGatewayDeferred(true); + workspace.setGatewayConfigured(false); + } else { + workspace.setGatewayDeferred(false); + workspace.setGatewayConfigured(true); + workspace.setGatewayPromptVariant('default'); + } } return; } @@ -401,22 +366,16 @@ export function useLiveTurn(options: { async function sendMessage(message: string, sendOptions: { deploy?: boolean; - apiKey?: string; - gatewaySkip?: boolean; } = {}) { const trimmed = message.trim(); if (!trimmed || loading) return; - const extractedKey = sendOptions.apiKey - ? undefined - : extractApiKeyFromUserText(trimmed); - const inboundApiKey = sendOptions.apiKey || extractedKey?.apiKey; + const extractedKey = extractApiKeyFromUserText(trimmed); + const inboundApiKey = extractedKey?.apiKey; const displayMessage = extractedKey?.maskedText || trimmed; const isDeploy = sendOptions.deploy === true; - const isGatewayCard = Boolean(sendOptions.apiKey || sendOptions.gatewaySkip); - const isGatewayContinue = Boolean(inboundApiKey || sendOptions.gatewaySkip); - const isStartingFromHome = !isDeploy && !isGatewayCard + const isStartingFromHome = !isDeploy && messages.length === 0 && !preview.preview && !workspace.deployment @@ -438,8 +397,7 @@ export function useLiveTurn(options: { const assistantMessageId = createMessageId('assistant'); activeTurnIdRef.current = assistantMessageId; - setMessages((current) => [ - ...current, + const turnMessages: ChatMessage[] = [ { id: userMessageId, role: 'user', content: displayMessage }, { id: assistantMessageId, @@ -448,16 +406,20 @@ export function useLiveTurn(options: { activities: [], status: 'running', }, - ]); + ]; + if (!isStartingFromHome) { + setMessages((current) => [...current, ...turnMessages]); + } if (!isDeploy) { workspace.setFilesRefreshing(true); - if (!isGatewayCard) setInput(''); + setInput(''); } - if (isGatewayContinue) { + if (inboundApiKey) { workspace.setGatewayNeeded(false); workspace.setGatewayBusy(false); } setLoading(true); + if (isStartingFromHome) setSessionPreparing(true); try { const requestAbortController = new AbortController(); @@ -481,31 +443,18 @@ export function useLiveTurn(options: { && resumeType.includes('text/event-stream') ) { await consumeEventStream(resumeResponse, (event) => { - if (event.type !== 'session_prep' || !event.data) return; - const prepEvents = sessionPrepToChatEvents(event.data, t.workspace.prepStages); - setMessages((current) => - current.map((item) => { - if (item.id !== assistantMessageId) return item; - let activities = item.activities ?? []; - for (const prepEvent of prepEvents) { - const folded = applyStreamEvent({ - id: item.id, - user: '', - assistant: item.content, - status: 'completed', - createdAt: 0, - activities, - } satisfies PersistedActivityTurn, prepEvent); - activities = folded.activities; - } - return { ...item, activities }; - }), - ); + if (event.type !== 'session_prep' || !event.data?.stage) return; + if (event.data.status === 'running' || event.data.stage === 'ready') { + setPrepStage(event.data.stage); + } }); } } catch (error) { if (error instanceof Error && error.name === 'AbortError') throw error; } + setMessages(turnMessages); + setSessionPreparing(false); + setPrepStage(null); } const response = isDeploy ? await startDeployTurn({ @@ -513,7 +462,6 @@ export function useLiveTurn(options: { turnId: assistantMessageId, language, ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), - ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), signal: requestAbortController.signal, }) : await startPromptTurn({ @@ -523,7 +471,6 @@ export function useLiveTurn(options: { model: modelRef.current, language, ...(inboundApiKey ? { apiKey: inboundApiKey } : {}), - ...(sendOptions.gatewaySkip ? { gatewaySkip: true } : {}), signal: requestAbortController.signal, }); await attachChatStream({ @@ -534,6 +481,8 @@ export function useLiveTurn(options: { }); } catch (error) { if ((error instanceof Error && error.name === 'AbortError') || stoppingRef.current) { + setSessionPreparing(false); + setPrepStage(null); setLoading(false); workspace.setFilesRefreshing(false); chatAbortControllerRef.current = null; @@ -554,6 +503,8 @@ export function useLiveTurn(options: { ), ); setLoading(false); + setSessionPreparing(false); + setPrepStage(null); workspace.setFilesRefreshing(false); chatAbortControllerRef.current = null; activeTurnIdRef.current = ''; @@ -569,6 +520,8 @@ export function useLiveTurn(options: { const stopped = markLastTurnStopped(messagesRef.current, stoppedText); setMessages(stopped.messages); setLoading(false); + setSessionPreparing(false); + setPrepStage(null); workspace.setFilesRefreshing(false); workspace.setGatewayNeeded(false); workspace.setGatewayBusy(false); @@ -587,6 +540,60 @@ export function useLiveTurn(options: { return stopRequest; } + async function applyGateway(decision: { apiKey?: string; skip?: boolean }) { + if (workspace.gatewayBusy) return; + const cid = conversationIdRef.current || conversationId; + if (!cid) return; + const apiKey = (decision.apiKey || '').trim(); + if (!decision.skip && !apiKey) return; + + workspace.setGatewayBusy(true); + try { + const response = await applyGatewayDecision({ + conversationId: cid, + ...(apiKey ? { apiKey } : {}), + ...(decision.skip ? { gatewaySkip: true } : {}), + }); + const data = await response.json().catch(() => null) as { + ok?: boolean; + live?: boolean; + skipped?: boolean; + configured?: boolean; + preview?: { + url?: string; + sandboxDebugUrl?: string; + kind?: 'sandbox' | 'makers'; + restarted?: boolean; + }; + download?: { url?: string; filename?: string }; + } | null; + if (!response.ok || !data?.ok) { + workspace.setGatewayBusy(false); + return; + } + if (decision.skip) { + workspace.setGatewayNeeded(false); + workspace.setGatewayDeferred(true); + workspace.setGatewayConfigured(false); + } else { + workspace.setGatewayNeeded(false); + workspace.setGatewayDeferred(false); + workspace.setGatewayConfigured(true); + workspace.setGatewayPromptVariant('default'); + } + workspace.setGatewayBusy(false); + if (data.preview && !data.live) { + preview.activatePreview(data.preview, new Map()); + } + if (data.download) { + workspace.setDownload(data.download); + } + void snapshot.refresh(cid); + } catch { + workspace.setGatewayBusy(false); + } + } + return { messages, setMessages, @@ -601,7 +608,12 @@ export function useLiveTurn(options: { stoppingRef, startLiveChatSessionRef, sendMessage, + applyGateway, stopCurrentTask, + sessionPreparing, + setSessionPreparing, + prepStage, + setPrepStage, }; } diff --git a/app/features/workspace/hooks/use-session-resume.ts b/app/features/workspace/hooks/use-session-resume.ts index abd12f8..8a0cc6e 100644 --- a/app/features/workspace/hooks/use-session-resume.ts +++ b/app/features/workspace/hooks/use-session-resume.ts @@ -166,7 +166,15 @@ export function useSessionResume(options: { }); live.setMessages(nextMessages); - workspace.setGatewayNeeded(Boolean(data.gatewayNeeded)); + if (data.gatewayNeeded) { + workspace.setGatewayNeeded(true); + workspace.setGatewayDeferred(false); + } else if (data.gatewaySkipped) { + workspace.setGatewayNeeded(false); + workspace.setGatewayDeferred(true); + } else { + workspace.setGatewayNeeded(false); + } workspace.setDeployment(data.deployment ?? null); if (data.hasProject || data.needsWorkspace || activeTask) { if (data.hasProject || data.needsWorkspace) { @@ -182,7 +190,12 @@ export function useSessionResume(options: { }; const applyWorkspace = (data: ResumeData) => { - if (data.gatewayNeeded) workspace.setGatewayNeeded(true); + if (data.gatewayNeeded) { + workspace.setGatewayNeeded(true); + workspace.setGatewayDeferred(false); + } else if (data.gatewaySkipped) { + workspace.setGatewayDeferred(true); + } snapshot.applySnapshot(data); }; @@ -208,7 +221,9 @@ export function useSessionResume(options: { if (cancelled || workspaceEpoch !== workspaceEpochRef.current || event.type === 'ping') return; if (event.type === 'session_prep' && event.data?.stage) { - setPrepStage(event.data.stage); + if (event.data.status === 'running' || event.data.stage === 'ready') { + setPrepStage(event.data.stage); + } return; } @@ -219,8 +234,9 @@ export function useSessionResume(options: { clearCachedConversationId(); conversationIdRef.current = null; setConversationId(null); + setResumeChecked(true); + setPrepStage(null); } - setResumeChecked(true); if (liveTaskId) { const conversationForRun = historyData.conversation_id || existing; diff --git a/app/features/workspace/hooks/use-workspace-state.ts b/app/features/workspace/hooks/use-workspace-state.ts index fca45b1..2167c0b 100644 --- a/app/features/workspace/hooks/use-workspace-state.ts +++ b/app/features/workspace/hooks/use-workspace-state.ts @@ -27,6 +27,9 @@ export function useWorkspaceState() { const [resultPanelOpen, setResultPanelOpen] = useState(false); const [dismissedDeployTurnId, setDismissedDeployTurnId] = useState(''); const [gatewayNeeded, setGatewayNeeded] = useState(false); + const [gatewayDeferred, setGatewayDeferred] = useState(false); + const [gatewayConfigured, setGatewayConfigured] = useState(false); + const [gatewayPromptVariant, setGatewayPromptVariant] = useState<'default' | 'deploy'>('default'); const [gatewayBusy, setGatewayBusy] = useState(false); const resetWorkspace = useCallback(() => { @@ -39,6 +42,9 @@ export function useWorkspaceState() { setResultPanelOpen(false); setDismissedDeployTurnId(''); setGatewayNeeded(false); + setGatewayDeferred(false); + setGatewayConfigured(false); + setGatewayPromptVariant('default'); setGatewayBusy(false); setSandboxTab(null); }, []); @@ -98,6 +104,12 @@ export function useWorkspaceState() { setDismissedDeployTurnId, gatewayNeeded, setGatewayNeeded, + gatewayDeferred, + setGatewayDeferred, + gatewayConfigured, + setGatewayConfigured, + gatewayPromptVariant, + setGatewayPromptVariant, gatewayBusy, setGatewayBusy, resetWorkspace, diff --git a/app/features/workspace/session-prep-progress.ts b/app/features/workspace/session-prep-progress.ts new file mode 100644 index 0000000..70bb54b --- /dev/null +++ b/app/features/workspace/session-prep-progress.ts @@ -0,0 +1,14 @@ +import type { SessionPrepStage } from '../../types/workspace'; + +const STAGE_RANGE: Record = { + conversation: { floor: 8, ceiling: 28, durationMs: 900 }, + sandbox: { floor: 32, ceiling: 52, durationMs: 2_400 }, + agent: { floor: 56, ceiling: 86, durationMs: 14_000 }, + workspace: { floor: 70, ceiling: 88, durationMs: 8_000 }, + preview: { floor: 88, ceiling: 96, durationMs: 4_000 }, + ready: { floor: 100, ceiling: 100, durationMs: 0 }, +}; + +export function prepStageRange(stage: SessionPrepStage | null) { + return STAGE_RANGE[stage || 'conversation']; +} diff --git a/app/features/workspace/workspace-api.ts b/app/features/workspace/workspace-api.ts index 978ee65..257ac30 100644 --- a/app/features/workspace/workspace-api.ts +++ b/app/features/workspace/workspace-api.ts @@ -69,6 +69,23 @@ export function fetchModelCatalog(signal?: AbortSignal) { .catch(() => null); } +export function applyGatewayDecision(options: { + conversationId: string; + apiKey?: string; + gatewaySkip?: boolean; + signal?: AbortSignal; +}) { + return fetch('/prompt', { + method: 'POST', + headers: conversationHeaders(options.conversationId), + body: JSON.stringify({ + ...(options.apiKey ? { apiKey: options.apiKey } : {}), + ...(options.gatewaySkip ? { gatewaySkip: true } : {}), + }), + signal: options.signal, + }); +} + export function startPromptTurn(options: { conversationId: string; message: string; @@ -76,7 +93,6 @@ export function startPromptTurn(options: { model?: string; language?: Locale; apiKey?: string; - gatewaySkip?: boolean; signal?: AbortSignal; }) { return fetch('/prompt', { @@ -88,7 +104,6 @@ export function startPromptTurn(options: { ...(options.model ? { model: options.model } : {}), ...(options.language ? { language: options.language } : {}), ...(options.apiKey ? { apiKey: options.apiKey } : {}), - ...(options.gatewaySkip ? { gatewaySkip: true } : {}), }), signal: options.signal, }); @@ -99,7 +114,6 @@ export function startDeployTurn(options: { turnId: string; language?: Locale; apiKey?: string; - gatewaySkip?: boolean; signal?: AbortSignal; }) { return fetch('/deploy', { @@ -109,7 +123,6 @@ export function startDeployTurn(options: { turnId: options.turnId, ...(options.language ? { language: options.language } : {}), ...(options.apiKey ? { apiKey: options.apiKey } : {}), - ...(options.gatewaySkip ? { gatewaySkip: true } : {}), }), signal: options.signal, }); diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index 02dd308..e78fe55 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -40,13 +40,13 @@ import { getTemplateDeployUrl, } from '@/app/lib/conversation'; import { LANGUAGE_STORAGE_KEY, TRANSLATIONS, type Locale } from '@/app/i18n'; -import { maskApiKey } from '../../../shared/gateway-secret'; import { previewDisplayPathFromPath } from '../../../shared/preview-display-path'; import type { ModelOption } from '../../../shared/models'; import { HomeStage } from './components/home-stage'; import { PreviewControls } from './components/preview-controls'; import { PreviewFrame } from './components/preview-frame'; import { SiteHeader } from './components/site-header'; +import { SessionPrepLoading } from './components/session-prep-loading'; import { WorkspaceErrorBar } from './components/workspace-error-bar'; import { fetchModelCatalog } from './workspace-api'; import { useLiveTurn } from './hooks/use-live-turn'; @@ -290,6 +290,10 @@ export function WorkspaceScreen() { function handleDeployProject() { if (!canDeployProject) return; + if (!workspace.gatewayConfigured && (workspace.gatewayNeeded || workspace.gatewayDeferred)) { + workspace.setGatewayPromptVariant('deploy'); + workspace.setGatewayNeeded(true); + } if (deployOfferTurnId) { workspace.setDismissedDeployTurnId(deployOfferTurnId); } @@ -309,6 +313,8 @@ export function WorkspaceScreen() { setConversationId(null); live.setMessages([]); live.setLoading(false); + live.setSessionPreparing(false); + live.setPrepStage(null); live.setInput(''); workspace.resetWorkspace(); preview.resetPreview(); @@ -331,19 +337,15 @@ export function WorkspaceScreen() { startNewProject(); } - if (!resume.resumeChecked) { + const prepStage = live.prepStage || resume.prepStage + || (live.sessionPreparing ? 'conversation' : null); + if (!resume.resumeChecked || live.sessionPreparing) { return ( -
-
+ ); } @@ -421,23 +423,35 @@ export function WorkspaceScreen() { onDismissDeployOffer={() => { if (deployOfferTurnId) workspace.setDismissedDeployTurnId(deployOfferTurnId); }} - gatewayPrompt={workspace.gatewayNeeded && !live.loading ? { + gatewayPrompt={workspace.gatewayNeeded ? { title: t.workspace.gatewayPromptTitle, + description: workspace.gatewayPromptVariant === 'deploy' + ? `${t.workspace.gatewayPromptDescription} ${t.workspace.gatewayPromptDeployHint}` + : t.workspace.gatewayPromptDescription, docs: t.workspace.gatewayPromptDocs, docsUrl: makersModelsDocsUrl, apiKey: t.workspace.gatewayPromptApiKey, continue: t.workspace.gatewayPromptContinue, skip: t.workspace.gatewayPromptSkip, } : null} + gatewayChip={workspace.gatewayDeferred && !workspace.gatewayNeeded + ? t.workspace.gatewayPromptChip + : null} + gatewaySaved={workspace.gatewayConfigured && !workspace.gatewayNeeded + ? t.workspace.gatewayPromptSaved + : null} gatewayBusy={workspace.gatewayBusy} onGatewaySubmit={(values) => { const apiKey = values.apiKey.trim(); - if (!apiKey || live.loading || workspace.gatewayBusy) return; - void live.sendMessage(`${t.workspace.gatewayPromptApiKey}: ${maskApiKey(apiKey)}`, { apiKey }); + if (!apiKey || workspace.gatewayBusy) return; + void live.applyGateway({ apiKey }); }} onGatewaySkip={() => { - if (live.loading || workspace.gatewayBusy) return; - void live.sendMessage(t.workspace.gatewayPromptSkip, { gatewaySkip: true }); + if (workspace.gatewayBusy) return; + void live.applyGateway({ skip: true }); + }} + onGatewayReopen={() => { + workspace.setGatewayNeeded(true); }} />} diff --git a/app/i18n.ts b/app/i18n.ts index 0815dec..9c768d8 100644 --- a/app/i18n.ts +++ b/app/i18n.ts @@ -161,11 +161,15 @@ export const TRANSLATIONS = { deployOfferAgain: '项目有更新,要重新部署吗?', deployOfferAction: '部署', deployOfferDismiss: '暂不', - gatewayPromptTitle: '集成 Models 调用大模型', - gatewayPromptDocs: '如何获取', - gatewayPromptApiKey: 'API Key', - gatewayPromptContinue: '继续', - gatewayPromptSkip: '跳过', + gatewayPromptTitle: '启用 AI 对话', + gatewayPromptDescription: '添加 Models API 密钥,即可在预览中试用对话。密钥只保存在此项目中,无需登录。', + gatewayPromptDeployHint: '发布到线上后,站点仍需使用这把密钥。', + gatewayPromptDocs: '如何获取密钥', + gatewayPromptApiKey: 'API 密钥', + gatewayPromptContinue: '添加', + gatewayPromptSkip: '稍后', + gatewayPromptChip: '添加 API 密钥', + gatewayPromptSaved: '已添加,可以在预览中试用对话。', preview: '预览', code: '代码', // The Claude JSONL file is the only history this product keeps. The chat @@ -191,6 +195,7 @@ export const TRANSLATIONS = { newProjectConfirmCancel: '取消', newProjectConfirmContinue: '停止并返回', resuming: '正在加载对话…', + preparing: '正在准备环境', restoringWorkspace: '正在还原代码与预览…', previewStarting: '预览启动中…', prepStages: { @@ -352,11 +357,15 @@ export const TRANSLATIONS = { deployOfferAgain: 'The project has updates. Deploy again?', deployOfferAction: 'Deploy', deployOfferDismiss: 'Not now', - gatewayPromptTitle: 'Integrate Models to call large models', - gatewayPromptDocs: 'How to get them', + gatewayPromptTitle: 'Enable AI chat', + gatewayPromptDescription: "Add a Models API key to try the conversation in Preview. It stays with this project — there's no account.", + gatewayPromptDeployHint: 'A published site needs this key too.', + gatewayPromptDocs: 'How to get a key', gatewayPromptApiKey: 'API Key', - gatewayPromptContinue: 'Continue', - gatewayPromptSkip: 'Skip', + gatewayPromptContinue: 'Add', + gatewayPromptSkip: 'Not Now', + gatewayPromptChip: 'Add API Key', + gatewayPromptSaved: 'Added. You can try the chat in Preview.', preview: 'Preview', code: 'Code', session: 'Session', @@ -380,6 +389,7 @@ export const TRANSLATIONS = { newProjectConfirmCancel: 'Cancel', newProjectConfirmContinue: 'Stop and leave', resuming: 'Loading conversation…', + preparing: 'Preparing the environment', restoringWorkspace: 'Restoring code and preview…', previewStarting: 'Starting preview…', prepStages: { diff --git a/app/styles/conversation.css b/app/styles/conversation.css index db9e9dc..094ec5d 100644 --- a/app/styles/conversation.css +++ b/app/styles/conversation.css @@ -338,6 +338,13 @@ line-height: 1.4; } +.gateway-prompt-description { + margin: 6px 0 0; + color: var(--n-700); + font-size: var(--fs-sm); + line-height: 1.5; +} + .gateway-prompt-docs { margin: 4px 0 0; font-size: var(--fs-sm); @@ -388,6 +395,33 @@ gap: 8px; } +.gateway-prompt-chip, +.gateway-prompt-saved { + align-self: flex-start; + margin: 0; + border: 1px solid var(--n-150); + border-radius: 999px; + background: var(--card); + padding: 6px 12px; + color: var(--n-800); + font-size: var(--fs-sm); + line-height: 1.3; +} + +.gateway-prompt-chip { + cursor: pointer; +} + +.gateway-prompt-chip:hover, +.gateway-prompt-chip:focus-visible { + background: var(--n-50); + color: var(--n-900); +} + +.gateway-prompt-saved { + color: var(--n-700); +} + .conversation-composer { display: grid; position: relative; diff --git a/app/styles/workspace.css b/app/styles/workspace.css index af25dbb..1431788 100644 --- a/app/styles/workspace.css +++ b/app/styles/workspace.css @@ -632,3 +632,77 @@ } } } + +/* Full-screen session warmup. A determinate bar replaces the spinner so a + multi-second CLI start still reads as progress, not a hang. */ +.session-prep { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: var(--n-0); +} + +.session-prep-card { + width: min(100% - 48px, 320px); + text-align: center; +} + +.session-prep-title { + margin: 0; + color: var(--n-900); + font-size: var(--fs-lg); + font-weight: 600; + letter-spacing: -0.01em; +} + +.session-prep-bar { + height: 6px; + margin: 18px 0 12px; + overflow: hidden; + border-radius: var(--r-full); + background: var(--n-150); +} + +.session-prep-bar-fill { + display: block; + position: relative; + height: 100%; + overflow: hidden; + border-radius: inherit; + background: var(--brand); +} + +.session-prep-bar-fill::after { + content: ''; + position: absolute; + top: 0; + left: -40%; + width: 40%; + height: 100%; + background: linear-gradient(90deg, transparent, var(--n-0), transparent); + opacity: 0.35; + animation: session-prep-sheen 1.6s ease-in-out infinite; +} + +.session-prep-stage { + margin: 0; + color: var(--n-500); + font-size: var(--fs-md); +} + +@keyframes session-prep-sheen { + 0% { + transform: translateX(0); + } + + 100% { + transform: translateX(350%); + } +} + +@media (prefers-reduced-motion: reduce) { + .session-prep-bar-fill::after { + animation: none; + } +} diff --git a/shared/protocol.ts b/shared/protocol.ts index cc9136b..400119f 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -126,6 +126,8 @@ export type ResumeData = { language?: 'zh' | 'en'; /** Resume should show the Models API key card. */ gatewayNeeded?: boolean; + /** User deferred the key; resume should show the reopen chip. */ + gatewaySkipped?: boolean; error?: string; }; diff --git a/shared/user-facing-reply.ts b/shared/user-facing-reply.ts index 7415fe0..505b973 100644 --- a/shared/user-facing-reply.ts +++ b/shared/user-facing-reply.ts @@ -29,16 +29,6 @@ export const STOPPED_TURN_REPLY: Readonly> = { en: 'Generation stopped. You can continue with another change.', }; -/** - * A turn that stopped so the user can type a Models API key. Same contract as - * a question turn: the project may already be on disk, preview is intentionally - * not up, and that must not read as a failed build. - */ -export const GATEWAY_CREDENTIALS_USER_REPLY: Readonly> = { - zh: '项目已经写好。要调用大模型请在下方输入 Models API Key;跳过也可以先预览和部署。', - en: 'The project is ready. Enter a Models API key below to call models, or skip to preview and deploy first.', -}; - export function compactUserFacingReply(text: string, fallback: string) { const normalized = text.replace(/\r/g, '').trim(); if (!normalized) return fallback; @@ -85,8 +75,8 @@ export type FinishedTurn = { /** Whether verification failed. */ buildFailed: boolean; /** - * The turn stopped to wait for the user (API key card, a clarifying - * question). A missing preview is then intentional, not a failed dest. + * The turn stopped to wait for the user (a clarifying question). + * A missing preview is then intentional, not a failed dest. */ waitingForUser?: boolean; /** The model's own reply, empty when it produced nothing usable. */ diff --git a/tests/deploy-task.test.ts b/tests/deploy-task.test.ts index 55a55b5..a896323 100644 --- a/tests/deploy-task.test.ts +++ b/tests/deploy-task.test.ts @@ -297,10 +297,12 @@ test('publishing leaves the composer and the files panel alone', async () => { const body = live.slice(start, live.indexOf('function stopCurrentTask(', start)); assert.ok(start >= 0 && body.length > 0); - assert.match(body, /const isStartingFromHome = !isDeploy && !isGatewayCard/); + assert.match(body, /const isStartingFromHome = !isDeploy/); assert.match(body, /if \(isStartingFromHome\) \{[\s\S]*?openSessionStream/); assert.match(body, /startPromptTurn\(/); assert.match(body, /startDeployTurn\(/); assert.match(body, /if \(!isDeploy\) \{\s*workspace\.setFilesRefreshing\(true\);/); - assert.match(body, /if \(!isGatewayCard\) setInput\(''\)/); + assert.match(body, /setInput\(''\)/); + assert.doesNotMatch(body, /isGatewayCard/); + assert.doesNotMatch(body, /gatewaySkip/); }); diff --git a/tests/gateway-prompt.test.ts b/tests/gateway-prompt.test.ts index 78b53ac..81c53a1 100644 --- a/tests/gateway-prompt.test.ts +++ b/tests/gateway-prompt.test.ts @@ -13,7 +13,7 @@ import { DEFAULT_AI_GATEWAY_BASE_URL, GATEWAY_CREDENTIALS_PAUSE_MESSAGE, applyUserGatewayDecision, - buildRequestGatewayCredentialsTool, + askUserForGatewayCredentials, declaredGatewayKeys, envAssignmentValue, gatewayBaseUrlForAgentFramework, @@ -22,7 +22,10 @@ import { readProjectGatewayEnv, sandboxGatewayKeyIsSet, shouldPauseForGatewayCredentials, + writeSuggestsAiGatewayProject, } from '../agents/_lib/project/gateway.ts'; +import { buildLoadMakersSkillTool } from '../agents/_lib/tools/makers-skills.ts'; +import { buildWriteProjectFileTool } from '../agents/_lib/tools/project-tools.ts'; import { projectState } from './helpers/fixtures.ts'; function sandboxFiles(initial: Array<[string, string]>) { @@ -43,6 +46,10 @@ function sandboxFiles(initial: Array<[string, string]>) { exists: async (target: string) => files.has(target) || [...files.keys()].some((path) => ( path === target || path.startsWith(`${target}/`) )), + makeDir: async () => undefined, + }, + commands: { + run: async () => ({ stdout: '', stderr: '', exitCode: 0 }), }, }, }, @@ -114,6 +121,13 @@ test('a provided key is written to .env and a skip is not', async () => { ); assert.equal(state.gatewayPromptPending, false); assert.equal(state.gatewaySkipped, false); + assert.deepEqual( + await readProjectGatewayEnv(context, projectState()), + { + AI_GATEWAY_API_KEY: 'sk-user', + AI_GATEWAY_BASE_URL: DEFAULT_AI_GATEWAY_BASE_URL, + }, + ); const skipped = projectState(); assert.deepEqual( @@ -151,7 +165,7 @@ test('a claude-agent-sdk project gets the origin without /v1', async () => { ); }); -test('preview pauses when an AI project has no key and continues after skip', async () => { +test('an AI project without a key is offered the card and dest is not blocked', async () => { const { context } = sandboxFiles([ ['projects/demo/app/.env.example', 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n'], ]); @@ -176,76 +190,107 @@ test('preview pauses when an AI project has no key and continues after skip', as assert.equal(await pauseForGatewayCredentialsIfNeeded(context, state), ''); }); -test('request_gateway_credentials asks once and does not wait', async () => { - const { context } = sandboxFiles([ - ['projects/demo/app/.env.example', 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n'], +test('the coding agent has no request_gateway_credentials tool', async () => { + const [assemble, gateway, prompt, chat] = await Promise.all([ + readFile('agents/_lib/tools/assemble.ts', 'utf8'), + readFile('agents/_lib/project/gateway.ts', 'utf8'), + readFile('agents/_lib/prompt.ts', 'utf8'), + readFile('agents/_lib/turn/chat.ts', 'utf8'), ]); + assert.doesNotMatch(assemble, /request_gateway_credentials/); + assert.doesNotMatch(assemble, /buildRequestGatewayCredentialsTool/); + assert.doesNotMatch(gateway, /buildRequestGatewayCredentialsTool/); + assert.doesNotMatch(gateway, /defineClaudeTool/); + assert.doesNotMatch(prompt, /request_gateway_credentials/); + assert.doesNotMatch(chat, /isRequestGatewayCredentialsTool/); + assert.doesNotMatch(chat, /hiddenToolUseIds/); +}); + +test('loading makers-agents offers the gateway card without ending the turn', async () => { const events: Array> = []; const state = projectState(); - const tool = buildRequestGatewayCredentialsTool({ - context, + const tool = buildLoadMakersSkillTool({ + context: { sandbox: { files: {} } } as never, state, - conversationId: 'conv-tool', + conversationId: 'conv-skill', send: (event) => { events.push(event); }, }); - const first = await tool.handler({}, {}); - const text = first.content?.[0] && 'text' in first.content[0] - ? String(first.content[0].text) + const result = await tool.handler({ skill: 'makers-agents' }, {}); + const text = result.content?.[0] && 'text' in result.content[0] + ? String(result.content[0].text) : ''; - assert.match(text, /askedUser/); - assert.equal(events.length, 1); + assert.match(text, /makers-agents|SKILL/); + assert.equal(events[0]?.type, 'gateway_credentials'); + assert.equal((events[0]?.data as { status?: string })?.status, 'needed'); assert.equal(state.gatewayPromptPending, true); +}); - const configured = sandboxFiles([ - ['projects/demo/app/.env.example', 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n'], - ['projects/demo/app/.env', `AI_GATEWAY_API_KEY=sk-user\nAI_GATEWAY_BASE_URL=${DEFAULT_AI_GATEWAY_BASE_URL}\n`], +test('writing agents/ or a gateway .env.example offers the card immediately', async () => { + const { context } = sandboxFiles([ + ['projects/demo/app/package.json', JSON.stringify({ dependencies: { '@openai/agents': 'latest' } })], ]); - const ready = buildRequestGatewayCredentialsTool({ - context: configured.context, - state: projectState(), - }); - const readyResult = await ready.handler({}, {}); - const readyText = readyResult.content?.[0] && 'text' in readyResult.content[0] - ? String(readyResult.content[0].text) - : ''; - assert.match(readyText, /configured": true/); - - const skippedState = projectState('projects/demo', { gatewaySkipped: true }); - const skippedTool = buildRequestGatewayCredentialsTool({ + const events: Array> = []; + const state = projectState(); + const tool = buildWriteProjectFileTool( context, - state: skippedState, - }); - const skippedResult = await skippedTool.handler({}, {}); - const skippedText = skippedResult.content?.[0] && 'text' in skippedResult.content[0] - ? String(skippedResult.content[0].text) - : ''; - assert.match(skippedText, /skipped": true/); - assert.match(skippedText, /not a preview or deploy failure/); - assert.deepEqual( - await readProjectGatewayEnv(configured.context, projectState()), + state, + undefined, { - AI_GATEWAY_API_KEY: 'sk-user', - AI_GATEWAY_BASE_URL: DEFAULT_AI_GATEWAY_BASE_URL, + conversationId: 'conv-write', + send: (event) => { events.push(event); }, }, ); + + assert.equal(writeSuggestsAiGatewayProject('agents/chat.ts', 'export {}'), true); + assert.equal(writeSuggestsAiGatewayProject('.env.example', 'AI_GATEWAY_API_KEY=\n'), true); + assert.equal(writeSuggestsAiGatewayProject('src/App.tsx', 'export default () => null;\n'), false); + + const result = await tool.handler({ + path: 'agents/chat.ts', + content: 'export async function onRequest() { return new Response("ok"); }\n', + }, {}); + assert.equal(result.isError, undefined); + assert.equal(events[0]?.type, 'gateway_credentials'); + assert.equal(state.gatewayPromptPending, true); + + const later = sandboxFiles([ + ['projects/demo/app/src/App.tsx', 'export default () => null;\n'], + ]); + const quietEvents: Array> = []; + const quiet = buildWriteProjectFileTool( + later.context, + projectState(), + undefined, + { send: (event) => { quietEvents.push(event); } }, + ); + await quiet.handler({ path: 'src/App.tsx', content: 'export default () => null;\n' }, {}); + assert.equal(quietEvents.length, 0); }); -test('the conversation card asks for API Key and submits a masked chat turn', async () => { - const [conversation, screen, live, api] = await Promise.all([ +test('the conversation card is visible while generating and submits without a chat turn', async () => { + const [conversation, screen, live, api, promptRoute, apply] = await Promise.all([ readFile('app/components/agent-conversation.tsx', 'utf8'), readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), readFile('app/features/workspace/workspace-api.ts', 'utf8'), + readFile('agents/prompt.ts', 'utf8'), + readFile('agents/_lib/session/gateway-apply.ts', 'utf8'), ]); const card = conversation.slice( conversation.indexOf('className="gateway-prompt"'), conversation.indexOf('className="gateway-prompt-actions"'), ); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptTitle, '集成 Models 调用大模型'); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptDocs, '如何获取'); - assert.equal(TRANSLATIONS.en.workspace.gatewayPromptDocs, 'How to get them'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptTitle, '启用 AI 对话'); + assert.equal( + TRANSLATIONS.zh.workspace.gatewayPromptDescription, + '添加 Models API 密钥,即可在预览中试用对话。密钥只保存在此项目中,无需登录。', + ); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptSkip, '稍后'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptContinue, '添加'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptDocs, '如何获取密钥'); + assert.equal(TRANSLATIONS.en.workspace.gatewayPromptDocs, 'How to get a key'); assert.equal( getMakersModelsDocsUrl('edgeone.dev'), 'https://pages.edgeone.ai/document/models', @@ -261,18 +306,38 @@ test('the conversation card asks for API Key and submits a masked chat turn', as assert.match(screen, /docsUrl: makersModelsDocsUrl/); assert.match(screen, /setMakersModelsDocsUrl\(getMakersModelsDocsUrl\(domain\)\)/); assert.match(card, /gatewayPrompt\.title/); + assert.match(card, /gatewayPrompt\.description/); assert.match(card, /href=\{gatewayPrompt\.docsUrl\}/); assert.match(card, /gatewayPrompt\.apiKey/); assert.doesNotMatch(card, /gatewayPrompt\.baseUrl/); assert.equal(DEFAULT_AI_GATEWAY_BASE_URL, 'https://ai-gateway.edgeone.link/v1'); assert.equal(AI_GATEWAY_ORIGIN, 'https://ai-gateway.edgeone.link'); - assert.match(screen, /maskApiKey\(apiKey\)/); + assert.match(screen, /gatewayPrompt=\{workspace\.gatewayNeeded \? \{/); + assert.doesNotMatch(screen, /gatewayNeeded && !live\.loading/); + assert.match(screen, /live\.applyGateway\(\{ apiKey \}\)/); + assert.match(screen, /live\.applyGateway\(\{ skip: true \}\)/); + assert.doesNotMatch(screen, /sendMessage\(`\$\{t\.workspace\.gatewayPromptApiKey\}/); + assert.doesNotMatch(screen, /sendMessage\(t\.workspace\.gatewayPromptSkip/); + assert.match(conversation, /className="gateway-prompt-chip"/); + assert.match(live, /async function applyGateway/); + assert.match(live, /if \(!trimmed \|\| loading\) return/); assert.match(live, /extractApiKeyFromUserText\(trimmed\)/); assert.match(live, /inboundApiKey \? \{ apiKey: inboundApiKey \}/); - assert.match(screen, /sendMessage\(`\$\{t\.workspace\.gatewayPromptApiKey\}: \$\{maskApiKey\(apiKey\)\}`, \{ apiKey \}\)/); - assert.match(screen, /sendMessage\(t\.workspace\.gatewayPromptSkip, \{ gatewaySkip: true \}\)/); - assert.doesNotMatch(api, /gateway-credentials/); - assert.match(api, /options\.apiKey \? \{ apiKey: options\.apiKey \}/); + assert.match(api, /function applyGatewayDecision/); + const applyClient = api.slice( + api.indexOf('export function applyGatewayDecision'), + api.indexOf('export function startPromptTurn'), + ); + assert.doesNotMatch(applyClient, /message:/); + const promptTurn = api.slice( + api.indexOf('export function startPromptTurn'), + api.indexOf('export function startDeployTurn'), + ); + assert.doesNotMatch(promptTurn, /gatewaySkip/); + assert.match(promptRoute, /!message && \(apiKey \|\| gatewaySkip\)/); + assert.match(promptRoute, /applyGatewayDecisionAndRespond/); + assert.doesNotMatch(apply, /createChatTask/); + assert.match(apply, /getLiveWorkspace/); const finalize = live.slice( live.indexOf('const finalizeAssistant'), live.indexOf('const applyResponse'), @@ -282,50 +347,27 @@ test('the conversation card asks for API Key and submits a masked chat turn', as assert.match(live, /workspace\.setGatewayNeeded\(true\)/); }); -test('the API key card waits until the assistant turn has finished', async () => { - const [conversation, screen] = await Promise.all([ - readFile('app/components/agent-conversation.tsx', 'utf8'), - readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), - ]); - - // The tool asks mid-stream, but showing the card then greys it out for the - // last few seconds of copy. Hold it until loading is false so it appears - // ready to type into. - assert.match(screen, /gatewayPrompt=\{workspace\.gatewayNeeded && !live\.loading \? \{/); - assert.match(conversation, /autoFocus/); - assert.match(conversation, /disabled=\{gatewayBusy\}/); -}); - -test('a turn waiting for the API key is completed, not a red error', async () => { - const [chat, helpers, prompt] = await Promise.all([ +test('a missing key no longer stops the turn or preview', async () => { + const [chat, prompt, commands] = await Promise.all([ readFile('agents/_lib/turn/chat.ts', 'utf8'), - readFile('agents/_lib/turn/checkpoint.ts', 'utf8'), readFile('agents/_lib/prompt.ts', 'utf8'), + readFile('agents/_lib/tools/commands-wrap.ts', 'utf8'), ]); - const pause = chat.slice( - chat.indexOf('if (state.gatewayPromptPending)'), - chat.indexOf('const sanitizedModelOutput'), - ); - assert.match(helpers, /GATEWAY_CREDENTIALS_USER_REPLY/); - assert.match(pause, /GATEWAY_CREDENTIALS_USER_REPLY\[replyLocale\]/); - assert.match(pause, /type: 'gateway_credentials'/); - assert.match(pause, /status: 'needed'/); - assert.match(pause, /ok: true,\s*\n\s*reply: pauseReply/); - // The card is gated on result/loading, so a Blob snapshot that hangs or - // fails must not sit in front of that event. Persist after it, unawaited. - assert.match(pause, /withSnapshot: false/); - assert.match(pause, /void checkpoint\.flush\(\)/); - assert.ok( - pause.indexOf("type: 'result'") < pause.indexOf('void checkpoint.flush()'), - 'result must go out before snapshot persist, or the card waits on Blob', - ); - assert.doesNotMatch(pause, /await checkpoint\.flush\(\)/); - assert.match(prompt, /do not say the preview is ready/); - assert.match(prompt, /preview and deploy must still run/); + assert.doesNotMatch(chat, /if \(state\.gatewayPromptPending\)/); + assert.doesNotMatch(chat, /GATEWAY_CREDENTIALS_USER_REPLY/); + assert.match(chat, /bindLiveWorkspace/); + assert.match(prompt, /Do not stop this turn/); + assert.doesNotMatch(prompt, /do not say the preview is ready/); + assert.doesNotMatch(prompt, /stop this turn: do not start a preview/); + assert.doesNotMatch(prompt, /request_gateway_credentials/); + assert.match(prompt, /A missing key is not a preview or deploy failure/); + assert.match(commands, /isDeploymentCommand/); + assert.match(commands, /askUserForGatewayCredentials/); + assert.match(commands, /pauseForGatewayCredentialsIfNeeded/); }); -test('the host writes .env from a chat sentence, not only from the card', async () => { +test('the host still writes .env from a chat sentence', async () => { const [chat, tasks, prompt] = await Promise.all([ readFile('agents/_lib/turn/chat.ts', 'utf8'), readFile('agents/_lib/session/task.ts', 'utf8'), @@ -336,3 +378,42 @@ test('the host writes .env from a chat sentence, not only from the card', async assert.match(prompt, /natural language/); assert.match(prompt, /配置好并重新预览/); }); + +test('applying a key or skip emits gateway_credentials resolved', async () => { + const { context } = sandboxFiles([ + ['projects/demo/app/.env.example', 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n'], + ]); + const events: Array> = []; + const state = projectState(); + await applyUserGatewayDecision(context, state, 'conv-resolved', { apiKey: 'sk-user' }, (event) => { + events.push(event); + }); + assert.equal(events.some((event) => ( + event.type === 'gateway_credentials' + && (event.data as { status?: string })?.status === 'resolved' + )), true); + + const skippedEvents: Array> = []; + await applyUserGatewayDecision(context, projectState(), 'conv-skip', { skip: true }, (event) => { + skippedEvents.push(event); + }); + assert.equal((skippedEvents[0]?.data as { skipped?: boolean })?.skipped, true); +}); + +test('askUserForGatewayCredentials does not re-ask after skip', async () => { + const { context } = sandboxFiles([ + ['projects/demo/app/.env.example', 'AI_GATEWAY_API_KEY=\nAI_GATEWAY_BASE_URL=\n'], + ]); + const events: Array> = []; + const state = projectState(); + await askUserForGatewayCredentials(context, state, { + send: (event) => { events.push(event); }, + }); + assert.equal(events.length, 1); + assert.equal(state.gatewayPromptPending, true); + state.gatewaySkipped = true; + await askUserForGatewayCredentials(context, state, { + send: (event) => { events.push(event); }, + }); + assert.equal(events.length, 1); +}); diff --git a/tests/prompt-single-source.test.ts b/tests/prompt-single-source.test.ts index 81eff56..9dbac99 100644 --- a/tests/prompt-single-source.test.ts +++ b/tests/prompt-single-source.test.ts @@ -148,9 +148,11 @@ test('the prompt keeps the sandbox corrections the skills cannot know about', () assert.ok(prompt.includes(makersProjectName)); assert.match(prompt, /Declare AI_GATEWAY_API_KEY= and AI_GATEWAY_BASE_URL=/); assert.match(prompt, /Never write a \.env file yourself/); - assert.match(prompt, /request_gateway_credentials/); + assert.doesNotMatch(prompt, /request_gateway_credentials/); assert.match(prompt, /masked API Key/); + assert.match(prompt, /Do not stop this turn/); assert.doesNotMatch(prompt, /The host asks the user/); + assert.doesNotMatch(prompt, /stop this turn: do not start a preview/); assert.match(prompt, /already shaped for OpenAI-compatible clients/); assert.match(prompt, /never concatenate \/v1\/chat\/completions/); assert.match( diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index 86f8daf..926bda8 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -126,7 +126,7 @@ test('starting a new project does not wait for the old stop request', async () = const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); const stopRoute = await readFile('agents/stop.ts', 'utf8'); const start = screen.indexOf('function confirmNewProject()'); - const end = screen.indexOf('if (!resume.resumeChecked)', start); + const end = screen.indexOf('if (!resume.resumeChecked || live.sessionPreparing)', start); const confirmBlock = screen.slice(start, end); const abortIndex = stopRoute.indexOf('abortActiveRun'); const snapshotIndex = stopRoute.indexOf('if (!discardProject)'); diff --git a/tests/session-prep.test.ts b/tests/session-prep.test.ts index ee56073..4e3158c 100644 --- a/tests/session-prep.test.ts +++ b/tests/session-prep.test.ts @@ -12,6 +12,7 @@ import { import { getConversationRecord } from '../agents/_lib/session/store.ts'; import { sseEvent } from '../agents/_lib/runtime/sse.ts'; import type { AgentContext } from '../agents/_lib/runtime/context.ts'; +import { prepStageRange } from '../app/features/workspace/session-prep-progress.ts'; function fakeContext() { const blobStore = createMemoryBlobStore(); @@ -33,6 +34,18 @@ function fakeContext() { }; } +test('session prep progress never jumps backward between stages', () => { + let previous = 0; + for (const stage of ['conversation', 'sandbox', 'agent', 'workspace', 'preview', 'ready'] as const) { + const range = prepStageRange(stage); + assert.ok(range.floor >= previous, `${stage} floor ${range.floor} went backward from ${previous}`); + assert.ok(range.ceiling >= range.floor); + previous = range.floor; + } + assert.equal(prepStageRange('ready').floor, 100); + assert.equal(prepStageRange(null).floor, prepStageRange('conversation').floor); +}); + test('session_prep events name the stage and status, not a user-facing sentence', () => { const payload = sessionPrepSse('create', 'sandbox', 'running'); assert.match(payload, /"type":"session_prep"/); @@ -169,20 +182,40 @@ test('frontend copy names each session prep stage in both languages', async () = assert.match(i18n, /Creating the conversation/); assert.match(i18n, /正在唤醒编码代理/); assert.match(i18n, /Waking the coding agent/); + assert.match(i18n, /正在准备环境/); + assert.match(i18n, /Preparing the environment/); }); -test('the workspace consumes session_prep instead of draining the stream', async () => { - const [api, liveTurn, resume] = await Promise.all([ +test('the workspace consumes session_prep as a loading screen, not a chat turn', async () => { + const [api, liveTurn, resume, screen] = await Promise.all([ readFile('app/features/workspace/workspace-api.ts', 'utf8'), readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), readFile('app/features/workspace/hooks/use-session-resume.ts', 'utf8'), + readFile('app/features/workspace/workspace-screen.tsx', 'utf8'), ]); assert.match(api, /params\.set\('model'/); assert.match(api, /params\.set\('language'/); assert.match(api, /params\.set\('mode'/); assert.match(liveTurn, /mode: 'create'/); - assert.match(liveTurn, /sessionPrepToChatEvents/); + assert.match(liveTurn, /setSessionPreparing\(true\)/); + assert.match(liveTurn, /setPrepStage\(event\.data\.stage\)/); + assert.doesNotMatch(liveTurn, /sessionPrepToChatEvents/); + assert.doesNotMatch(liveTurn, /name: 'environment'/); assert.doesNotMatch(liveTurn, /consumeEventStream\(resumeResponse, \(\) => \{\}\)/); assert.match(resume, /mode: 'restore'/); assert.match(resume, /setPrepStage\(event\.data\.stage\)/); + assert.match(screen, /live\.sessionPreparing/); + assert.match(screen, /SessionPrepLoading/); + assert.match(screen, /t\.workspace\.preparing/); + const loading = await readFile( + 'app/features/workspace/components/session-prep-loading.tsx', + 'utf8', + ); + assert.match(loading, /role="progressbar"/); + assert.match(loading, /usePrepProgress/); + const css = await readFile('app/styles/workspace.css', 'utf8'); + assert.match(css, /\.session-prep-bar-fill/); + assert.match(css, /@keyframes session-prep-sheen/); + assert.match(resume, /if \(!restored\) \{[\s\S]*?setResumeChecked\(true\)/); + assert.match(resume, /finally \{[\s\S]*setResumeChecked\(true\)/); }); diff --git a/tests/user-facing-reply.test.ts b/tests/user-facing-reply.test.ts index 4ec1cb1..93fb5f9 100644 --- a/tests/user-facing-reply.test.ts +++ b/tests/user-facing-reply.test.ts @@ -3,7 +3,6 @@ import { readFile } from 'node:fs/promises'; import test from 'node:test'; import { compactUserFacingReply, - GATEWAY_CREDENTIALS_USER_REPLY, resolveFinishedTurn, withLiveDeploymentUrl, } from '../shared/user-facing-reply.ts'; @@ -71,22 +70,14 @@ test('a turn that stopped to ask a question is not a preview that failed', () => assert.equal(outcome.reply, QUESTION_TURN); }); -test('skipping a Models API key still offers preview and deploy', () => { - assert.equal( - GATEWAY_CREDENTIALS_USER_REPLY.zh, - '项目已经写好。要调用大模型请在下方输入 Models API Key;跳过也可以先预览和部署。', - ); - assert.match(GATEWAY_CREDENTIALS_USER_REPLY.en, /skip to preview and deploy first/); -}); - -test('waiting for an API key is not a preview that failed', () => { +test('waiting for a clarifying question is not a preview that failed', () => { const outcome = resolveFinishedTurn({ filesWritten: true, previewUrl: '', buildFailed: false, waitingForUser: true, modelReply: 'AI 聊天助手已经做好了:支持流式输出、多轮对话。', - fallbackReply: GATEWAY_CREDENTIALS_USER_REPLY.zh, + fallbackReply: '已按你的需求完成。', failureReply: '项目已生成,但预览暂时不可用,请重试。', }); From 12b3ca13423c4d57ed80e7c24ea319adc933738e Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 16:41:27 +0800 Subject: [PATCH 13/26] fix(workspace): restore the original API key card copy Keep the host-owned BYOK flow, but show the previous title, docs link, and Skip/Continue labels. --- app/features/workspace/workspace-screen.tsx | 6 ++-- app/i18n.ts | 32 ++++++++++----------- tests/gateway-prompt.test.ts | 18 ++++++------ 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index e78fe55..cad33fe 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -425,9 +425,9 @@ export function WorkspaceScreen() { }} gatewayPrompt={workspace.gatewayNeeded ? { title: t.workspace.gatewayPromptTitle, - description: workspace.gatewayPromptVariant === 'deploy' - ? `${t.workspace.gatewayPromptDescription} ${t.workspace.gatewayPromptDeployHint}` - : t.workspace.gatewayPromptDescription, + ...(workspace.gatewayPromptVariant === 'deploy' + ? { description: t.workspace.gatewayPromptDeployHint } + : {}), docs: t.workspace.gatewayPromptDocs, docsUrl: makersModelsDocsUrl, apiKey: t.workspace.gatewayPromptApiKey, diff --git a/app/i18n.ts b/app/i18n.ts index 9c768d8..a253bc3 100644 --- a/app/i18n.ts +++ b/app/i18n.ts @@ -161,15 +161,14 @@ export const TRANSLATIONS = { deployOfferAgain: '项目有更新,要重新部署吗?', deployOfferAction: '部署', deployOfferDismiss: '暂不', - gatewayPromptTitle: '启用 AI 对话', - gatewayPromptDescription: '添加 Models API 密钥,即可在预览中试用对话。密钥只保存在此项目中,无需登录。', - gatewayPromptDeployHint: '发布到线上后,站点仍需使用这把密钥。', - gatewayPromptDocs: '如何获取密钥', - gatewayPromptApiKey: 'API 密钥', - gatewayPromptContinue: '添加', - gatewayPromptSkip: '稍后', - gatewayPromptChip: '添加 API 密钥', - gatewayPromptSaved: '已添加,可以在预览中试用对话。', + gatewayPromptTitle: '集成 Models 调用大模型', + gatewayPromptDeployHint: '上线后的站点也要这把 Key。', + gatewayPromptDocs: '如何获取', + gatewayPromptApiKey: 'API Key', + gatewayPromptContinue: '继续', + gatewayPromptSkip: '跳过', + gatewayPromptChip: '配置 API Key', + gatewayPromptSaved: '已写入,预览正在用这把 Key 重连。', preview: '预览', code: '代码', // The Claude JSONL file is the only history this product keeps. The chat @@ -357,15 +356,14 @@ export const TRANSLATIONS = { deployOfferAgain: 'The project has updates. Deploy again?', deployOfferAction: 'Deploy', deployOfferDismiss: 'Not now', - gatewayPromptTitle: 'Enable AI chat', - gatewayPromptDescription: "Add a Models API key to try the conversation in Preview. It stays with this project — there's no account.", - gatewayPromptDeployHint: 'A published site needs this key too.', - gatewayPromptDocs: 'How to get a key', + gatewayPromptTitle: 'Integrate Models to call large models', + gatewayPromptDeployHint: 'A live site needs this key too.', + gatewayPromptDocs: 'How to get them', gatewayPromptApiKey: 'API Key', - gatewayPromptContinue: 'Add', - gatewayPromptSkip: 'Not Now', - gatewayPromptChip: 'Add API Key', - gatewayPromptSaved: 'Added. You can try the chat in Preview.', + gatewayPromptContinue: 'Continue', + gatewayPromptSkip: 'Skip', + gatewayPromptChip: 'Set up API Key', + gatewayPromptSaved: 'Saved. Preview is reconnecting with this key.', preview: 'Preview', code: 'Code', session: 'Session', diff --git a/tests/gateway-prompt.test.ts b/tests/gateway-prompt.test.ts index 81c53a1..2856cd0 100644 --- a/tests/gateway-prompt.test.ts +++ b/tests/gateway-prompt.test.ts @@ -282,15 +282,15 @@ test('the conversation card is visible while generating and submits without a ch conversation.indexOf('className="gateway-prompt-actions"'), ); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptTitle, '启用 AI 对话'); - assert.equal( - TRANSLATIONS.zh.workspace.gatewayPromptDescription, - '添加 Models API 密钥,即可在预览中试用对话。密钥只保存在此项目中,无需登录。', - ); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptSkip, '稍后'); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptContinue, '添加'); - assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptDocs, '如何获取密钥'); - assert.equal(TRANSLATIONS.en.workspace.gatewayPromptDocs, 'How to get a key'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptTitle, '集成 Models 调用大模型'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptSkip, '跳过'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptContinue, '继续'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptDocs, '如何获取'); + assert.equal(TRANSLATIONS.zh.workspace.gatewayPromptApiKey, 'API Key'); + assert.equal(TRANSLATIONS.en.workspace.gatewayPromptTitle, 'Integrate Models to call large models'); + assert.equal(TRANSLATIONS.en.workspace.gatewayPromptSkip, 'Skip'); + assert.equal(TRANSLATIONS.en.workspace.gatewayPromptContinue, 'Continue'); + assert.equal(TRANSLATIONS.en.workspace.gatewayPromptDocs, 'How to get them'); assert.equal( getMakersModelsDocsUrl('edgeone.dev'), 'https://pages.edgeone.ai/document/models', From d8a8e04ab767019858c33e0d3d53fad2d2b8deae Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 16:52:27 +0800 Subject: [PATCH 14/26] fix(workspace): show the session tab only during local development The raw JSONL transcript is a debug surface; production should only offer Preview and Code. --- app/features/workspace/workspace-screen.tsx | 13 ++++++++----- app/i18n.ts | 7 ++++--- tests/route-consolidation.test.ts | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/features/workspace/workspace-screen.tsx b/app/features/workspace/workspace-screen.tsx index cad33fe..3717767 100644 --- a/app/features/workspace/workspace-screen.tsx +++ b/app/features/workspace/workspace-screen.tsx @@ -107,6 +107,7 @@ const SessionPanel = dynamic( () => import('@/app/components/session-panel').then((mod) => mod.SessionPanel), { ssr: false, loading: PanelLoading }, ); +const SHOW_SESSION_TAB = process.env.NODE_ENV === 'development'; export function WorkspaceScreen() { const [language, setLanguage] = useState('zh'); @@ -490,10 +491,12 @@ export function WorkspaceScreen() { {t.workspace.code} {workspace.filesRefreshing && {t.files.refreshing}} - - - {t.workspace.session} - + {SHOW_SESSION_TAB && ( + + + {t.workspace.session} + + )}
@@ -610,7 +613,7 @@ export function WorkspaceScreen() {
)} - {workspace.sandboxTab === 'session' && ( + {SHOW_SESSION_TAB && workspace.sandboxTab === 'session' && (
{ From 0347b25fff5db3f367af9b037de0e045bc6aa1f5 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 16:53:46 +0800 Subject: [PATCH 15/26] perf(workspace): stream the panel snapshot instead of refetching after a turn The chat stream already has the file tree and preview. A closing workspace event carries lastBuild without attaching sandbox again for GET /workspace. --- agents/_lib/project/gateway.ts | 2 +- agents/_lib/project/snapshot.ts | 31 ++++++--- agents/_lib/turn/chat.ts | 45 +++++++----- agents/_lib/turn/checkpoint.ts | 4 +- agents/_lib/turn/deploy.ts | 11 ++- agents/_lib/turn/result.ts | 11 ++- agents/_lib/types.ts | 2 +- app/features/workspace/hooks/use-live-turn.ts | 8 ++- shared/protocol.ts | 1 + tests/route-consolidation.test.ts | 69 +++++++++++++++++++ 10 files changed, 147 insertions(+), 37 deletions(-) diff --git a/agents/_lib/project/gateway.ts b/agents/_lib/project/gateway.ts index c0d53f7..202cabe 100644 --- a/agents/_lib/project/gateway.ts +++ b/agents/_lib/project/gateway.ts @@ -166,7 +166,7 @@ async function publishFileTreeAfterEnvWrite( }, }); } catch { - // The files panel refreshes again at the end of the turn. + // The files panel still receives the listing on this SSE when a turn is live. } } diff --git a/agents/_lib/project/snapshot.ts b/agents/_lib/project/snapshot.ts index 157d081..91d340c 100644 --- a/agents/_lib/project/snapshot.ts +++ b/agents/_lib/project/snapshot.ts @@ -24,6 +24,25 @@ function jsonResponse(obj: Record, status = 200) { }); } +/** Project panel payload from in-memory state. Pass `items` only after a listing. */ +export function workspaceSnapshotFromState( + conversationId: string, + state: ProjectState, + items?: FileTreeItem[], +): WorkspaceSnapshot { + const preview = previewLinkFromState(state); + const hasFiles = items?.some((item) => item.type === 'file') === true; + return { + ok: true, + conversation_id: conversationId, + ...(items ? { files: { root: state.appDir, items } } : {}), + ...(preview.url ? { preview } : {}), + deployment: state.deployment, + build: state.lastBuild, + ...(hasFiles ? { download: { url: '/download', filename: 'source.zip' } } : {}), + }; +} + export async function loadWorkspaceSnapshot( context: AgentContext, conversationId: string, @@ -35,17 +54,7 @@ export async function loadWorkspaceSnapshot( } catch { items = []; } - const hasFiles = items.some((item) => item.type === 'file'); - const preview = previewLinkFromState(state); - return { - ok: true, - conversation_id: conversationId, - files: { root: state.appDir, items }, - ...(preview.url ? { preview } : {}), - deployment: state.deployment, - build: state.lastBuild, - ...(hasFiles ? { download: { url: '/download', filename: 'source.zip' } } : {}), - }; + return workspaceSnapshotFromState(conversationId, state, items); } export async function runWorkspaceSnapshotPipeline(context: AgentContext): Promise { diff --git a/agents/_lib/turn/chat.ts b/agents/_lib/turn/chat.ts index 94a4bec..2004c82 100644 --- a/agents/_lib/turn/chat.ts +++ b/agents/_lib/turn/chat.ts @@ -10,9 +10,11 @@ import { setDeployment, setLastBuild, } from '../project/workspace-store.ts'; +import { workspaceSnapshotFromState } from '../project/snapshot.ts'; import type { AgentProgressEvent, DeploymentInfo, + FileTreeItem, StreamSend, } from '../types.ts'; import { toAppRelPath } from '../utils/paths.ts'; @@ -148,6 +150,17 @@ export async function runChatPipeline( send(event); }; const fileTreePush = createFileTreePushController(context, state, send); + let flushedItems: FileTreeItem[] | undefined; + const rememberTree = (items: FileTreeItem[]) => { + if (items.length > 0) flushedItems = items; + }; + const finishResult = (extra: Omit) => { + sendTurnResult( + send, + slimResult(conversationId, extra), + workspaceSnapshotFromState(conversationId, state, flushedItems), + ); + }; const handleProjectFilesChanged = async (file?: { path: string; content: string }) => { if (file) { const path = toAppRelPath(file.path, state.appDir) || file.path; @@ -234,11 +247,11 @@ export async function runChatPipeline( await finalizeTurn(stoppedReply, 'stopped', { withSnapshot: modelResult.projectTouched, }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: false, stopped: true, reply: stoppedReply, - })); + }); return; } @@ -278,11 +291,11 @@ export async function runChatPipeline( await finalizeTurn(assistantReply, 'failed', { withSnapshot: modelResult.projectTouched, }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: false, reply: assistantReply, error: modelResult.error || undefined, - })); + }); return; } @@ -309,10 +322,10 @@ export async function runChatPipeline( await finalizeTurn(assistantReply, operationOk ? 'completed' : 'failed', { withState: Boolean(state.previewUrl) || modelResult.deploymentTouched, }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: operationOk, reply: assistantReply, - })); + }); return; } @@ -323,7 +336,7 @@ export async function runChatPipeline( previewVerified = await startHostPreview('[preview] host start failed:'); } - await fileTreePush.flush('Failed to read the file list.'); + rememberTree(await fileTreePush.flush('Failed to read the file list.')); let build = await runVerification(context, state, { previewVerified, }); @@ -336,10 +349,10 @@ export async function runChatPipeline( await persistWorkspace(context, conversationId, state); const fatalReply = build.stderr || 'The task failed, and the remaining workflow was stopped.'; await finalizeTurn(fatalReply, 'failed', { withSnapshot: true }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: false, reply: fatalReply, - })); + }); return; } @@ -364,11 +377,11 @@ export async function runChatPipeline( if (autoFixResult.stopped || abortSignal?.aborted) { const stoppedReply = STOPPED_TURN_REPLY[replyLocale]; await finalizeTurn(stoppedReply, 'stopped', { withSnapshot: true }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: false, stopped: true, reply: stoppedReply, - })); + }); return; } const rawAutoFixReply = stripReturnedPreviewLinks(sanitizeAssistantText( @@ -394,17 +407,17 @@ export async function runChatPipeline( }); } - await fileTreePush.flush('Failed to read the file list after auto-fix.'); + rememberTree(await fileTreePush.flush('Failed to read the file list after auto-fix.')); build = await runVerification(context, state); if (build.fatal) { setLastBuild(state, build); await persistWorkspace(context, conversationId, state); const fatalReply = build.stderr || 'The task failed, and the remaining workflow was stopped.'; await finalizeTurn(fatalReply, 'failed', { withSnapshot: true }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: false, reply: fatalReply, - })); + }); return; } @@ -455,8 +468,8 @@ export async function runChatPipeline( const turnOk = modelResult.success && !turnFailed; await finalizeTurn(reply, turnOk ? 'completed' : 'failed', { withSnapshot: true }); - sendTurnResult(send, slimResult(conversationId, { + finishResult({ ok: turnOk, reply, - })); + }); } diff --git a/agents/_lib/turn/checkpoint.ts b/agents/_lib/turn/checkpoint.ts index 850229b..26d22b9 100644 --- a/agents/_lib/turn/checkpoint.ts +++ b/agents/_lib/turn/checkpoint.ts @@ -264,7 +264,9 @@ export function createFileTreePushController( }); return items; } catch (error) { - // Non-fatal: the turn pushes the final tree again when it completes. + // Non-fatal: the turn reuses whatever listing it already has for the + // closing workspace event, and GET /workspace remains a pull fallback. + console.warn('[file-tree]', error instanceof Error ? error.message : fallbackMessage); return []; } diff --git a/agents/_lib/turn/deploy.ts b/agents/_lib/turn/deploy.ts index 2824e3f..b5c39b0 100644 --- a/agents/_lib/turn/deploy.ts +++ b/agents/_lib/turn/deploy.ts @@ -17,9 +17,11 @@ import { import { resolveGatewayUserTurn } from '../../../shared/gateway-secret.ts'; import { describeMissingMakersRuntimeToken } from '../makers/token.ts'; import { prepareMakersSession } from '../makers/session.ts'; +import { workspaceSnapshotFromState } from '../project/snapshot.ts'; import type { AgentProgressEvent, DeploymentInfo, + FileTreeItem, StreamSend, } from '../types.ts'; import { @@ -214,16 +216,21 @@ export async function runDeployPipeline( // No `build` field: publishing runs no verification, and reporting one would // clear whatever the last generation said about the project. + let files: FileTreeItem[] = []; const finish = async (reply: string, status: 'completed' | 'failed') => { await turn.finalize(reply, status); sendTurnResult(send, { ok: status === 'completed', reply, conversation_id: conversationId, - }); + }, workspaceSnapshotFromState( + conversationId, + state, + files.length > 0 ? files : undefined, + )); }; - const files = await getFileTree(context, state).catch(() => []); + files = await getFileTree(context, state).catch(() => []); if (!files.some((item) => item.type === 'file')) { await finish(copy.noProject, 'failed'); return; diff --git a/agents/_lib/turn/result.ts b/agents/_lib/turn/result.ts index 8225ffd..f62410f 100644 --- a/agents/_lib/turn/result.ts +++ b/agents/_lib/turn/result.ts @@ -1,6 +1,13 @@ -import type { ChatResponse } from '../../../shared/protocol.ts'; +import type { ChatResponse, WorkspaceSnapshot } from '../../../shared/protocol.ts'; import type { StreamSend } from '../types.ts'; -export function sendTurnResult(send: StreamSend, data: ChatResponse) { +export function sendTurnResult( + send: StreamSend, + data: ChatResponse, + snapshot?: WorkspaceSnapshot, +) { + if (snapshot) { + send({ type: 'workspace', data: snapshot }); + } send({ type: 'result', data }); } diff --git a/agents/_lib/types.ts b/agents/_lib/types.ts index c45ff9c..f7103f9 100644 --- a/agents/_lib/types.ts +++ b/agents/_lib/types.ts @@ -35,7 +35,7 @@ export type ProjectState = { previewKind?: PreviewKind; /** Latest live deployment, kept separate from the sandbox preview iframe. */ deployment?: DeploymentInfo; - /** Last verification result; GET /workspace exposes it independently of the chat stream. */ + /** Last verification result; streamed on `workspace` and also on GET /workspace. */ lastBuild?: BuildInfo; /** The host is waiting for a Models API key in the next user turn. */ gatewayPromptPending?: boolean; diff --git a/app/features/workspace/hooks/use-live-turn.ts b/app/features/workspace/hooks/use-live-turn.ts index 794d953..ba2f2d7 100644 --- a/app/features/workspace/hooks/use-live-turn.ts +++ b/app/features/workspace/hooks/use-live-turn.ts @@ -203,8 +203,6 @@ export function useLiveTurn(options: { const finalText = data.reply || data.error || t.response.noDisplay; const finalStatus: AssistantStatus = data.stopped ? 'stopped' : data.ok === false ? 'error' : 'done'; finalizeAssistant(finalText, finalStatus); - const cid = data.conversation_id || sessionOptions.requestConversationId; - if (cid) void snapshot.refresh(cid); }; const handleStreamEvent = (event: ChatStreamEvent) => { @@ -237,6 +235,11 @@ export function useLiveTurn(options: { } return; } + if (event.type === 'workspace' && event.data) { + snapshot.applySnapshot(event.data); + workspace.setFilesRefreshing(false); + return; + } if (event.type === 'result' && event.data) { applyResponse(event.data); setLoading(false); @@ -588,7 +591,6 @@ export function useLiveTurn(options: { if (data.download) { workspace.setDownload(data.download); } - void snapshot.refresh(cid); } catch { workspace.setGatewayBusy(false); } diff --git a/shared/protocol.ts b/shared/protocol.ts index 400119f..09b5e27 100644 --- a/shared/protocol.ts +++ b/shared/protocol.ts @@ -173,6 +173,7 @@ export type ChatStreamEvent = } | { type: 'result'; data?: ChatResponse } | { type: 'agent'; data?: Pick } + | { type: 'workspace'; data?: WorkspaceSnapshot } | { type: 'file_tree'; data?: FileTree } | { type: 'file_changed'; diff --git a/tests/route-consolidation.test.ts b/tests/route-consolidation.test.ts index ea45659..3f260f1 100644 --- a/tests/route-consolidation.test.ts +++ b/tests/route-consolidation.test.ts @@ -1,6 +1,7 @@ import assert from 'node:assert/strict'; import { access, readFile } from 'node:fs/promises'; import test from 'node:test'; +import { workspaceSnapshotFromState } from '../agents/_lib/project/snapshot.ts'; test('the model menu is an edge function, not an agent route', async () => { const route = await readFile('edge-functions/models.ts', 'utf8'); @@ -163,10 +164,78 @@ test('workspace snapshot and preview status are pullable without the chat stream const workspace = await readFile('agents/workspace.ts', 'utf8'); const preview = await readFile('agents/preview.ts', 'utf8'); const client = await readFile('app/features/workspace/workspace-api.ts', 'utf8'); + const previewSurface = await readFile('app/features/workspace/hooks/use-preview-surface.ts', 'utf8'); assert.match(workspace, /onRequestGet/); assert.match(workspace, /runWorkspaceSnapshotPipeline/); assert.match(preview, /onRequestGet/); assert.match(client, /fetch\('\/workspace'/); assert.match(client, /fetch\(`\/file\?paths=/); + assert.match(previewSurface, /void options\.refreshWorkspace\?\.\(id\)/); +}); + +test('a finished turn streams the workspace snapshot instead of GET /workspace', async () => { + const [ + protocol, + result, + chat, + deploy, + snapshot, + live, + ] = await Promise.all([ + readFile('shared/protocol.ts', 'utf8'), + readFile('agents/_lib/turn/result.ts', 'utf8'), + readFile('agents/_lib/turn/chat.ts', 'utf8'), + readFile('agents/_lib/turn/deploy.ts', 'utf8'), + readFile('agents/_lib/project/snapshot.ts', 'utf8'), + readFile('app/features/workspace/hooks/use-live-turn.ts', 'utf8'), + ]); + + assert.match(protocol, /type: 'workspace'; data\?: WorkspaceSnapshot/); + assert.match(result, /type: 'workspace'/); + assert.match(snapshot, /export function workspaceSnapshotFromState/); + assert.match(chat, /workspaceSnapshotFromState\(conversationId, state, flushedItems\)/); + assert.match(chat, /rememberTree\(await fileTreePush\.flush/); + assert.match(deploy, /workspaceSnapshotFromState\(/); + assert.match(live, /event\.type === 'workspace' && event\.data/); + assert.match(live, /snapshot\.applySnapshot\(event\.data\)/); + + const applyResponse = live.slice( + live.indexOf('const applyResponse'), + live.indexOf('const handleStreamEvent'), + ); + assert.doesNotMatch(applyResponse, /snapshot\.refresh/); + + const applyGateway = live.slice(live.indexOf('async function applyGateway')); + assert.doesNotMatch(applyGateway, /snapshot\.refresh/); +}); + +test('workspaceSnapshotFromState reuses a listing and skips files when none was passed', () => { + const withFiles = workspaceSnapshotFromState('c1', { + created: true, + sessionDir: 'projects/c1', + appDir: 'projects/c1/app', + previewUrl: 'https://preview.example', + previewKind: 'sandbox', + lastBuild: { status: 'failed', stderr: 'boom' }, + deployment: { status: 'success', startedAt: 1, url: 'https://live.example' }, + }, [{ path: 'index.html', name: 'index.html', type: 'file', depth: 0 }]); + + assert.equal(withFiles.ok, true); + assert.equal(withFiles.conversation_id, 'c1'); + assert.equal(withFiles.files?.items.length, 1); + assert.equal(withFiles.preview?.url, 'https://preview.example'); + assert.equal(withFiles.download?.url, '/download'); + assert.equal(withFiles.build?.status, 'failed'); + assert.equal(withFiles.deployment?.url, 'https://live.example'); + + const metaOnly = workspaceSnapshotFromState('c1', { + created: true, + sessionDir: 'projects/c1', + appDir: 'projects/c1/app', + lastBuild: { status: 'success' }, + }); + assert.equal(metaOnly.files, undefined); + assert.equal(metaOnly.download, undefined); + assert.equal(metaOnly.build?.status, 'success'); }); From 35ab8579c6c61bc63fe701962af99cee0a7f5442 Mon Sep 17 00:00:00 2001 From: xindeli Date: Fri, 18 Sep 2026 17:13:28 +0800 Subject: [PATCH 16/26] refactor(workspace): split oversized UI files into feature modules Keep conversation, files, result panel, and live-turn as small modules under features/workspace so the screen only orchestrates. Tests now read a directory surface, and architecture checks stop those files from growing back together. --- app/components/agent-conversation.tsx | 527 ------------------ app/components/files-panel.tsx | 506 ----------------- .../conversation/activity-blocks.tsx | 101 ++++ .../conversation/assistant-turn.tsx | 62 +++ .../components/conversation/composer.tsx | 70 +++ .../components/conversation/deploy-offer.tsx | 36 ++ .../conversation/gateway-prompt.tsx | 111 ++++ .../components/conversation/index.tsx | 142 +++++ .../components/conversation/markdown.tsx | 92 +++ .../components/conversation/types.ts | 46 ++ .../workspace/components/files/code-theme.ts | 72 +++ .../components/files/file-content-view.tsx | 87 +++ .../workspace/components/files/file-tree.tsx | 110 ++++ .../workspace/components/files/format.ts | 17 + .../workspace/components/files/index.tsx | 71 +++ .../components/files/use-file-preview.ts | 197 +++++++ .../workspace/components/home-stage.tsx | 2 +- .../workspace/components/lazy-panels.tsx | 29 + .../workspace}/components/model-picker.tsx | 2 +- .../components/new-project-dialog.tsx | 53 ++ .../components/result-panel/files-pane.tsx | 34 ++ .../components/result-panel/index.tsx | 125 +++++ .../components/result-panel/preview-pane.tsx | 68 +++ .../result-panel/result-panel-toggle.tsx | 30 + .../result-panel/result-panel-topbar.tsx | 135 +++++ .../workspace}/components/session-panel.tsx | 10 +- .../workspace/hooks/live/session-prep.ts | 45 ++ .../workspace/hooks/live/stream-handlers.ts | 259 +++++++++ .../workspace/hooks/live/turn-messages.ts | 88 +++ .../workspace/hooks/live/use-gateway.ts | 68 +++ .../workspace/hooks/preview-identity.ts | 21 + .../workspace/hooks/use-deploy-offer.ts | 62 +++ app/features/workspace/hooks/use-live-turn.ts | 401 +++---------- .../workspace/hooks/use-new-project.ts | 78 +++ .../workspace/hooks/use-platform-links.ts | 42 ++ .../workspace/hooks/use-preview-refresh.ts | 172 ++++++ .../workspace/hooks/use-preview-surface.ts | 183 ++---- .../workspace/hooks/use-workspace-copy.ts | 53 ++ app/features/workspace/workspace-screen.tsx | 479 +++------------- app/i18n.ts | 446 +-------------- app/i18n/en.ts | 193 +++++++ app/i18n/types.ts | 27 + app/i18n/zh.ts | 222 ++++++++ tests/app-shell.test.ts | 42 +- tests/architecture.test.ts | 38 ++ tests/deploy-task.test.ts | 41 +- tests/gateway-prompt.test.ts | 9 +- tests/helpers/source.ts | 69 +++ tests/makers-compat.test.ts | 3 +- tests/models.test.ts | 9 +- tests/preview-path.test.ts | 15 +- tests/route-consolidation.test.ts | 49 +- tests/session-prep.test.ts | 14 +- tests/turn-messages.test.ts | 112 ++++ 54 files changed, 3506 insertions(+), 2469 deletions(-) delete mode 100644 app/components/agent-conversation.tsx delete mode 100644 app/components/files-panel.tsx create mode 100644 app/features/workspace/components/conversation/activity-blocks.tsx create mode 100644 app/features/workspace/components/conversation/assistant-turn.tsx create mode 100644 app/features/workspace/components/conversation/composer.tsx create mode 100644 app/features/workspace/components/conversation/deploy-offer.tsx create mode 100644 app/features/workspace/components/conversation/gateway-prompt.tsx create mode 100644 app/features/workspace/components/conversation/index.tsx create mode 100644 app/features/workspace/components/conversation/markdown.tsx create mode 100644 app/features/workspace/components/conversation/types.ts create mode 100644 app/features/workspace/components/files/code-theme.ts create mode 100644 app/features/workspace/components/files/file-content-view.tsx create mode 100644 app/features/workspace/components/files/file-tree.tsx create mode 100644 app/features/workspace/components/files/format.ts create mode 100644 app/features/workspace/components/files/index.tsx create mode 100644 app/features/workspace/components/files/use-file-preview.ts create mode 100644 app/features/workspace/components/lazy-panels.tsx rename app/{ => features/workspace}/components/model-picker.tsx (98%) create mode 100644 app/features/workspace/components/new-project-dialog.tsx create mode 100644 app/features/workspace/components/result-panel/files-pane.tsx create mode 100644 app/features/workspace/components/result-panel/index.tsx create mode 100644 app/features/workspace/components/result-panel/preview-pane.tsx create mode 100644 app/features/workspace/components/result-panel/result-panel-toggle.tsx create mode 100644 app/features/workspace/components/result-panel/result-panel-topbar.tsx rename app/{ => features/workspace}/components/session-panel.tsx (95%) create mode 100644 app/features/workspace/hooks/live/session-prep.ts create mode 100644 app/features/workspace/hooks/live/stream-handlers.ts create mode 100644 app/features/workspace/hooks/live/turn-messages.ts create mode 100644 app/features/workspace/hooks/live/use-gateway.ts create mode 100644 app/features/workspace/hooks/preview-identity.ts create mode 100644 app/features/workspace/hooks/use-deploy-offer.ts create mode 100644 app/features/workspace/hooks/use-new-project.ts create mode 100644 app/features/workspace/hooks/use-platform-links.ts create mode 100644 app/features/workspace/hooks/use-preview-refresh.ts create mode 100644 app/features/workspace/hooks/use-workspace-copy.ts create mode 100644 app/i18n/en.ts create mode 100644 app/i18n/types.ts create mode 100644 app/i18n/zh.ts create mode 100644 tests/helpers/source.ts create mode 100644 tests/turn-messages.test.ts diff --git a/app/components/agent-conversation.tsx b/app/components/agent-conversation.tsx deleted file mode 100644 index 12e27bb..0000000 --- a/app/components/agent-conversation.tsx +++ /dev/null @@ -1,527 +0,0 @@ -'use client'; - -import { FormEvent, ReactNode, memo, useEffect, useMemo, useRef, useState } from 'react'; -import { - ArrowUp, - Check, - CircleAlert, - Copy, - Square, -} from 'lucide-react'; -import ReactMarkdown from 'react-markdown'; -import remarkGfm from 'remark-gfm'; -import { - buildAssistantTimeline, - lastTimelineText, - trailingTimelineContent, -} from '../lib/assistant-timeline'; -import { withoutPlatformName } from '../../shared/platform-name'; -import { ModelPicker } from './model-picker'; -import type { AssistantActivity } from '../../shared/protocol'; -import type { ModelOption } from '../../shared/models'; - -export type ConversationMessage = { - id: string; - role: 'user' | 'assistant'; - content: string; - activities?: AssistantActivity[]; - status?: 'running' | 'done' | 'error' | 'stopped'; -}; - -type ConversationCopy = { - running: string; - completed: string; - failed: string; - stopped: string; - thinking: string; - info: string; - usage: string; - compact: string; - status: string; - placeholder: string; - send: string; - stop: string; - modelLabel: string; - copyLink: string; - linkCopied: string; -}; - -export type DeployOfferCopy = { - prompt: string; - deploy: string; - dismiss: string; -}; - -export type GatewayPromptCopy = { - title: string; - description?: string; - docs: string; - docsUrl: string; - apiKey: string; - continue: string; - skip: string; -}; - -function infoLabel( - infoType: Extract['infoType'], - copy: ConversationCopy, -) { - if (infoType === 'usage') return copy.usage; - if (infoType === 'compact') return copy.compact; - if (infoType === 'status') return copy.status; - return copy.info; -} - -function statusLabel(status: Extract['status'], copy: ConversationCopy) { - if (status === 'running') return copy.running; - if (status === 'failed') return copy.failed; - if (status === 'stopped') return copy.stopped; - return copy.completed; -} - -function formatTimestamp(value?: number) { - if (!value) return ''; - try { - return new Date(value).toISOString(); - } catch { - return String(value); - } -} - -function maybeJson(value?: string) { - if (!value) return undefined; - const trimmed = value.trim(); - if (trimmed.startsWith('{') || trimmed.startsWith('[')) { - try { - return JSON.parse(trimmed); - } catch { - return value; - } - } - return value; -} - -function formatToolDump(activity: Extract, copy: ConversationCopy) { - return JSON.stringify({ - name: activity.name, - id: activity.toolUseId, - status: activity.status, - statusLabel: statusLabel(activity.status, copy), - command: activity.command || undefined, - phaseHint: activity.phaseHint || undefined, - fileCount: activity.fileCount, - startedAt: formatTimestamp(activity.startedAt) || undefined, - endedAt: formatTimestamp(activity.endedAt) || undefined, - durationMs: activity.startedAt && activity.endedAt - ? activity.endedAt - activity.startedAt - : undefined, - input: maybeJson(activity.inputSummary), - output: maybeJson(activity.outputSummary), - }, null, 2); -} - -function ThinkingBlock({ content, copy }: { content: string; copy: ConversationCopy }) { - return ( -
- {copy.thinking} -
{content}
-
- ); -} - -function InfoBlock({ - activity, - copy, -}: { - activity: Extract; - copy: ConversationCopy; -}) { - const label = infoLabel(activity.infoType, copy); - const title = activity.title && activity.title !== label ? `${label} · ${activity.title}` : label; - return ( -
- {title} - {activity.content ?
{activity.content}
: null} -
- ); -} - -function ToolBlock({ - activity, - copy, -}: { - activity: Extract; - copy: ConversationCopy; -}) { - return ( -
- {activity.name} · {statusLabel(activity.status, copy)} -
{formatToolDump(activity, copy)}
-
- ); -} - -function plainText(node: ReactNode): string { - if (typeof node === 'string') return node; - if (typeof node === 'number') return String(node); - if (Array.isArray(node)) return node.map(plainText).join(''); - return ''; -} - -function ConversationLink({ href, copy, children }: { - href?: string; - copy: ConversationCopy; - children?: ReactNode; -}) { - const [copied, setCopied] = useState(false); - const url = href || ''; - // An address spelled out in full is something the user takes elsewhere. A link - // behind words is meant to be followed, and a button beside it would only - // crowd the sentence it sits in. - const isAddress = Boolean(url) && plainText(children).trim() === url; - - useEffect(() => { - if (!copied) return; - const timer = window.setTimeout(() => setCopied(false), 1600); - return () => window.clearTimeout(timer); - }, [copied]); - - const anchor = ( -
- {children} - - ); - - if (!isAddress) { - return anchor; - } - - const label = copied ? copy.linkCopied : copy.copyLink; - const handleCopy = async () => { - if (!navigator.clipboard) return; - try { - await navigator.clipboard.writeText(url); - setCopied(true); - } catch { - setCopied(false); - } - }; - - return ( - - {anchor} - - - ); -} - -function Markdown({ content, copy }: { content: string; copy: ConversationCopy }) { - return ( -
- ( - {children} - ), - }} - > - {withoutPlatformName(content)} - -
- ); -} - -const AssistantTurn = memo(function AssistantTurn({ message, copy }: { - message: ConversationMessage; - copy: ConversationCopy; -}) { - const activities = message.activities ?? []; - const blocks = useMemo(() => buildAssistantTimeline(activities), [activities]); - const lastText = lastTimelineText(blocks); - const trailing = trailingTimelineContent(lastText?.content, message.content, message.status); - const hasRunningTool = activities.some( - (activity) => activity.kind === 'tool' && activity.status === 'running', - ); - - return ( -
-
- {blocks.map((block) => { - if (block.kind === 'text') { - return ; - } - if (block.kind === 'thinking') { - return ; - } - if (block.kind === 'info') { - return ; - } - return ; - })} - {trailing && ( - message.status === 'error' ? ( -
-
- ) : ( - - ) - )} - {message.status === 'running' && !hasRunningTool && ( -
- - - -
- )} -
-
- ); -}); - -export function AgentConversation({ - messages, - input, - loading, - canSend, - compact, - copy, - models, - model, - onModelChange, - onInputChange, - onSubmit, - onStop, - deployOffer, - onDeployOffer, - onDismissDeployOffer, - gatewayPrompt, - gatewayChip, - gatewaySaved, - gatewayBusy, - onGatewaySubmit, - onGatewaySkip, - onGatewayReopen, -}: { - messages: ConversationMessage[]; - input: string; - loading: boolean; - canSend: boolean; - compact: boolean; - copy: ConversationCopy; - models: readonly ModelOption[]; - model: string; - onModelChange: (model: string) => void; - onInputChange: (value: string) => void; - onSubmit: () => void; - onStop: () => void; - deployOffer?: DeployOfferCopy | null; - onDeployOffer?: () => void; - onDismissDeployOffer?: () => void; - gatewayPrompt?: GatewayPromptCopy | null; - gatewayChip?: string | null; - gatewaySaved?: string | null; - gatewayBusy?: boolean; - onGatewaySubmit?: (values: { apiKey: string }) => void; - onGatewaySkip?: () => void; - onGatewayReopen?: () => void; -}) { - const [gatewayApiKey, setGatewayApiKey] = useState(''); - const gatewayInputRef = useRef(null); - const gatewayVisible = Boolean(gatewayPrompt); - - useEffect(() => { - if (!gatewayVisible) { - setGatewayApiKey(''); - return; - } - const node = gatewayInputRef.current; - if (!node || node.disabled) return; - node.focus(); - }, [gatewayVisible]); - const scrollRef = useRef(null); - const followOutputRef = useRef(true); - const signature = messages.map((message) => [ - message.id, - message.status, - message.content, - message.activities?.map((activity) => { - if (activity.kind === 'text' || activity.kind === 'thinking') return activity.content; - if (activity.kind === 'info') return `${activity.infoType}:${activity.content}`; - return `${activity.toolUseId}:${activity.status}:${activity.inputSummary || ''}:${activity.outputSummary || ''}`; - }).join('|'), - ].join(':')).join('\n'); - - useEffect(() => { - const node = scrollRef.current; - if (node && followOutputRef.current) node.scrollTop = node.scrollHeight; - }, [signature]); - - const submit = (event: FormEvent) => { - event.preventDefault(); - onSubmit(); - }; - - return ( -
-
{ - const node = event.currentTarget; - followOutputRef.current = node.scrollHeight - node.scrollTop - node.clientHeight < 72; - }} - > -
- {messages.map((message) => message.role === 'user' ? ( -
-
{message.content}
-
- ) : ( - - ))} -
-
-
- {gatewayPrompt && ( -
{ - event.preventDefault(); - if (gatewayBusy) return; - onGatewaySubmit?.({ - apiKey: gatewayApiKey.trim(), - }); - }} - > -
-

{gatewayPrompt.title}

- {gatewayPrompt.description && ( -

{gatewayPrompt.description}

- )} -

- - {gatewayPrompt.docs} - -

-
- -
- - -
-
- )} - {!gatewayPrompt && gatewayChip && ( - - )} - {!gatewayPrompt && !gatewayChip && gatewaySaved && ( -

{gatewaySaved}

- )} - {deployOffer && ( -
- {deployOffer.prompt} -
- - -
-
- )} -
-