@cloudflare/sandbox 0.0.0-7f4442b → 0.0.0-80fd0f2
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/CHANGELOG.md +52 -16
- package/Dockerfile +78 -31
- package/README.md +9 -2
- package/dist/index.d.ts +1924 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3245 -65
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/clients/base-client.ts +39 -24
- package/src/clients/command-client.ts +8 -8
- package/src/clients/file-client.ts +31 -26
- package/src/clients/git-client.ts +9 -3
- package/src/clients/index.ts +12 -16
- package/src/clients/interpreter-client.ts +51 -47
- package/src/clients/port-client.ts +10 -10
- package/src/clients/process-client.ts +11 -8
- package/src/clients/sandbox-client.ts +2 -4
- package/src/clients/types.ts +6 -2
- package/src/clients/utility-client.ts +10 -6
- package/src/errors/adapter.ts +90 -32
- package/src/errors/classes.ts +189 -64
- package/src/errors/index.ts +9 -5
- package/src/file-stream.ts +11 -6
- package/src/index.ts +22 -15
- package/src/interpreter.ts +50 -41
- package/src/request-handler.ts +24 -21
- package/src/sandbox.ts +339 -149
- package/src/security.ts +21 -6
- package/src/sse-parser.ts +4 -3
- package/src/version.ts +1 -1
- package/tests/base-client.test.ts +116 -80
- package/tests/command-client.test.ts +149 -112
- package/tests/file-client.test.ts +309 -197
- package/tests/file-stream.test.ts +24 -20
- package/tests/get-sandbox.test.ts +10 -10
- package/tests/git-client.test.ts +260 -101
- package/tests/port-client.test.ts +100 -108
- package/tests/process-client.test.ts +204 -179
- package/tests/request-handler.test.ts +117 -65
- package/tests/sandbox.test.ts +219 -67
- package/tests/sse-parser.test.ts +17 -16
- package/tests/utility-client.test.ts +79 -72
- package/tsdown.config.ts +12 -0
- package/vitest.config.ts +6 -6
- package/dist/chunk-3GQHWV6X.js +0 -2484
- package/dist/chunk-3GQHWV6X.js.map +0 -1
- package/dist/chunk-BFVUNTP4.js +0 -104
- package/dist/chunk-BFVUNTP4.js.map +0 -1
- package/dist/chunk-EKSWCBCA.js +0 -86
- package/dist/chunk-EKSWCBCA.js.map +0 -1
- package/dist/chunk-FE4PJSRB.js +0 -7
- package/dist/chunk-FE4PJSRB.js.map +0 -1
- package/dist/chunk-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-Z532A7QC.js +0 -78
- package/dist/chunk-Z532A7QC.js.map +0 -1
- package/dist/file-stream.d.ts +0 -43
- package/dist/file-stream.js +0 -9
- package/dist/file-stream.js.map +0 -1
- package/dist/interpreter.d.ts +0 -33
- package/dist/interpreter.js +0 -8
- package/dist/interpreter.js.map +0 -1
- package/dist/request-handler.d.ts +0 -18
- package/dist/request-handler.js +0 -13
- package/dist/request-handler.js.map +0 -1
- package/dist/sandbox-CLZWpfGc.d.ts +0 -613
- package/dist/sandbox.d.ts +0 -4
- package/dist/sandbox.js +0 -13
- package/dist/sandbox.js.map +0 -1
- package/dist/security.d.ts +0 -31
- package/dist/security.js +0 -13
- package/dist/security.js.map +0 -1
- package/dist/sse-parser.d.ts +0 -28
- package/dist/sse-parser.js +0 -11
- package/dist/sse-parser.js.map +0 -1
- package/dist/version.d.ts +0 -8
- package/dist/version.js +0 -7
- package/dist/version.js.map +0 -1
|
@@ -5,12 +5,12 @@ import type {
|
|
|
5
5
|
VersionResponse
|
|
6
6
|
} from '../src/clients';
|
|
7
7
|
import { UtilityClient } from '../src/clients/utility-client';
|
|
8
|
-
import {
|
|
9
|
-
SandboxError
|
|
10
|
-
} from '../src/errors';
|
|
8
|
+
import { SandboxError } from '../src/errors';
|
|
11
9
|
|
|
12
10
|
// Mock data factory for creating test responses
|
|
13
|
-
const mockPingResponse = (
|
|
11
|
+
const mockPingResponse = (
|
|
12
|
+
overrides: Partial<PingResponse> = {}
|
|
13
|
+
): PingResponse => ({
|
|
14
14
|
success: true,
|
|
15
15
|
message: 'pong',
|
|
16
16
|
uptime: 12345,
|
|
@@ -18,7 +18,10 @@ const mockPingResponse = (overrides: Partial<PingResponse> = {}): PingResponse =
|
|
|
18
18
|
...overrides
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
const mockCommandsResponse = (
|
|
21
|
+
const mockCommandsResponse = (
|
|
22
|
+
commands: string[],
|
|
23
|
+
overrides: Partial<CommandsResponse> = {}
|
|
24
|
+
): CommandsResponse => ({
|
|
22
25
|
success: true,
|
|
23
26
|
availableCommands: commands,
|
|
24
27
|
count: commands.length,
|
|
@@ -26,7 +29,10 @@ const mockCommandsResponse = (commands: string[], overrides: Partial<CommandsRes
|
|
|
26
29
|
...overrides
|
|
27
30
|
});
|
|
28
31
|
|
|
29
|
-
const mockVersionResponse = (
|
|
32
|
+
const mockVersionResponse = (
|
|
33
|
+
version: string = '0.4.5',
|
|
34
|
+
overrides: Partial<VersionResponse> = {}
|
|
35
|
+
): VersionResponse => ({
|
|
30
36
|
success: true,
|
|
31
37
|
version,
|
|
32
38
|
timestamp: '2023-01-01T00:00:00Z',
|
|
@@ -45,7 +51,7 @@ describe('UtilityClient', () => {
|
|
|
45
51
|
|
|
46
52
|
client = new UtilityClient({
|
|
47
53
|
baseUrl: 'http://test.com',
|
|
48
|
-
port: 3000
|
|
54
|
+
port: 3000
|
|
49
55
|
});
|
|
50
56
|
});
|
|
51
57
|
|
|
@@ -55,10 +61,9 @@ describe('UtilityClient', () => {
|
|
|
55
61
|
|
|
56
62
|
describe('health checking', () => {
|
|
57
63
|
it('should check sandbox health successfully', async () => {
|
|
58
|
-
mockFetch.mockResolvedValue(
|
|
59
|
-
JSON.stringify(mockPingResponse()),
|
|
60
|
-
|
|
61
|
-
));
|
|
64
|
+
mockFetch.mockResolvedValue(
|
|
65
|
+
new Response(JSON.stringify(mockPingResponse()), { status: 200 })
|
|
66
|
+
);
|
|
62
67
|
|
|
63
68
|
const result = await client.ping();
|
|
64
69
|
|
|
@@ -69,10 +74,11 @@ describe('UtilityClient', () => {
|
|
|
69
74
|
const messages = ['pong', 'alive', 'ok'];
|
|
70
75
|
|
|
71
76
|
for (const message of messages) {
|
|
72
|
-
mockFetch.mockResolvedValueOnce(
|
|
73
|
-
JSON.stringify(mockPingResponse({ message })),
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
mockFetch.mockResolvedValueOnce(
|
|
78
|
+
new Response(JSON.stringify(mockPingResponse({ message })), {
|
|
79
|
+
status: 200
|
|
80
|
+
})
|
|
81
|
+
);
|
|
76
82
|
|
|
77
83
|
const result = await client.ping();
|
|
78
84
|
expect(result).toBe(message);
|
|
@@ -87,11 +93,11 @@ describe('UtilityClient', () => {
|
|
|
87
93
|
const healthChecks = await Promise.all([
|
|
88
94
|
client.ping(),
|
|
89
95
|
client.ping(),
|
|
90
|
-
client.ping()
|
|
96
|
+
client.ping()
|
|
91
97
|
]);
|
|
92
98
|
|
|
93
99
|
expect(healthChecks).toHaveLength(3);
|
|
94
|
-
healthChecks.forEach(result => {
|
|
100
|
+
healthChecks.forEach((result) => {
|
|
95
101
|
expect(result).toBe('pong');
|
|
96
102
|
});
|
|
97
103
|
|
|
@@ -104,10 +110,9 @@ describe('UtilityClient', () => {
|
|
|
104
110
|
code: 'HEALTH_CHECK_FAILED'
|
|
105
111
|
};
|
|
106
112
|
|
|
107
|
-
mockFetch.mockResolvedValue(
|
|
108
|
-
JSON.stringify(errorResponse),
|
|
109
|
-
|
|
110
|
-
));
|
|
113
|
+
mockFetch.mockResolvedValue(
|
|
114
|
+
new Response(JSON.stringify(errorResponse), { status: 503 })
|
|
115
|
+
);
|
|
111
116
|
|
|
112
117
|
await expect(client.ping()).rejects.toThrow();
|
|
113
118
|
});
|
|
@@ -123,10 +128,11 @@ describe('UtilityClient', () => {
|
|
|
123
128
|
it('should discover available system commands', async () => {
|
|
124
129
|
const systemCommands = ['ls', 'cat', 'echo', 'grep', 'find'];
|
|
125
130
|
|
|
126
|
-
mockFetch.mockResolvedValue(
|
|
127
|
-
JSON.stringify(mockCommandsResponse(systemCommands)),
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
mockFetch.mockResolvedValue(
|
|
132
|
+
new Response(JSON.stringify(mockCommandsResponse(systemCommands)), {
|
|
133
|
+
status: 200
|
|
134
|
+
})
|
|
135
|
+
);
|
|
130
136
|
|
|
131
137
|
const result = await client.getCommands();
|
|
132
138
|
|
|
@@ -139,10 +145,11 @@ describe('UtilityClient', () => {
|
|
|
139
145
|
it('should handle minimal command environments', async () => {
|
|
140
146
|
const minimalCommands = ['sh', 'echo', 'cat'];
|
|
141
147
|
|
|
142
|
-
mockFetch.mockResolvedValue(
|
|
143
|
-
JSON.stringify(mockCommandsResponse(minimalCommands)),
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
mockFetch.mockResolvedValue(
|
|
149
|
+
new Response(JSON.stringify(mockCommandsResponse(minimalCommands)), {
|
|
150
|
+
status: 200
|
|
151
|
+
})
|
|
152
|
+
);
|
|
146
153
|
|
|
147
154
|
const result = await client.getCommands();
|
|
148
155
|
|
|
@@ -153,10 +160,11 @@ describe('UtilityClient', () => {
|
|
|
153
160
|
it('should handle large command environments', async () => {
|
|
154
161
|
const richCommands = Array.from({ length: 150 }, (_, i) => `cmd_${i}`);
|
|
155
162
|
|
|
156
|
-
mockFetch.mockResolvedValue(
|
|
157
|
-
JSON.stringify(mockCommandsResponse(richCommands)),
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
mockFetch.mockResolvedValue(
|
|
164
|
+
new Response(JSON.stringify(mockCommandsResponse(richCommands)), {
|
|
165
|
+
status: 200
|
|
166
|
+
})
|
|
167
|
+
);
|
|
160
168
|
|
|
161
169
|
const result = await client.getCommands();
|
|
162
170
|
|
|
@@ -165,10 +173,9 @@ describe('UtilityClient', () => {
|
|
|
165
173
|
});
|
|
166
174
|
|
|
167
175
|
it('should handle empty command environments', async () => {
|
|
168
|
-
mockFetch.mockResolvedValue(
|
|
169
|
-
JSON.stringify(mockCommandsResponse([])),
|
|
170
|
-
|
|
171
|
-
));
|
|
176
|
+
mockFetch.mockResolvedValue(
|
|
177
|
+
new Response(JSON.stringify(mockCommandsResponse([])), { status: 200 })
|
|
178
|
+
);
|
|
172
179
|
|
|
173
180
|
const result = await client.getCommands();
|
|
174
181
|
|
|
@@ -182,10 +189,9 @@ describe('UtilityClient', () => {
|
|
|
182
189
|
code: 'PERMISSION_DENIED'
|
|
183
190
|
};
|
|
184
191
|
|
|
185
|
-
mockFetch.mockResolvedValue(
|
|
186
|
-
JSON.stringify(errorResponse),
|
|
187
|
-
|
|
188
|
-
));
|
|
192
|
+
mockFetch.mockResolvedValue(
|
|
193
|
+
new Response(JSON.stringify(errorResponse), { status: 403 })
|
|
194
|
+
);
|
|
189
195
|
|
|
190
196
|
await expect(client.getCommands()).rejects.toThrow();
|
|
191
197
|
});
|
|
@@ -193,10 +199,9 @@ describe('UtilityClient', () => {
|
|
|
193
199
|
|
|
194
200
|
describe('error handling and resilience', () => {
|
|
195
201
|
it('should handle malformed server responses gracefully', async () => {
|
|
196
|
-
mockFetch.mockResolvedValue(
|
|
197
|
-
'invalid json {',
|
|
198
|
-
|
|
199
|
-
));
|
|
202
|
+
mockFetch.mockResolvedValue(
|
|
203
|
+
new Response('invalid json {', { status: 200 })
|
|
204
|
+
);
|
|
200
205
|
|
|
201
206
|
await expect(client.ping()).rejects.toThrow(SandboxError);
|
|
202
207
|
});
|
|
@@ -210,10 +215,9 @@ describe('UtilityClient', () => {
|
|
|
210
215
|
|
|
211
216
|
it('should handle partial service failures', async () => {
|
|
212
217
|
// First call (ping) succeeds
|
|
213
|
-
mockFetch.mockResolvedValueOnce(
|
|
214
|
-
JSON.stringify(mockPingResponse()),
|
|
215
|
-
|
|
216
|
-
));
|
|
218
|
+
mockFetch.mockResolvedValueOnce(
|
|
219
|
+
new Response(JSON.stringify(mockPingResponse()), { status: 200 })
|
|
220
|
+
);
|
|
217
221
|
|
|
218
222
|
// Second call (getCommands) fails
|
|
219
223
|
const errorResponse = {
|
|
@@ -221,10 +225,9 @@ describe('UtilityClient', () => {
|
|
|
221
225
|
code: 'SERVICE_UNAVAILABLE'
|
|
222
226
|
};
|
|
223
227
|
|
|
224
|
-
mockFetch.mockResolvedValueOnce(
|
|
225
|
-
JSON.stringify(errorResponse),
|
|
226
|
-
|
|
227
|
-
));
|
|
228
|
+
mockFetch.mockResolvedValueOnce(
|
|
229
|
+
new Response(JSON.stringify(errorResponse), { status: 503 })
|
|
230
|
+
);
|
|
228
231
|
|
|
229
232
|
const pingResult = await client.ping();
|
|
230
233
|
expect(pingResult).toBe('pong');
|
|
@@ -239,7 +242,9 @@ describe('UtilityClient', () => {
|
|
|
239
242
|
if (callCount % 2 === 0) {
|
|
240
243
|
return Promise.reject(new Error('Intermittent failure'));
|
|
241
244
|
} else {
|
|
242
|
-
return Promise.resolve(
|
|
245
|
+
return Promise.resolve(
|
|
246
|
+
new Response(JSON.stringify(mockPingResponse()))
|
|
247
|
+
);
|
|
243
248
|
}
|
|
244
249
|
});
|
|
245
250
|
|
|
@@ -247,7 +252,7 @@ describe('UtilityClient', () => {
|
|
|
247
252
|
client.ping(), // Should succeed (call 1)
|
|
248
253
|
client.ping(), // Should fail (call 2)
|
|
249
254
|
client.ping(), // Should succeed (call 3)
|
|
250
|
-
client.ping()
|
|
255
|
+
client.ping() // Should fail (call 4)
|
|
251
256
|
]);
|
|
252
257
|
|
|
253
258
|
expect(results[0].status).toBe('fulfilled');
|
|
@@ -259,10 +264,11 @@ describe('UtilityClient', () => {
|
|
|
259
264
|
|
|
260
265
|
describe('version checking', () => {
|
|
261
266
|
it('should get container version successfully', async () => {
|
|
262
|
-
mockFetch.mockResolvedValue(
|
|
263
|
-
JSON.stringify(mockVersionResponse('0.4.5')),
|
|
264
|
-
|
|
265
|
-
|
|
267
|
+
mockFetch.mockResolvedValue(
|
|
268
|
+
new Response(JSON.stringify(mockVersionResponse('0.4.5')), {
|
|
269
|
+
status: 200
|
|
270
|
+
})
|
|
271
|
+
);
|
|
266
272
|
|
|
267
273
|
const result = await client.getVersion();
|
|
268
274
|
|
|
@@ -273,10 +279,11 @@ describe('UtilityClient', () => {
|
|
|
273
279
|
const versions = ['1.0.0', '2.5.3-beta', '0.0.1', '10.20.30'];
|
|
274
280
|
|
|
275
281
|
for (const version of versions) {
|
|
276
|
-
mockFetch.mockResolvedValueOnce(
|
|
277
|
-
JSON.stringify(mockVersionResponse(version)),
|
|
278
|
-
|
|
279
|
-
|
|
282
|
+
mockFetch.mockResolvedValueOnce(
|
|
283
|
+
new Response(JSON.stringify(mockVersionResponse(version)), {
|
|
284
|
+
status: 200
|
|
285
|
+
})
|
|
286
|
+
);
|
|
280
287
|
|
|
281
288
|
const result = await client.getVersion();
|
|
282
289
|
expect(result).toBe(version);
|
|
@@ -285,10 +292,9 @@ describe('UtilityClient', () => {
|
|
|
285
292
|
|
|
286
293
|
it('should return "unknown" when version endpoint does not exist (backward compatibility)', async () => {
|
|
287
294
|
// Simulate 404 or other error for old containers
|
|
288
|
-
mockFetch.mockResolvedValue(
|
|
289
|
-
JSON.stringify({ error: 'Not Found' }),
|
|
290
|
-
|
|
291
|
-
));
|
|
295
|
+
mockFetch.mockResolvedValue(
|
|
296
|
+
new Response(JSON.stringify({ error: 'Not Found' }), { status: 404 })
|
|
297
|
+
);
|
|
292
298
|
|
|
293
299
|
const result = await client.getVersion();
|
|
294
300
|
|
|
@@ -304,10 +310,11 @@ describe('UtilityClient', () => {
|
|
|
304
310
|
});
|
|
305
311
|
|
|
306
312
|
it('should handle version response with unknown value', async () => {
|
|
307
|
-
mockFetch.mockResolvedValue(
|
|
308
|
-
JSON.stringify(mockVersionResponse('unknown')),
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
mockFetch.mockResolvedValue(
|
|
314
|
+
new Response(JSON.stringify(mockVersionResponse('unknown')), {
|
|
315
|
+
status: 200
|
|
316
|
+
})
|
|
317
|
+
);
|
|
311
318
|
|
|
312
319
|
const result = await client.getVersion();
|
|
313
320
|
|
|
@@ -324,7 +331,7 @@ describe('UtilityClient', () => {
|
|
|
324
331
|
it('should initialize with full options', () => {
|
|
325
332
|
const fullOptionsClient = new UtilityClient({
|
|
326
333
|
baseUrl: 'http://custom.com',
|
|
327
|
-
port: 8080
|
|
334
|
+
port: 8080
|
|
328
335
|
});
|
|
329
336
|
expect(fullOptionsClient).toBeInstanceOf(UtilityClient);
|
|
330
337
|
});
|
package/tsdown.config.ts
ADDED
package/vitest.config.ts
CHANGED
|
@@ -18,14 +18,14 @@ export default defineWorkersConfig({
|
|
|
18
18
|
poolOptions: {
|
|
19
19
|
workers: {
|
|
20
20
|
wrangler: {
|
|
21
|
-
configPath: './tests/wrangler.jsonc'
|
|
21
|
+
configPath: './tests/wrangler.jsonc'
|
|
22
22
|
},
|
|
23
23
|
singleWorker: true,
|
|
24
|
-
isolatedStorage: false
|
|
25
|
-
}
|
|
26
|
-
}
|
|
24
|
+
isolatedStorage: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
27
|
},
|
|
28
28
|
esbuild: {
|
|
29
|
-
target: 'esnext'
|
|
30
|
-
}
|
|
29
|
+
target: 'esnext'
|
|
30
|
+
}
|
|
31
31
|
});
|