@statewalker/webrun-files-s3 0.5.0 → 0.7.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.
@@ -1,33 +1,40 @@
1
1
  /**
2
- * S3 implementation of IFilesApi
2
+ * S3 implementation of FilesApi
3
3
  *
4
4
  * Provides a filesystem-like interface over S3 object storage.
5
- * Supports all IFilesApi methods including the optional move, copy, and mkdir.
6
5
  */
7
6
 
8
7
  import type { S3Client } from "@aws-sdk/client-s3";
9
8
  import {
9
+ AbortMultipartUploadCommand,
10
+ CompleteMultipartUploadCommand,
10
11
  CopyObjectCommand,
12
+ CreateMultipartUploadCommand,
11
13
  DeleteObjectCommand,
14
+ GetObjectCommand,
12
15
  HeadObjectCommand,
13
16
  ListObjectsV2Command,
14
17
  PutObjectCommand,
18
+ UploadPartCommand,
15
19
  } from "@aws-sdk/client-s3";
16
20
  import type {
17
- CopyOptions,
18
- FileHandle,
19
21
  FileInfo,
20
- FileRef,
21
- IFilesApi,
22
+ FileStats,
23
+ FilesApi,
22
24
  ListOptions,
25
+ ReadOptions,
23
26
  } from "@statewalker/webrun-files";
24
- import { basename, resolveFileRef } from "@statewalker/webrun-files";
25
- import { S3FileHandle } from "./s3-file-handle.js";
27
+ import { basename, normalizePath } from "@statewalker/webrun-files";
28
+
29
+ /** Default part size for multipart uploads: 5MB (S3 minimum) */
30
+ const DEFAULT_PART_SIZE = 5 * 1024 * 1024;
31
+
32
+ /** Threshold for using multipart upload: 5MB */
33
+ const MULTIPART_THRESHOLD = 5 * 1024 * 1024;
26
34
 
27
35
  export interface S3FilesApiOptions {
28
36
  /**
29
37
  * Pre-configured S3Client instance.
30
- * Allows full control over credentials, region, endpoint.
31
38
  */
32
39
  client: S3Client;
33
40
 
@@ -38,42 +45,40 @@ export interface S3FilesApiOptions {
38
45
 
39
46
  /**
40
47
  * Optional key prefix (acts as root directory).
41
- * All paths will be relative to this prefix.
42
48
  * @example "projects/my-app/data"
43
49
  */
44
50
  prefix?: string;
45
51
 
46
52
  /**
47
- * Minimum part size for multipart uploads (5MB minimum for S3).
48
- * @default 5 * 1024 * 1024 (5MB)
53
+ * Part size for multipart uploads in bytes.
54
+ * S3 requires minimum 5MB for all parts except the last.
55
+ * @default 5242880 (5MB)
49
56
  */
50
57
  multipartPartSize?: number;
51
58
  }
52
59
 
53
- export class S3FilesApi implements IFilesApi {
60
+ /**
61
+ * S3 filesystem implementation of FilesApi.
62
+ *
63
+ * Provides a filesystem-like interface over S3 object storage.
64
+ * Works with AWS S3 and S3-compatible services.
65
+ */
66
+ export class S3FilesApi implements FilesApi {
54
67
  private client: S3Client;
55
68
  private bucket: string;
56
69
  private prefix: string;
57
- private multipartPartSize: number;
70
+ private partSize: number;
58
71
 
59
72
  constructor(options: S3FilesApiOptions) {
60
73
  this.client = options.client;
61
74
  this.bucket = options.bucket;
62
- // Normalize prefix: remove leading/trailing slashes
63
75
  this.prefix = (options.prefix ?? "").replace(/^\/+|\/+$/g, "");
64
- this.multipartPartSize = options.multipartPartSize ?? 5 * 1024 * 1024;
76
+ this.partSize = options.multipartPartSize ?? DEFAULT_PART_SIZE;
65
77
  }
66
78
 
67
- /**
68
- * Converts a virtual path to an S3 key.
69
- * @example resolveKey("/docs/file.txt") => "prefix/docs/file.txt"
70
- */
71
- private resolveKey(file: FileRef): string {
72
- const normalized = resolveFileRef(file);
73
- // Remove leading slash and combine with prefix
74
- const relativePath = normalized.startsWith("/")
75
- ? normalized.substring(1)
76
- : normalized;
79
+ private resolveKey(path: string): string {
80
+ const normalized = normalizePath(path);
81
+ const relativePath = normalized.startsWith("/") ? normalized.substring(1) : normalized;
77
82
 
78
83
  if (this.prefix) {
79
84
  return relativePath ? `${this.prefix}/${relativePath}` : this.prefix;
@@ -81,9 +86,6 @@ export class S3FilesApi implements IFilesApi {
81
86
  return relativePath;
82
87
  }
83
88
 
84
- /**
85
- * Converts an S3 key back to a virtual path.
86
- */
87
89
  private keyToPath(key: string): string {
88
90
  let relativePath = key;
89
91
  if (this.prefix && key.startsWith(this.prefix)) {
@@ -92,21 +94,193 @@ export class S3FilesApi implements IFilesApi {
92
94
  relativePath = relativePath.substring(1);
93
95
  }
94
96
  }
95
- return "/" + relativePath;
97
+ return `/${relativePath}`;
96
98
  }
97
99
 
98
- /**
99
- * Lists objects in a virtual directory.
100
- *
101
- * Uses S3's delimiter feature to simulate directory listing.
102
- * CommonPrefixes represent subdirectories.
103
- */
104
- async *list(file: FileRef, options: ListOptions = {}): AsyncGenerator<FileInfo> {
105
- const { recursive = false } = options;
106
- const prefix = this.resolveKey(file);
107
- // For root path ("/"), resolveKey returns empty string or prefix
108
- // We need to ensure proper prefix handling
109
- const normalizedPrefix = prefix ? prefix + "/" : (this.prefix ? this.prefix + "/" : "");
100
+ async *read(path: string, options?: ReadOptions): AsyncIterable<Uint8Array> {
101
+ const key = this.resolveKey(path);
102
+
103
+ try {
104
+ const start = options?.start ?? 0;
105
+ const length = options?.length;
106
+
107
+ // Return empty if length is explicitly 0
108
+ if (length === 0) {
109
+ return;
110
+ }
111
+
112
+ let range: string | undefined;
113
+ if (start > 0 || length !== undefined) {
114
+ const end = length !== undefined ? start + length - 1 : "";
115
+ range = `bytes=${start}-${end}`;
116
+ }
117
+
118
+ const command = new GetObjectCommand({
119
+ Bucket: this.bucket,
120
+ Key: key,
121
+ Range: range,
122
+ });
123
+
124
+ const response = await this.client.send(command);
125
+
126
+ if (response.Body) {
127
+ const stream = response.Body as AsyncIterable<Uint8Array>;
128
+ for await (const chunk of stream) {
129
+ yield chunk;
130
+ }
131
+ }
132
+ } catch (error: unknown) {
133
+ const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
134
+ // Handle NotFound (404) and InvalidRange (416) - return empty
135
+ if (
136
+ err.name === "NotFound" ||
137
+ err.name === "InvalidRange" ||
138
+ err.$metadata?.httpStatusCode === 404 ||
139
+ err.$metadata?.httpStatusCode === 416
140
+ ) {
141
+ return;
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ async write(
148
+ path: string,
149
+ content: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,
150
+ ): Promise<void> {
151
+ const key = this.resolveKey(path);
152
+
153
+ // Buffer for accumulating small writes
154
+ let buffer: Uint8Array[] = [];
155
+ let bufferSize = 0;
156
+ let uploadId: string | undefined;
157
+ const parts: { ETag: string; PartNumber: number }[] = [];
158
+ let partNumber = 1;
159
+
160
+ const flushPart = async (isLast: boolean) => {
161
+ if (buffer.length === 0) return;
162
+
163
+ const partLength = buffer.reduce((sum, c) => sum + c.length, 0);
164
+ const partBuffer = new Uint8Array(partLength);
165
+ let offset = 0;
166
+ for (const chunk of buffer) {
167
+ partBuffer.set(chunk, offset);
168
+ offset += chunk.length;
169
+ }
170
+ buffer = [];
171
+ bufferSize = 0;
172
+
173
+ // For small files (under threshold) and this is the only/last part, use simple PutObject
174
+ if (!uploadId && isLast && partLength < MULTIPART_THRESHOLD) {
175
+ const command = new PutObjectCommand({
176
+ Bucket: this.bucket,
177
+ Key: key,
178
+ Body: partBuffer,
179
+ ContentLength: partLength,
180
+ });
181
+ await this.client.send(command);
182
+ return;
183
+ }
184
+
185
+ // Start multipart upload if not already started
186
+ if (!uploadId) {
187
+ const createCommand = new CreateMultipartUploadCommand({
188
+ Bucket: this.bucket,
189
+ Key: key,
190
+ });
191
+ const { UploadId } = await this.client.send(createCommand);
192
+ if (!UploadId) {
193
+ throw new Error("Failed to create multipart upload");
194
+ }
195
+ uploadId = UploadId;
196
+ }
197
+
198
+ // Upload the part
199
+ const uploadCommand = new UploadPartCommand({
200
+ Bucket: this.bucket,
201
+ Key: key,
202
+ UploadId: uploadId,
203
+ PartNumber: partNumber,
204
+ Body: partBuffer,
205
+ });
206
+
207
+ const response = await this.client.send(uploadCommand);
208
+ if (!response.ETag) {
209
+ throw new Error(`Failed to upload part ${partNumber}`);
210
+ }
211
+
212
+ parts.push({
213
+ ETag: response.ETag,
214
+ PartNumber: partNumber,
215
+ });
216
+ partNumber++;
217
+ };
218
+
219
+ try {
220
+ // Process chunks as they arrive
221
+ for await (const chunk of content) {
222
+ buffer.push(chunk);
223
+ bufferSize += chunk.length;
224
+
225
+ // Flush when buffer reaches part size
226
+ if (bufferSize >= this.partSize) {
227
+ await flushPart(false);
228
+ }
229
+ }
230
+
231
+ // Flush remaining data
232
+ await flushPart(true);
233
+
234
+ // Complete multipart upload if one was started
235
+ if (uploadId) {
236
+ const completeCommand = new CompleteMultipartUploadCommand({
237
+ Bucket: this.bucket,
238
+ Key: key,
239
+ UploadId: uploadId,
240
+ MultipartUpload: {
241
+ Parts: parts.sort((a, b) => a.PartNumber - b.PartNumber),
242
+ },
243
+ });
244
+ await this.client.send(completeCommand);
245
+ }
246
+ } catch (error) {
247
+ // Abort multipart upload on error
248
+ if (uploadId) {
249
+ const abortCommand = new AbortMultipartUploadCommand({
250
+ Bucket: this.bucket,
251
+ Key: key,
252
+ UploadId: uploadId,
253
+ });
254
+
255
+ try {
256
+ await this.client.send(abortCommand);
257
+ } catch {
258
+ // Ignore abort errors
259
+ }
260
+ }
261
+
262
+ throw error;
263
+ }
264
+ }
265
+
266
+ async mkdir(path: string): Promise<void> {
267
+ const key = this.resolveKey(path);
268
+ const dirKey = key.endsWith("/") ? key : `${key}/`;
269
+
270
+ const command = new PutObjectCommand({
271
+ Bucket: this.bucket,
272
+ Key: dirKey,
273
+ Body: new Uint8Array(0),
274
+ ContentLength: 0,
275
+ });
276
+
277
+ await this.client.send(command);
278
+ }
279
+
280
+ async *list(path: string, options?: ListOptions): AsyncIterable<FileInfo> {
281
+ const recursive = options?.recursive ?? false;
282
+ const prefix = this.resolveKey(path);
283
+ const normalizedPrefix = prefix ? `${prefix}/` : this.prefix ? `${this.prefix}/` : "";
110
284
 
111
285
  let continuationToken: string | undefined;
112
286
 
@@ -121,45 +295,38 @@ export class S3FilesApi implements IFilesApi {
121
295
 
122
296
  const response = await this.client.send(command);
123
297
 
124
- // Yield directories (CommonPrefixes)
125
298
  if (!recursive && response.CommonPrefixes) {
126
299
  for (const cpPrefix of response.CommonPrefixes) {
127
300
  if (!cpPrefix.Prefix) continue;
128
301
 
129
- // Remove trailing slash
130
302
  const dirKey = cpPrefix.Prefix.endsWith("/")
131
303
  ? cpPrefix.Prefix.slice(0, -1)
132
304
  : cpPrefix.Prefix;
133
305
 
134
- const path = this.keyToPath(dirKey);
306
+ const dirPath = this.keyToPath(dirKey);
135
307
 
136
308
  yield {
137
309
  kind: "directory",
138
- name: basename(path),
139
- path,
310
+ name: basename(dirPath),
311
+ path: dirPath,
140
312
  lastModified: 0,
141
313
  };
142
314
  }
143
315
  }
144
316
 
145
- // Yield files (Contents)
146
317
  if (response.Contents) {
147
318
  for (const obj of response.Contents) {
148
319
  if (!obj.Key) continue;
149
-
150
- // Skip if it's just the prefix itself (directory marker)
151
320
  if (obj.Key === normalizedPrefix) continue;
152
- if (obj.Key + "/" === normalizedPrefix) continue;
153
-
154
- // Skip directory markers (keys ending with /)
321
+ if (`${obj.Key}/` === normalizedPrefix) continue;
155
322
  if (obj.Key.endsWith("/")) continue;
156
323
 
157
- const path = this.keyToPath(obj.Key);
324
+ const filePath = this.keyToPath(obj.Key);
158
325
 
159
326
  yield {
160
327
  kind: "file",
161
- name: basename(path),
162
- path,
328
+ name: basename(filePath),
329
+ path: filePath,
163
330
  size: obj.Size ?? 0,
164
331
  lastModified: obj.LastModified?.getTime() ?? 0,
165
332
  };
@@ -170,19 +337,13 @@ export class S3FilesApi implements IFilesApi {
170
337
  } while (continuationToken);
171
338
  }
172
339
 
173
- /**
174
- * Gets object metadata using HEAD request.
175
- */
176
- async stats(file: FileRef): Promise<FileInfo | undefined> {
177
- const normalized = resolveFileRef(file);
178
- const key = this.resolveKey(file);
340
+ async stats(path: string): Promise<FileStats | undefined> {
341
+ const normalized = normalizePath(path);
342
+ const key = this.resolveKey(path);
179
343
 
180
- // Handle root directory
181
344
  if (normalized === "/") {
182
345
  return {
183
346
  kind: "directory",
184
- name: "",
185
- path: "/",
186
347
  lastModified: 0,
187
348
  };
188
349
  }
@@ -198,11 +359,8 @@ export class S3FilesApi implements IFilesApi {
198
359
 
199
360
  return {
200
361
  kind: "file",
201
- name: basename(normalized),
202
- path: normalized,
203
362
  size: response.ContentLength ?? 0,
204
363
  lastModified: response.LastModified?.getTime() ?? 0,
205
- type: response.ContentType,
206
364
  };
207
365
  } catch (error: unknown) {
208
366
  const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
@@ -211,8 +369,8 @@ export class S3FilesApi implements IFilesApi {
211
369
  }
212
370
  }
213
371
 
214
- // Check if it's a virtual directory by listing with prefix
215
- const prefix = key ? key + "/" : "";
372
+ // Check if it's a virtual directory
373
+ const prefix = key ? `${key}/` : "";
216
374
  const listCommand = new ListObjectsV2Command({
217
375
  Bucket: this.bucket,
218
376
  Prefix: prefix,
@@ -227,8 +385,6 @@ export class S3FilesApi implements IFilesApi {
227
385
  ) {
228
386
  return {
229
387
  kind: "directory",
230
- name: basename(normalized),
231
- path: normalized,
232
388
  lastModified: 0,
233
389
  };
234
390
  }
@@ -236,15 +392,16 @@ export class S3FilesApi implements IFilesApi {
236
392
  return undefined;
237
393
  }
238
394
 
239
- /**
240
- * Deletes an object or all objects under a prefix (directory).
241
- */
242
- async remove(file: FileRef): Promise<boolean> {
243
- const key = this.resolveKey(file);
395
+ async exists(path: string): Promise<boolean> {
396
+ const stats = await this.stats(path);
397
+ return stats !== undefined;
398
+ }
399
+
400
+ async remove(path: string): Promise<boolean> {
401
+ const key = this.resolveKey(path);
244
402
 
245
403
  // First try to delete as a single object
246
404
  try {
247
- // Check if object exists
248
405
  const headCommand = new HeadObjectCommand({
249
406
  Bucket: this.bucket,
250
407
  Key: key,
@@ -252,7 +409,6 @@ export class S3FilesApi implements IFilesApi {
252
409
 
253
410
  await this.client.send(headCommand);
254
411
 
255
- // Object exists, delete it
256
412
  const deleteCommand = new DeleteObjectCommand({
257
413
  Bucket: this.bucket,
258
414
  Key: key,
@@ -265,11 +421,10 @@ export class S3FilesApi implements IFilesApi {
265
421
  if (err.name !== "NotFound" && err.$metadata?.httpStatusCode !== 404) {
266
422
  throw error;
267
423
  }
268
- // Object doesn't exist, continue to check if it's a directory
269
424
  }
270
425
 
271
426
  // Delete all objects under this prefix (directory)
272
- const prefix = key ? key + "/" : "";
427
+ const prefix = key ? `${key}/` : "";
273
428
  const keysToDelete: string[] = [];
274
429
 
275
430
  let continuationToken: string | undefined;
@@ -298,7 +453,6 @@ export class S3FilesApi implements IFilesApi {
298
453
  return false;
299
454
  }
300
455
 
301
- // Delete objects one by one (more compatible with MinIO and other S3-compatible stores)
302
456
  for (const keyToDelete of keysToDelete) {
303
457
  const deleteCommand = new DeleteObjectCommand({
304
458
  Bucket: this.bucket,
@@ -311,69 +465,20 @@ export class S3FilesApi implements IFilesApi {
311
465
  return true;
312
466
  }
313
467
 
314
- /**
315
- * Opens an S3 object for random access.
316
- * Creates the object if it doesn't exist.
317
- */
318
- async open(file: FileRef): Promise<FileHandle> {
319
- const key = this.resolveKey(file);
320
-
321
- let size = 0;
322
-
323
- // Get current size if object exists
324
- try {
325
- const command = new HeadObjectCommand({
326
- Bucket: this.bucket,
327
- Key: key,
328
- });
329
-
330
- const response = await this.client.send(command);
331
- size = response.ContentLength ?? 0;
332
- } catch (error: unknown) {
333
- const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
334
- if (err.name !== "NotFound" && err.$metadata?.httpStatusCode !== 404) {
335
- throw error;
336
- }
337
- // Object doesn't exist, will be created on write
338
- }
339
-
340
- return new S3FileHandle({
341
- client: this.client,
342
- bucket: this.bucket,
343
- key,
344
- size,
345
- multipartPartSize: this.multipartPartSize,
346
- });
347
- }
348
-
349
- /**
350
- * Moves an object using Copy + Delete.
351
- */
352
- async move(source: FileRef, target: FileRef): Promise<boolean> {
468
+ async move(source: string, target: string): Promise<boolean> {
353
469
  const copied = await this.copy(source, target);
354
470
  if (!copied) return false;
355
-
356
471
  return this.remove(source);
357
472
  }
358
473
 
359
- /**
360
- * Copies an object or directory using S3 CopyObject.
361
- */
362
- async copy(
363
- source: FileRef,
364
- target: FileRef,
365
- options: CopyOptions = {},
366
- ): Promise<boolean> {
474
+ async copy(source: string, target: string): Promise<boolean> {
367
475
  const sourceKey = this.resolveKey(source);
368
476
  const targetKey = this.resolveKey(target);
369
- const { recursive = true } = options;
370
477
 
371
- // Check if source is a file
372
- const sourceInfo = await this.stats(source);
373
- if (!sourceInfo) return false;
478
+ const sourceStats = await this.stats(source);
479
+ if (!sourceStats) return false;
374
480
 
375
- if (sourceInfo.kind === "file") {
376
- // Copy single object
481
+ if (sourceStats.kind === "file") {
377
482
  const command = new CopyObjectCommand({
378
483
  Bucket: this.bucket,
379
484
  Key: targetKey,
@@ -385,12 +490,8 @@ export class S3FilesApi implements IFilesApi {
385
490
  }
386
491
 
387
492
  // Copy directory (all objects under prefix)
388
- if (!recursive) {
389
- return false;
390
- }
391
-
392
- const sourcePrefix = sourceKey + "/";
393
- const targetPrefix = targetKey + "/";
493
+ const sourcePrefix = `${sourceKey}/`;
494
+ const targetPrefix = `${targetKey}/`;
394
495
 
395
496
  let continuationToken: string | undefined;
396
497
  let copied = false;
@@ -427,29 +528,4 @@ export class S3FilesApi implements IFilesApi {
427
528
 
428
529
  return copied;
429
530
  }
430
-
431
- /**
432
- * Creates a directory marker in S3.
433
- *
434
- * S3 doesn't have real directories, but some tools expect
435
- * a zero-byte object with trailing slash as a directory marker.
436
- * However, for our purposes we don't actually need to create markers
437
- * since directories are implied by files within them.
438
- */
439
- async mkdir(file: FileRef): Promise<void> {
440
- // For S3, directories don't really need to be created explicitly
441
- // They exist implicitly when files exist inside them
442
- // But we create an empty object as a marker for compatibility
443
- const key = this.resolveKey(file);
444
- const dirKey = key.endsWith("/") ? key : key + "/";
445
-
446
- const command = new PutObjectCommand({
447
- Bucket: this.bucket,
448
- Key: dirKey,
449
- Body: new Uint8Array(0),
450
- ContentLength: 0,
451
- });
452
-
453
- await this.client.send(command);
454
- }
455
531
  }
@@ -1,75 +0,0 @@
1
- /**
2
- * S3 FileHandle implementation
3
- *
4
- * Provides random access read/write operations for S3 objects.
5
- * Uses HTTP Range headers for efficient partial reads.
6
- * Since S3 objects are immutable, writes require re-uploading the entire object.
7
- */
8
- import type { S3Client } from "@aws-sdk/client-s3";
9
- import type { AppendOptions, BinaryStream, FileHandle, ReadStreamOptions, WriteStreamOptions } from "@statewalker/webrun-files";
10
- export interface S3FileHandleOptions {
11
- client: S3Client;
12
- bucket: string;
13
- key: string;
14
- size: number;
15
- multipartPartSize: number;
16
- }
17
- export declare class S3FileHandle implements FileHandle {
18
- private client;
19
- private bucket;
20
- private key;
21
- private _size;
22
- private multipartPartSize;
23
- private closed;
24
- constructor(options: S3FileHandleOptions);
25
- get size(): number;
26
- close(): Promise<void>;
27
- /**
28
- * Reads a range of bytes from the S3 object.
29
- * Uses HTTP Range header for efficient partial reads.
30
- * Streams data directly from S3 without buffering the entire response.
31
- */
32
- createReadStream(options?: ReadStreamOptions): AsyncGenerator<Uint8Array>;
33
- /**
34
- * Writes content to the S3 object using streaming.
35
- *
36
- * S3 objects are immutable, so this operation replaces the entire object.
37
- * Uses streaming multipart upload to avoid buffering the entire file in memory.
38
- * Only buffers one part at a time (5MB by default).
39
- *
40
- * Note: start position > 0 uses UploadPartCopy to preserve existing content
41
- * without downloading it.
42
- */
43
- createWriteStream(data: BinaryStream, options?: WriteStreamOptions): Promise<number>;
44
- /**
45
- * Appends content to the end of the S3 object using streaming.
46
- *
47
- * Uses UploadPartCopy to preserve existing content without downloading it,
48
- * then streams new data as additional parts.
49
- */
50
- appendFile(data: BinaryStream, options?: AppendOptions): Promise<number>;
51
- /**
52
- * Streaming multipart upload that buffers only one part at a time.
53
- * Uses UploadPartCopy to preserve existing content without downloading it
54
- * for large files. For small existing content (< part size), downloads and
55
- * combines with new data to meet S3's minimum part size requirement.
56
- */
57
- private streamingMultipartUpload;
58
- /**
59
- * Download a range of bytes from the S3 object.
60
- */
61
- private downloadRange;
62
- /**
63
- * Upload a single part and record it in the parts array.
64
- */
65
- private uploadPart;
66
- /**
67
- * Abort a multipart upload, ignoring errors.
68
- */
69
- private abortMultipartUpload;
70
- /**
71
- * Random access read from S3 object using HTTP Range header.
72
- */
73
- read(buffer: Uint8Array, offset: number, length: number, position: number): Promise<number>;
74
- }
75
- //# sourceMappingURL=s3-file-handle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"s3-file-handle.d.ts","sourceRoot":"","sources":["../src/s3-file-handle.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AASnD,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,2BAA2B,CAAC;AAGnC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AA+BD,qBAAa,YAAa,YAAW,UAAU;IAC7C,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,mBAAmB;IAQxC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;OAIG;IACI,gBAAgB,CACrB,OAAO,GAAE,iBAAsB,GAC9B,cAAc,CAAC,UAAU,CAAC;IA6B7B;;;;;;;;;OASG;IACG,iBAAiB,CACrB,IAAI,EAAE,YAAY,EAClB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBlF;;;;;OAKG;YACW,wBAAwB;IAgLtC;;OAEG;YACW,aAAa;IAwC3B;;OAEG;YACW,UAAU;IAuBxB;;OAEG;YACW,oBAAoB;IAYlC;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAoDlG"}