@hyperbrowser/sdk 0.93.0 → 1.0.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/README.md +190 -0
- package/dist/client.d.ts +2 -20
- package/dist/client.js +5 -17
- package/dist/error.d.ts +27 -0
- package/dist/error.js +32 -0
- package/dist/image-builds.d.ts +6 -0
- package/dist/image-builds.js +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +12 -1
- package/dist/retry.d.ts +14 -0
- package/dist/retry.js +49 -0
- package/dist/sandbox/base.d.ts +27 -0
- package/dist/sandbox/base.js +298 -99
- package/dist/sandbox/files.d.ts +24 -25
- package/dist/sandbox/files.js +146 -37
- package/dist/sandbox/image-build/artifacts.d.ts +16 -0
- package/dist/sandbox/image-build/artifacts.js +16 -0
- package/dist/sandbox/image-build/blake2b.d.ts +2 -0
- package/dist/sandbox/image-build/blake2b.js +89 -0
- package/dist/sandbox/image-build/common.d.ts +19 -0
- package/dist/sandbox/image-build/common.js +109 -0
- package/dist/sandbox/image-build/context.d.ts +43 -0
- package/dist/sandbox/image-build/context.js +599 -0
- package/dist/sandbox/image-build/docker-image.d.ts +34 -0
- package/dist/sandbox/image-build/docker-image.js +472 -0
- package/dist/sandbox/image-build/dockerfile-analysis.d.ts +17 -0
- package/dist/sandbox/image-build/dockerfile-analysis.js +409 -0
- package/dist/sandbox/image-build/dockerignore.d.ts +18 -0
- package/dist/sandbox/image-build/dockerignore.js +275 -0
- package/dist/sandbox/image-build/gzip.d.ts +11 -0
- package/dist/sandbox/image-build/gzip.js +91 -0
- package/dist/sandbox/image-build/image-init.d.ts +5 -0
- package/dist/sandbox/image-build/image-init.js +97 -0
- package/dist/sandbox/image-build/index.d.ts +13 -0
- package/dist/sandbox/image-build/index.js +36 -0
- package/dist/sandbox/image-build/local-docker.d.ts +11 -0
- package/dist/sandbox/image-build/local-docker.js +72 -0
- package/dist/sandbox/image-build/resolution.d.ts +27 -0
- package/dist/sandbox/image-build/resolution.js +137 -0
- package/dist/sandbox/image-build/tar.d.ts +41 -0
- package/dist/sandbox/image-build/tar.js +378 -0
- package/dist/sandbox/image-build/upload.d.ts +10 -0
- package/dist/sandbox/image-build/upload.js +143 -0
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.js +4 -1
- package/dist/sandbox/process-output.d.ts +36 -0
- package/dist/sandbox/process-output.js +117 -0
- package/dist/sandbox/process.d.ts +25 -1
- package/dist/sandbox/process.js +197 -29
- package/dist/sandbox/terminal.js +9 -5
- package/dist/sandbox/ws.d.ts +4 -1
- package/dist/sandbox/ws.js +33 -7
- package/dist/services/base.d.ts +2 -0
- package/dist/services/base.js +46 -30
- package/dist/services/normalize.d.ts +3 -0
- package/dist/services/normalize.js +21 -0
- package/dist/services/sandboxes.d.ts +40 -3
- package/dist/services/sandboxes.js +569 -53
- package/dist/services/volumes.js +29 -14
- package/dist/types/index.d.ts +2 -1
- package/dist/types/sandbox.d.ts +246 -30
- package/dist/types/volume.d.ts +7 -7
- package/package.json +24 -5
package/dist/sandbox/base.js
CHANGED
|
@@ -3,69 +3,184 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RuntimeTransport = void 0;
|
|
6
|
+
exports.RuntimeTransport = exports.PROCESS_STREAM_IDLE_TIMEOUT_MS = void 0;
|
|
7
|
+
const stream_1 = require("stream");
|
|
8
|
+
const string_decoder_1 = require("string_decoder");
|
|
7
9
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
-
const
|
|
10
|
+
const error_1 = require("../error");
|
|
11
|
+
const retry_1 = require("../retry");
|
|
9
12
|
const ws_1 = require("./ws");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"ECONNRESET",
|
|
13
|
-
"ECONNREFUSED",
|
|
14
|
-
"EAI_AGAIN",
|
|
15
|
-
"ETIMEDOUT",
|
|
16
|
-
"ESOCKETTIMEDOUT",
|
|
17
|
-
]);
|
|
13
|
+
// The receiver sends process SSE keepalives every 15 seconds.
|
|
14
|
+
exports.PROCESS_STREAM_IDLE_TIMEOUT_MS = 60000;
|
|
18
15
|
const getRequestId = (response) => {
|
|
19
16
|
return response.headers.get("x-request-id") || response.headers.get("request-id") || undefined;
|
|
20
17
|
};
|
|
21
|
-
const
|
|
22
|
-
if (!(error instanceof Error)) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
const networkError = error;
|
|
26
|
-
return (networkError.name === "AbortError" ||
|
|
27
|
-
networkError.type === "request-timeout" ||
|
|
28
|
-
(networkError.code ? RETRYABLE_NETWORK_CODES.has(networkError.code) : false));
|
|
29
|
-
};
|
|
18
|
+
const isEventStream = (response) => (response.headers.get("content-type") || "").includes("text/event-stream");
|
|
30
19
|
class RuntimeTransport {
|
|
20
|
+
closeResponse(response) {
|
|
21
|
+
this.responseCleanup.get(response)?.();
|
|
22
|
+
}
|
|
23
|
+
failure(error, path, init, response) {
|
|
24
|
+
if (error instanceof error_1.HyperbrowserError) {
|
|
25
|
+
return new error_1.HyperbrowserError(error.message.replace(/^\[Hyperbrowser\]: /, ""), {
|
|
26
|
+
statusCode: error.statusCode ?? response?.status,
|
|
27
|
+
code: error.code,
|
|
28
|
+
requestId: error.requestId ?? (response ? getRequestId(response) : undefined),
|
|
29
|
+
service: error.service ?? "runtime",
|
|
30
|
+
retryable: error.retryable,
|
|
31
|
+
details: error.details,
|
|
32
|
+
cause: error.cause ?? error,
|
|
33
|
+
method: error.method ?? init?.method ?? "GET",
|
|
34
|
+
path: error.path ?? (0, error_1.errorRequestPath)(path),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const canceled = init?.signal?.aborted;
|
|
38
|
+
return new error_1.HyperbrowserError((0, error_1.networkErrorMessage)(error, "Runtime request failed"), {
|
|
39
|
+
code: canceled ? "request_aborted" : undefined,
|
|
40
|
+
statusCode: response?.status,
|
|
41
|
+
requestId: response ? getRequestId(response) : undefined,
|
|
42
|
+
service: "runtime",
|
|
43
|
+
retryable: !canceled && (0, retry_1.isRetryableNetworkError)(error),
|
|
44
|
+
method: init?.method ?? "GET",
|
|
45
|
+
path: (0, error_1.errorRequestPath)(path),
|
|
46
|
+
cause: error,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
31
49
|
constructor(resolveConnection, timeout = 30000, runtimeProxyOverride) {
|
|
32
50
|
this.resolveConnection = resolveConnection;
|
|
33
51
|
this.timeout = timeout;
|
|
34
52
|
this.runtimeProxyOverride = runtimeProxyOverride;
|
|
53
|
+
this.responseCleanup = new WeakMap();
|
|
35
54
|
}
|
|
36
55
|
async requestJSON(path, init, params) {
|
|
37
56
|
const response = await this.fetchWithAuth(path, init, params);
|
|
38
|
-
if (response.headers.get("content-length") === "0") {
|
|
39
|
-
return {};
|
|
40
|
-
}
|
|
41
57
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
const text = await response.text();
|
|
59
|
+
if (!text)
|
|
60
|
+
return {};
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch (cause) {
|
|
65
|
+
throw new error_1.HyperbrowserError("Failed to parse JSON response", {
|
|
66
|
+
statusCode: response.status,
|
|
67
|
+
requestId: getRequestId(response),
|
|
68
|
+
service: "runtime",
|
|
69
|
+
cause,
|
|
70
|
+
method: init?.method ?? "GET",
|
|
71
|
+
path: (0, error_1.errorRequestPath)(path),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw this.failure(error, path, init, response);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
this.closeResponse(response);
|
|
51
80
|
}
|
|
52
81
|
}
|
|
53
82
|
async requestBuffer(path, init, params) {
|
|
54
83
|
const response = await this.fetchWithAuth(path, init, params);
|
|
55
|
-
|
|
84
|
+
try {
|
|
85
|
+
return await response.buffer();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw this.failure(error, path, init, response);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
this.closeResponse(response);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Pull-based binary transfer. Breaking iteration closes the HTTP connection. */
|
|
95
|
+
async *streamBytes(path, init, params) {
|
|
96
|
+
const response = await this.fetchWithAuth(path, init, params, true, true);
|
|
97
|
+
try {
|
|
98
|
+
if (!response.body)
|
|
99
|
+
return;
|
|
100
|
+
const iterator = response.body[Symbol.asyncIterator]();
|
|
101
|
+
for (;;) {
|
|
102
|
+
const next = await this.readChunk(iterator, this.timeout);
|
|
103
|
+
if (next.done)
|
|
104
|
+
return;
|
|
105
|
+
yield Buffer.from(next.value);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
throw this.failure(error, path, init, response);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
this.closeResponse(response);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
readChunk(iterator, timeout) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const timer = setTimeout(() => reject(new error_1.HyperbrowserError(`Runtime stream idle for ${timeout}ms without data or keepalives`, { code: "stream_idle_timeout", service: "runtime", retryable: true })), timeout);
|
|
118
|
+
iterator.next().then((value) => {
|
|
119
|
+
clearTimeout(timer);
|
|
120
|
+
resolve(value);
|
|
121
|
+
}, (error) => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
reject(error);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
56
126
|
}
|
|
57
127
|
async *streamSSE(path, params) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
128
|
+
const stream = await this.openSSE(path, params);
|
|
129
|
+
try {
|
|
130
|
+
yield* stream.events;
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
stream.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Open a server-sent event stream. POST streams carry a JSON body and
|
|
138
|
+
* require an event-stream response, since the request may have side effects
|
|
139
|
+
* that must not be retried blindly.
|
|
140
|
+
*/
|
|
141
|
+
async openSSE(path, params, init = {}) {
|
|
142
|
+
const method = init.method ?? "GET";
|
|
143
|
+
const headers = {
|
|
144
|
+
Accept: "text/event-stream",
|
|
145
|
+
...(init.body !== undefined ? { "content-type": "application/json" } : {}),
|
|
146
|
+
...(init.headers ?? {}),
|
|
147
|
+
};
|
|
148
|
+
const response = await this.fetchWithAuth(path, { method, headers, body: init.body, signal: init.signal }, params, true, true);
|
|
149
|
+
if (method === "POST" && !isEventStream(response)) {
|
|
150
|
+
this.closeResponse(response);
|
|
151
|
+
throw new error_1.HyperbrowserError("Receiver does not support streaming command start; update the receiver. " +
|
|
152
|
+
"The command may have started; do not retry it automatically.", { code: "streaming_not_supported", service: "runtime", retryable: false,
|
|
153
|
+
method, path: (0, error_1.errorRequestPath)(path), requestId: getRequestId(response), statusCode: response.status });
|
|
154
|
+
}
|
|
64
155
|
const body = response.body;
|
|
156
|
+
const idleTimeoutMs = init.idleTimeoutMs ?? exports.PROCESS_STREAM_IDLE_TIMEOUT_MS;
|
|
157
|
+
let closed = false;
|
|
158
|
+
const close = () => {
|
|
159
|
+
if (closed) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
closed = true;
|
|
163
|
+
this.closeResponse(response);
|
|
164
|
+
};
|
|
165
|
+
const parsed = this.parseSSE(body, idleTimeoutMs, () => closed, close, init.signal);
|
|
166
|
+
const failure = (error) => this.failure(error, path, { method, signal: init.signal }, response);
|
|
167
|
+
const events = (async function* () {
|
|
168
|
+
try {
|
|
169
|
+
yield* parsed;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
throw failure(error);
|
|
173
|
+
}
|
|
174
|
+
})();
|
|
175
|
+
return { events, close };
|
|
176
|
+
}
|
|
177
|
+
async *parseSSE(body, idleTimeoutMs, isClosed, close, signal) {
|
|
65
178
|
if (!body) {
|
|
66
179
|
return;
|
|
67
180
|
}
|
|
68
181
|
let buffer = "";
|
|
182
|
+
let skipLineFeed = false;
|
|
183
|
+
const decoder = new string_decoder_1.StringDecoder("utf8");
|
|
69
184
|
let eventName = "message";
|
|
70
185
|
let eventId;
|
|
71
186
|
let dataLines = [];
|
|
@@ -93,88 +208,165 @@ class RuntimeTransport {
|
|
|
93
208
|
dataLines = [];
|
|
94
209
|
return event;
|
|
95
210
|
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
211
|
+
const readLine = (line) => {
|
|
212
|
+
if (line === "")
|
|
213
|
+
return flushEvent();
|
|
214
|
+
if (line.startsWith(":"))
|
|
215
|
+
return null;
|
|
216
|
+
const separator = line.indexOf(":");
|
|
217
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
218
|
+
// Match the Python transport's field-value space normalization.
|
|
219
|
+
const value = separator === -1 ? "" : line.slice(separator + 1).replace(/^ +/, "");
|
|
220
|
+
switch (field) {
|
|
221
|
+
case "event":
|
|
222
|
+
eventName = value || "message";
|
|
101
223
|
break;
|
|
224
|
+
case "data":
|
|
225
|
+
dataLines.push(value);
|
|
226
|
+
break;
|
|
227
|
+
case "id":
|
|
228
|
+
eventId = value;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
};
|
|
233
|
+
const iterator = body[Symbol.asyncIterator]();
|
|
234
|
+
try {
|
|
235
|
+
for (;;) {
|
|
236
|
+
let next;
|
|
237
|
+
try {
|
|
238
|
+
next = await this.readChunk(iterator, idleTimeoutMs);
|
|
102
239
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (signal?.aborted)
|
|
242
|
+
throw new error_1.HyperbrowserError("Command collection canceled", {
|
|
243
|
+
code: "request_aborted",
|
|
244
|
+
service: "runtime",
|
|
245
|
+
cause: error,
|
|
246
|
+
});
|
|
247
|
+
if (isClosed()) {
|
|
248
|
+
return;
|
|
112
249
|
}
|
|
113
|
-
|
|
250
|
+
if (error instanceof error_1.HyperbrowserError) {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
throw new error_1.HyperbrowserError(error instanceof Error ? error.message : "Runtime stream failed", { service: "runtime", retryable: (0, retry_1.isRetryableNetworkError)(error), cause: error });
|
|
114
254
|
}
|
|
115
|
-
if (
|
|
116
|
-
|
|
255
|
+
if (next.done) {
|
|
256
|
+
break;
|
|
117
257
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
eventId = value;
|
|
130
|
-
break;
|
|
131
|
-
default:
|
|
258
|
+
buffer += decoder.write(Buffer.from(next.value));
|
|
259
|
+
while (true) {
|
|
260
|
+
if (skipLineFeed) {
|
|
261
|
+
if (!buffer)
|
|
262
|
+
break;
|
|
263
|
+
if (buffer.startsWith("\n"))
|
|
264
|
+
buffer = buffer.slice(1);
|
|
265
|
+
skipLineFeed = false;
|
|
266
|
+
}
|
|
267
|
+
const newlineIndex = buffer.search(/[\r\n]/);
|
|
268
|
+
if (newlineIndex === -1) {
|
|
132
269
|
break;
|
|
270
|
+
}
|
|
271
|
+
const line = buffer.slice(0, newlineIndex);
|
|
272
|
+
// Deliver bare CR immediately; absorb a following LF even across chunks.
|
|
273
|
+
skipLineFeed = buffer[newlineIndex] === "\r";
|
|
274
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
275
|
+
const event = readLine(line);
|
|
276
|
+
if (event)
|
|
277
|
+
yield event;
|
|
133
278
|
}
|
|
134
279
|
}
|
|
280
|
+
buffer += decoder.end();
|
|
281
|
+
if (buffer)
|
|
282
|
+
readLine(buffer);
|
|
283
|
+
const trailing = flushEvent();
|
|
284
|
+
if (trailing) {
|
|
285
|
+
yield trailing;
|
|
286
|
+
}
|
|
135
287
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
yield trailing;
|
|
288
|
+
finally {
|
|
289
|
+
close();
|
|
139
290
|
}
|
|
140
291
|
}
|
|
141
|
-
async fetchWithAuth(path, init, params, allowRefresh = true) {
|
|
292
|
+
async fetchWithAuth(path, init, params, allowRefresh = true, streaming = false) {
|
|
142
293
|
const connection = await this.resolveConnection(false);
|
|
143
|
-
const response = await this.fetchForConnection(connection, path, init, params);
|
|
294
|
+
const response = await this.fetchForConnection(connection, path, init, params, streaming);
|
|
144
295
|
if (response.status === 401 && allowRefresh) {
|
|
296
|
+
this.closeResponse(response);
|
|
297
|
+
if (init?.body instanceof stream_1.Readable) {
|
|
298
|
+
throw new error_1.HyperbrowserError("Runtime authentication expired during a streaming upload; obtain a new stream before retrying", {
|
|
299
|
+
statusCode: 401,
|
|
300
|
+
code: "stream_not_replayable",
|
|
301
|
+
service: "runtime",
|
|
302
|
+
retryable: false,
|
|
303
|
+
method: init?.method ?? "GET",
|
|
304
|
+
path: (0, error_1.errorRequestPath)(path),
|
|
305
|
+
requestId: getRequestId(response),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
145
308
|
const refreshed = await this.resolveConnection(true);
|
|
146
|
-
const retryResponse = await this.fetchForConnection(refreshed, path, init, params);
|
|
147
|
-
return this.assertResponse(retryResponse);
|
|
309
|
+
const retryResponse = await this.fetchForConnection(refreshed, path, init, params, streaming);
|
|
310
|
+
return this.assertResponse(retryResponse, path, init);
|
|
148
311
|
}
|
|
149
|
-
return this.assertResponse(response);
|
|
312
|
+
return this.assertResponse(response, path, init);
|
|
150
313
|
}
|
|
151
|
-
async fetchForConnection(connection, path, init, params) {
|
|
314
|
+
async fetchForConnection(connection, path, init, params, streaming = false) {
|
|
152
315
|
const target = (0, ws_1.resolveRuntimeTransportTarget)(connection.baseUrl, this.buildRequestPath(path, params), this.runtimeProxyOverride);
|
|
153
316
|
const headers = this.buildHeaders(connection, init?.headers, target.hostHeader);
|
|
154
317
|
const controller = new AbortController();
|
|
155
|
-
|
|
318
|
+
let timer = setTimeout(() => controller.abort(), this.timeout);
|
|
319
|
+
const upload = init?.body instanceof stream_1.Readable ? init.body : undefined;
|
|
320
|
+
const uploadProgress = () => {
|
|
321
|
+
clearTimeout(timer);
|
|
322
|
+
timer = setTimeout(() => controller.abort(), this.timeout);
|
|
323
|
+
};
|
|
324
|
+
upload?.on("data", uploadProgress);
|
|
325
|
+
const callerSignal = init?.signal;
|
|
326
|
+
const abortFromCaller = () => controller.abort();
|
|
327
|
+
if (callerSignal?.aborted)
|
|
328
|
+
controller.abort();
|
|
329
|
+
else
|
|
330
|
+
callerSignal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
331
|
+
const detach = () => {
|
|
332
|
+
clearTimeout(timer);
|
|
333
|
+
timer = undefined;
|
|
334
|
+
callerSignal?.removeEventListener("abort", abortFromCaller);
|
|
335
|
+
upload?.removeListener("data", uploadProgress);
|
|
336
|
+
};
|
|
156
337
|
try {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
338
|
+
const response = await (0, node_fetch_1.default)(target.url, { ...init, headers, signal: controller.signal });
|
|
339
|
+
clearTimeout(timer);
|
|
340
|
+
upload?.removeListener("data", uploadProgress);
|
|
341
|
+
// Header and body budgets are separate. Streams use a read-idle budget.
|
|
342
|
+
timer =
|
|
343
|
+
streaming && response.ok ? undefined : setTimeout(() => controller.abort(), this.timeout);
|
|
344
|
+
const body = response.body;
|
|
345
|
+
let disposed = false;
|
|
346
|
+
const close = () => {
|
|
347
|
+
if (disposed)
|
|
348
|
+
return;
|
|
349
|
+
disposed = true;
|
|
350
|
+
detach();
|
|
351
|
+
if (!body?.readableEnded)
|
|
352
|
+
controller.abort();
|
|
353
|
+
body?.destroy();
|
|
354
|
+
};
|
|
355
|
+
this.responseCleanup.set(response, close);
|
|
356
|
+
if (body) {
|
|
357
|
+
body.once("end", detach);
|
|
358
|
+
body.once("close", close);
|
|
166
359
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
cause: error,
|
|
171
|
-
});
|
|
360
|
+
else
|
|
361
|
+
close();
|
|
362
|
+
return response;
|
|
172
363
|
}
|
|
173
|
-
|
|
174
|
-
|
|
364
|
+
catch (error) {
|
|
365
|
+
detach();
|
|
366
|
+
throw this.failure(error, path, init);
|
|
175
367
|
}
|
|
176
368
|
}
|
|
177
|
-
async assertResponse(response) {
|
|
369
|
+
async assertResponse(response, path, init) {
|
|
178
370
|
if (response.ok) {
|
|
179
371
|
return response;
|
|
180
372
|
}
|
|
@@ -196,16 +388,23 @@ class RuntimeTransport {
|
|
|
196
388
|
}
|
|
197
389
|
}
|
|
198
390
|
}
|
|
199
|
-
catch {
|
|
391
|
+
catch (error) {
|
|
392
|
+
if (init?.signal?.aborted)
|
|
393
|
+
throw this.failure(error, path, init, response);
|
|
200
394
|
// Keep the fallback message.
|
|
201
395
|
}
|
|
202
|
-
|
|
396
|
+
finally {
|
|
397
|
+
this.closeResponse(response);
|
|
398
|
+
}
|
|
399
|
+
throw new error_1.HyperbrowserError(message, {
|
|
203
400
|
statusCode: response.status,
|
|
204
401
|
code,
|
|
205
402
|
requestId: getRequestId(response),
|
|
206
|
-
retryable: RETRYABLE_STATUS_CODES.has(response.status),
|
|
403
|
+
retryable: retry_1.RETRYABLE_STATUS_CODES.has(response.status),
|
|
207
404
|
service: "runtime",
|
|
208
405
|
details,
|
|
406
|
+
method: init?.method ?? "GET",
|
|
407
|
+
path: (0, error_1.errorRequestPath)(path),
|
|
209
408
|
});
|
|
210
409
|
}
|
|
211
410
|
buildHeaders(connection, rawHeaders, hostHeader) {
|
package/dist/sandbox/files.d.ts
CHANGED
|
@@ -1,40 +1,26 @@
|
|
|
1
1
|
import { Blob, Buffer } from "buffer";
|
|
2
2
|
import { ReadableStream } from "node:stream/web";
|
|
3
3
|
import { RuntimeTransport } from "./base";
|
|
4
|
-
import { SandboxFileChmodParams, SandboxFileChownParams, SandboxFileCopyParams, SandboxFileInfo, SandboxFileListOptions, SandboxFileMakeDirOptions, SandboxFileReadOptions, SandboxFileSystemEvent, SandboxFileTransferResult, SandboxFileWriteData, SandboxFileWriteEntry, SandboxFileWriteInfo, SandboxFileBytesWriteOptions, SandboxFileTextWriteOptions, SandboxPresignFileParams, SandboxPresignedUrl, SandboxWatchDirOptions } from "../types/sandbox";
|
|
5
|
-
interface RawFileWatchEvent {
|
|
6
|
-
seq: number;
|
|
7
|
-
path: string;
|
|
8
|
-
op: string;
|
|
9
|
-
timestamp: number;
|
|
10
|
-
}
|
|
11
|
-
interface RawFileWatchStatus {
|
|
12
|
-
id: string;
|
|
13
|
-
path: string;
|
|
14
|
-
recursive: boolean;
|
|
15
|
-
active: boolean;
|
|
16
|
-
error?: string;
|
|
17
|
-
createdAt: number;
|
|
18
|
-
stoppedAt?: number;
|
|
19
|
-
oldestSeq?: number;
|
|
20
|
-
lastSeq?: number;
|
|
21
|
-
eventCount?: number;
|
|
22
|
-
}
|
|
4
|
+
import { SandboxFileUploadStream, SandboxFileUploadStreamOptions, SandboxFileDownloadStreamOptions, SandboxFileMoveParams, SandboxFileRenameOptions, SandboxFileWatchParams, SandboxFileWatchStatus, SandboxFileWatchEventsParams, SandboxFileWatchStreamEvent, SandboxFileChmodParams, SandboxFileChownParams, SandboxFileCopyParams, SandboxFileInfo, SandboxFileListOptions, SandboxFileMakeDirOptions, SandboxFileReadOptions, SandboxFileSystemEvent, SandboxFileTransferResult, SandboxFileWriteData, SandboxFileWriteEntry, SandboxFileWriteInfo, SandboxFileBytesWriteOptions, SandboxFileTextWriteOptions, SandboxPresignFileParams, SandboxPresignedUrl, SandboxWatchDirOptions } from "../types/sandbox";
|
|
23
5
|
interface RuntimeConnectionInfo {
|
|
24
6
|
sandboxId: string;
|
|
25
7
|
baseUrl: string;
|
|
26
8
|
token: string;
|
|
27
9
|
}
|
|
28
|
-
declare class
|
|
10
|
+
export declare class SandboxFileWatchHandle {
|
|
29
11
|
private readonly transport;
|
|
30
12
|
private readonly getConnectionInfo;
|
|
31
|
-
private
|
|
13
|
+
private status;
|
|
32
14
|
private readonly runtimeProxyOverride?;
|
|
33
|
-
|
|
15
|
+
private readonly connections;
|
|
16
|
+
constructor(transport: RuntimeTransport, getConnectionInfo: () => Promise<RuntimeConnectionInfo>, status: SandboxFileWatchStatus, runtimeProxyOverride?: string | undefined);
|
|
34
17
|
get id(): string;
|
|
35
18
|
get path(): string;
|
|
19
|
+
get current(): SandboxFileWatchStatus;
|
|
20
|
+
toJSON(): SandboxFileWatchStatus;
|
|
21
|
+
refresh(includeEvents?: boolean): Promise<this>;
|
|
36
22
|
stop(): Promise<void>;
|
|
37
|
-
events(
|
|
23
|
+
events(options?: SandboxFileWatchEventsParams): AsyncGenerator<SandboxFileWatchStreamEvent>;
|
|
38
24
|
}
|
|
39
25
|
export declare class SandboxWatchDirHandle {
|
|
40
26
|
private readonly watch;
|
|
@@ -43,7 +29,8 @@ export declare class SandboxWatchDirHandle {
|
|
|
43
29
|
private timeout?;
|
|
44
30
|
private stopRequested;
|
|
45
31
|
private exitNotified;
|
|
46
|
-
|
|
32
|
+
private invokingCallback;
|
|
33
|
+
constructor(watch: SandboxFileWatchHandle, onEvent: (event: SandboxFileSystemEvent) => void | Promise<void>, onExit?: ((error?: Error) => void | Promise<void>) | undefined, timeoutMs?: number);
|
|
47
34
|
stop(): Promise<void>;
|
|
48
35
|
private run;
|
|
49
36
|
}
|
|
@@ -78,7 +65,7 @@ export declare class SandboxFilesApi {
|
|
|
78
65
|
upload(path: string, data: Buffer | Uint8Array | string): Promise<SandboxFileTransferResult>;
|
|
79
66
|
download(path: string): Promise<Buffer>;
|
|
80
67
|
makeDir(path: string, options?: SandboxFileMakeDirOptions): Promise<boolean>;
|
|
81
|
-
rename(oldPath: string, newPath: string): Promise<SandboxFileInfo>;
|
|
68
|
+
rename(oldPath: string, newPath: string, options?: SandboxFileRenameOptions): Promise<SandboxFileInfo>;
|
|
82
69
|
remove(path: string, options?: {
|
|
83
70
|
recursive?: boolean;
|
|
84
71
|
}): Promise<void>;
|
|
@@ -86,6 +73,18 @@ export declare class SandboxFilesApi {
|
|
|
86
73
|
chmod(params: SandboxFileChmodParams): Promise<void>;
|
|
87
74
|
chown(params: SandboxFileChownParams): Promise<void>;
|
|
88
75
|
watchDir(path: string, onEvent: (event: SandboxFileSystemEvent) => void | Promise<void>, options?: SandboxWatchDirOptions): Promise<SandboxWatchDirHandle>;
|
|
76
|
+
watch(path: string, options?: SandboxFileWatchParams): Promise<SandboxFileWatchHandle>;
|
|
77
|
+
getWatch(id: string, includeEvents?: boolean): Promise<SandboxFileWatchHandle>;
|
|
78
|
+
stat(path: string): Promise<SandboxFileInfo>;
|
|
79
|
+
mkdir(path: string, options?: SandboxFileMakeDirOptions): Promise<boolean>;
|
|
80
|
+
move(params: SandboxFileMoveParams): Promise<SandboxFileInfo>;
|
|
81
|
+
delete(path: string, options?: {
|
|
82
|
+
recursive?: boolean;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
/** Upload without buffering; the source is closed on completion, rejection or cancellation. */
|
|
85
|
+
uploadStream(path: string, source: SandboxFileUploadStream, options?: SandboxFileUploadStreamOptions): Promise<SandboxFileTransferResult>;
|
|
86
|
+
/** Pull-based download; consume with for-await or Readable.from(). */
|
|
87
|
+
downloadStream(path: string, options?: SandboxFileDownloadStreamOptions): AsyncGenerator<Buffer>;
|
|
89
88
|
uploadUrl(path: string, options?: Omit<SandboxPresignFileParams, "path">): Promise<SandboxPresignedUrl>;
|
|
90
89
|
downloadUrl(path: string, options?: Omit<SandboxPresignFileParams, "path">): Promise<SandboxPresignedUrl>;
|
|
91
90
|
private readWire;
|