@blaxel/core 0.2.47-preview.106 → 0.2.48-preview.107
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/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/common/settings.js +2 -2
- package/dist/cjs/sandbox/client/sdk.gen.js +117 -1
- package/dist/cjs/sandbox/filesystem/filesystem.js +139 -6
- package/dist/cjs/types/sandbox/client/sdk.gen.d.ts +32 -2
- package/dist/cjs/types/sandbox/client/types.gen.d.ts +238 -0
- package/dist/cjs/types/sandbox/filesystem/filesystem.d.ts +6 -1
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/common/settings.js +2 -2
- package/dist/cjs-browser/sandbox/client/sdk.gen.js +117 -1
- package/dist/cjs-browser/sandbox/filesystem/filesystem.js +139 -6
- package/dist/cjs-browser/types/sandbox/client/sdk.gen.d.ts +32 -2
- package/dist/cjs-browser/types/sandbox/client/types.gen.d.ts +238 -0
- package/dist/cjs-browser/types/sandbox/filesystem/filesystem.d.ts +6 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/common/settings.js +2 -2
- package/dist/esm/sandbox/client/sdk.gen.js +110 -0
- package/dist/esm/sandbox/filesystem/filesystem.js +140 -7
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/common/settings.js +2 -2
- package/dist/esm-browser/sandbox/client/sdk.gen.js +110 -0
- package/dist/esm-browser/sandbox/filesystem/filesystem.js +140 -7
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// This file is auto-generated by @hey-api/openapi-ts
|
|
2
|
+
import { formDataBodySerializer } from '@hey-api/client-fetch';
|
|
2
3
|
import { client as _heyApiClient } from "./client.gen.js";
|
|
3
4
|
/**
|
|
4
5
|
* Welcome message
|
|
@@ -163,6 +164,115 @@ export const getCodegenRerankingByPath = (options) => {
|
|
|
163
164
|
...options
|
|
164
165
|
});
|
|
165
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* List multipart uploads
|
|
169
|
+
* List all active multipart uploads
|
|
170
|
+
*/
|
|
171
|
+
export const getFilesystemMultipart = (options) => {
|
|
172
|
+
return (options?.client ?? _heyApiClient).get({
|
|
173
|
+
security: [
|
|
174
|
+
{
|
|
175
|
+
scheme: 'bearer',
|
|
176
|
+
type: 'http'
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
url: '/filesystem-multipart',
|
|
180
|
+
...options
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Abort multipart upload
|
|
185
|
+
* Abort a multipart upload and clean up all parts
|
|
186
|
+
*/
|
|
187
|
+
export const deleteFilesystemMultipartByUploadIdAbort = (options) => {
|
|
188
|
+
return (options.client ?? _heyApiClient).delete({
|
|
189
|
+
security: [
|
|
190
|
+
{
|
|
191
|
+
scheme: 'bearer',
|
|
192
|
+
type: 'http'
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
url: '/filesystem-multipart/{uploadId}/abort',
|
|
196
|
+
...options
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Complete multipart upload
|
|
201
|
+
* Complete a multipart upload by assembling all parts
|
|
202
|
+
*/
|
|
203
|
+
export const postFilesystemMultipartByUploadIdComplete = (options) => {
|
|
204
|
+
return (options.client ?? _heyApiClient).post({
|
|
205
|
+
security: [
|
|
206
|
+
{
|
|
207
|
+
scheme: 'bearer',
|
|
208
|
+
type: 'http'
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
url: '/filesystem-multipart/{uploadId}/complete',
|
|
212
|
+
...options,
|
|
213
|
+
headers: {
|
|
214
|
+
'Content-Type': 'application/json',
|
|
215
|
+
...options?.headers
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Upload part
|
|
221
|
+
* Upload a single part of a multipart upload
|
|
222
|
+
*/
|
|
223
|
+
export const putFilesystemMultipartByUploadIdPart = (options) => {
|
|
224
|
+
return (options.client ?? _heyApiClient).put({
|
|
225
|
+
...formDataBodySerializer,
|
|
226
|
+
security: [
|
|
227
|
+
{
|
|
228
|
+
scheme: 'bearer',
|
|
229
|
+
type: 'http'
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
url: '/filesystem-multipart/{uploadId}/part',
|
|
233
|
+
...options,
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': null,
|
|
236
|
+
...options?.headers
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* List parts
|
|
242
|
+
* List all uploaded parts for a multipart upload
|
|
243
|
+
*/
|
|
244
|
+
export const getFilesystemMultipartByUploadIdParts = (options) => {
|
|
245
|
+
return (options.client ?? _heyApiClient).get({
|
|
246
|
+
security: [
|
|
247
|
+
{
|
|
248
|
+
scheme: 'bearer',
|
|
249
|
+
type: 'http'
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
url: '/filesystem-multipart/{uploadId}/parts',
|
|
253
|
+
...options
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Initiate multipart upload
|
|
258
|
+
* Initiate a multipart upload session for a file
|
|
259
|
+
*/
|
|
260
|
+
export const postFilesystemMultipartInitiateByPath = (options) => {
|
|
261
|
+
return (options.client ?? _heyApiClient).post({
|
|
262
|
+
security: [
|
|
263
|
+
{
|
|
264
|
+
scheme: 'bearer',
|
|
265
|
+
type: 'http'
|
|
266
|
+
}
|
|
267
|
+
],
|
|
268
|
+
url: '/filesystem-multipart/initiate/{path}',
|
|
269
|
+
...options,
|
|
270
|
+
headers: {
|
|
271
|
+
'Content-Type': 'application/json',
|
|
272
|
+
...options?.headers
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
};
|
|
166
276
|
/**
|
|
167
277
|
* Delete file or directory
|
|
168
278
|
* Delete a file or directory
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { settings } from "../../common/settings.js";
|
|
2
|
+
import { fs } from "../../common/node.js";
|
|
2
3
|
import { SandboxAction } from "../action.js";
|
|
3
|
-
import { deleteFilesystemByPath, getFilesystemByPath, getWatchFilesystemByPath, putFilesystemByPath } from "../client/index.js";
|
|
4
|
-
|
|
4
|
+
import { deleteFilesystemByPath, getFilesystemByPath, getWatchFilesystemByPath, putFilesystemByPath, postFilesystemMultipartInitiateByPath, putFilesystemMultipartByUploadIdPart, postFilesystemMultipartByUploadIdComplete, deleteFilesystemMultipartByUploadIdAbort } from "../client/index.js";
|
|
5
|
+
// Multipart upload constants
|
|
6
|
+
const MULTIPART_THRESHOLD = 5 * 1024 * 1024; // 5MB
|
|
7
|
+
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB per part
|
|
8
|
+
const MAX_PARALLEL_UPLOADS = 3; // Number of parallel part uploads
|
|
5
9
|
export class SandboxFileSystem extends SandboxAction {
|
|
6
10
|
process;
|
|
7
11
|
constructor(sandbox, process) {
|
|
@@ -22,6 +26,14 @@ export class SandboxFileSystem extends SandboxAction {
|
|
|
22
26
|
}
|
|
23
27
|
async write(path, content) {
|
|
24
28
|
path = this.formatPath(path);
|
|
29
|
+
// Calculate content size in bytes
|
|
30
|
+
const contentSize = new Blob([content]).size;
|
|
31
|
+
// Use multipart upload for large files
|
|
32
|
+
if (contentSize > MULTIPART_THRESHOLD) {
|
|
33
|
+
const blob = new Blob([content]);
|
|
34
|
+
return await this.uploadWithMultipart(path, blob, "0644");
|
|
35
|
+
}
|
|
36
|
+
// Use regular upload for small files
|
|
25
37
|
const { response, data, error } = await putFilesystemByPath({
|
|
26
38
|
path: { path },
|
|
27
39
|
body: { content },
|
|
@@ -33,12 +45,20 @@ export class SandboxFileSystem extends SandboxAction {
|
|
|
33
45
|
}
|
|
34
46
|
async writeBinary(path, content) {
|
|
35
47
|
path = this.formatPath(path);
|
|
36
|
-
const formData = new FormData();
|
|
37
48
|
// Convert content to Blob regardless of input type
|
|
38
49
|
let fileBlob;
|
|
50
|
+
// Check if it's already a Blob or File (including duck-typing for cross-realm Blobs)
|
|
39
51
|
if (content instanceof Blob || content instanceof File) {
|
|
40
52
|
fileBlob = content;
|
|
41
53
|
}
|
|
54
|
+
else if (typeof content === 'object' && content !== null &&
|
|
55
|
+
'size' in content && 'type' in content &&
|
|
56
|
+
'arrayBuffer' in content &&
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
58
|
+
typeof content.arrayBuffer === 'function') {
|
|
59
|
+
// Handle Blob-like objects (cross-realm Blobs)
|
|
60
|
+
fileBlob = content;
|
|
61
|
+
}
|
|
42
62
|
else if (Buffer.isBuffer(content)) {
|
|
43
63
|
// Convert Buffer to Blob
|
|
44
64
|
fileBlob = new Blob([content]);
|
|
@@ -47,14 +67,29 @@ export class SandboxFileSystem extends SandboxAction {
|
|
|
47
67
|
// Convert Uint8Array to Blob
|
|
48
68
|
fileBlob = new Blob([content]);
|
|
49
69
|
}
|
|
70
|
+
else if (ArrayBuffer.isView(content)) {
|
|
71
|
+
// Handle other TypedArray views
|
|
72
|
+
fileBlob = new Blob([content]);
|
|
73
|
+
}
|
|
50
74
|
else if (typeof content === 'string') {
|
|
51
|
-
|
|
75
|
+
// Read file from local filesystem (Node.js only)
|
|
76
|
+
if (!fs) {
|
|
77
|
+
throw new Error("File path upload is only supported in Node.js environments");
|
|
78
|
+
}
|
|
79
|
+
const buffer = fs.readFileSync(content);
|
|
52
80
|
fileBlob = new Blob([buffer]);
|
|
53
81
|
}
|
|
54
82
|
else {
|
|
55
|
-
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
|
|
84
|
+
const typeName = content?.constructor?.name ?? typeof content;
|
|
85
|
+
throw new Error(`Unsupported content type: ${typeName}`);
|
|
86
|
+
}
|
|
87
|
+
// Use multipart upload for large files
|
|
88
|
+
if (fileBlob.size > MULTIPART_THRESHOLD) {
|
|
89
|
+
return await this.uploadWithMultipart(path, fileBlob, "0644");
|
|
56
90
|
}
|
|
57
|
-
//
|
|
91
|
+
// Use regular upload for small files
|
|
92
|
+
const formData = new FormData();
|
|
58
93
|
formData.append("file", fileBlob, "test-binary.bin");
|
|
59
94
|
formData.append("permissions", "0644");
|
|
60
95
|
formData.append("path", path);
|
|
@@ -129,10 +164,13 @@ export class SandboxFileSystem extends SandboxAction {
|
|
|
129
164
|
return data;
|
|
130
165
|
}
|
|
131
166
|
async download(src, destinationPath, { mode = 0o644 } = {}) {
|
|
167
|
+
if (!fs) {
|
|
168
|
+
throw new Error("File download to local filesystem is only supported in Node.js environments");
|
|
169
|
+
}
|
|
132
170
|
const blob = await this.readBinary(src);
|
|
133
171
|
const arrayBuffer = await blob.arrayBuffer();
|
|
134
172
|
const buffer = Buffer.from(arrayBuffer);
|
|
135
|
-
|
|
173
|
+
fs.writeFileSync(destinationPath, buffer, { mode: mode ?? 0o644 });
|
|
136
174
|
}
|
|
137
175
|
async rm(path, recursive = false) {
|
|
138
176
|
path = this.formatPath(path);
|
|
@@ -260,4 +298,99 @@ export class SandboxFileSystem extends SandboxAction {
|
|
|
260
298
|
formatPath(path) {
|
|
261
299
|
return path;
|
|
262
300
|
}
|
|
301
|
+
// Multipart upload helper methods
|
|
302
|
+
async initiateMultipartUpload(path, permissions = "0644") {
|
|
303
|
+
path = this.formatPath(path);
|
|
304
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
305
|
+
const { data } = await postFilesystemMultipartInitiateByPath({
|
|
306
|
+
path: { path },
|
|
307
|
+
body: { permissions },
|
|
308
|
+
baseUrl: this.url,
|
|
309
|
+
client: this.client,
|
|
310
|
+
throwOnError: true,
|
|
311
|
+
});
|
|
312
|
+
return data;
|
|
313
|
+
}
|
|
314
|
+
async uploadPart(uploadId, partNumber, fileBlob) {
|
|
315
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
316
|
+
const { data } = await putFilesystemMultipartByUploadIdPart({
|
|
317
|
+
path: { uploadId },
|
|
318
|
+
query: { partNumber },
|
|
319
|
+
body: { file: fileBlob },
|
|
320
|
+
baseUrl: this.url,
|
|
321
|
+
client: this.client,
|
|
322
|
+
throwOnError: true,
|
|
323
|
+
});
|
|
324
|
+
return data;
|
|
325
|
+
}
|
|
326
|
+
async completeMultipartUpload(uploadId, parts) {
|
|
327
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
328
|
+
const { data } = await postFilesystemMultipartByUploadIdComplete({
|
|
329
|
+
path: { uploadId },
|
|
330
|
+
body: { parts },
|
|
331
|
+
baseUrl: this.url,
|
|
332
|
+
client: this.client,
|
|
333
|
+
throwOnError: true,
|
|
334
|
+
});
|
|
335
|
+
return data;
|
|
336
|
+
}
|
|
337
|
+
async abortMultipartUpload(uploadId) {
|
|
338
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
339
|
+
await deleteFilesystemMultipartByUploadIdAbort({
|
|
340
|
+
path: { uploadId },
|
|
341
|
+
baseUrl: this.url,
|
|
342
|
+
client: this.client,
|
|
343
|
+
throwOnError: true,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
async uploadWithMultipart(path, blob, permissions = "0644") {
|
|
347
|
+
// Initiate multipart upload
|
|
348
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
349
|
+
const initResponse = await this.initiateMultipartUpload(path, permissions);
|
|
350
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
351
|
+
const uploadId = initResponse.uploadId;
|
|
352
|
+
if (!uploadId) {
|
|
353
|
+
throw new Error("Failed to get upload ID from initiate response");
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
const size = blob.size;
|
|
357
|
+
const numParts = Math.ceil(size / CHUNK_SIZE);
|
|
358
|
+
const parts = [];
|
|
359
|
+
// Upload parts in batches for parallel processing
|
|
360
|
+
for (let i = 0; i < numParts; i += MAX_PARALLEL_UPLOADS) {
|
|
361
|
+
const batch = [];
|
|
362
|
+
for (let j = 0; j < MAX_PARALLEL_UPLOADS && i + j < numParts; j++) {
|
|
363
|
+
const partNumber = i + j + 1;
|
|
364
|
+
const start = (partNumber - 1) * CHUNK_SIZE;
|
|
365
|
+
const end = Math.min(start + CHUNK_SIZE, size);
|
|
366
|
+
const chunk = blob.slice(start, end);
|
|
367
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
368
|
+
batch.push(this.uploadPart(uploadId, partNumber, chunk));
|
|
369
|
+
}
|
|
370
|
+
// Wait for batch to complete
|
|
371
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
372
|
+
const batchResults = await Promise.all(batch);
|
|
373
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument
|
|
374
|
+
parts.push(...batchResults.map((r) => ({ partNumber: r.partNumber, etag: r.etag })));
|
|
375
|
+
}
|
|
376
|
+
// Sort parts by partNumber to ensure correct order
|
|
377
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
378
|
+
parts.sort((a, b) => (a.partNumber ?? 0) - (b.partNumber ?? 0));
|
|
379
|
+
// Complete the upload
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
381
|
+
return await this.completeMultipartUpload(uploadId, parts);
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
// Abort the upload on failure
|
|
385
|
+
try {
|
|
386
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
387
|
+
await this.abortMultipartUpload(uploadId);
|
|
388
|
+
}
|
|
389
|
+
catch (abortError) {
|
|
390
|
+
// Log but don't throw - we want to throw the original error
|
|
391
|
+
console.error('Failed to abort multipart upload:', abortError);
|
|
392
|
+
}
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
263
396
|
}
|