@yarlisai/sandbox 0.1.0-alpha.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 YarlisAI
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.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @yarlisai/sandbox
2
+
3
+ Pluggable sandbox runner for executing untrusted user JavaScript. Built on a **Port/Adapter** pattern (ADR 0007). Swap between a `worker_threads` pool, an in-process eval, or any custom transport by changing one line.
4
+
5
+ ## Port signature
6
+
7
+ ```ts
8
+ export interface SandboxRunner {
9
+ readonly name: string
10
+ execute(args: SandboxExecuteArgs): Promise<SandboxExecuteResult>
11
+ dispose(): Promise<void>
12
+ }
13
+
14
+ export interface SandboxClientConfig {
15
+ adapter: SandboxRunner
16
+ }
17
+ ```
18
+
19
+ `execute()` runs ONE piece of user code and resolves with its return value. `dispose()` tears the runner down (drain pool / close handles).
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ bun add @yarlisai/sandbox@alpha
25
+ ```
26
+
27
+ The default `node-worker` adapter relies on Node's built-in `worker_threads` and `vm` — no extra runtime deps.
28
+
29
+ ## Architecture
30
+
31
+ ```
32
+ Your app ──► SandboxClient ──► SandboxRunner (port) ──► [node-worker | memory | custom] (adapter)
33
+ ```
34
+
35
+ - **`SandboxRunner`** — the port: `{ name, execute(args), dispose() }`
36
+ - **`createSandboxClient({ adapter })`** — thin façade so callers don't reach into the adapter directly
37
+ - **Adapters** — each implements `SandboxRunner`
38
+
39
+ ## Usage
40
+
41
+ ```ts
42
+ import { createSandboxClient, nodeWorkerAdapter } from '@yarlisai/sandbox'
43
+
44
+ // Production: pool of worker_threads, vm.Script + wall-clock kill switch.
45
+ const sandbox = createSandboxClient({
46
+ adapter: nodeWorkerAdapter({ maxWorkers: 4, workerMemoryMb: 256 }),
47
+ })
48
+
49
+ const { result } = await sandbox.execute({
50
+ code: 'return params.a + params.b',
51
+ executionParams: { a: 1, b: 2 },
52
+ envVars: {},
53
+ contextVariables: {},
54
+ isCustomTool: false,
55
+ timeout: 5_000,
56
+ secureFetchImpl: (url, init) => fetch(url, init as RequestInit),
57
+ consoleSink: {
58
+ log: (m) => console.log(m),
59
+ info: (m) => console.info(m),
60
+ warn: (m) => console.warn(m),
61
+ error: (m) => console.error(m),
62
+ },
63
+ })
64
+
65
+ // Graceful shutdown.
66
+ await sandbox.dispose()
67
+ ```
68
+
69
+ ## Message protocol
70
+
71
+ The `node-worker` adapter speaks the following protocol over `postMessage`:
72
+
73
+ ```
74
+ main → worker
75
+ { type: 'execute', execId, code, contextVariables, executionParams,
76
+ envVars, isCustomTool, syncTimeout }
77
+ { type: 'fetch_response', id, ok, status, statusText, headers, body, error? }
78
+
79
+ worker → main
80
+ { type: 'fetch', id, url, init } // proxied back to secureFetchImpl
81
+ { type: 'console', level, message } // forwarded to consoleSink
82
+ { type: 'result', execId, value }
83
+ { type: 'error', execId, error: { name, message, stack } }
84
+ ```
85
+
86
+ Both message types are exported as `SandboxMainToWorkerMessage` and `SandboxWorkerToMainMessage` for adapter authors who want to reuse the same worker source. The raw worker source is also exported as `WORKER_SOURCE`.
87
+
88
+ ## Security caveats
89
+
90
+ - `node-worker` is **isolation, not sandboxing**: a `worker_threads` Worker shares the host filesystem, native crypto, and (without `resourceLimits`) memory. Code that bypasses `vm.runInContext` (e.g. by triggering an unhandled rejection deeper than the script) can in principle reach those surfaces. Combine with OS-level sandboxing if you accept code from anonymous users.
91
+ - All `fetch()` calls inside the sandbox proxy back to `secureFetchImpl` on the main thread. **You must SSRF-validate every URL there** — the worker has no network restriction of its own.
92
+ - The wall-clock timeout is enforced by `worker.terminate()` on the main thread. The `vm.Script` `timeout` option is a second kill switch; do not rely on it alone (microtask-starvation loops can outlast it).
93
+ - The `memory` adapter has **no isolation**. It runs user code in the same process via `new Function()` for unit tests / dev loops only. Never accept untrusted input through it.
94
+
95
+ ## Writing a custom adapter
96
+
97
+ ```ts
98
+ import type { SandboxRunner, SandboxExecuteArgs } from '@yarlisai/sandbox'
99
+
100
+ export function myAdapter(): SandboxRunner {
101
+ return {
102
+ name: 'my-adapter',
103
+ async execute(args: SandboxExecuteArgs) {
104
+ // call your runtime (firecracker, gvisor, e2b, ...)
105
+ return { result: someValue }
106
+ },
107
+ async dispose() {
108
+ // optional teardown
109
+ },
110
+ }
111
+ }
112
+ ```
113
+
114
+ Drop it into `createSandboxClient({ adapter: myAdapter() })` — done.
115
+
116
+ ## Built-in adapters
117
+
118
+ | Adapter | Factory | Transport | Use for |
119
+ |---|---|---|---|
120
+ | Node Worker | `nodeWorkerAdapter` | `worker_threads` pool + `vm.Script` | production |
121
+ | Memory | `memoryAdapter` | `new Function(code)` in same process | unit tests, dev |
122
+
123
+ ## Build
124
+
125
+ ```bash
126
+ bun install
127
+ cd packages/sandbox
128
+ bun run build
129
+ ```
130
+
131
+ ## Related
132
+
133
+ - [`@yarlisai/core`](../core/README.md) — logger / env helpers used by sibling packages.
134
+ - [ADR 0007 — Port/Adapter protocol](../../docs/architecture/decisions/0007-yarlisai-port-adapter.md) — the rules every service package follows.
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,12 @@
1
+ /**
2
+ * In-memory adapter for the SandboxRunner port.
3
+ *
4
+ * Runs user code in the SAME process via `new Function()`. There is **no
5
+ * isolation, no resource limit, no timeout enforcement, and no SSRF gate**.
6
+ * Use only in unit tests / dev loops where you control the input code.
7
+ *
8
+ * Production callers should use `nodeWorkerAdapter`.
9
+ */
10
+ import type { SandboxRunner } from '../types';
11
+ export declare function memoryAdapter(): SandboxRunner;
12
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAA4C,aAAa,EAAE,MAAM,UAAU,CAAA;AAEvF,wBAAgB,aAAa,IAAI,aAAa,CA+B7C"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * In-memory adapter for the SandboxRunner port.
3
+ *
4
+ * Runs user code in the SAME process via `new Function()`. There is **no
5
+ * isolation, no resource limit, no timeout enforcement, and no SSRF gate**.
6
+ * Use only in unit tests / dev loops where you control the input code.
7
+ *
8
+ * Production callers should use `nodeWorkerAdapter`.
9
+ */
10
+ export function memoryAdapter() {
11
+ return {
12
+ name: 'memory',
13
+ async execute(args) {
14
+ const { code, executionParams, envVars, contextVariables, secureFetchImpl, consoleSink } = args;
15
+ // Build a flat ctx object. Keys collide last-write-wins, mirroring the
16
+ // node-worker spread order: params, envVars, contextVariables, fetch, console.
17
+ const ctx = {
18
+ params: executionParams ?? {},
19
+ environmentVariables: envVars ?? {},
20
+ ...(contextVariables ?? {}),
21
+ fetch: secureFetchImpl,
22
+ console: consoleSink,
23
+ };
24
+ // Wrap in async IIFE so user code can `await` and `return`.
25
+ const wrapped = `return (async () => { ${code} })()`;
26
+ // eslint-disable-next-line no-new-func
27
+ const fn = new Function('ctx', `with (ctx) { ${wrapped} }`);
28
+ const value = await fn(ctx);
29
+ return { result: value };
30
+ },
31
+ async dispose() {
32
+ // Nothing to tear down.
33
+ },
34
+ };
35
+ }
36
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/adapters/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,CAAC,OAAO,CAAC,IAAwB;YACpC,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,GACtF,IAAI,CAAA;YAEN,uEAAuE;YACvE,+EAA+E;YAC/E,MAAM,GAAG,GAA4B;gBACnC,MAAM,EAAE,eAAe,IAAI,EAAE;gBAC7B,oBAAoB,EAAE,OAAO,IAAI,EAAE;gBACnC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;gBAC3B,KAAK,EAAE,eAAe;gBACtB,OAAO,EAAE,WAAW;aACrB,CAAA;YAED,4DAA4D;YAC5D,MAAM,OAAO,GAAG,yBAAyB,IAAI,OAAO,CAAA;YACpD,uCAAuC;YACvC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,KAAK,EAAE,gBAAgB,OAAO,IAAI,CAErC,CAAA;YAErB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAA;YAC3B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,OAAO;YACX,wBAAwB;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Node `worker_threads` adapter for the SandboxRunner port.
3
+ *
4
+ * Architecture:
5
+ * - A small pool (default 4) of reusable Workers spawned with `{ eval: true }`
6
+ * pointing at WORKER_SOURCE.
7
+ * - One execution per worker at a time. Concurrent calls queue.
8
+ * - Wall-clock timeout enforced by `worker.terminate()` on the main thread.
9
+ * A second `vm.Script` timeout inside the worker is a belt-and-braces.
10
+ * - All `fetch()` calls inside the sandbox proxy back to the caller-supplied
11
+ * `secureFetchImpl` over postMessage so SSRF / authn checks can run on the
12
+ * main thread.
13
+ *
14
+ * No external deps — `worker_threads` and `vm` are built into Node.
15
+ */
16
+ import type { NodeWorkerAdapterConfig, SandboxRunner } from '../types';
17
+ /**
18
+ * Construct a worker-pool-backed `SandboxRunner`.
19
+ *
20
+ * The returned runner owns its own pool. Call `dispose()` to drain it (e.g.
21
+ * between integration tests or on graceful shutdown).
22
+ */
23
+ export declare function nodeWorkerAdapter(config?: NodeWorkerAdapterConfig): SandboxRunner;
24
+ //# sourceMappingURL=node-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-worker.d.ts","sourceRoot":"","sources":["../../src/adapters/node-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAEV,uBAAuB,EAGvB,aAAa,EAEd,MAAM,UAAU,CAAA;AAmHjB;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,uBAA4B,GAAG,aAAa,CAkJrF"}
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Node `worker_threads` adapter for the SandboxRunner port.
3
+ *
4
+ * Architecture:
5
+ * - A small pool (default 4) of reusable Workers spawned with `{ eval: true }`
6
+ * pointing at WORKER_SOURCE.
7
+ * - One execution per worker at a time. Concurrent calls queue.
8
+ * - Wall-clock timeout enforced by `worker.terminate()` on the main thread.
9
+ * A second `vm.Script` timeout inside the worker is a belt-and-braces.
10
+ * - All `fetch()` calls inside the sandbox proxy back to the caller-supplied
11
+ * `secureFetchImpl` over postMessage so SSRF / authn checks can run on the
12
+ * main thread.
13
+ *
14
+ * No external deps — `worker_threads` and `vm` are built into Node.
15
+ */
16
+ import { Worker } from 'worker_threads';
17
+ import { SandboxTimeoutError } from '../types';
18
+ import { WORKER_SOURCE } from './worker-source';
19
+ const DEFAULT_MAX_WORKERS = 4;
20
+ const DEFAULT_WORKER_MEMORY_MB = 256;
21
+ class WorkerPool {
22
+ maxWorkers;
23
+ workerMemoryMb;
24
+ workers = [];
25
+ waiters = [];
26
+ constructor(maxWorkers, workerMemoryMb) {
27
+ this.maxWorkers = maxWorkers;
28
+ this.workerMemoryMb = workerMemoryMb;
29
+ }
30
+ size() {
31
+ return this.workers.length;
32
+ }
33
+ acquire() {
34
+ return new Promise((resolve, reject) => {
35
+ const idle = this.workers.find((w) => !w.busy);
36
+ if (idle) {
37
+ idle.busy = true;
38
+ resolve(idle);
39
+ return;
40
+ }
41
+ if (this.workers.length < this.maxWorkers) {
42
+ try {
43
+ const pooled = this.spawn();
44
+ pooled.busy = true;
45
+ resolve(pooled);
46
+ }
47
+ catch (err) {
48
+ reject(err);
49
+ }
50
+ return;
51
+ }
52
+ this.waiters.push((pooled) => {
53
+ pooled.busy = true;
54
+ resolve(pooled);
55
+ });
56
+ });
57
+ }
58
+ release(pooled) {
59
+ pooled.busy = false;
60
+ const next = this.waiters.shift();
61
+ if (next) {
62
+ next(pooled);
63
+ }
64
+ }
65
+ retire(pooled) {
66
+ const idx = this.workers.indexOf(pooled);
67
+ if (idx >= 0)
68
+ this.workers.splice(idx, 1);
69
+ if (this.waiters.length > 0 && this.workers.length < this.maxWorkers) {
70
+ try {
71
+ const pooledNew = this.spawn();
72
+ const waiter = this.waiters.shift();
73
+ if (waiter) {
74
+ pooledNew.busy = true;
75
+ waiter(pooledNew);
76
+ }
77
+ }
78
+ catch {
79
+ // Best-effort. Waiters reject on their own timeout in the caller.
80
+ }
81
+ }
82
+ }
83
+ async drain() {
84
+ const workers = this.workers.slice();
85
+ this.workers = [];
86
+ this.waiters = [];
87
+ await Promise.all(workers.map((p) => p.worker.terminate().catch(() => {
88
+ /* swallow */
89
+ })));
90
+ }
91
+ spawn() {
92
+ const worker = new Worker(WORKER_SOURCE, {
93
+ eval: true,
94
+ resourceLimits: {
95
+ maxOldGenerationSizeMb: this.workerMemoryMb,
96
+ },
97
+ });
98
+ const pooled = { worker, busy: false };
99
+ this.workers.push(pooled);
100
+ worker.once('exit', () => {
101
+ this.retire(pooled);
102
+ });
103
+ worker.on('error', () => {
104
+ this.retire(pooled);
105
+ });
106
+ return pooled;
107
+ }
108
+ }
109
+ /**
110
+ * Construct a worker-pool-backed `SandboxRunner`.
111
+ *
112
+ * The returned runner owns its own pool. Call `dispose()` to drain it (e.g.
113
+ * between integration tests or on graceful shutdown).
114
+ */
115
+ export function nodeWorkerAdapter(config = {}) {
116
+ const pool = new WorkerPool(config.maxWorkers ?? DEFAULT_MAX_WORKERS, config.workerMemoryMb ?? DEFAULT_WORKER_MEMORY_MB);
117
+ let nextExecId = 1;
118
+ return {
119
+ name: 'node-worker',
120
+ async execute(args) {
121
+ const { code, executionParams, envVars, contextVariables, isCustomTool, timeout, secureFetchImpl, consoleSink, } = args;
122
+ const pooled = await pool.acquire();
123
+ const worker = pooled.worker;
124
+ const execId = nextExecId++;
125
+ let timer = null;
126
+ let settled = false;
127
+ let terminated = false;
128
+ return new Promise((resolve, reject) => {
129
+ const cleanup = () => {
130
+ if (timer) {
131
+ clearTimeout(timer);
132
+ timer = null;
133
+ }
134
+ worker.off('message', onMessage);
135
+ worker.off('error', onError);
136
+ worker.off('exit', onExit);
137
+ };
138
+ const settleSuccess = (value) => {
139
+ if (settled)
140
+ return;
141
+ settled = true;
142
+ cleanup();
143
+ pool.release(pooled);
144
+ resolve({ result: value });
145
+ };
146
+ const settleFailure = (err, kill) => {
147
+ if (settled)
148
+ return;
149
+ settled = true;
150
+ cleanup();
151
+ if (kill) {
152
+ terminated = true;
153
+ worker.terminate().catch(() => {
154
+ /* already gone */
155
+ });
156
+ pool.retire(pooled);
157
+ }
158
+ else {
159
+ pool.release(pooled);
160
+ }
161
+ reject(err);
162
+ };
163
+ const onMessage = (msg) => {
164
+ if (!msg || typeof msg !== 'object')
165
+ return;
166
+ switch (msg.type) {
167
+ case 'console': {
168
+ const level = msg.level;
169
+ const sink = consoleSink[level] ?? consoleSink.log;
170
+ try {
171
+ sink(String(msg.message ?? ''));
172
+ }
173
+ catch {
174
+ /* sink failures must not break execution */
175
+ }
176
+ return;
177
+ }
178
+ case 'fetch': {
179
+ handleFetchRpc(worker, msg, secureFetchImpl);
180
+ return;
181
+ }
182
+ case 'result': {
183
+ if (msg.execId !== execId)
184
+ return;
185
+ settleSuccess(msg.value);
186
+ return;
187
+ }
188
+ case 'error': {
189
+ if (msg.execId !== execId)
190
+ return;
191
+ const e = new Error(msg.error?.message ?? 'Function execution failed');
192
+ if (msg.error?.name)
193
+ e.name = msg.error.name;
194
+ if (msg.error?.stack)
195
+ e.stack = msg.error.stack;
196
+ settleFailure(e, false);
197
+ return;
198
+ }
199
+ }
200
+ };
201
+ const onError = (err) => {
202
+ settleFailure(err, true);
203
+ };
204
+ const onExit = (_code) => {
205
+ if (settled)
206
+ return;
207
+ // Worker died unexpectedly. If we initiated termination, `settled`
208
+ // is normally already true by the time this fires.
209
+ const reason = terminated
210
+ ? new SandboxTimeoutError(timeout)
211
+ : new Error('Function worker exited unexpectedly');
212
+ settleFailure(reason, false);
213
+ };
214
+ worker.on('message', onMessage);
215
+ worker.on('error', onError);
216
+ worker.on('exit', onExit);
217
+ timer = setTimeout(() => {
218
+ settleFailure(new SandboxTimeoutError(timeout), true);
219
+ }, timeout);
220
+ try {
221
+ worker.postMessage({
222
+ type: 'execute',
223
+ execId,
224
+ code,
225
+ contextVariables,
226
+ executionParams,
227
+ envVars,
228
+ isCustomTool,
229
+ syncTimeout: timeout,
230
+ });
231
+ }
232
+ catch (err) {
233
+ settleFailure(err, true);
234
+ }
235
+ });
236
+ },
237
+ async dispose() {
238
+ await pool.drain();
239
+ },
240
+ };
241
+ }
242
+ async function handleFetchRpc(worker, msg, secureFetchImpl) {
243
+ const { id, url, init } = msg;
244
+ try {
245
+ const res = (await secureFetchImpl(url, init));
246
+ let bodyText = null;
247
+ let status = 200;
248
+ let statusText = 'OK';
249
+ let ok = true;
250
+ const headersOut = {};
251
+ let outUrl = url;
252
+ if (res && typeof res === 'object') {
253
+ if (typeof res.status === 'number')
254
+ status = res.status;
255
+ if (typeof res.statusText === 'string')
256
+ statusText = res.statusText;
257
+ if (typeof res.ok === 'boolean')
258
+ ok = res.ok;
259
+ else
260
+ ok = status >= 200 && status < 300;
261
+ if (typeof res.url === 'string')
262
+ outUrl = res.url;
263
+ if (res.headers && typeof res.headers.entries === 'function') {
264
+ for (const [k, v] of res.headers.entries()) {
265
+ headersOut[String(k).toLowerCase()] = String(v);
266
+ }
267
+ }
268
+ else if (res.headers && typeof res.headers === 'object') {
269
+ for (const [k, v] of Object.entries(res.headers)) {
270
+ headersOut[String(k).toLowerCase()] = String(v);
271
+ }
272
+ }
273
+ if (typeof res.text === 'function') {
274
+ try {
275
+ bodyText = await res.text();
276
+ }
277
+ catch {
278
+ bodyText = null;
279
+ }
280
+ }
281
+ else if (typeof res === 'string') {
282
+ bodyText = res;
283
+ }
284
+ }
285
+ worker.postMessage({
286
+ type: 'fetch_response',
287
+ id,
288
+ ok,
289
+ status,
290
+ statusText,
291
+ headers: headersOut,
292
+ body: bodyText,
293
+ url: outUrl,
294
+ });
295
+ }
296
+ catch (err) {
297
+ worker.postMessage({
298
+ type: 'fetch_response',
299
+ id,
300
+ error: { name: err?.name ?? 'Error', message: err?.message ?? String(err) },
301
+ });
302
+ }
303
+ }
304
+ //# sourceMappingURL=node-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-worker.js","sourceRoot":"","sources":["../../src/adapters/node-worker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Worker source for the Function-block sandbox.
3
+ *
4
+ * This file exports a string literal containing the JS source that runs in a
5
+ * `worker_threads` Worker (spawned with `{ eval: true }`). The string is
6
+ * intentionally self-contained — no `require` of TS-only modules, no
7
+ * cross-package imports — because it is parsed and executed by Node directly
8
+ * inside the worker. Loading via `eval: true` instead of a file path avoids
9
+ * Next.js bundling / `import.meta.url` resolution issues in the API route.
10
+ *
11
+ * Wire format (postMessage payloads):
12
+ *
13
+ * main → worker:
14
+ * { type: 'execute', execId, code, contextVariables, executionParams,
15
+ * envVars, isCustomTool, syncTimeout }
16
+ * { type: 'fetch_response', id, ok, status, statusText, headers, body, error? }
17
+ *
18
+ * worker → main:
19
+ * { type: 'fetch', id, url, init }
20
+ * { type: 'console', level, message }
21
+ * { type: 'result', execId, value }
22
+ * { type: 'error', execId, error: { name, message, stack } }
23
+ *
24
+ * Each Worker handles ONE execution at a time. The pool serialises calls.
25
+ */
26
+ export declare const WORKER_SOURCE: string;
27
+ //# sourceMappingURL=worker-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-source.d.ts","sourceRoot":"","sources":["../../src/adapters/worker-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,aAAa,QA4MzB,CAAA"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Worker source for the Function-block sandbox.
3
+ *
4
+ * This file exports a string literal containing the JS source that runs in a
5
+ * `worker_threads` Worker (spawned with `{ eval: true }`). The string is
6
+ * intentionally self-contained — no `require` of TS-only modules, no
7
+ * cross-package imports — because it is parsed and executed by Node directly
8
+ * inside the worker. Loading via `eval: true` instead of a file path avoids
9
+ * Next.js bundling / `import.meta.url` resolution issues in the API route.
10
+ *
11
+ * Wire format (postMessage payloads):
12
+ *
13
+ * main → worker:
14
+ * { type: 'execute', execId, code, contextVariables, executionParams,
15
+ * envVars, isCustomTool, syncTimeout }
16
+ * { type: 'fetch_response', id, ok, status, statusText, headers, body, error? }
17
+ *
18
+ * worker → main:
19
+ * { type: 'fetch', id, url, init }
20
+ * { type: 'console', level, message }
21
+ * { type: 'result', execId, value }
22
+ * { type: 'error', execId, error: { name, message, stack } }
23
+ *
24
+ * Each Worker handles ONE execution at a time. The pool serialises calls.
25
+ */
26
+ export const WORKER_SOURCE = String.raw `
27
+ 'use strict';
28
+
29
+ const { parentPort } = require('worker_threads');
30
+ const vm = require('vm');
31
+
32
+ if (!parentPort) {
33
+ throw new Error('sandbox-worker must be spawned via worker_threads with a parentPort');
34
+ }
35
+
36
+ // Track in-flight fetch RPCs so we can settle them when main posts back.
37
+ let nextFetchId = 1;
38
+ const pendingFetches = new Map();
39
+
40
+ function sendToParent(msg) {
41
+ try {
42
+ parentPort.postMessage(msg);
43
+ } catch (err) {
44
+ // Last resort — main is gone; nothing we can do from inside the worker.
45
+ }
46
+ }
47
+
48
+ function makeSecureFetch() {
49
+ return function secureFetch(input, init) {
50
+ const id = nextFetchId++;
51
+ const url = typeof input === 'string' ? input : (input && input.url) || input;
52
+
53
+ let initSerialisable;
54
+ if (init && typeof init === 'object') {
55
+ initSerialisable = {};
56
+ // Only forward the fields we know how to serialise across MessageChannel.
57
+ if (init.method) initSerialisable.method = init.method;
58
+ if (init.headers) {
59
+ // Headers might be a Headers instance, plain object, or [k,v][] array.
60
+ if (typeof init.headers.entries === 'function') {
61
+ const h = {};
62
+ for (const [k, v] of init.headers.entries()) h[k] = v;
63
+ initSerialisable.headers = h;
64
+ } else {
65
+ initSerialisable.headers = init.headers;
66
+ }
67
+ }
68
+ if (typeof init.body !== 'undefined') {
69
+ // Strings and JSON-serialisable objects only. Streams/blobs would
70
+ // throw on postMessage anyway.
71
+ initSerialisable.body = init.body;
72
+ }
73
+ if (init.redirect) initSerialisable.redirect = init.redirect;
74
+ }
75
+
76
+ return new Promise((resolve, reject) => {
77
+ pendingFetches.set(id, { resolve, reject });
78
+ sendToParent({ type: 'fetch', id, url, init: initSerialisable });
79
+ });
80
+ };
81
+ }
82
+
83
+ function makeConsole() {
84
+ function fwd(level) {
85
+ return function () {
86
+ const args = Array.prototype.slice.call(arguments);
87
+ const message = args
88
+ .map((a) => {
89
+ if (typeof a === 'string') return a;
90
+ try {
91
+ return JSON.stringify(a);
92
+ } catch {
93
+ return String(a);
94
+ }
95
+ })
96
+ .join(' ');
97
+ sendToParent({ type: 'console', level, message });
98
+ };
99
+ }
100
+ return {
101
+ log: fwd('log'),
102
+ info: fwd('info'),
103
+ warn: fwd('warn'),
104
+ error: fwd('error'),
105
+ debug: fwd('log'),
106
+ };
107
+ }
108
+
109
+ function buildScript({ code, isCustomTool, executionParams }) {
110
+ const wrapperLines = ['(async () => {', ' try {'];
111
+ if (isCustomTool) {
112
+ wrapperLines.push(' // For custom tools, make parameters directly accessible');
113
+ for (const key of Object.keys(executionParams || {})) {
114
+ wrapperLines.push(' const ' + key + ' = params.' + key + ';');
115
+ }
116
+ }
117
+ const userCodeStartLine = wrapperLines.length + 1;
118
+ const fullScript = [
119
+ ...wrapperLines,
120
+ ' ' + (code || '').split('\n').join('\n '),
121
+ ' } catch (error) {',
122
+ ' console.error(error);',
123
+ ' throw error;',
124
+ ' }',
125
+ '})()',
126
+ ].join('\n');
127
+ return { fullScript, userCodeStartLine };
128
+ }
129
+
130
+ async function runOne(msg) {
131
+ const {
132
+ execId,
133
+ code,
134
+ contextVariables,
135
+ executionParams,
136
+ envVars,
137
+ isCustomTool,
138
+ syncTimeout,
139
+ } = msg;
140
+
141
+ try {
142
+ const ctx = vm.createContext({
143
+ params: executionParams || {},
144
+ environmentVariables: envVars || {},
145
+ ...(contextVariables || {}),
146
+ fetch: makeSecureFetch(),
147
+ console: makeConsole(),
148
+ // Surface a few harmless globals that user code may reach for.
149
+ setTimeout,
150
+ clearTimeout,
151
+ setInterval,
152
+ clearInterval,
153
+ Buffer,
154
+ URL,
155
+ URLSearchParams,
156
+ TextEncoder,
157
+ TextDecoder,
158
+ });
159
+
160
+ const { fullScript } = buildScript({ code, isCustomTool, executionParams });
161
+ const script = new vm.Script(fullScript, { filename: 'user-function.js' });
162
+
163
+ // The sync timeout stays armed inside the worker as a SECOND kill switch.
164
+ // The main thread also enforces a wall-clock timeout via worker.terminate().
165
+ // We cap the script-level timeout at the requested syncTimeout value so we
166
+ // do not undershoot the wall-clock deadline.
167
+ const result = await script.runInContext(ctx, {
168
+ timeout: typeof syncTimeout === 'number' && syncTimeout > 0 ? syncTimeout : 30000,
169
+ displayErrors: true,
170
+ breakOnSigint: true,
171
+ });
172
+
173
+ sendToParent({ type: 'result', execId, value: result });
174
+ } catch (err) {
175
+ const error = {
176
+ name: (err && err.name) || 'Error',
177
+ message: (err && err.message) || String(err),
178
+ stack: err && err.stack,
179
+ };
180
+ sendToParent({ type: 'error', execId, error });
181
+ }
182
+ }
183
+
184
+ parentPort.on('message', (msg) => {
185
+ if (!msg || typeof msg !== 'object') return;
186
+ if (msg.type === 'execute') {
187
+ runOne(msg);
188
+ return;
189
+ }
190
+ if (msg.type === 'fetch_response') {
191
+ const pending = pendingFetches.get(msg.id);
192
+ if (!pending) return;
193
+ pendingFetches.delete(msg.id);
194
+ if (msg.error) {
195
+ const err = new Error(msg.error.message || 'fetch failed');
196
+ if (msg.error.name) err.name = msg.error.name;
197
+ pending.reject(err);
198
+ return;
199
+ }
200
+ // Reconstruct a Response-shaped object that user code can interact with.
201
+ const headers = msg.headers || {};
202
+ const body = msg.body;
203
+ const response = {
204
+ ok: !!msg.ok,
205
+ status: msg.status,
206
+ statusText: msg.statusText,
207
+ headers: {
208
+ get: (name) => headers[String(name).toLowerCase()] ?? null,
209
+ entries: () => Object.entries(headers),
210
+ forEach: (cb) => {
211
+ for (const [k, v] of Object.entries(headers)) cb(v, k);
212
+ },
213
+ },
214
+ url: msg.url,
215
+ text: () => Promise.resolve(typeof body === 'string' ? body : JSON.stringify(body ?? '')),
216
+ json: () =>
217
+ Promise.resolve(
218
+ typeof body === 'string' ? (body ? JSON.parse(body) : null) : body
219
+ ),
220
+ arrayBuffer: () =>
221
+ Promise.resolve(
222
+ typeof body === 'string'
223
+ ? new TextEncoder().encode(body).buffer
224
+ : new TextEncoder().encode(JSON.stringify(body ?? '')).buffer
225
+ ),
226
+ };
227
+ pending.resolve(response);
228
+ }
229
+ });
230
+ `;
231
+ //# sourceMappingURL=worker-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-source.js","sourceRoot":"","sources":["../../src/adapters/worker-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4MtC,CAAA"}
@@ -0,0 +1,35 @@
1
+ import type { SandboxClient, SandboxClientConfig } from './types';
2
+ /**
3
+ * Create a provider-agnostic sandbox client.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import { createSandboxClient, nodeWorkerAdapter } from '@yarlisai/sandbox'
8
+ *
9
+ * const sandbox = createSandboxClient({
10
+ * adapter: nodeWorkerAdapter({ maxWorkers: 4 }),
11
+ * })
12
+ *
13
+ * const { result } = await sandbox.execute({
14
+ * code: 'return params.a + params.b',
15
+ * executionParams: { a: 1, b: 2 },
16
+ * envVars: {},
17
+ * contextVariables: {},
18
+ * isCustomTool: false,
19
+ * timeout: 5000,
20
+ * secureFetchImpl: (url, init) => fetch(url, init),
21
+ * consoleSink: {
22
+ * log: (m) => console.log(m),
23
+ * info: (m) => console.info(m),
24
+ * warn: (m) => console.warn(m),
25
+ * error: (m) => console.error(m),
26
+ * },
27
+ * })
28
+ * ```
29
+ *
30
+ * Swap adapters by changing only the `adapter` field — `nodeWorkerAdapter`
31
+ * for production, `memoryAdapter` for tests, or roll your own. All implement
32
+ * the same `SandboxRunner` port.
33
+ */
34
+ export declare function createSandboxClient(config: SandboxClientConfig): SandboxClient;
35
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EAGpB,MAAM,SAAS,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAY9E"}
package/dist/client.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Create a provider-agnostic sandbox client.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { createSandboxClient, nodeWorkerAdapter } from '@yarlisai/sandbox'
7
+ *
8
+ * const sandbox = createSandboxClient({
9
+ * adapter: nodeWorkerAdapter({ maxWorkers: 4 }),
10
+ * })
11
+ *
12
+ * const { result } = await sandbox.execute({
13
+ * code: 'return params.a + params.b',
14
+ * executionParams: { a: 1, b: 2 },
15
+ * envVars: {},
16
+ * contextVariables: {},
17
+ * isCustomTool: false,
18
+ * timeout: 5000,
19
+ * secureFetchImpl: (url, init) => fetch(url, init),
20
+ * consoleSink: {
21
+ * log: (m) => console.log(m),
22
+ * info: (m) => console.info(m),
23
+ * warn: (m) => console.warn(m),
24
+ * error: (m) => console.error(m),
25
+ * },
26
+ * })
27
+ * ```
28
+ *
29
+ * Swap adapters by changing only the `adapter` field — `nodeWorkerAdapter`
30
+ * for production, `memoryAdapter` for tests, or roll your own. All implement
31
+ * the same `SandboxRunner` port.
32
+ */
33
+ export function createSandboxClient(config) {
34
+ return {
35
+ execute(args) {
36
+ return config.adapter.execute(args);
37
+ },
38
+ dispose() {
39
+ return config.adapter.dispose();
40
+ },
41
+ get adapter() {
42
+ return config.adapter.name;
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA2B;IAC7D,OAAO;QACL,OAAO,CAAC,IAAwB;YAC9B,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACrC,CAAC;QACD,OAAO;YACL,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;QACjC,CAAC;QACD,IAAI,OAAO;YACT,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAA;QAC5B,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { memoryAdapter } from './adapters/memory';
2
+ export { nodeWorkerAdapter } from './adapters/node-worker';
3
+ export { WORKER_SOURCE } from './adapters/worker-source';
4
+ export { createSandboxClient } from './client';
5
+ export * from './types';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAC9C,cAAc,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // @yarlisai/sandbox — pluggable sandbox runner for executing untrusted JS code.
2
+ //
3
+ // Add a new adapter: implement `SandboxRunner` (see types.ts) and export a factory.
4
+ export { memoryAdapter } from './adapters/memory';
5
+ // First-party adapters
6
+ export { nodeWorkerAdapter } from './adapters/node-worker';
7
+ // Re-exported for adapter authors who want to embed the same worker source.
8
+ export { WORKER_SOURCE } from './adapters/worker-source';
9
+ export { createSandboxClient } from './client';
10
+ export * from './types';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,oFAAoF;AAEpF,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,uBAAuB;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,4EAA4E;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAC9C,cAAc,SAAS,CAAA"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * @yarlisai/sandbox — port for executing untrusted JS code in isolation.
3
+ *
4
+ * Adapters (`node-worker`, `memory`, custom) implement `SandboxRunner`. Callers
5
+ * use `createSandboxClient({ adapter })` and never see the underlying transport.
6
+ *
7
+ * The default `node-worker` adapter spawns a small pool of `worker_threads`
8
+ * Workers and runs each piece of user code inside `vm.Script.runInContext`
9
+ * with a wall-clock kill switch. The `memory` adapter runs code synchronously
10
+ * in the parent process — no isolation, intended for unit tests / dev loops.
11
+ */
12
+ /** Console output forwarded out of the sandbox to the caller. */
13
+ export interface ConsoleSink {
14
+ log: (msg: string) => void;
15
+ info: (msg: string) => void;
16
+ warn: (msg: string) => void;
17
+ error: (msg: string) => void;
18
+ }
19
+ /**
20
+ * Caller-supplied fetch implementation. The sandbox cannot make network calls
21
+ * directly — all `fetch()` invocations inside the sandbox proxy back to this
22
+ * function on the main thread, where workspace-scoped headers / SSRF guards
23
+ * can be applied before the request leaves the box.
24
+ */
25
+ export type SecureFetchImpl = (url: string, init?: unknown) => Promise<Response> | Promise<unknown>;
26
+ /** Arguments for a single `execute()` call. */
27
+ export interface SandboxExecuteArgs {
28
+ /** Raw user code (JavaScript). The sandbox wraps this in an async IIFE. */
29
+ code: string;
30
+ /** Parameters made available to the script as `params.<name>`. */
31
+ executionParams: Record<string, unknown>;
32
+ /** Available to the script as `environmentVariables.<name>`. */
33
+ envVars: Record<string, unknown>;
34
+ /** Free-form context variables spread into the VM context. */
35
+ contextVariables: Record<string, unknown>;
36
+ /**
37
+ * If true, the wrapper destructures `executionParams` into top-level
38
+ * `const`s before user code runs (custom-tool ergonomics).
39
+ */
40
+ isCustomTool: boolean;
41
+ /** Wall-clock + in-VM timeout in milliseconds. */
42
+ timeout: number;
43
+ /** Main-thread fetch impl (SSRF / authn / quota gate). */
44
+ secureFetchImpl: SecureFetchImpl;
45
+ /** Sink for forwarded `console.*` output. */
46
+ consoleSink: ConsoleSink;
47
+ }
48
+ export interface SandboxExecuteResult {
49
+ /** The awaited return value of the user code. */
50
+ result: unknown;
51
+ }
52
+ /**
53
+ * Port: any sandbox adapter must implement this.
54
+ * - `execute()` runs ONE piece of user code and resolves with its return value.
55
+ * - `dispose()` tears the runner down (drain pool / close handles). Idempotent.
56
+ */
57
+ export interface SandboxRunner {
58
+ readonly name: string;
59
+ execute(args: SandboxExecuteArgs): Promise<SandboxExecuteResult>;
60
+ dispose(): Promise<void>;
61
+ }
62
+ /** Configuration for the worker-pool adapter. */
63
+ export interface NodeWorkerAdapterConfig {
64
+ /** Maximum live workers. Defaults to 4. */
65
+ maxWorkers?: number;
66
+ /** Per-worker `maxOldGenerationSizeMb`. Defaults to 256. */
67
+ workerMemoryMb?: number;
68
+ }
69
+ /** `createSandboxClient` config. */
70
+ export interface SandboxClientConfig {
71
+ adapter: SandboxRunner;
72
+ }
73
+ /** Public client surface returned by `createSandboxClient`. */
74
+ export interface SandboxClient {
75
+ execute(args: SandboxExecuteArgs): Promise<SandboxExecuteResult>;
76
+ dispose(): Promise<void>;
77
+ readonly adapter: string;
78
+ }
79
+ export type SandboxMainToWorkerMessage = {
80
+ type: 'execute';
81
+ execId: number;
82
+ code: string;
83
+ contextVariables: Record<string, unknown>;
84
+ executionParams: Record<string, unknown>;
85
+ envVars: Record<string, unknown>;
86
+ isCustomTool: boolean;
87
+ syncTimeout: number;
88
+ } | {
89
+ type: 'fetch_response';
90
+ id: number;
91
+ ok?: boolean;
92
+ status?: number;
93
+ statusText?: string;
94
+ headers?: Record<string, string>;
95
+ body?: string | null;
96
+ url?: string;
97
+ error?: {
98
+ name?: string;
99
+ message?: string;
100
+ };
101
+ };
102
+ export type SandboxWorkerToMainMessage = {
103
+ type: 'fetch';
104
+ id: number;
105
+ url: string;
106
+ init?: unknown;
107
+ } | {
108
+ type: 'console';
109
+ level: 'log' | 'info' | 'warn' | 'error';
110
+ message: string;
111
+ } | {
112
+ type: 'result';
113
+ execId: number;
114
+ value: unknown;
115
+ } | {
116
+ type: 'error';
117
+ execId: number;
118
+ error: {
119
+ name?: string;
120
+ message?: string;
121
+ stack?: string;
122
+ };
123
+ };
124
+ /**
125
+ * Reasons surfaced by the `node-worker` adapter when the wall-clock timeout
126
+ * fires. The `name` is preserved so existing matchers in apps continue to
127
+ * recognise the error.
128
+ */
129
+ export declare class SandboxTimeoutError extends Error {
130
+ constructor(timeout: number);
131
+ }
132
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @yarlisai/sandbox — port for executing untrusted JS code in isolation.
3
+ *
4
+ * Adapters (`node-worker`, `memory`, custom) implement `SandboxRunner`. Callers
5
+ * use `createSandboxClient({ adapter })` and never see the underlying transport.
6
+ *
7
+ * The default `node-worker` adapter spawns a small pool of `worker_threads`
8
+ * Workers and runs each piece of user code inside `vm.Script.runInContext`
9
+ * with a wall-clock kill switch. The `memory` adapter runs code synchronously
10
+ * in the parent process — no isolation, intended for unit tests / dev loops.
11
+ */
12
+ /**
13
+ * Reasons surfaced by the `node-worker` adapter when the wall-clock timeout
14
+ * fires. The `name` is preserved so existing matchers in apps continue to
15
+ * recognise the error.
16
+ */
17
+ export class SandboxTimeoutError extends Error {
18
+ constructor(timeout) {
19
+ // Phrasing intentionally includes both "exceeded" and "timed out" so the
20
+ // legacy `vm.Script` watchdog message and the worker wall-clock kill share
21
+ // the same matcher in tests and user-facing surfaces.
22
+ super(`Sandbox execution exceeded ${timeout}ms timeout (execution timed out)`);
23
+ this.name = 'SandboxTimeoutError';
24
+ }
25
+ }
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAmHH;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,yEAAyE;QACzE,2EAA2E;QAC3E,sDAAsD;QACtD,KAAK,CAAC,8BAA8B,OAAO,kCAAkC,CAAC,CAAA;QAC9E,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@yarlisai/sandbox",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Pluggable sandbox runner for executing untrusted user code (worker_threads pool + in-memory fallback).",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "bun": "./src/index.ts",
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "lint": "biome check .",
17
+ "typecheck": "tsc --noEmit",
18
+ "build": "bun run clean && bunx tsc -p tsconfig.build.json",
19
+ "clean": "rm -rf dist",
20
+ "prepublishOnly": "bun run build"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^20.11.0",
24
+ "typescript": "^5.7.3"
25
+ },
26
+ "license": "MIT",
27
+ "author": "YarlisAI <support@yarlis.ai>",
28
+ "homepage": "https://github.com/yarlisai/mybotbox-platform/tree/main/packages/sandbox#readme",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/yarlisai/mybotbox-platform.git",
32
+ "directory": "packages/sandbox"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/yarlisai/mybotbox-platform/issues"
36
+ },
37
+ "keywords": [
38
+ "yarlisai",
39
+ "framework",
40
+ "sandbox",
41
+ "worker_threads",
42
+ "code-execution",
43
+ "vm"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "registry": "https://registry.npmjs.org/"
48
+ },
49
+ "module": "./dist/index.js",
50
+ "files": [
51
+ "dist",
52
+ "README.md",
53
+ "LICENSE"
54
+ ],
55
+ "sideEffects": false
56
+ }