Compare commits

..

2 Commits

Author SHA1 Message Date
github-actions[bot] 6716188e10 Release @llamaindex/server@0.0.9 (#1830)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-04-09 17:44:13 +07:00
Thuc Pham 0b75bd6d92 feat: component dir in llamaindex server (#1828) 2025-04-09 17:25:21 +07:00
16 changed files with 472 additions and 221 deletions
@@ -0,0 +1,159 @@
---
title: Using LlamaIndex Server
description: Running LlamaIndex workflows with both API endpoints and a user interface for interaction
---
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
# LlamaIndex Server
LlamaIndexServer is a Next.js-based application that allows you to quickly launch your [LlamaIndex Workflows](https://ts.llamaindex.ai/docs/llamaindex/modules/agents/workflows) and [Agent Workflows](https://ts.llamaindex.ai/docs/llamaindex/modules/agents/agent_workflow) as an API server with an optional chat UI. It provides a complete environment for running LlamaIndex workflows with both API endpoints and a user interface for interaction.
## Features
- Serving a workflow as a chatbot
- Built on Next.js for high performance and easy API development
- Optional built-in chat UI with extendable UI components
- Prebuilt development code
## Installation
<Tabs groupId="install" items={["npm", "yarn", "pnpm"]} persist>
```shell tab="npm"
npm install @llamaindex/server
```
```shell tab="yarn"
yarn add @llamaindex/server
```
```shell tab="pnpm"
pnpm add @llamaindex/server
```
</Tabs>
## Quick Start
Create index.ts file and add the following code:
```ts
import { LlamaIndexServer } from "@llamaindex/server";
import { wiki } from "@llamaindex/tools"; // or any other tool
const createWorkflow = () => agent({ tools: [wiki()] })
new LlamaIndexServer({
workflow: createWorkflow,
appTitle: "LlamaIndex App",
starterQuestions: ["Who is the first president of the United States?"],
}).start();
```
## Running the Server
In the same directory as `index.ts`, run the following command to start the server:
```bash
tsx index.ts
```
The server will start at `http://localhost:3000`
You can also make a request to the server:
```bash
curl -X POST "http://localhost:3000/api/chat" -H "Content-Type: application/json" -d '{"message": "Who is the first president of the United States?"}'
```
## Configuration Options
The LlamaIndexServer accepts the following configuration
- `workflow`: A callable function that creates a workflow instance for each request
- `starterQuestions`: List of starter questions for the chat UI
- `appTitle`: The title of the application
- `componentsDir`: The directory for custom UI components rendering events emitted by the workflow. The default is undefined, which does not render custom UI components.
LlamaIndexServer accepts all the configuration options from Nextjs Custom Server such as `port`, `hostname`, `dev`, etc.
See all Nextjs Custom Server options [here](https://nextjs.org/docs/app/building-your-application/configuring/custom-server).
## Default Endpoints and Features
### Chat Endpoint
The server includes a default chat endpoint at `/api/chat` for handling chat interactions.
### Chat UI
The server always provides a chat interface at the root path (`/`) with:
- Configurable starter questions
- Real-time chat interface
- API endpoint integration
### Static File Serving
- The server automatically mounts the `data` and `output` folders at `{server_url}{api_prefix}/files/data` (default: `/api/files/data`) and `{server_url}{api_prefix}/files/output` (default: `/api/files/output`) respectively.
- Your workflows can use both folders to store and access files. As a convention, the `data` folder is used for documents that are ingested and the `output` folder is used for documents that are generated by the workflow.
## Custom UI Components
The LlamaIndex server provides support for rendering workflow events using custom UI components, allowing you to extend and customize the chat interface.
### Overview
Custom UI components are a powerful feature that enables you to:
- Add custom interface elements to the chat UI using React JSX or TSX files
- Extend the default chat interface functionality
- Create specialized visualizations or interactions
### Configuration
Your workflow must emit events that fit this structure, allowing the LlamaIndex server to display the right UI components based on the event type.
```json
{
"type": "<event_name>",
"data": <data model>
}
```
### Server Setup
1. Initialize the LlamaIndex server with a component directory:
```ts
new LlamaIndexServer({
workflow: workflowFactory,
appTitle: "LlamaIndex App",
componentsDir: "components",
}).start();
```
2. Add the custom component code to the directory following the naming pattern:
- File Extension: `.jsx` and `.tsx` for React components
- File Name: Should match the event type from your workflow (e.g., `deep_research_event.jsx` for handling `deep_research_event` type that you defined in your workflow). If there are TSX and JSX files with the same name, the TSX file will be used.
- Component Name: Export a default React component named `Component` that receives props from the event data
Example component structure:
```jsx
function Component({ events }) {
// Your component logic here
return (
// Your UI code here
);
}
```
## Best Practices
1. Always provide a workflow factory that creates fresh workflow instances
2. Use environment variables for sensitive configuration
3. Use starter questions to guide users in the chat UI
## Getting Started with a New Project
Want to start a new project with LlamaIndexServer? Check out our [create-llama](https://github.com/run-llama/create-llama) tool to quickly generate a new project with LlamaIndexServer.
@@ -2,5 +2,5 @@
"title": "Chat UI",
"description": "Use chat-ui to add a chat interface to your LlamaIndexTS application.",
"defaultOpen": false,
"pages": ["install", "chat", "rsc"]
"pages": ["install", "chat", "rsc", "llamaindex-server"]
}
+6
View File
@@ -1,5 +1,11 @@
# @llamaindex/server
## 0.0.9
### Patch Changes
- 0b75bd6: feat: component dir in llamaindex server
## 0.0.8
### Patch Changes
@@ -4,12 +4,25 @@ import { ChatSection as ChatSectionUI } from "@llamaindex/chat-ui";
import "@llamaindex/chat-ui/styles/markdown.css";
import "@llamaindex/chat-ui/styles/pdf.css";
import { useChat } from "ai/react";
import { useEffect, useState } from "react";
import Header from "./header";
import CustomChatInput from "./ui/chat/chat-input";
import CustomChatMessages from "./ui/chat/chat-messages";
import {
ComponentDef,
fetchComponentDefinitions,
} from "./ui/chat/dynamic-events";
import { getConfig } from "./ui/lib/utils";
export default function ChatSection() {
const [componentDefs, setComponentDefs] = useState<ComponentDef[]>([]);
// fetch component definitions and use Babel to tranform JSX code to JS code
// this is triggered only once when the page is initialised
useEffect(() => {
fetchComponentDefinitions().then(setComponentDefs);
}, []);
const handler = useChat({
api: getConfig("CHAT_API"),
onError: (error: unknown) => {
@@ -28,7 +41,7 @@ export default function ChatSection() {
<div className="flex h-[85vh] w-full flex-col gap-2">
<Header />
<ChatSectionUI handler={handler} className="min-h-0 w-full flex-1">
<CustomChatMessages />
<CustomChatMessages componentDefs={componentDefs} />
<CustomChatInput />
</ChatSectionUI>
</div>
@@ -1,17 +1,21 @@
"use client";
import { ChatMessage } from "@llamaindex/chat-ui";
import { DeepResearchCard } from "./custom/deep-research-card";
import { ComponentDef, DynamicEvents } from "./dynamic-events";
import { ToolAnnotations } from "./tools/chat-tools";
export function ChatMessageContent() {
export function ChatMessageContent({
componentDefs,
}: {
componentDefs: ComponentDef[];
}) {
return (
<ChatMessage.Content>
<ChatMessage.Content.Event />
<ChatMessage.Content.AgentEvent />
<DeepResearchCard />
<ToolAnnotations />
<ChatMessage.Content.Image />
<DynamicEvents componentDefs={componentDefs} />
<ChatMessage.Content.Markdown />
<ChatMessage.Content.DocumentFile />
<ChatMessage.Content.Source />
@@ -4,9 +4,15 @@ import { ChatMessage, ChatMessages, useChatUI } from "@llamaindex/chat-ui";
import { ChatMessageAvatar } from "./chat-avatar";
import { ChatMessageContent } from "./chat-message-content";
import { ChatStarter } from "./chat-starter";
import { ComponentDef } from "./dynamic-events";
export default function CustomChatMessages() {
export default function CustomChatMessages({
componentDefs,
}: {
componentDefs: ComponentDef[];
}) {
const { messages } = useChatUI();
return (
<ChatMessages className="rounded-xl shadow-xl">
<ChatMessages.List>
@@ -17,7 +23,7 @@ export default function CustomChatMessages() {
isLast={index === messages.length - 1}
>
<ChatMessageAvatar />
<ChatMessageContent />
<ChatMessageContent componentDefs={componentDefs} />
<ChatMessage.Actions />
</ChatMessage>
))}
@@ -1,209 +0,0 @@
"use client";
import { getCustomAnnotation, useChatMessage } from "@llamaindex/chat-ui";
import {
AlertCircle,
CheckCircle2,
CircleDashed,
Clock,
NotebookPen,
Search,
} from "lucide-react";
import { useMemo } from "react";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../accordion";
import { Card, CardContent, CardHeader, CardTitle } from "../../card";
import { cn } from "../../lib/utils";
import { Markdown } from "./markdown";
// Streaming event types
type EventState = "pending" | "inprogress" | "done" | "error";
type DeepResearchEvent = {
type: "deep_research_event";
data: {
event: "retrieve" | "analyze" | "answer";
state: EventState;
id?: string;
question?: string;
answer?: string | null;
};
};
// UI state types
type QuestionState = {
id: string;
question: string;
answer: string | null;
state: EventState;
isOpen: boolean;
};
type DeepResearchCardState = {
retrieve: {
state: EventState | null;
};
analyze: {
state: EventState | null;
questions: QuestionState[];
};
};
interface DeepResearchCardProps {
className?: string;
}
const stateIcon: Record<EventState, React.ReactNode> = {
pending: <Clock className="h-4 w-4 text-yellow-500" />,
inprogress: <CircleDashed className="h-4 w-4 animate-spin text-blue-500" />,
done: <CheckCircle2 className="h-4 w-4 text-green-500" />,
error: <AlertCircle className="h-4 w-4 text-red-500" />,
};
// Transform the state based on the event without mutations
const transformState = (
state: DeepResearchCardState,
event: DeepResearchEvent,
): DeepResearchCardState => {
switch (event.data.event) {
case "answer": {
const { id, question, answer } = event.data;
if (!id || !question) return state;
const updatedQuestions = state.analyze.questions.map((q) => {
if (q.id !== id) return q;
return {
...q,
state: event.data.state,
answer: answer ?? q.answer,
};
});
const newQuestion = !state.analyze.questions.some((q) => q.id === id)
? [
{
id,
question,
answer: answer ?? null,
state: event.data.state,
isOpen: false,
},
]
: [];
return {
...state,
analyze: {
...state.analyze,
questions: [...updatedQuestions, ...newQuestion],
},
};
}
case "retrieve":
case "analyze":
return {
...state,
[event.data.event]: {
...state[event.data.event],
state: event.data.state,
},
};
default:
return state;
}
};
// Convert deep research events to state
const deepResearchEventsToState = (
events: DeepResearchEvent[] | undefined,
): DeepResearchCardState => {
if (!events?.length) {
return {
retrieve: { state: null },
analyze: { state: null, questions: [] },
};
}
const initialState: DeepResearchCardState = {
retrieve: { state: null },
analyze: { state: null, questions: [] },
};
return events.reduce(
(acc: DeepResearchCardState, event: DeepResearchEvent) =>
transformState(acc, event),
initialState,
);
};
export function DeepResearchCard({ className }: DeepResearchCardProps) {
const { message } = useChatMessage();
const state = useMemo(() => {
const deepResearchEvents = getCustomAnnotation<DeepResearchEvent>(
message.annotations,
(annotation) => annotation?.type === "deep_research_event",
);
if (!deepResearchEvents.length) return null;
return deepResearchEventsToState(deepResearchEvents);
}, [message.annotations]);
if (!state) return null;
return (
<Card className={cn("w-full", className)}>
<CardHeader className="space-y-4">
{state.retrieve.state !== null && (
<CardTitle className="flex items-center gap-2">
<Search className="h-5 w-5" />
{state.retrieve.state === "inprogress"
? "Searching..."
: "Search completed"}
</CardTitle>
)}
{state.analyze.state !== null && (
<CardTitle className="flex items-center gap-2 border-t pt-4">
<NotebookPen className="h-5 w-5" />
{state.analyze.state === "inprogress" ? "Analyzing..." : "Analysis"}
</CardTitle>
)}
</CardHeader>
<CardContent>
{state.analyze.questions.length > 0 && (
<Accordion type="single" collapsible className="space-y-2">
{state.analyze.questions.map((question: QuestionState) => (
<AccordionItem
key={question.id}
value={question.id}
className="rounded-lg border [&[data-state=open]>div]:rounded-b-none"
>
<AccordionTrigger className="hover:bg-accent gap-2 px-3 py-3 hover:no-underline">
<div className="flex w-full items-center gap-2">
<div className="flex-shrink-0">
{stateIcon[question.state]}
</div>
<span className="flex-1 text-left font-medium">
{question.question}
</span>
</div>
</AccordionTrigger>
{question.answer && (
<AccordionContent className="border-t px-3 py-3">
<Markdown content={question.answer} />
</AccordionContent>
)}
</AccordionItem>
))}
</Accordion>
)}
</CardContent>
</Card>
);
}
@@ -0,0 +1,147 @@
"use client";
import * as Babel from "@babel/standalone"; // Import Babel standalone for runtime transpilation
import {
getChatUIAnnotation,
JSONValue,
MessageAnnotation,
MessageAnnotationType,
useChatMessage,
} from "@llamaindex/chat-ui";
import React, { useEffect, useRef } from "react";
import { getConfig } from "../lib/utils";
export type ComponentDef = {
type: string; // eg. deep_research_event
code: string; // eg. export const DeepResearchEvent = () => {...}
filename: string; // eg. deep_research_event.tsx
};
type EventComponent = ComponentDef & {
events: JSONValue[];
};
// image, document_file, sources, events, suggested_questions, agent
const BUILT_IN_CHATUI_COMPONENTS = Object.values(MessageAnnotationType);
export const DynamicEvents = ({
componentDefs,
}: {
componentDefs: ComponentDef[];
}) => {
const {
message: { annotations },
} = useChatMessage();
const shownWarningsRef = useRef<Set<string>>(new Set()); // track warnings
// Check for missing components in annotations
useEffect(() => {
if (!annotations?.length) return;
const availableComponents = new Set(componentDefs.map((comp) => comp.type));
annotations.forEach((annotation: MessageAnnotation) => {
const type = annotation.type;
if (!type) return; // skip if annotation doesn't have a type
const events = getChatUIAnnotation(annotations, type);
// Skip if it's a built-in component or if we've already shown the warning
if (
BUILT_IN_CHATUI_COMPONENTS.includes(type) ||
shownWarningsRef.current.has(type)
) {
return;
}
// If we have events for a type but no component definition, show a warning
if (events && !availableComponents.has(type)) {
console.warn(
`No component found for event type: ${type}. Please add a component file named ${type}.tsx or ${type}.jsx in your components directory.`,
);
shownWarningsRef.current.add(type);
}
});
}, [annotations, componentDefs]);
const components: EventComponent[] = componentDefs
.map((comp) => {
const events = getChatUIAnnotation(annotations, comp.type) as JSONValue[]; // get all event data by type
if (!events?.length) return null;
return { ...comp, events };
})
.filter((comp) => comp !== null);
if (components.length === 0) return null;
return (
<div className="components-container">
{components.map((component, index) => {
return (
<React.Fragment key={`${component.type}-${index}`}>
{renderEventComponent(component)}
</React.Fragment>
);
})}
</div>
);
};
export async function fetchComponentDefinitions(): Promise<ComponentDef[]> {
const endpoint = getConfig("COMPONENTS_API");
if (!endpoint) return [];
try {
const response = await fetch(endpoint);
const components = (await response.json()) as ComponentDef[];
// Only need to handle transpilation now
const transpiledComponents = components
.map((comp) => ({
...comp,
code: transpileCode(comp.code, comp.filename),
}))
.filter((comp): comp is ComponentDef => comp.code !== null);
return transpiledComponents;
} catch (error) {
console.log("Error fetching dynamic components:", error);
return [];
}
}
// convert TSX code to JS code using Babel
function transpileCode(code: string, filename: string): string | null {
try {
const transpiledCode = Babel.transform(code, {
presets: ["react", "typescript"],
filename,
}).code;
if (!transpiledCode) {
console.error("Transpiled code is empty");
return null;
}
return transpiledCode;
} catch (error) {
console.error("Error transpiling code:", error);
return null;
}
}
function renderEventComponent(component: EventComponent) {
try {
const Component = createComponentFromCode(component.code);
return React.createElement(Component, { events: component.events });
} catch (error) {
console.error(`Error rendering component ${component.type}:`, error);
return null;
}
}
function createComponentFromCode(code: string) {
const componentFn = new Function("React", `${code}; return Component;`);
return componentFn(React);
}
+4
View File
@@ -211,3 +211,7 @@
animation: slideIn 0.5s ease-out;
}
}
.components-container * {
font-family: inherit !important;
}
+3 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/server",
"description": "LlamaIndex Server",
"version": "0.0.8",
"version": "0.0.9",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
@@ -43,6 +43,7 @@
"@types/node": "^22.9.0",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/babel__standalone": "^7.1.9",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
@@ -54,6 +55,7 @@
},
"dependencies": {
"ai": "^4.2.0",
"@babel/standalone": "^7.27.0",
"@llamaindex/env": "workspace:*",
"llamaindex": "workspace:*",
"next": "^15.2.4",
+1 -1
View File
@@ -10,9 +10,9 @@ import {
import { runWorkflow } from "../utils/workflow";
export const handleChat = async (
workflowFactory: WorkflowFactory,
req: IncomingMessage,
res: ServerResponse,
workflowFactory: WorkflowFactory,
) => {
try {
const body = await parseRequestBody(req);
@@ -0,0 +1,69 @@
import fs from "fs";
import type { IncomingMessage, ServerResponse } from "http";
import path from "path";
import { promisify } from "util";
import { sendJSONResponse } from "../utils/request";
export const getComponents = async (
req: IncomingMessage,
res: ServerResponse,
componentsDir: string,
) => {
try {
const exists = await promisify(fs.exists)(componentsDir);
if (!exists) {
return sendJSONResponse(res, 404, {
error: "Components directory not found",
});
}
const files = await promisify(fs.readdir)(componentsDir);
// filter files with valid extensions
const validExtensions = [".tsx", ".jsx"];
const filteredFiles = files.filter((file) =>
validExtensions.includes(path.extname(file)),
);
// filter duplicate components
const uniqueFiles = filterDuplicateComponents(filteredFiles);
const components = await Promise.all(
uniqueFiles.map(async (file) => {
const filePath = path.join(componentsDir, file);
const content = await promisify(fs.readFile)(filePath, "utf-8");
return {
type: path.basename(file, path.extname(file)),
code: content,
filename: file,
};
}),
);
sendJSONResponse(res, 200, components);
} catch (error) {
console.error("Error reading components:", error);
sendJSONResponse(res, 500, { error: "Failed to read components" });
}
};
function filterDuplicateComponents(files: string[]) {
const compMap = new Map<string, string>();
for (const file of files) {
const type = path.basename(file, path.extname(file));
if (compMap.has(type)) {
const existingComp = compMap.get(type)!;
if (file.endsWith(".tsx") && !existingComp.endsWith(".tsx")) {
// prefer .tsx files over others
console.warn(`Preferring ${file} over ${existingComp}`);
compMap.set(type, file);
}
} else {
compMap.set(type, file);
}
}
return Array.from(compMap.values());
}
+27 -2
View File
@@ -4,8 +4,10 @@ import { createServer } from "http";
import next from "next";
import path from "path";
import { parse } from "url";
import { promisify } from "util";
import { handleChat } from "./handlers/chat";
import { getLlamaCloudConfig } from "./handlers/cloud";
import { getComponents } from "./handlers/components";
import { handleServeFiles } from "./handlers/files";
import type { LlamaIndexServerOptions, ServerWorkflow } from "./types";
@@ -17,12 +19,18 @@ export class LlamaIndexServer {
port: number;
app: ReturnType<typeof next>;
workflowFactory: () => Promise<ServerWorkflow> | ServerWorkflow;
componentsDir?: string | undefined;
constructor(options: LlamaIndexServerOptions) {
const { workflow, ...nextAppOptions } = options;
this.app = next({ dev, dir: nextDir, ...nextAppOptions });
this.port = nextAppOptions.port ?? parseInt(process.env.PORT || "3000", 10);
this.workflowFactory = workflow;
this.componentsDir = options.componentsDir;
if (this.componentsDir) {
this.createComponentsDir(this.componentsDir);
}
this.modifyConfig(options);
}
@@ -33,6 +41,7 @@ export class LlamaIndexServer {
const llamaCloudApi = getEnv("LLAMA_CLOUD_API_KEY")
? "/api/chat/config/llamacloud"
: undefined;
const componentsApi = this.componentsDir ? "/api/components" : undefined;
// content in javascript format
const content = `
@@ -40,12 +49,20 @@ export class LlamaIndexServer {
CHAT_API: '/api/chat',
APP_TITLE: ${JSON.stringify(appTitle)},
LLAMA_CLOUD_API: ${JSON.stringify(llamaCloudApi)},
STARTER_QUESTIONS: ${JSON.stringify(starterQuestions)}
STARTER_QUESTIONS: ${JSON.stringify(starterQuestions)},
COMPONENTS_API: ${JSON.stringify(componentsApi)}
}
`;
fs.writeFileSync(configFile, content);
}
private async createComponentsDir(componentsDir: string) {
const exists = await promisify(fs.exists)(componentsDir);
if (!exists) {
await promisify(fs.mkdir)(componentsDir);
}
}
async start() {
await this.app.prepare();
@@ -54,13 +71,21 @@ export class LlamaIndexServer {
const pathname = parsedUrl.pathname;
if (pathname === "/api/chat" && req.method === "POST") {
return handleChat(this.workflowFactory, req, res);
return handleChat(req, res, this.workflowFactory);
}
if (pathname?.startsWith("/api/files") && req.method === "GET") {
return handleServeFiles(req, res, pathname);
}
if (
this.componentsDir &&
pathname === "/api/components" &&
req.method === "GET"
) {
return getComponents(req, res, this.componentsDir);
}
if (
getEnv("LLAMA_CLOUD_API_KEY") &&
pathname === "/api/chat/config/llamacloud" &&
+1
View File
@@ -28,4 +28,5 @@ export type LlamaIndexServerOptions = NextAppOptions & {
workflow: WorkflowFactory;
starterQuestions?: string[];
appTitle?: string;
componentsDir?: string;
};
+1 -1
View File
@@ -20,7 +20,7 @@ export async function parseRequestBody(request: IncomingMessage) {
export function sendJSONResponse(
response: ServerResponse,
statusCode: number,
body: Record<string, unknown> | string,
body: Record<string, unknown> | string | Array<unknown>,
) {
response.statusCode = statusCode;
response.setHeader("Content-Type", "application/json");
+24
View File
@@ -1793,6 +1793,9 @@ importers:
packages/server:
dependencies:
'@babel/standalone':
specifier: ^7.27.0
version: 7.27.0
'@llamaindex/chat-ui':
specifier: 0.3.2
version: 0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -1848,6 +1851,9 @@ importers:
'@tailwindcss/postcss':
specifier: ^4
version: 4.0.9
'@types/babel__standalone':
specifier: ^7.1.9
version: 7.1.9
'@types/node':
specifier: ^22.9.0
version: 22.9.0
@@ -2503,6 +2509,10 @@ packages:
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
engines: {node: '>=6.9.0'}
'@babel/standalone@7.27.0':
resolution: {integrity: sha512-UxFDpi+BuSz6Q1X73P3ZSM1CB7Nbbqys+7COi/tdouRuaqRsJ6GAzUyxTswbqItHSItVY3frQdd+paBHHGEk9g==}
engines: {node: '>=6.9.0'}
'@babel/template@7.26.8':
resolution: {integrity: sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==}
engines: {node: '>=6.9.0'}
@@ -5706,6 +5716,9 @@ packages:
'@types/babel__generator@7.6.8':
resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
'@types/babel__standalone@7.1.9':
resolution: {integrity: sha512-IcCNPLqpevUD7UpV8QB0uwQPOyoOKACFf0YtYWRHcmxcakaje4Q7dbG2+jMqxw/I8Zk0NHvEps66WwS7z/UaaA==}
'@types/babel__template@7.4.4':
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
@@ -14091,6 +14104,8 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
'@babel/standalone@7.27.0': {}
'@babel/template@7.26.8':
dependencies:
'@babel/code-frame': 7.26.2
@@ -17545,6 +17560,15 @@ snapshots:
dependencies:
'@babel/types': 7.26.8
'@types/babel__standalone@7.1.9':
dependencies:
'@babel/parser': 7.26.8
'@babel/types': 7.26.8
'@types/babel__core': 7.20.5
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.6
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.26.8