@daytonaio/sdk 0.110.2-alpha.9 → 0.111.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daytonaio/sdk",
3
- "version": "0.110.2-alpha.9",
3
+ "version": "0.111.0",
4
4
  "description": "TypeScript SDK for Daytona",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
@@ -26,7 +26,6 @@
26
26
  "@aws-sdk/lib-storage": "^3.798.0",
27
27
  "@iarna/toml": "^2.2.5",
28
28
  "axios": "^1.11.0",
29
- "buffer": "^6.0.3",
30
29
  "busboy": "^1.0.0",
31
30
  "dotenv": "^17.0.1",
32
31
  "expand-tilde": "^2.0.2",
@@ -36,7 +35,7 @@
36
35
  "pathe": "^2.0.3",
37
36
  "shell-quote": "^1.8.2",
38
37
  "tar": "^6.2.0",
39
- "@daytonaio/api-client": "0.110.2-alpha.9"
38
+ "@daytonaio/api-client": "0.111.0"
40
39
  },
41
40
  "packageManager": "yarn@4.6.0",
42
41
  "type": "commonjs"
@@ -1,5 +1,4 @@
1
1
  import { Configuration, FileInfo, Match, ReplaceResult, SearchFilesResponse, ToolboxApi } from '@daytonaio/api-client';
2
- import { Buffer } from 'buffer';
3
2
  /**
4
3
  * Parameters for setting file permissions in the Sandbox.
5
4
  *
@@ -44,7 +43,7 @@ export interface FileUpload {
44
43
  * @property {string} [destination] - Destination path in the local filesystem where the file content will be
45
44
  * streamed to. If not provided, the file will be downloaded in the bytes buffer (might cause memory issues if the file is large).
46
45
  */
47
- interface FileDownloadRequest {
46
+ export interface FileDownloadRequest {
48
47
  source: string;
49
48
  destination?: string;
50
49
  }
@@ -57,11 +56,25 @@ interface FileDownloadRequest {
57
56
  * or bytes content (if no destination in the request), undefined if failed or no data received.
58
57
  * @property {string | undefined} [error] - Error message if the download failed, undefined if successful.
59
58
  */
60
- interface FileDownloadResponse {
59
+ export interface FileDownloadResponse {
61
60
  source: string;
62
61
  result?: Buffer | string;
63
62
  error?: string;
64
63
  }
64
+ /**
65
+ * Represents metadata for a file download operation.
66
+ *
67
+ * @interface
68
+ * @property {string | undefined} [destination] - Destination path in the local filesystem where the file content will be streamed to.
69
+ * @property {string | undefined} [error] - Error message if the download failed, undefined if successful.
70
+ * @property {Buffer | string | Uint8Array | undefined} [result] - The download result - file path (if destination provided in the request)
71
+ * or bytes content (if no destination in the request), undefined if failed or no data received.
72
+ */
73
+ export interface DownloadMetadata {
74
+ destination?: string;
75
+ error?: string;
76
+ result?: Buffer | string | Uint8Array;
77
+ }
65
78
  /**
66
79
  * Provides file system operations within a Sandbox.
67
80
  *
@@ -311,4 +324,3 @@ export declare class FileSystem {
311
324
  uploadFiles(files: FileUpload[], timeout?: number): Promise<void>;
312
325
  private makeFilePayload;
313
326
  }
314
- export {};
package/src/FileSystem.js CHANGED
@@ -7,12 +7,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.FileSystem = void 0;
8
8
  const tslib_1 = require("tslib");
9
9
  const pathe = tslib_1.__importStar(require("pathe"));
10
- const form_data_1 = tslib_1.__importDefault(require("form-data"));
10
+ const DaytonaError_1 = require("./errors/DaytonaError");
11
+ const FileTransfer_1 = require("./utils/FileTransfer");
11
12
  const Import_1 = require("./utils/Import");
12
13
  const Runtime_1 = require("./utils/Runtime");
13
- const busboy_1 = tslib_1.__importDefault(require("busboy"));
14
- const DaytonaError_1 = require("./errors/DaytonaError");
15
- const buffer_1 = require("buffer");
16
14
  /**
17
15
  * Provides file system operations within a Sandbox.
18
16
  *
@@ -102,247 +100,37 @@ class FileSystem {
102
100
  async downloadFiles(files, timeoutSec = 30 * 60) {
103
101
  if (files.length === 0)
104
102
  return [];
105
- const isNodeLike = typeof process !== 'undefined' &&
106
- !!process.versions?.node &&
107
- typeof globalThis.Buffer !== 'undefined';
108
- const srcFileMetaMap = new Map();
109
- const fileTasks = [];
110
- // Validate & prepare destinations
103
+ const isNonStreamingRuntime = Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER || Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS;
104
+ // Prepare destinations and metadata
105
+ const metadataMap = new Map();
111
106
  for (const f of files) {
112
- srcFileMetaMap.set(f.source, { dst: f.destination });
107
+ metadataMap.set(f.source, { destination: f.destination });
113
108
  if (f.destination) {
114
- if (isNodeLike) {
115
- const fs = await (0, Import_1.dynamicImport)('fs', 'Downloading files to local files is not supported: ');
116
- await fs.promises.mkdir(pathe.dirname(f.destination), { recursive: true });
117
- }
118
- else {
119
- throw new DaytonaError_1.DaytonaError('Downloading files to local files is not supported in this runtime.');
120
- }
109
+ const fs = await (0, Import_1.dynamicImport)('fs', 'Downloading files to local files is not supported: ');
110
+ await fs.promises.mkdir(pathe.dirname(f.destination), { recursive: true });
121
111
  }
122
112
  }
123
- const response = await this.toolboxApi.downloadFiles(this.sandboxId, { paths: Array.from(srcFileMetaMap.keys()) }, undefined, {
124
- responseType: 'stream',
113
+ const response = await this.toolboxApi.downloadFiles(this.sandboxId, { paths: files.map((f) => f.source) }, undefined, {
114
+ responseType: isNonStreamingRuntime ? 'arraybuffer' : 'stream',
125
115
  timeout: timeoutSec * 1000,
126
116
  });
127
- const contentType = getHeader(response.headers, 'content-type') || '';
128
- let responseData = response.data;
129
- // Normalize responseData across adapters
130
- if (responseData && typeof responseData === 'object') {
131
- if (responseData.body && typeof responseData.body.getReader === 'function') {
132
- responseData = responseData.body; // WHATWG ReadableStream
133
- }
134
- else if (responseData.stream) {
135
- responseData = responseData.stream; // Some adapters use .stream
136
- }
137
- }
138
- // -------------------- NODE PATH (busboy) --------------------
139
- if (isNodeLike) {
140
- await new Promise((resolve, reject) => {
141
- const bb = (0, busboy_1.default)({
142
- headers: response.headers,
143
- preservePath: true,
144
- });
145
- // busboy emits: ('file', fieldName, fileStream, fileInfo)
146
- bb.on('file', (fieldName, fileStream, fileInfo) => {
147
- const source = fileInfo?.filename;
148
- if (!source) {
149
- safeAbort(responseData);
150
- reject(new DaytonaError_1.DaytonaError(`Received unexpected file "${fileInfo?.filename}".`));
151
- return;
152
- }
153
- const meta = srcFileMetaMap.get(source);
154
- if (!meta) {
155
- safeAbort(responseData);
156
- reject(new DaytonaError_1.DaytonaError(`Target metadata missing for valid source: ${source}`));
157
- return;
158
- }
159
- if (fieldName === 'error') {
160
- let buf = buffer_1.Buffer.alloc(0);
161
- fileStream.on('data', (chunk) => {
162
- buf = buffer_1.Buffer.concat([buf, chunk]);
163
- });
164
- fileStream.on('end', () => {
165
- meta.error = buf.toString('utf-8').trim();
166
- });
167
- fileStream.on('error', (err) => {
168
- meta.error = `Stream error: ${err.message}`;
169
- });
170
- }
171
- else if (fieldName === 'file') {
172
- if (meta.dst) {
173
- fileTasks.push(new Promise((resolveInner) => {
174
- (0, Import_1.dynamicImport)('fs', 'Downloading files to local files is not supported: ').then((fs) => {
175
- const writeStream = fs.createWriteStream(meta.dst, { autoClose: true });
176
- fileStream.pipe(writeStream);
177
- writeStream.on('finish', () => {
178
- meta.result = meta.dst;
179
- resolveInner();
180
- });
181
- writeStream.on('error', (err) => {
182
- meta.error = `Write stream failed: ${err.message}`;
183
- resolveInner();
184
- });
185
- fileStream.on('error', (err) => {
186
- meta.error = `Read stream failed: ${err.message}`;
187
- });
188
- });
189
- }));
190
- }
191
- else {
192
- const chunks = [];
193
- fileStream.on('data', (chunk) => {
194
- // Ensure chunk is Buffer (it is in Node)
195
- chunks.push(buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(chunk));
196
- });
197
- fileStream.on('end', () => {
198
- meta.result = buffer_1.Buffer.concat(chunks);
199
- });
200
- fileStream.on('error', (err) => {
201
- meta.error = `Read failed: ${err.message}`;
202
- });
203
- }
204
- }
205
- else {
206
- // Unknown fieldname; drain
207
- fileStream.resume();
208
- }
209
- });
210
- bb.on('error', (err) => {
211
- safeAbort(responseData);
212
- reject(err);
213
- });
214
- bb.on('finish', resolve);
215
- // Feed body into busboy, coercing to Buffer as needed
216
- if (typeof responseData?.pipe === 'function') {
217
- responseData.pipe(bb);
218
- }
219
- else if (typeof responseData === 'string' ||
220
- responseData instanceof ArrayBuffer ||
221
- ArrayBuffer.isView(responseData)) {
222
- try {
223
- const u8 = toUint8Array(responseData);
224
- bb.write(buffer_1.Buffer.from(u8));
225
- bb.end();
226
- }
227
- catch (err) {
228
- bb.destroy(err);
229
- }
230
- }
231
- else if (typeof responseData?.getReader === 'function') {
232
- ;
233
- (async () => {
234
- try {
235
- const reader = responseData.getReader();
236
- while (true) {
237
- const { done, value } = await reader.read();
238
- if (done)
239
- break;
240
- bb.write(buffer_1.Buffer.from(value));
241
- }
242
- bb.end();
243
- }
244
- catch (err) {
245
- bb.destroy(err);
246
- }
247
- })();
248
- }
249
- else if (responseData?.[Symbol.asyncIterator]) {
250
- ;
251
- (async () => {
252
- try {
253
- for await (const chunk of responseData) {
254
- const buf = buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(toUint8Array(chunk));
255
- bb.write(buf);
256
- }
257
- bb.end();
258
- }
259
- catch (err) {
260
- bb.destroy(err);
261
- }
262
- })();
263
- }
264
- else {
265
- const typeInfo = responseData
266
- ? `type: ${typeof responseData}, constructor: ${responseData.constructor?.name}, keys: ${Object.keys(responseData)
267
- .slice(0, 10)
268
- .join(', ')}`
269
- : 'null or undefined';
270
- reject(new DaytonaError_1.DaytonaError(`Unsupported stream type for response data. ${typeInfo}`));
271
- }
272
- });
273
- await Promise.all(fileTasks);
274
- return finalizeResults(files, srcFileMetaMap);
275
- }
276
- // -------------------- BROWSER / EDGE PATH (no busboy) --------------------
277
- // Collect all bytes first
278
- const bodyBytes = await collectBodyBytes(responseData);
279
- // Path A: multipart/form-data → let platform parse it
280
- if (/^multipart\/form-data/i.test(contentType) && typeof Response !== 'undefined') {
281
- try {
282
- // Make a fresh Uint8Array so its .buffer is a concrete ArrayBuffer (not ArrayBufferLike/SAB)
283
- const bytesCopy = new Uint8Array(bodyBytes.byteLength);
284
- bytesCopy.set(bodyBytes);
285
- // Use the ArrayBuffer directly to satisfy BodyInit/BlobPart typing
286
- const blob = new Blob([bytesCopy.buffer], { type: contentType });
287
- const res = new Response(blob);
288
- const fd = await res.formData();
289
- // No fd.entries() (avoids dom.iterable). Use forEach + Promise bucket.
290
- const ops = [];
291
- fd.forEach((value, field) => {
292
- ops.push((async () => {
293
- if (isRuntimeFile(value)) {
294
- const file = value;
295
- const source = file.name;
296
- const meta = srcFileMetaMap.get(source);
297
- if (!meta)
298
- return;
299
- if (field === 'error') {
300
- const text = await file.text();
301
- meta.error = String(text).trim();
302
- }
303
- else if (field === 'file') {
304
- const ab = await file.arrayBuffer();
305
- meta.result = toBufferIfAvailable(new Uint8Array(ab));
306
- }
307
- }
308
- else {
309
- // Non-file field (rare)
310
- if (field === 'error') {
311
- for (const meta of srcFileMetaMap.values()) {
312
- meta.error = String(value);
313
- }
314
- }
315
- }
316
- })());
317
- });
318
- await Promise.all(ops);
319
- return finalizeResults(files, srcFileMetaMap);
320
- }
321
- catch {
322
- // Fall through to manual multipart parsing
323
- }
117
+ const stream = (0, FileTransfer_1.normalizeResponseStream)(response.data);
118
+ // Node.js path: use busboy for efficient streaming
119
+ if (isNonStreamingRuntime) {
120
+ await (0, FileTransfer_1.processDownloadFilesResponseWithBuffered)(stream, response.headers, metadataMap);
324
121
  }
325
- // Path B: generic multipart/* parser (handles multipart/mixed, etc.)
326
- const boundary = getMultipartBoundary(contentType);
327
- if (!boundary) {
328
- throw new DaytonaError_1.DaytonaError(`Missing multipart boundary in Content-Type: "${contentType}"`);
329
- }
330
- const parts = parseMultipart(bodyBytes, boundary);
331
- for (const p of parts) {
332
- const source = p.filename;
333
- if (!source)
334
- continue;
335
- const meta = srcFileMetaMap.get(source);
336
- if (!meta)
337
- continue;
338
- if (p.name === 'error') {
339
- meta.error = utf8Decode(p.data).trim();
340
- }
341
- else if (p.name === 'file') {
342
- meta.result = toBufferIfAvailable(p.data);
343
- }
122
+ else {
123
+ await (0, FileTransfer_1.processDownloadFilesResponseWithBusboy)(stream, response.headers, metadataMap);
344
124
  }
345
- return finalizeResults(files, srcFileMetaMap);
125
+ return files.map((f) => {
126
+ const metadata = metadataMap.get(f.source);
127
+ const error = metadata?.error || (!metadata?.result ? 'No data received for this file' : undefined);
128
+ return {
129
+ source: f.source,
130
+ result: error ? undefined : metadata.result,
131
+ error,
132
+ };
133
+ });
346
134
  }
347
135
  /**
348
136
  * Searches for text patterns within files in the Sandbox.
@@ -500,18 +288,18 @@ class FileSystem {
500
288
  * await fs.uploadFiles(files);
501
289
  */
502
290
  async uploadFiles(files, timeout = 30 * 60) {
291
+ const isNonStreamingRuntime = Runtime_1.RUNTIME === Runtime_1.Runtime.DENO || Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER || Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS;
503
292
  // Use native FormData in Deno
504
- const FormDataClass = Runtime_1.RUNTIME === Runtime_1.Runtime.DENO || Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS || Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER
505
- ? form_data_1.default
293
+ const FormDataClass = isNonStreamingRuntime
294
+ ? FormData
506
295
  : (await (0, Import_1.dynamicImport)('form-data', 'Uploading files is not supported: '));
507
296
  const form = new FormDataClass();
508
297
  for (const [i, { source, destination }] of files.entries()) {
509
298
  form.append(`files[${i}].path`, destination);
510
299
  const payload = await this.makeFilePayload(source);
511
- // the third arg sets filename in Content-Disposition
512
300
  form.append(`files[${i}].file`, payload, destination);
513
301
  }
514
- if (Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS || Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER) {
302
+ if (isNonStreamingRuntime) {
515
303
  const url = `${this.clientConfig.basePath}/toolbox/${this.sandboxId}/toolbox/files/bulk-upload`;
516
304
  await fetch(url, {
517
305
  method: 'POST',
@@ -529,231 +317,20 @@ class FileSystem {
529
317
  }
530
318
  }
531
319
  async makeFilePayload(source) {
532
- // 1) filepath
320
+ // String = file path
533
321
  if (typeof source === 'string') {
534
322
  const fs = await (0, Import_1.dynamicImport)('fs', 'Uploading file from local file system is not supported: ');
535
323
  return fs.createReadStream(source);
536
324
  }
537
- // 2) browser → Blob
538
- if (Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER || Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS) {
539
- return new Blob([source], { type: 'application/octet-stream' });
325
+ // Blob
326
+ if (Runtime_1.RUNTIME === Runtime_1.Runtime.BROWSER || Runtime_1.RUNTIME === Runtime_1.Runtime.SERVERLESS || Runtime_1.RUNTIME === Runtime_1.Runtime.DENO) {
327
+ // Use .slice() to ensure we have a concrete ArrayBuffer, not ArrayBufferLike
328
+ return new Blob([source.slice()], { type: 'application/octet-stream' });
540
329
  }
541
- // 3) Node (or other server runtimes) → stream.Readable
330
+ // Readable stream
542
331
  const stream = await (0, Import_1.dynamicImport)('stream', 'Uploading file is not supported: ');
543
332
  return stream.Readable.from(source);
544
333
  }
545
334
  }
546
335
  exports.FileSystem = FileSystem;
547
- /* ==================== Helpers (keep in same file) ==================== */
548
- function getHeader(headers, key) {
549
- if (!headers)
550
- return undefined;
551
- // Axios can give lowercase keys; some envs may differ
552
- const k = Object.keys(headers).find((h) => h.toLowerCase() === key.toLowerCase());
553
- const val = k ? headers[k] : undefined;
554
- // Flatten array-ish values
555
- return Array.isArray(val) ? val[0] : val;
556
- }
557
- function safeAbort(streamish) {
558
- if (streamish && typeof streamish.destroy === 'function')
559
- streamish.destroy();
560
- else if (streamish && typeof streamish.cancel === 'function')
561
- streamish.cancel();
562
- }
563
- function toUint8Array(data) {
564
- if (typeof data === 'string') {
565
- return new TextEncoder().encode(data);
566
- }
567
- if (data instanceof ArrayBuffer)
568
- return new Uint8Array(data);
569
- if (ArrayBuffer.isView(data)) {
570
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
571
- }
572
- throw new DaytonaError_1.DaytonaError('Unsupported data type for byte conversion.');
573
- }
574
- async function collectBodyBytes(body) {
575
- if (!body)
576
- return new Uint8Array(0);
577
- if (typeof body.getReader === 'function') {
578
- const reader = body.getReader();
579
- const chunks = [];
580
- let total = 0;
581
- while (true) {
582
- const { done, value } = await reader.read();
583
- if (done)
584
- break;
585
- if (value && value.byteLength) {
586
- chunks.push(value);
587
- total += value.byteLength;
588
- }
589
- }
590
- return concatU8(chunks, total);
591
- }
592
- if (body?.[Symbol.asyncIterator]) {
593
- const chunks = [];
594
- let total = 0;
595
- for await (const chunk of body) {
596
- const u8 = toUint8Array(chunk);
597
- chunks.push(u8);
598
- total += u8.byteLength;
599
- }
600
- return concatU8(chunks, total);
601
- }
602
- if (typeof body === 'string' || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
603
- return toUint8Array(body);
604
- }
605
- if (typeof Blob !== 'undefined' && body instanceof Blob) {
606
- const ab = await body.arrayBuffer();
607
- return new Uint8Array(ab);
608
- }
609
- if (typeof Response !== 'undefined' && body instanceof Response) {
610
- const ab = await body.arrayBuffer();
611
- return new Uint8Array(ab);
612
- }
613
- throw new DaytonaError_1.DaytonaError('Unsupported response body for browser parsing.');
614
- }
615
- function concatU8(parts, total) {
616
- const size = total ?? parts.reduce((s, p) => s + p.byteLength, 0);
617
- const out = new Uint8Array(size);
618
- let off = 0;
619
- for (const p of parts) {
620
- out.set(p, off);
621
- off += p.byteLength;
622
- }
623
- return out;
624
- }
625
- function getMultipartBoundary(ct) {
626
- const m = /boundary="?([^";]+)"?/i.exec(ct || '');
627
- return m ? m[1] : null;
628
- }
629
- /** Minimal multipart parser (binary-safe), supports CRLF line breaks. */
630
- function parseMultipart(body, boundary) {
631
- const enc = new TextEncoder();
632
- const dashBoundary = enc.encode(`--${boundary}`);
633
- const crlf = enc.encode('\r\n');
634
- const boundaryLine = concatU8([dashBoundary, crlf]);
635
- const starts = findAll(body, dashBoundary);
636
- if (starts.length === 0)
637
- return [];
638
- const parts = [];
639
- for (let i = 0; i < starts.length; i++) {
640
- const start = starts[i];
641
- // Part header starts after "--boundary\r\n"
642
- const headerStart = indexAfter(body, start, boundaryLine);
643
- if (headerStart < 0)
644
- continue;
645
- // Part ends before next "--boundary" (trim trailing CRLF)
646
- const nextStart = starts[i + 1] ?? body.length;
647
- let partEnd = nextStart - 2;
648
- if (partEnd < headerStart)
649
- partEnd = headerStart;
650
- // Split headers/body by \r\n\r\n
651
- const sep = findSequence(body, headerStart, partEnd, enc.encode('\r\n\r\n'));
652
- if (sep < 0)
653
- continue;
654
- const headersBytes = body.subarray(headerStart, sep);
655
- const dataStart = sep + 4;
656
- const data = body.subarray(dataStart, partEnd);
657
- const headersText = new TextDecoder('utf-8').decode(headersBytes);
658
- const headers = {};
659
- for (const line of headersText.split(/\r\n/)) {
660
- const idx = line.indexOf(':');
661
- if (idx > -1) {
662
- const k = line.slice(0, idx).trim().toLowerCase();
663
- const v = line.slice(idx + 1).trim();
664
- headers[k] = v;
665
- }
666
- }
667
- const cd = headers['content-disposition'] || '';
668
- const name = getDispositionParam(cd, 'name');
669
- const filename = getDispositionParam(cd, 'filename');
670
- parts.push({ name, filename, headers, data });
671
- }
672
- return parts;
673
- }
674
- function findAll(hay, needle) {
675
- const res = [];
676
- let i = 0;
677
- while (i <= hay.length - needle.length) {
678
- let ok = true;
679
- for (let j = 0; j < needle.length; j++) {
680
- if (hay[i + j] !== needle[j]) {
681
- ok = false;
682
- break;
683
- }
684
- }
685
- if (ok) {
686
- res.push(i);
687
- i += needle.length;
688
- }
689
- else {
690
- i++;
691
- }
692
- }
693
- return res;
694
- }
695
- function findSequence(hay, start, end, needle) {
696
- let i = start;
697
- while (i <= end - needle.length) {
698
- let ok = true;
699
- for (let j = 0; j < needle.length; j++) {
700
- if (hay[i + j] !== needle[j]) {
701
- ok = false;
702
- break;
703
- }
704
- }
705
- if (ok)
706
- return i;
707
- i++;
708
- }
709
- return -1;
710
- }
711
- function indexAfter(hay, start, seq) {
712
- for (let j = 0; j < seq.length; j++) {
713
- if (hay[start + j] !== seq[j])
714
- return -1;
715
- }
716
- return start + seq.length;
717
- }
718
- function getDispositionParam(cd, key) {
719
- const re = new RegExp(`${key}\\*?=("(?:[^"]+)"|[^;]+)`, 'i');
720
- const m = re.exec(cd);
721
- if (!m)
722
- return undefined;
723
- let val = m[1];
724
- if (val.startsWith('"') && val.endsWith('"'))
725
- val = val.slice(1, -1);
726
- return val;
727
- }
728
- function utf8Decode(u8) {
729
- return new TextDecoder('utf-8').decode(u8);
730
- }
731
- /** Return Buffer in Node / with polyfill, else keep Uint8Array. */
732
- function toBufferIfAvailable(u8) {
733
- const B = globalThis.Buffer;
734
- return B && typeof B.from === 'function' ? B.from(u8) : u8;
735
- }
736
- function finalizeResults(files, metaMap) {
737
- const results = [];
738
- for (const f of files) {
739
- const meta = metaMap.get(f.source);
740
- let err = meta?.error;
741
- if (!err && !meta?.result)
742
- err = 'No data received for this file';
743
- let res;
744
- if (!err && meta) {
745
- // In browsers this may be Uint8Array; cast so public type stays Buffer|string
746
- res = meta.result;
747
- }
748
- else if (!meta) {
749
- err = 'No writer metadata found';
750
- }
751
- results.push({ source: f.source, result: res, error: err });
752
- }
753
- return results;
754
- }
755
- function isRuntimeFile(v) {
756
- const F = globalThis.File;
757
- return typeof F !== 'undefined' && v instanceof F;
758
- }
759
336
  //# sourceMappingURL=FileSystem.js.map