@cloudflare/sandbox 0.4.12 → 0.4.15
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/.turbo/turbo-build.log +13 -47
- package/CHANGELOG.md +46 -16
- package/Dockerfile +78 -31
- package/README.md +9 -2
- package/dist/index.d.ts +1889 -9
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3144 -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 +3 -4
- 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 +188 -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-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-JXZMAU2C.js +0 -559
- package/dist/chunk-JXZMAU2C.js.map +0 -1
- package/dist/chunk-UJ3TV4M6.js +0 -7
- package/dist/chunk-UJ3TV4M6.js.map +0 -1
- package/dist/chunk-YE265ASX.js +0 -2484
- package/dist/chunk-YE265ASX.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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import type {
|
|
3
|
-
ExposePortResponse,
|
|
2
|
+
import type {
|
|
3
|
+
ExposePortResponse,
|
|
4
4
|
GetExposedPortsResponse,
|
|
5
|
-
UnexposePortResponse
|
|
5
|
+
UnexposePortResponse
|
|
6
6
|
} from '../src/clients';
|
|
7
7
|
import { PortClient } from '../src/clients/port-client';
|
|
8
8
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
PortError,
|
|
12
12
|
PortInUseError,
|
|
13
13
|
PortNotExposedError,
|
|
14
|
-
SandboxError,
|
|
14
|
+
SandboxError,
|
|
15
15
|
ServiceNotRespondingError
|
|
16
16
|
} from '../src/errors';
|
|
17
17
|
|
|
@@ -21,13 +21,13 @@ describe('PortClient', () => {
|
|
|
21
21
|
|
|
22
22
|
beforeEach(() => {
|
|
23
23
|
vi.clearAllMocks();
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
mockFetch = vi.fn();
|
|
26
26
|
global.fetch = mockFetch as unknown as typeof fetch;
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
client = new PortClient({
|
|
29
29
|
baseUrl: 'http://test.com',
|
|
30
|
-
port: 3000
|
|
30
|
+
port: 3000
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
|
|
@@ -42,13 +42,12 @@ describe('PortClient', () => {
|
|
|
42
42
|
port: 3001,
|
|
43
43
|
exposedAt: 'https://preview-abc123.workers.dev',
|
|
44
44
|
name: 'web-server',
|
|
45
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
45
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
46
46
|
};
|
|
47
|
-
|
|
48
|
-
mockFetch.mockResolvedValue(
|
|
49
|
-
JSON.stringify(mockResponse),
|
|
50
|
-
|
|
51
|
-
));
|
|
47
|
+
|
|
48
|
+
mockFetch.mockResolvedValue(
|
|
49
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
50
|
+
);
|
|
52
51
|
|
|
53
52
|
const result = await client.exposePort(3001, 'session-123', 'web-server');
|
|
54
53
|
|
|
@@ -65,13 +64,12 @@ describe('PortClient', () => {
|
|
|
65
64
|
port: 8080,
|
|
66
65
|
exposedAt: 'https://api-def456.workers.dev',
|
|
67
66
|
name: 'api-server',
|
|
68
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
67
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
69
68
|
};
|
|
70
|
-
|
|
71
|
-
mockFetch.mockResolvedValue(
|
|
72
|
-
JSON.stringify(mockResponse),
|
|
73
|
-
|
|
74
|
-
));
|
|
69
|
+
|
|
70
|
+
mockFetch.mockResolvedValue(
|
|
71
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
72
|
+
);
|
|
75
73
|
|
|
76
74
|
const result = await client.exposePort(8080, 'session-456', 'api-server');
|
|
77
75
|
|
|
@@ -86,13 +84,12 @@ describe('PortClient', () => {
|
|
|
86
84
|
success: true,
|
|
87
85
|
port: 5000,
|
|
88
86
|
exposedAt: 'https://service-ghi789.workers.dev',
|
|
89
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
87
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
90
88
|
};
|
|
91
|
-
|
|
92
|
-
mockFetch.mockResolvedValue(
|
|
93
|
-
JSON.stringify(mockResponse),
|
|
94
|
-
|
|
95
|
-
));
|
|
89
|
+
|
|
90
|
+
mockFetch.mockResolvedValue(
|
|
91
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
92
|
+
);
|
|
96
93
|
|
|
97
94
|
const result = await client.exposePort(5000, 'session-789');
|
|
98
95
|
|
|
@@ -101,7 +98,6 @@ describe('PortClient', () => {
|
|
|
101
98
|
expect(result.name).toBeUndefined();
|
|
102
99
|
expect(result.exposedAt).toBeDefined();
|
|
103
100
|
});
|
|
104
|
-
|
|
105
101
|
});
|
|
106
102
|
|
|
107
103
|
describe('service management', () => {
|
|
@@ -112,35 +108,34 @@ describe('PortClient', () => {
|
|
|
112
108
|
{
|
|
113
109
|
port: 3000,
|
|
114
110
|
exposedAt: 'https://frontend-abc123.workers.dev',
|
|
115
|
-
name: 'frontend'
|
|
111
|
+
name: 'frontend'
|
|
116
112
|
},
|
|
117
113
|
{
|
|
118
114
|
port: 4000,
|
|
119
115
|
exposedAt: 'https://api-def456.workers.dev',
|
|
120
|
-
name: 'api'
|
|
116
|
+
name: 'api'
|
|
121
117
|
},
|
|
122
118
|
{
|
|
123
119
|
port: 5432,
|
|
124
120
|
exposedAt: 'https://db-ghi789.workers.dev',
|
|
125
|
-
name: 'database'
|
|
121
|
+
name: 'database'
|
|
126
122
|
}
|
|
127
123
|
],
|
|
128
124
|
count: 3,
|
|
129
|
-
timestamp: '2023-01-01T00:10:00Z'
|
|
125
|
+
timestamp: '2023-01-01T00:10:00Z'
|
|
130
126
|
};
|
|
131
|
-
|
|
132
|
-
mockFetch.mockResolvedValue(
|
|
133
|
-
JSON.stringify(mockResponse),
|
|
134
|
-
|
|
135
|
-
));
|
|
127
|
+
|
|
128
|
+
mockFetch.mockResolvedValue(
|
|
129
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
130
|
+
);
|
|
136
131
|
|
|
137
132
|
const result = await client.getExposedPorts('session-list');
|
|
138
133
|
|
|
139
134
|
expect(result.success).toBe(true);
|
|
140
135
|
expect(result.count).toBe(3);
|
|
141
136
|
expect(result.ports).toHaveLength(3);
|
|
142
|
-
|
|
143
|
-
result.ports.forEach(service => {
|
|
137
|
+
|
|
138
|
+
result.ports.forEach((service) => {
|
|
144
139
|
expect(service.exposedAt).toContain('.workers.dev');
|
|
145
140
|
expect(service.port).toBeGreaterThan(0);
|
|
146
141
|
expect(service.name).toBeDefined();
|
|
@@ -152,13 +147,12 @@ describe('PortClient', () => {
|
|
|
152
147
|
success: true,
|
|
153
148
|
ports: [],
|
|
154
149
|
count: 0,
|
|
155
|
-
timestamp: '2023-01-01T00:00:00Z'
|
|
150
|
+
timestamp: '2023-01-01T00:00:00Z'
|
|
156
151
|
};
|
|
157
|
-
|
|
158
|
-
mockFetch.mockResolvedValue(
|
|
159
|
-
JSON.stringify(mockResponse),
|
|
160
|
-
|
|
161
|
-
));
|
|
152
|
+
|
|
153
|
+
mockFetch.mockResolvedValue(
|
|
154
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
155
|
+
);
|
|
162
156
|
|
|
163
157
|
const result = await client.getExposedPorts('session-empty');
|
|
164
158
|
|
|
@@ -171,20 +165,18 @@ describe('PortClient', () => {
|
|
|
171
165
|
const mockResponse: UnexposePortResponse = {
|
|
172
166
|
success: true,
|
|
173
167
|
port: 3001,
|
|
174
|
-
timestamp: '2023-01-01T00:15:00Z'
|
|
168
|
+
timestamp: '2023-01-01T00:15:00Z'
|
|
175
169
|
};
|
|
176
|
-
|
|
177
|
-
mockFetch.mockResolvedValue(
|
|
178
|
-
JSON.stringify(mockResponse),
|
|
179
|
-
|
|
180
|
-
));
|
|
170
|
+
|
|
171
|
+
mockFetch.mockResolvedValue(
|
|
172
|
+
new Response(JSON.stringify(mockResponse), { status: 200 })
|
|
173
|
+
);
|
|
181
174
|
|
|
182
175
|
const result = await client.unexposePort(3001, 'session-unexpose');
|
|
183
176
|
|
|
184
177
|
expect(result.success).toBe(true);
|
|
185
178
|
expect(result.port).toBe(3001);
|
|
186
179
|
});
|
|
187
|
-
|
|
188
180
|
});
|
|
189
181
|
|
|
190
182
|
describe('port validation and error handling', () => {
|
|
@@ -193,14 +185,14 @@ describe('PortClient', () => {
|
|
|
193
185
|
error: 'Port already exposed: 3000',
|
|
194
186
|
code: 'PORT_ALREADY_EXPOSED'
|
|
195
187
|
};
|
|
196
|
-
|
|
197
|
-
mockFetch.mockResolvedValue(
|
|
198
|
-
JSON.stringify(errorResponse),
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
188
|
+
|
|
189
|
+
mockFetch.mockResolvedValue(
|
|
190
|
+
new Response(JSON.stringify(errorResponse), { status: 409 })
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
await expect(client.exposePort(3000, 'session-err')).rejects.toThrow(
|
|
194
|
+
PortAlreadyExposedError
|
|
195
|
+
);
|
|
204
196
|
});
|
|
205
197
|
|
|
206
198
|
it('should handle invalid port numbers', async () => {
|
|
@@ -208,14 +200,14 @@ describe('PortClient', () => {
|
|
|
208
200
|
error: 'Invalid port number: 0',
|
|
209
201
|
code: 'INVALID_PORT_NUMBER'
|
|
210
202
|
};
|
|
211
|
-
|
|
212
|
-
mockFetch.mockResolvedValue(
|
|
213
|
-
JSON.stringify(errorResponse),
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
|
|
204
|
+
mockFetch.mockResolvedValue(
|
|
205
|
+
new Response(JSON.stringify(errorResponse), { status: 400 })
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
await expect(client.exposePort(0, 'session-err')).rejects.toThrow(
|
|
209
|
+
InvalidPortError
|
|
210
|
+
);
|
|
219
211
|
});
|
|
220
212
|
|
|
221
213
|
it('should handle port in use errors', async () => {
|
|
@@ -223,14 +215,14 @@ describe('PortClient', () => {
|
|
|
223
215
|
error: 'Port in use: 3000 is already bound by another process',
|
|
224
216
|
code: 'PORT_IN_USE'
|
|
225
217
|
};
|
|
226
|
-
|
|
227
|
-
mockFetch.mockResolvedValue(
|
|
228
|
-
JSON.stringify(errorResponse),
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
218
|
+
|
|
219
|
+
mockFetch.mockResolvedValue(
|
|
220
|
+
new Response(JSON.stringify(errorResponse), { status: 409 })
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
await expect(client.exposePort(3000, 'session-err')).rejects.toThrow(
|
|
224
|
+
PortInUseError
|
|
225
|
+
);
|
|
234
226
|
});
|
|
235
227
|
|
|
236
228
|
it('should handle service not responding errors', async () => {
|
|
@@ -238,14 +230,14 @@ describe('PortClient', () => {
|
|
|
238
230
|
error: 'Service not responding on port 8080',
|
|
239
231
|
code: 'SERVICE_NOT_RESPONDING'
|
|
240
232
|
};
|
|
241
|
-
|
|
242
|
-
mockFetch.mockResolvedValue(
|
|
243
|
-
JSON.stringify(errorResponse),
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
|
|
234
|
+
mockFetch.mockResolvedValue(
|
|
235
|
+
new Response(JSON.stringify(errorResponse), { status: 503 })
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
await expect(client.exposePort(8080, 'session-err')).rejects.toThrow(
|
|
239
|
+
ServiceNotRespondingError
|
|
240
|
+
);
|
|
249
241
|
});
|
|
250
242
|
|
|
251
243
|
it('should handle unexpose non-existent port', async () => {
|
|
@@ -253,14 +245,14 @@ describe('PortClient', () => {
|
|
|
253
245
|
error: 'Port not exposed: 9999',
|
|
254
246
|
code: 'PORT_NOT_EXPOSED'
|
|
255
247
|
};
|
|
256
|
-
|
|
257
|
-
mockFetch.mockResolvedValue(
|
|
258
|
-
JSON.stringify(errorResponse),
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
248
|
+
|
|
249
|
+
mockFetch.mockResolvedValue(
|
|
250
|
+
new Response(JSON.stringify(errorResponse), { status: 404 })
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await expect(client.unexposePort(9999, 'session-err')).rejects.toThrow(
|
|
254
|
+
PortNotExposedError
|
|
255
|
+
);
|
|
264
256
|
});
|
|
265
257
|
|
|
266
258
|
it('should handle port operation failures', async () => {
|
|
@@ -268,14 +260,14 @@ describe('PortClient', () => {
|
|
|
268
260
|
error: 'Port operation failed: unable to setup proxy',
|
|
269
261
|
code: 'PORT_OPERATION_ERROR'
|
|
270
262
|
};
|
|
271
|
-
|
|
272
|
-
mockFetch.mockResolvedValue(
|
|
273
|
-
JSON.stringify(errorResponse),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
263
|
+
|
|
264
|
+
mockFetch.mockResolvedValue(
|
|
265
|
+
new Response(JSON.stringify(errorResponse), { status: 500 })
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
await expect(client.exposePort(3000, 'session-err')).rejects.toThrow(
|
|
269
|
+
PortError
|
|
270
|
+
);
|
|
279
271
|
});
|
|
280
272
|
});
|
|
281
273
|
|
|
@@ -283,19 +275,19 @@ describe('PortClient', () => {
|
|
|
283
275
|
it('should handle network failures gracefully', async () => {
|
|
284
276
|
mockFetch.mockRejectedValue(new Error('Network connection failed'));
|
|
285
277
|
|
|
286
|
-
await expect(client.exposePort(3000, 'session-net'))
|
|
287
|
-
|
|
278
|
+
await expect(client.exposePort(3000, 'session-net')).rejects.toThrow(
|
|
279
|
+
'Network connection failed'
|
|
280
|
+
);
|
|
288
281
|
});
|
|
289
282
|
|
|
290
283
|
it('should handle malformed server responses', async () => {
|
|
291
|
-
mockFetch.mockResolvedValue(
|
|
292
|
-
'invalid json {',
|
|
293
|
-
|
|
294
|
-
));
|
|
284
|
+
mockFetch.mockResolvedValue(
|
|
285
|
+
new Response('invalid json {', { status: 200 })
|
|
286
|
+
);
|
|
295
287
|
|
|
296
|
-
await expect(client.exposePort(3000, 'session-malform'))
|
|
297
|
-
|
|
288
|
+
await expect(client.exposePort(3000, 'session-malform')).rejects.toThrow(
|
|
289
|
+
SandboxError
|
|
290
|
+
);
|
|
298
291
|
});
|
|
299
|
-
|
|
300
292
|
});
|
|
301
|
-
});
|
|
293
|
+
});
|