@robinbraemer/codemode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +278 -0
- package/dist/chunk-NSUQUO7S.js +115 -0
- package/dist/chunk-NSUQUO7S.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-ZWSO33DZ.js +148 -0
- package/dist/chunk-ZWSO33DZ.js.map +1 -0
- package/dist/codemode-DbXujxeq.d.ts +198 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +308 -0
- package/dist/index.js.map +1 -0
- package/dist/isolated-vm-57EIYEJM.js +8 -0
- package/dist/isolated-vm-57EIYEJM.js.map +1 -0
- package/dist/mcp.d.ts +38 -0
- package/dist/mcp.js +4063 -0
- package/dist/mcp.js.map +1 -0
- package/dist/quickjs-BGVQS2YE.js +8 -0
- package/dist/quickjs-BGVQS2YE.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CNAP - Cloud Native Application Platform
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# codemode
|
|
2
|
+
|
|
3
|
+
Two MCP tools that replace hundreds. Give an AI agent your OpenAPI spec and a request handler — it discovers and calls your entire API by writing JavaScript in a sandboxed runtime.
|
|
4
|
+
|
|
5
|
+
Instead of defining individual MCP tools for every API endpoint (`list-pods`, `create-product`, `get-logs`, ...), CodeMode exposes just **two tools**:
|
|
6
|
+
|
|
7
|
+
- **`search`** — the agent writes JS to filter your OpenAPI spec and discover endpoints
|
|
8
|
+
- **`execute`** — the agent writes JS to call your API via an injected client
|
|
9
|
+
|
|
10
|
+
This is the same pattern [Cloudflare uses](https://blog.cloudflare.com/code-mode-mcp/) to expose 2,500+ API endpoints through just two MCP tools, reducing context window usage by 99.9%.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add codemode
|
|
16
|
+
|
|
17
|
+
# Install a sandbox runtime (pick one):
|
|
18
|
+
pnpm add isolated-vm # V8 isolates — fastest, recommended
|
|
19
|
+
pnpm add quickjs-emscripten # WASM — portable fallback
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { CodeMode } from 'codemode';
|
|
26
|
+
import { Hono } from 'hono';
|
|
27
|
+
|
|
28
|
+
const app = new Hono();
|
|
29
|
+
app.get('/v1/clusters', (c) => c.json([{ id: '1', name: 'prod' }]));
|
|
30
|
+
app.post('/v1/clusters', async (c) => {
|
|
31
|
+
const body = await c.req.json();
|
|
32
|
+
return c.json({ id: '2', ...body }, 201);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const codemode = new CodeMode({
|
|
36
|
+
spec: myOpenAPISpec, // OpenAPI 3.x spec, or async getter
|
|
37
|
+
request: app.request.bind(app), // in-process, no network hop
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// The agent searches the spec to discover endpoints...
|
|
41
|
+
const search = await codemode.callTool('search', {
|
|
42
|
+
code: `async () => {
|
|
43
|
+
return Object.entries(spec.paths)
|
|
44
|
+
.filter(([p]) => p.includes('/clusters'))
|
|
45
|
+
.flatMap(([path, methods]) =>
|
|
46
|
+
Object.entries(methods)
|
|
47
|
+
.filter(([m]) => ['get','post','put','delete'].includes(m))
|
|
48
|
+
.map(([method, op]) => ({ method: method.toUpperCase(), path, summary: op.summary }))
|
|
49
|
+
);
|
|
50
|
+
}`
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ...then executes API calls
|
|
54
|
+
const result = await codemode.callTool('execute', {
|
|
55
|
+
code: `async () => {
|
|
56
|
+
const res = await api.request({ method: "GET", path: "/v1/clusters" });
|
|
57
|
+
return res.body;
|
|
58
|
+
}`
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## MCP Server Integration
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
66
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
67
|
+
import { CodeMode } from 'codemode';
|
|
68
|
+
import { registerTools } from 'codemode/mcp';
|
|
69
|
+
|
|
70
|
+
const codemode = new CodeMode({
|
|
71
|
+
spec: () => fetchOpenAPISpec(),
|
|
72
|
+
request: app.request.bind(app),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const server = new McpServer({ name: 'my-api', version: '1.0.0' });
|
|
76
|
+
registerTools(codemode, server);
|
|
77
|
+
|
|
78
|
+
const transport = new StdioServerTransport();
|
|
79
|
+
await server.connect(transport);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## How It Works
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
AI Agent
|
|
86
|
+
│ writes JavaScript code
|
|
87
|
+
▼
|
|
88
|
+
CodeMode MCP Server
|
|
89
|
+
│
|
|
90
|
+
├─ search(code) → runs JS with OpenAPI spec as a global
|
|
91
|
+
│ → agent discovers endpoints, schemas, parameters
|
|
92
|
+
│
|
|
93
|
+
└─ execute(code) → runs JS with injected request client
|
|
94
|
+
→ api.request() calls your handler in-process
|
|
95
|
+
→ no network hop, auth handled automatically
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
All code runs in an isolated sandbox (V8 isolate or QuickJS WASM). The sandbox has zero I/O by default — no `require`, no `process`, no `fetch`, no filesystem. The only way to interact with the outside world is through the injected globals (`spec` for search, `{namespace}.request()` for execute).
|
|
99
|
+
|
|
100
|
+
Each tool call gets a fresh sandbox with no state carried over between calls.
|
|
101
|
+
|
|
102
|
+
## API
|
|
103
|
+
|
|
104
|
+
### `new CodeMode(options)`
|
|
105
|
+
|
|
106
|
+
| Option | Type | Default | Description |
|
|
107
|
+
|--------|------|---------|-------------|
|
|
108
|
+
| `spec` | `OpenAPISpec \| () => OpenAPISpec \| Promise<OpenAPISpec>` | required | OpenAPI 3.x spec or async getter |
|
|
109
|
+
| `request` | `(input, init?) => Response` | required | Fetch-compatible handler (`app.request.bind(app)` for Hono) |
|
|
110
|
+
| `namespace` | `string` | `"api"` | Client name in sandbox (`api.request(...)`) |
|
|
111
|
+
| `baseUrl` | `string` | `"http://localhost"` | Base URL for relative paths |
|
|
112
|
+
| `sandbox` | `{ memoryMB?, timeoutMs? }` | `{ 64, 30000 }` | Sandbox resource limits |
|
|
113
|
+
| `executor` | `Executor` | auto-detect | Custom sandbox executor |
|
|
114
|
+
|
|
115
|
+
### Methods
|
|
116
|
+
|
|
117
|
+
#### `codemode.tools(): ToolDefinition[]`
|
|
118
|
+
|
|
119
|
+
Returns MCP-compatible tool definitions for `search` and `execute`.
|
|
120
|
+
|
|
121
|
+
#### `codemode.callTool(name, { code }): Promise<ToolCallResult>`
|
|
122
|
+
|
|
123
|
+
Route a tool call. Returns `{ content: [{ type: "text", text }], isError? }`.
|
|
124
|
+
|
|
125
|
+
#### `codemode.search(code): Promise<ToolCallResult>`
|
|
126
|
+
|
|
127
|
+
Run search code directly (shorthand for `callTool('search', { code })`).
|
|
128
|
+
|
|
129
|
+
#### `codemode.execute(code): Promise<ToolCallResult>`
|
|
130
|
+
|
|
131
|
+
Run execute code directly (shorthand for `callTool('execute', { code })`).
|
|
132
|
+
|
|
133
|
+
#### `codemode.setToolNames(search, execute): this`
|
|
134
|
+
|
|
135
|
+
Override default tool names. Useful when running multiple CodeMode instances.
|
|
136
|
+
|
|
137
|
+
#### `codemode.dispose(): void`
|
|
138
|
+
|
|
139
|
+
Clean up sandbox resources.
|
|
140
|
+
|
|
141
|
+
## Sandbox API
|
|
142
|
+
|
|
143
|
+
### Inside `search`
|
|
144
|
+
|
|
145
|
+
The `spec` global is the full OpenAPI 3.x document:
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Find endpoints by keyword
|
|
149
|
+
async () => {
|
|
150
|
+
return Object.entries(spec.paths)
|
|
151
|
+
.filter(([p]) => p.includes('/clusters'))
|
|
152
|
+
.flatMap(([path, methods]) =>
|
|
153
|
+
Object.entries(methods)
|
|
154
|
+
.filter(([m]) => ['get','post','put','delete','patch'].includes(m))
|
|
155
|
+
.map(([method, op]) => ({
|
|
156
|
+
method: method.toUpperCase(), path, summary: op.summary
|
|
157
|
+
}))
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Get a specific schema
|
|
162
|
+
async () => spec.components?.schemas?.Product
|
|
163
|
+
|
|
164
|
+
// Spec metadata
|
|
165
|
+
async () => ({
|
|
166
|
+
title: spec.info.title,
|
|
167
|
+
version: spec.info.version,
|
|
168
|
+
endpoints: Object.keys(spec.paths).length,
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Inside `execute`
|
|
173
|
+
|
|
174
|
+
The `{namespace}.request()` function makes API calls through the host handler:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
// GET with query params
|
|
178
|
+
async () => {
|
|
179
|
+
const res = await api.request({
|
|
180
|
+
method: "GET",
|
|
181
|
+
path: "/v1/clusters",
|
|
182
|
+
query: { limit: 10 },
|
|
183
|
+
});
|
|
184
|
+
return res.body;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// POST with body
|
|
188
|
+
async () => {
|
|
189
|
+
return api.request({
|
|
190
|
+
method: "POST",
|
|
191
|
+
path: "/v1/products",
|
|
192
|
+
body: { name: "Redis", chart: "bitnami/redis" },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Chain calls
|
|
197
|
+
async () => {
|
|
198
|
+
const list = await api.request({ method: "GET", path: "/v1/clusters" });
|
|
199
|
+
const details = await Promise.all(
|
|
200
|
+
list.body.map(c =>
|
|
201
|
+
api.request({ method: "GET", path: `/v1/clusters/${c.id}` })
|
|
202
|
+
)
|
|
203
|
+
);
|
|
204
|
+
return details.map(d => d.body);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Request options:**
|
|
209
|
+
|
|
210
|
+
| Field | Type | Description |
|
|
211
|
+
|-------|------|-------------|
|
|
212
|
+
| `method` | `string` | HTTP method (`"GET"`, `"POST"`, etc.) |
|
|
213
|
+
| `path` | `string` | API path (`"/v1/clusters"`) |
|
|
214
|
+
| `query` | `Record<string, string \| number \| boolean>` | Query parameters (optional) |
|
|
215
|
+
| `body` | `unknown` | Request body, auto-serialized as JSON (optional) |
|
|
216
|
+
| `headers` | `Record<string, string>` | Additional headers (optional) |
|
|
217
|
+
|
|
218
|
+
**Response:** `{ status: number, headers: Record<string, string>, body: unknown }`
|
|
219
|
+
|
|
220
|
+
## Executors
|
|
221
|
+
|
|
222
|
+
CodeMode auto-detects your installed sandbox runtime. You can also pass one explicitly:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { CodeMode, IsolatedVMExecutor } from 'codemode';
|
|
226
|
+
|
|
227
|
+
const codemode = new CodeMode({
|
|
228
|
+
spec,
|
|
229
|
+
request: handler,
|
|
230
|
+
executor: new IsolatedVMExecutor({ memoryMB: 128, timeoutMs: 60_000 }),
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Executor | Package | Performance | Portability |
|
|
235
|
+
|----------|---------|-------------|-------------|
|
|
236
|
+
| `IsolatedVMExecutor` | `isolated-vm` | Native V8 speed | Node.js |
|
|
237
|
+
| `QuickJSExecutor` | `quickjs-emscripten` | ~3-5x slower (still fast) | Node.js, Bun, browsers |
|
|
238
|
+
|
|
239
|
+
Both are optional peer dependencies. Install at least one.
|
|
240
|
+
|
|
241
|
+
### Custom Executor
|
|
242
|
+
|
|
243
|
+
Implement the `Executor` interface to use your own sandbox:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { CodeMode, type Executor, type ExecuteResult } from 'codemode';
|
|
247
|
+
|
|
248
|
+
class MyExecutor implements Executor {
|
|
249
|
+
async execute(code: string, globals: Record<string, unknown>): Promise<ExecuteResult> {
|
|
250
|
+
// `code` is an async arrow function as a string: "async () => { ... }"
|
|
251
|
+
// `globals` contains named values to inject:
|
|
252
|
+
// - plain data (objects, arrays, primitives) → read-only values
|
|
253
|
+
// - functions → callable host functions
|
|
254
|
+
// - objects with function values → namespace with callable methods
|
|
255
|
+
return { result: ..., logs: [] };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
dispose() { /* clean up */ }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const codemode = new CodeMode({
|
|
262
|
+
spec,
|
|
263
|
+
request: handler,
|
|
264
|
+
executor: new MyExecutor(),
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Token Efficiency
|
|
269
|
+
|
|
270
|
+
| Approach | Context Tokens |
|
|
271
|
+
|----------|---------------|
|
|
272
|
+
| Individual MCP tools (15-50+ tools) | ~15,000-50,000+ |
|
|
273
|
+
| Full OpenAPI spec in context | ~1,000,000+ |
|
|
274
|
+
| **CodeMode (2 tools)** | **~1,000** |
|
|
275
|
+
|
|
276
|
+
## License
|
|
277
|
+
|
|
278
|
+
MIT
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/executor/isolated-vm.ts
|
|
2
|
+
var IsolatedVMExecutor = class {
|
|
3
|
+
memoryMB;
|
|
4
|
+
timeoutMs;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.memoryMB = options.memoryMB ?? 64;
|
|
7
|
+
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
8
|
+
}
|
|
9
|
+
async execute(code, globals) {
|
|
10
|
+
const ivm = (await import("isolated-vm")).default ?? await import("isolated-vm");
|
|
11
|
+
const isolate = new ivm.Isolate({ memoryLimit: this.memoryMB });
|
|
12
|
+
const logs = [];
|
|
13
|
+
try {
|
|
14
|
+
const context = await isolate.createContext();
|
|
15
|
+
const jail = context.global;
|
|
16
|
+
await jail.set("global", jail.derefInto());
|
|
17
|
+
jail.setSync(
|
|
18
|
+
"__log",
|
|
19
|
+
new ivm.Callback((...args) => {
|
|
20
|
+
logs.push(
|
|
21
|
+
args.map((a) => typeof a === "string" ? a : stringify(a)).join(" ")
|
|
22
|
+
);
|
|
23
|
+
})
|
|
24
|
+
);
|
|
25
|
+
await context.eval(`
|
|
26
|
+
globalThis.console = {
|
|
27
|
+
log: (...args) => __log(...args),
|
|
28
|
+
warn: (...args) => __log(...args),
|
|
29
|
+
error: (...args) => __log(...args),
|
|
30
|
+
};
|
|
31
|
+
`);
|
|
32
|
+
let refCounter = 0;
|
|
33
|
+
for (const [name, value] of Object.entries(globals)) {
|
|
34
|
+
if (typeof value === "function") {
|
|
35
|
+
const refName = `__ref${refCounter++}`;
|
|
36
|
+
await jail.set(refName, new ivm.Reference(value));
|
|
37
|
+
await context.eval(`
|
|
38
|
+
globalThis[${JSON.stringify(name)}] = function(...args) {
|
|
39
|
+
return ${refName}.apply(undefined, args, {
|
|
40
|
+
arguments: { copy: true },
|
|
41
|
+
result: { promise: true, copy: true },
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
`);
|
|
45
|
+
} else if (isNamespaceWithMethods(value)) {
|
|
46
|
+
const ns = value;
|
|
47
|
+
let nsSetup = `globalThis[${JSON.stringify(name)}] = {};
|
|
48
|
+
`;
|
|
49
|
+
for (const [key, val] of Object.entries(ns)) {
|
|
50
|
+
if (typeof val === "function") {
|
|
51
|
+
const refName = `__ref${refCounter++}`;
|
|
52
|
+
await jail.set(refName, new ivm.Reference(val));
|
|
53
|
+
nsSetup += `
|
|
54
|
+
globalThis[${JSON.stringify(name)}][${JSON.stringify(key)}] = function(...args) {
|
|
55
|
+
return ${refName}.apply(undefined, args, {
|
|
56
|
+
arguments: { copy: true },
|
|
57
|
+
result: { promise: true, copy: true },
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const dataProps = Object.entries(ns).filter(([, v]) => typeof v !== "function");
|
|
64
|
+
if (dataProps.length > 0) {
|
|
65
|
+
const dataObj = Object.fromEntries(dataProps);
|
|
66
|
+
nsSetup += `Object.assign(globalThis[${JSON.stringify(name)}], ${JSON.stringify(dataObj)});
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
await context.eval(nsSetup);
|
|
70
|
+
} else {
|
|
71
|
+
await context.eval(
|
|
72
|
+
`globalThis[${JSON.stringify(name)}] = ${JSON.stringify(value)};`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const wrappedCode = `(${code})()`;
|
|
77
|
+
const script = await isolate.compileScript(wrappedCode);
|
|
78
|
+
const result = await script.run(context, {
|
|
79
|
+
timeout: this.timeoutMs,
|
|
80
|
+
promise: true,
|
|
81
|
+
copy: true
|
|
82
|
+
});
|
|
83
|
+
context.release();
|
|
84
|
+
return { result, logs };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return {
|
|
87
|
+
result: void 0,
|
|
88
|
+
error: err instanceof Error ? err.message : String(err),
|
|
89
|
+
logs
|
|
90
|
+
};
|
|
91
|
+
} finally {
|
|
92
|
+
if (!isolate.isDisposed) {
|
|
93
|
+
isolate.dispose();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function isNamespaceWithMethods(value) {
|
|
99
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).some(
|
|
100
|
+
(v) => typeof v === "function"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
function stringify(value) {
|
|
104
|
+
if (typeof value === "string") return value;
|
|
105
|
+
try {
|
|
106
|
+
return JSON.stringify(value);
|
|
107
|
+
} catch {
|
|
108
|
+
return String(value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
IsolatedVMExecutor
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=chunk-NSUQUO7S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/executor/isolated-vm.ts"],"sourcesContent":["import type { Executor, ExecuteResult, SandboxOptions } from \"../types.js\";\n\n/**\n * Executor implementation using isolated-vm (V8 isolates).\n * Requires `isolated-vm` v6+ as a peer dependency.\n *\n * Each execute() call creates a fresh V8 isolate with its own heap — no state\n * leaks between calls. The sandbox has zero I/O capabilities by default (no\n * fetch, no fs, no require). The only way out is through injected host functions.\n */\nexport class IsolatedVMExecutor implements Executor {\n private memoryMB: number;\n private timeoutMs: number;\n\n constructor(options: SandboxOptions = {}) {\n this.memoryMB = options.memoryMB ?? 64;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n }\n\n async execute(\n code: string,\n globals: Record<string, unknown>,\n ): Promise<ExecuteResult> {\n // @ts-ignore — optional peer dependency\n const ivm = (await import(\"isolated-vm\")).default ?? (await import(\"isolated-vm\"));\n const isolate = new ivm.Isolate({ memoryLimit: this.memoryMB });\n const logs: string[] = [];\n\n try {\n const context = await isolate.createContext();\n const jail = context.global;\n await jail.set(\"global\", jail.derefInto());\n\n // Inject console (captures logs via Callback)\n jail.setSync(\n \"__log\",\n new ivm.Callback((...args: unknown[]) => {\n logs.push(\n args\n .map((a) => (typeof a === \"string\" ? a : stringify(a)))\n .join(\" \"),\n );\n }),\n );\n await context.eval(`\n globalThis.console = {\n log: (...args) => __log(...args),\n warn: (...args) => __log(...args),\n error: (...args) => __log(...args),\n };\n `);\n\n // Inject globals\n let refCounter = 0;\n for (const [name, value] of Object.entries(globals)) {\n if (typeof value === \"function\") {\n // Async host function: set Reference, wrap with .apply() in isolate\n const refName = `__ref${refCounter++}`;\n await jail.set(refName, new ivm.Reference(value));\n await context.eval(`\n globalThis[${JSON.stringify(name)}] = function(...args) {\n return ${refName}.apply(undefined, args, {\n arguments: { copy: true },\n result: { promise: true, copy: true },\n });\n };\n `);\n } else if (isNamespaceWithMethods(value)) {\n // Namespace object with methods (e.g. { request: fn })\n const ns = value as Record<string, unknown>;\n let nsSetup = `globalThis[${JSON.stringify(name)}] = {};\\n`;\n\n for (const [key, val] of Object.entries(ns)) {\n if (typeof val === \"function\") {\n const refName = `__ref${refCounter++}`;\n await jail.set(refName, new ivm.Reference(val));\n nsSetup += `\n globalThis[${JSON.stringify(name)}][${JSON.stringify(key)}] = function(...args) {\n return ${refName}.apply(undefined, args, {\n arguments: { copy: true },\n result: { promise: true, copy: true },\n });\n };\n `;\n }\n }\n\n // Inject non-function properties as JSON\n const dataProps = Object.entries(ns).filter(([, v]) => typeof v !== \"function\");\n if (dataProps.length > 0) {\n const dataObj = Object.fromEntries(dataProps);\n nsSetup += `Object.assign(globalThis[${JSON.stringify(name)}], ${JSON.stringify(dataObj)});\\n`;\n }\n\n await context.eval(nsSetup);\n } else {\n // Plain data: inject as JSON\n await context.eval(\n `globalThis[${JSON.stringify(name)}] = ${JSON.stringify(value)};`,\n );\n }\n }\n\n // Execute the code\n const wrappedCode = `(${code})()`;\n const script = await isolate.compileScript(wrappedCode);\n const result = await script.run(context, {\n timeout: this.timeoutMs,\n promise: true,\n copy: true,\n });\n\n context.release();\n return { result, logs };\n } catch (err) {\n return {\n result: undefined,\n error: err instanceof Error ? err.message : String(err),\n logs,\n };\n } finally {\n if (!isolate.isDisposed) {\n isolate.dispose();\n }\n }\n }\n}\n\nfunction isNamespaceWithMethods(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value as Record<string, unknown>).some(\n (v) => typeof v === \"function\",\n )\n );\n}\n\nfunction stringify(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// src/executor/quickjs.ts
|
|
2
|
+
var QuickJSExecutor = class {
|
|
3
|
+
memoryBytes;
|
|
4
|
+
timeoutMs;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.memoryBytes = (options.memoryMB ?? 64) * 1024 * 1024;
|
|
7
|
+
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
8
|
+
}
|
|
9
|
+
async execute(code, globals) {
|
|
10
|
+
const qjs = await import("quickjs-emscripten");
|
|
11
|
+
const logs = [];
|
|
12
|
+
const vm = await qjs.newAsyncContext();
|
|
13
|
+
const runtime = vm.runtime;
|
|
14
|
+
runtime.setMemoryLimit(this.memoryBytes);
|
|
15
|
+
runtime.setMaxStackSize(1024 * 320);
|
|
16
|
+
const deadline = Date.now() + this.timeoutMs;
|
|
17
|
+
runtime.setInterruptHandler(() => Date.now() > deadline);
|
|
18
|
+
try {
|
|
19
|
+
injectConsole(vm, logs);
|
|
20
|
+
for (const [name, value] of Object.entries(globals)) {
|
|
21
|
+
if (typeof value === "function") {
|
|
22
|
+
injectAsyncFunction(vm, name, value);
|
|
23
|
+
} else if (typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).some((v) => typeof v === "function")) {
|
|
24
|
+
injectNamespace(vm, name, value);
|
|
25
|
+
} else {
|
|
26
|
+
const jsonStr = JSON.stringify(value);
|
|
27
|
+
const handle = vm.evalCode(`(${jsonStr})`);
|
|
28
|
+
if (handle.error) {
|
|
29
|
+
handle.error.dispose();
|
|
30
|
+
} else {
|
|
31
|
+
vm.setProp(vm.global, name, handle.value);
|
|
32
|
+
handle.value.dispose();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const wrappedCode = `(${code})()`;
|
|
37
|
+
const resultHandle = await vm.evalCodeAsync(wrappedCode);
|
|
38
|
+
if (resultHandle.error) {
|
|
39
|
+
let error = vm.dump(resultHandle.error);
|
|
40
|
+
try {
|
|
41
|
+
resultHandle.error.dispose();
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
if (typeof error === "object" && error?.type === "rejected") {
|
|
45
|
+
error = error.reason;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
result: void 0,
|
|
49
|
+
error: typeof error === "object" && error?.message ? error.message : String(error),
|
|
50
|
+
logs
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
let result = vm.dump(resultHandle.value);
|
|
54
|
+
try {
|
|
55
|
+
resultHandle.value.dispose();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
result = unwrapPromiseResult(result);
|
|
59
|
+
return { result, logs };
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return {
|
|
62
|
+
result: void 0,
|
|
63
|
+
error: err instanceof Error ? err.message : String(err),
|
|
64
|
+
logs
|
|
65
|
+
};
|
|
66
|
+
} finally {
|
|
67
|
+
try {
|
|
68
|
+
vm.dispose();
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
runtime.dispose();
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function injectConsole(vm, logs) {
|
|
79
|
+
const consoleObj = vm.newObject();
|
|
80
|
+
const logFn = vm.newFunction("log", (...args) => {
|
|
81
|
+
const values = args.map((h) => vm.dump(h));
|
|
82
|
+
logs.push(values.map((v) => typeof v === "string" ? v : JSON.stringify(v)).join(" "));
|
|
83
|
+
});
|
|
84
|
+
vm.setProp(consoleObj, "log", logFn);
|
|
85
|
+
vm.setProp(consoleObj, "warn", logFn);
|
|
86
|
+
vm.setProp(consoleObj, "error", logFn);
|
|
87
|
+
vm.setProp(vm.global, "console", consoleObj);
|
|
88
|
+
logFn.dispose();
|
|
89
|
+
consoleObj.dispose();
|
|
90
|
+
}
|
|
91
|
+
function injectAsyncFunction(vm, name, fn) {
|
|
92
|
+
const handle = vm.newAsyncifiedFunction(name, async (...argHandles) => {
|
|
93
|
+
const args = argHandles.map((h) => vm.dump(h));
|
|
94
|
+
const result = await fn(...args);
|
|
95
|
+
return marshalToVM(vm, result);
|
|
96
|
+
});
|
|
97
|
+
vm.setProp(vm.global, name, handle);
|
|
98
|
+
handle.dispose();
|
|
99
|
+
}
|
|
100
|
+
function injectNamespace(vm, name, ns) {
|
|
101
|
+
const nsObj = vm.newObject();
|
|
102
|
+
for (const [key, value] of Object.entries(ns)) {
|
|
103
|
+
if (typeof value === "function") {
|
|
104
|
+
const handle = vm.newAsyncifiedFunction(key, async (...argHandles) => {
|
|
105
|
+
const args = argHandles.map((h) => vm.dump(h));
|
|
106
|
+
const result = await value(...args);
|
|
107
|
+
return marshalToVM(vm, result);
|
|
108
|
+
});
|
|
109
|
+
vm.setProp(nsObj, key, handle);
|
|
110
|
+
handle.dispose();
|
|
111
|
+
} else {
|
|
112
|
+
const jsonStr = JSON.stringify(value);
|
|
113
|
+
const handle = vm.evalCode(`(${jsonStr})`);
|
|
114
|
+
if (!handle.error) {
|
|
115
|
+
vm.setProp(nsObj, key, handle.value);
|
|
116
|
+
handle.value.dispose();
|
|
117
|
+
} else {
|
|
118
|
+
handle.error.dispose();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
vm.setProp(vm.global, name, nsObj);
|
|
123
|
+
nsObj.dispose();
|
|
124
|
+
}
|
|
125
|
+
function unwrapPromiseResult(result) {
|
|
126
|
+
if (typeof result === "object" && result !== null && "type" in result && result.type === "fulfilled" && "value" in result) {
|
|
127
|
+
return result.value;
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function marshalToVM(vm, value) {
|
|
132
|
+
if (value === void 0 || value === null) return vm.undefined;
|
|
133
|
+
if (typeof value === "string") return vm.newString(value);
|
|
134
|
+
if (typeof value === "number") return vm.newNumber(value);
|
|
135
|
+
if (typeof value === "boolean") return value ? vm.true : vm.false;
|
|
136
|
+
const jsonStr = JSON.stringify(value);
|
|
137
|
+
const result = vm.evalCode(`(${jsonStr})`);
|
|
138
|
+
if (result.error) {
|
|
139
|
+
result.error.dispose();
|
|
140
|
+
return vm.undefined;
|
|
141
|
+
}
|
|
142
|
+
return result.value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
QuickJSExecutor
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=chunk-ZWSO33DZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/executor/quickjs.ts"],"sourcesContent":["import type { Executor, ExecuteResult, SandboxOptions } from \"../types.js\";\n\n/**\n * Executor implementation using quickjs-emscripten (QuickJS compiled to WASM).\n * Requires `quickjs-emscripten` as a peer dependency.\n *\n * Advantages over isolated-vm:\n * - Pure WASM, no native dependencies (works everywhere including Bun)\n * - WASM-level sandbox isolation\n *\n * Tradeoffs:\n * - ~3-5x slower than V8 for compute (negligible for API orchestration)\n * - Only one async suspension at a time per module\n */\nexport class QuickJSExecutor implements Executor {\n private memoryBytes: number;\n private timeoutMs: number;\n\n constructor(options: SandboxOptions = {}) {\n this.memoryBytes = (options.memoryMB ?? 64) * 1024 * 1024;\n this.timeoutMs = options.timeoutMs ?? 30_000;\n }\n\n async execute(\n code: string,\n globals: Record<string, unknown>,\n ): Promise<ExecuteResult> {\n const qjs = await import(\"quickjs-emscripten\");\n const logs: string[] = [];\n\n // Use the convenience function that manages module/runtime lifecycle\n const vm = await qjs.newAsyncContext();\n const runtime = vm.runtime;\n\n runtime.setMemoryLimit(this.memoryBytes);\n runtime.setMaxStackSize(1024 * 320); // 320KB stack\n\n // Interrupt after timeout\n const deadline = Date.now() + this.timeoutMs;\n runtime.setInterruptHandler(() => Date.now() > deadline);\n\n try {\n // Inject console\n injectConsole(vm, logs);\n\n // Inject globals\n for (const [name, value] of Object.entries(globals)) {\n if (typeof value === \"function\") {\n injectAsyncFunction(vm, name, value as (...args: unknown[]) => unknown);\n } else if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value as Record<string, unknown>).some((v) => typeof v === \"function\")\n ) {\n injectNamespace(vm, name, value as Record<string, unknown>);\n } else {\n // Inject as JSON data\n const jsonStr = JSON.stringify(value);\n const handle = vm.evalCode(`(${jsonStr})`);\n if (handle.error) {\n handle.error.dispose();\n } else {\n vm.setProp(vm.global, name, handle.value);\n handle.value.dispose();\n }\n }\n }\n\n // Execute the code\n const wrappedCode = `(${code})()`;\n const resultHandle = await vm.evalCodeAsync(wrappedCode);\n\n if (resultHandle.error) {\n let error = vm.dump(resultHandle.error);\n try { resultHandle.error.dispose(); } catch { /* already disposed */ }\n // Unwrap rejected promise wrapper\n if (typeof error === \"object\" && error?.type === \"rejected\") {\n error = error.reason;\n }\n return {\n result: undefined,\n error: typeof error === \"object\" && error?.message ? error.message : String(error),\n logs,\n };\n }\n\n let result = vm.dump(resultHandle.value);\n // The value handle from async evaluation may already be managed/disposed\n // by the runtime, so we guard the dispose call\n try { resultHandle.value.dispose(); } catch { /* already disposed */ }\n\n // evalCodeAsync wraps async results as { type: 'fulfilled', value } or { type: 'rejected', reason }\n result = unwrapPromiseResult(result);\n\n return { result, logs };\n } catch (err) {\n return {\n result: undefined,\n error: err instanceof Error ? err.message : String(err),\n logs,\n };\n } finally {\n // Dispose context first, then runtime\n // Use try/catch because quickjs-emscripten can throw during\n // cleanup when host references are freed\n try { vm.dispose(); } catch { /* ignore cleanup errors */ }\n try { runtime.dispose(); } catch { /* ignore cleanup errors */ }\n }\n }\n}\n\nfunction injectConsole(vm: any, logs: string[]): void {\n const consoleObj = vm.newObject();\n\n const logFn = vm.newFunction(\"log\", (...args: any[]) => {\n const values = args.map((h: any) => vm.dump(h));\n logs.push(values.map((v: unknown) => (typeof v === \"string\" ? v : JSON.stringify(v))).join(\" \"));\n });\n\n vm.setProp(consoleObj, \"log\", logFn);\n vm.setProp(consoleObj, \"warn\", logFn);\n vm.setProp(consoleObj, \"error\", logFn);\n vm.setProp(vm.global, \"console\", consoleObj);\n\n logFn.dispose();\n consoleObj.dispose();\n}\n\nfunction injectAsyncFunction(\n vm: any,\n name: string,\n fn: (...args: unknown[]) => unknown,\n): void {\n const handle = vm.newAsyncifiedFunction(name, async (...argHandles: any[]) => {\n const args = argHandles.map((h: any) => vm.dump(h));\n const result = await fn(...args);\n return marshalToVM(vm, result);\n });\n vm.setProp(vm.global, name, handle);\n handle.dispose();\n}\n\nfunction injectNamespace(\n vm: any,\n name: string,\n ns: Record<string, unknown>,\n): void {\n const nsObj = vm.newObject();\n\n for (const [key, value] of Object.entries(ns)) {\n if (typeof value === \"function\") {\n const handle = vm.newAsyncifiedFunction(key, async (...argHandles: any[]) => {\n const args = argHandles.map((h: any) => vm.dump(h));\n const result = await value(...args);\n return marshalToVM(vm, result);\n });\n vm.setProp(nsObj, key, handle);\n handle.dispose();\n } else {\n const jsonStr = JSON.stringify(value);\n const handle = vm.evalCode(`(${jsonStr})`);\n if (!handle.error) {\n vm.setProp(nsObj, key, handle.value);\n handle.value.dispose();\n } else {\n handle.error.dispose();\n }\n }\n }\n\n vm.setProp(vm.global, name, nsObj);\n nsObj.dispose();\n}\n\n/**\n * When evalCodeAsync runs an async arrow function, the dump of the result\n * is `{ type: 'fulfilled', value: X }` instead of just `X`.\n * This unwraps it.\n */\nfunction unwrapPromiseResult(result: unknown): unknown {\n if (\n typeof result === \"object\" &&\n result !== null &&\n \"type\" in result &&\n (result as any).type === \"fulfilled\" &&\n \"value\" in result\n ) {\n return (result as any).value;\n }\n return result;\n}\n\nfunction marshalToVM(vm: any, value: unknown): any {\n if (value === undefined || value === null) return vm.undefined;\n if (typeof value === \"string\") return vm.newString(value);\n if (typeof value === \"number\") return vm.newNumber(value);\n if (typeof value === \"boolean\") return value ? vm.true : vm.false;\n\n // For objects/arrays, serialize to JSON and parse inside VM\n const jsonStr = JSON.stringify(value);\n const result = vm.evalCode(`(${jsonStr})`);\n if (result.error) {\n result.error.dispose();\n return vm.undefined;\n }\n return result.value;\n}\n"],"mappings":";AAcO,IAAM,kBAAN,MAA0C;AAAA,EACvC;AAAA,EACA;AAAA,EAER,YAAY,UAA0B,CAAC,GAAG;AACxC,SAAK,eAAe,QAAQ,YAAY,MAAM,OAAO;AACrD,SAAK,YAAY,QAAQ,aAAa;AAAA,EACxC;AAAA,EAEA,MAAM,QACJ,MACA,SACwB;AACxB,UAAM,MAAM,MAAM,OAAO,oBAAoB;AAC7C,UAAM,OAAiB,CAAC;AAGxB,UAAM,KAAK,MAAM,IAAI,gBAAgB;AACrC,UAAM,UAAU,GAAG;AAEnB,YAAQ,eAAe,KAAK,WAAW;AACvC,YAAQ,gBAAgB,OAAO,GAAG;AAGlC,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,YAAQ,oBAAoB,MAAM,KAAK,IAAI,IAAI,QAAQ;AAEvD,QAAI;AAEF,oBAAc,IAAI,IAAI;AAGtB,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AACnD,YAAI,OAAO,UAAU,YAAY;AAC/B,8BAAoB,IAAI,MAAM,KAAwC;AAAA,QACxE,WACE,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,OAAO,KAAgC,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,UAAU,GACnF;AACA,0BAAgB,IAAI,MAAM,KAAgC;AAAA,QAC5D,OAAO;AAEL,gBAAM,UAAU,KAAK,UAAU,KAAK;AACpC,gBAAM,SAAS,GAAG,SAAS,IAAI,OAAO,GAAG;AACzC,cAAI,OAAO,OAAO;AAChB,mBAAO,MAAM,QAAQ;AAAA,UACvB,OAAO;AACL,eAAG,QAAQ,GAAG,QAAQ,MAAM,OAAO,KAAK;AACxC,mBAAO,MAAM,QAAQ;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AAGA,YAAM,cAAc,IAAI,IAAI;AAC5B,YAAM,eAAe,MAAM,GAAG,cAAc,WAAW;AAEvD,UAAI,aAAa,OAAO;AACtB,YAAI,QAAQ,GAAG,KAAK,aAAa,KAAK;AACtC,YAAI;AAAE,uBAAa,MAAM,QAAQ;AAAA,QAAG,QAAQ;AAAA,QAAyB;AAErE,YAAI,OAAO,UAAU,YAAY,OAAO,SAAS,YAAY;AAC3D,kBAAQ,MAAM;AAAA,QAChB;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,MAAM,UAAU,OAAO,KAAK;AAAA,UACjF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,GAAG,KAAK,aAAa,KAAK;AAGvC,UAAI;AAAE,qBAAa,MAAM,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAyB;AAGrE,eAAS,oBAAoB,MAAM;AAEnC,aAAO,EAAE,QAAQ,KAAK;AAAA,IACxB,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACtD;AAAA,MACF;AAAA,IACF,UAAE;AAIA,UAAI;AAAE,WAAG,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAA8B;AAC1D,UAAI;AAAE,gBAAQ,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAA8B;AAAA,IACjE;AAAA,EACF;AACF;AAEA,SAAS,cAAc,IAAS,MAAsB;AACpD,QAAM,aAAa,GAAG,UAAU;AAEhC,QAAM,QAAQ,GAAG,YAAY,OAAO,IAAI,SAAgB;AACtD,UAAM,SAAS,KAAK,IAAI,CAAC,MAAW,GAAG,KAAK,CAAC,CAAC;AAC9C,SAAK,KAAK,OAAO,IAAI,CAAC,MAAgB,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAE,EAAE,KAAK,GAAG,CAAC;AAAA,EACjG,CAAC;AAED,KAAG,QAAQ,YAAY,OAAO,KAAK;AACnC,KAAG,QAAQ,YAAY,QAAQ,KAAK;AACpC,KAAG,QAAQ,YAAY,SAAS,KAAK;AACrC,KAAG,QAAQ,GAAG,QAAQ,WAAW,UAAU;AAE3C,QAAM,QAAQ;AACd,aAAW,QAAQ;AACrB;AAEA,SAAS,oBACP,IACA,MACA,IACM;AACN,QAAM,SAAS,GAAG,sBAAsB,MAAM,UAAU,eAAsB;AAC5E,UAAM,OAAO,WAAW,IAAI,CAAC,MAAW,GAAG,KAAK,CAAC,CAAC;AAClD,UAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,WAAO,YAAY,IAAI,MAAM;AAAA,EAC/B,CAAC;AACD,KAAG,QAAQ,GAAG,QAAQ,MAAM,MAAM;AAClC,SAAO,QAAQ;AACjB;AAEA,SAAS,gBACP,IACA,MACA,IACM;AACN,QAAM,QAAQ,GAAG,UAAU;AAE3B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,EAAE,GAAG;AAC7C,QAAI,OAAO,UAAU,YAAY;AAC/B,YAAM,SAAS,GAAG,sBAAsB,KAAK,UAAU,eAAsB;AAC3E,cAAM,OAAO,WAAW,IAAI,CAAC,MAAW,GAAG,KAAK,CAAC,CAAC;AAClD,cAAM,SAAS,MAAM,MAAM,GAAG,IAAI;AAClC,eAAO,YAAY,IAAI,MAAM;AAAA,MAC/B,CAAC;AACD,SAAG,QAAQ,OAAO,KAAK,MAAM;AAC7B,aAAO,QAAQ;AAAA,IACjB,OAAO;AACL,YAAM,UAAU,KAAK,UAAU,KAAK;AACpC,YAAM,SAAS,GAAG,SAAS,IAAI,OAAO,GAAG;AACzC,UAAI,CAAC,OAAO,OAAO;AACjB,WAAG,QAAQ,OAAO,KAAK,OAAO,KAAK;AACnC,eAAO,MAAM,QAAQ;AAAA,MACvB,OAAO;AACL,eAAO,MAAM,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,KAAG,QAAQ,GAAG,QAAQ,MAAM,KAAK;AACjC,QAAM,QAAQ;AAChB;AAOA,SAAS,oBAAoB,QAA0B;AACrD,MACE,OAAO,WAAW,YAClB,WAAW,QACX,UAAU,UACT,OAAe,SAAS,eACzB,WAAW,QACX;AACA,WAAQ,OAAe;AAAA,EACzB;AACA,SAAO;AACT;AAEA,SAAS,YAAY,IAAS,OAAqB;AACjD,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO,GAAG;AACrD,MAAI,OAAO,UAAU,SAAU,QAAO,GAAG,UAAU,KAAK;AACxD,MAAI,OAAO,UAAU,SAAU,QAAO,GAAG,UAAU,KAAK;AACxD,MAAI,OAAO,UAAU,UAAW,QAAO,QAAQ,GAAG,OAAO,GAAG;AAG5D,QAAM,UAAU,KAAK,UAAU,KAAK;AACpC,QAAM,SAAS,GAAG,SAAS,IAAI,OAAO,GAAG;AACzC,MAAI,OAAO,OAAO;AAChB,WAAO,MAAM,QAAQ;AACrB,WAAO,GAAG;AAAA,EACZ;AACA,SAAO,OAAO;AAChB;","names":[]}
|