@maroonedsoftware/storage 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -1,67 +1,14 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
-
4
- // src/storage.provider.ts
5
- import { Injectable } from "injectkit";
6
- function _ts_decorate(decorators, target, key, desc) {
7
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
8
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
9
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
- return c > 3 && r && Object.defineProperty(target, key, r), r;
11
- }
12
- __name(_ts_decorate, "_ts_decorate");
13
- var StorageProvider = class {
14
- static {
15
- __name(this, "StorageProvider");
16
- }
17
- };
18
- StorageProvider = _ts_decorate([
19
- Injectable()
20
- ], StorageProvider);
21
-
22
- // src/storage.errors.ts
23
- import { ServerkitError } from "@maroonedsoftware/errors";
24
- var StorageError = class extends ServerkitError {
25
- static {
26
- __name(this, "StorageError");
27
- }
28
- constructor(message, options) {
29
- super(message, options);
30
- this.name = "StorageError";
31
- }
32
- };
33
- var StorageObjectNotFoundError = class extends StorageError {
34
- static {
35
- __name(this, "StorageObjectNotFoundError");
36
- }
37
- key;
38
- constructor(key, options) {
39
- super(`storage object '${key}' not found`, options), this.key = key;
40
- this.name = "StorageObjectNotFoundError";
41
- }
42
- };
43
- var StorageAccessDeniedError = class extends StorageError {
44
- static {
45
- __name(this, "StorageAccessDeniedError");
46
- }
47
- key;
48
- constructor(key, options) {
49
- super(`access denied for storage object '${key}'`, options), this.key = key;
50
- this.name = "StorageAccessDeniedError";
51
- }
52
- };
53
- var StorageOperationNotSupportedError = class extends StorageError {
54
- static {
55
- __name(this, "StorageOperationNotSupportedError");
56
- }
57
- constructor(operation, options) {
58
- super(`storage operation '${operation}' is not supported by this backend`, options);
59
- this.name = "StorageOperationNotSupportedError";
60
- }
61
- };
1
+ import {
2
+ StorageAccessDeniedError,
3
+ StorageError,
4
+ StorageObjectNotFoundError,
5
+ StorageOperationNotSupportedError,
6
+ StorageProvider,
7
+ __name
8
+ } from "./chunk-UBT2CAIO.js";
62
9
 
63
10
  // src/disk.storage.provider.ts
64
- import { Injectable as Injectable2 } from "injectkit";
11
+ import { Injectable } from "injectkit";
65
12
  import { createReadStream, createWriteStream } from "fs";
66
13
  import { copyFile, mkdir, readdir, rename, rm, stat as fsStat, unlink } from "fs/promises";
67
14
  import { dirname, join, posix, relative, resolve, sep } from "path";
@@ -69,13 +16,13 @@ import { pipeline } from "stream/promises";
69
16
  import { Readable } from "stream";
70
17
  import { DateTime } from "luxon";
71
18
  import mime from "mime-types";
72
- function _ts_decorate2(decorators, target, key, desc) {
19
+ function _ts_decorate(decorators, target, key, desc) {
73
20
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
74
21
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
75
22
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
76
23
  return c > 3 && r && Object.defineProperty(target, key, r), r;
77
24
  }
78
- __name(_ts_decorate2, "_ts_decorate");
25
+ __name(_ts_decorate, "_ts_decorate");
79
26
  function _ts_metadata(k, v) {
80
27
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
81
28
  }
@@ -256,8 +203,8 @@ var DiskStorageProvider = class extends StorageProvider {
256
203
  }
257
204
  }
258
205
  };
259
- DiskStorageProvider = _ts_decorate2([
260
- Injectable2(),
206
+ DiskStorageProvider = _ts_decorate([
207
+ Injectable(),
261
208
  _ts_metadata("design:type", Function),
262
209
  _ts_metadata("design:paramtypes", [
263
210
  typeof DiskStorageProviderOptions === "undefined" ? Object : DiskStorageProviderOptions
@@ -275,394 +222,9 @@ function isAccessDenied(error) {
275
222
  return code === "EACCES" || code === "EPERM";
276
223
  }
277
224
  __name(isAccessDenied, "isAccessDenied");
278
-
279
- // src/s3.storage.provider.ts
280
- import { Injectable as Injectable3 } from "injectkit";
281
- import { Readable as Readable2 } from "stream";
282
- import { DateTime as DateTime2 } from "luxon";
283
- import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand } from "@aws-sdk/client-s3";
284
- import { Upload } from "@aws-sdk/lib-storage";
285
- import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
286
- function _ts_decorate3(decorators, target, key, desc) {
287
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
288
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
289
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
290
- return c > 3 && r && Object.defineProperty(target, key, r), r;
291
- }
292
- __name(_ts_decorate3, "_ts_decorate");
293
- function _ts_metadata2(k, v) {
294
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
295
- }
296
- __name(_ts_metadata2, "_ts_metadata");
297
- var S3StorageProviderOptions = class {
298
- static {
299
- __name(this, "S3StorageProviderOptions");
300
- }
301
- /** Name of the bucket all keys live in. */
302
- bucket;
303
- constructor(init) {
304
- this.bucket = init.bucket;
305
- }
306
- };
307
- var S3StorageProvider = class extends StorageProvider {
308
- static {
309
- __name(this, "S3StorageProvider");
310
- }
311
- client;
312
- options;
313
- constructor(client, options) {
314
- super(), this.client = client, this.options = options;
315
- }
316
- async write(key, body, options) {
317
- const shared = {
318
- Bucket: this.options.bucket,
319
- Key: key,
320
- ContentType: options?.contentType,
321
- ContentLength: options?.contentLength,
322
- CacheControl: options?.cacheControl,
323
- Metadata: options?.metadata
324
- };
325
- if (body instanceof Readable2) {
326
- const upload = new Upload({
327
- client: this.client,
328
- params: {
329
- ...shared,
330
- Body: body
331
- }
332
- });
333
- await upload.done();
334
- return;
335
- }
336
- await this.client.send(new PutObjectCommand({
337
- ...shared,
338
- Body: body
339
- }));
340
- }
341
- async read(key, options) {
342
- try {
343
- const response = await this.client.send(new GetObjectCommand({
344
- Bucket: this.options.bucket,
345
- Key: key,
346
- Range: toRangeHeader(options?.range)
347
- }));
348
- return response.Body;
349
- } catch (error) {
350
- throw this.mapError(key, error);
351
- }
352
- }
353
- async stat(key) {
354
- try {
355
- const head = await this.client.send(new HeadObjectCommand({
356
- Bucket: this.options.bucket,
357
- Key: key
358
- }));
359
- return {
360
- key,
361
- size: head.ContentLength ?? 0,
362
- contentType: head.ContentType,
363
- etag: head.ETag,
364
- lastModified: head.LastModified ? DateTime2.fromJSDate(head.LastModified) : void 0,
365
- metadata: head.Metadata
366
- };
367
- } catch (error) {
368
- throw this.mapError(key, error);
369
- }
370
- }
371
- async exists(key) {
372
- try {
373
- await this.client.send(new HeadObjectCommand({
374
- Bucket: this.options.bucket,
375
- Key: key
376
- }));
377
- return true;
378
- } catch (error) {
379
- if (isNotFound2(error)) {
380
- return false;
381
- }
382
- throw this.mapError(key, error);
383
- }
384
- }
385
- async delete(key) {
386
- await this.client.send(new DeleteObjectCommand({
387
- Bucket: this.options.bucket,
388
- Key: key
389
- }));
390
- }
391
- /**
392
- * Server-side copy via `CopyObjectCommand`.
393
- *
394
- * Note: S3's single-request `CopyObject` is capped at 5 GB. Copying a larger
395
- * object requires a multipart copy (`UploadPartCopy`), which this provider
396
- * does not yet implement — such copies will fail. {@link move} inherits the
397
- * same limit since it delegates to `copy`.
398
- */
399
- async copy(sourceKey, destinationKey) {
400
- try {
401
- await this.client.send(new CopyObjectCommand({
402
- Bucket: this.options.bucket,
403
- Key: destinationKey,
404
- // CopySource must be the URL-encoded `bucket/key` of the source object.
405
- CopySource: `${this.options.bucket}/${encodeURIComponent(sourceKey)}`
406
- }));
407
- } catch (error) {
408
- throw this.mapError(sourceKey, error);
409
- }
410
- }
411
- async move(sourceKey, destinationKey) {
412
- await this.copy(sourceKey, destinationKey);
413
- await this.delete(sourceKey);
414
- }
415
- async list(options) {
416
- const response = await this.client.send(new ListObjectsV2Command({
417
- Bucket: this.options.bucket,
418
- Prefix: options?.prefix,
419
- MaxKeys: options?.limit,
420
- ContinuationToken: options?.cursor
421
- }));
422
- const objects = (response.Contents ?? []).map((item) => ({
423
- key: item.Key ?? "",
424
- size: item.Size ?? 0,
425
- etag: item.ETag,
426
- lastModified: item.LastModified ? DateTime2.fromJSDate(item.LastModified) : void 0
427
- }));
428
- return {
429
- objects,
430
- cursor: response.IsTruncated ? response.NextContinuationToken : void 0
431
- };
432
- }
433
- async getSignedUrl(key, options) {
434
- const command = options.operation === "write" ? new PutObjectCommand({
435
- Bucket: this.options.bucket,
436
- Key: key,
437
- ContentType: options.contentType
438
- }) : new GetObjectCommand({
439
- Bucket: this.options.bucket,
440
- Key: key
441
- });
442
- return getSignedUrl(this.client, command, {
443
- expiresIn: Math.round(options.expiresIn.as("seconds"))
444
- });
445
- }
446
- mapError(key, error) {
447
- if (isNotFound2(error)) {
448
- return new StorageObjectNotFoundError(key, {
449
- cause: error
450
- });
451
- }
452
- if (isAccessDenied2(error)) {
453
- return new StorageAccessDeniedError(key, {
454
- cause: error
455
- });
456
- }
457
- return error;
458
- }
459
- };
460
- S3StorageProvider = _ts_decorate3([
461
- Injectable3(),
462
- _ts_metadata2("design:type", Function),
463
- _ts_metadata2("design:paramtypes", [
464
- typeof S3Client === "undefined" ? Object : S3Client,
465
- typeof S3StorageProviderOptions === "undefined" ? Object : S3StorageProviderOptions
466
- ])
467
- ], S3StorageProvider);
468
- function errorName(error) {
469
- return typeof error === "object" && error !== null ? error.name : void 0;
470
- }
471
- __name(errorName, "errorName");
472
- function statusCode(error) {
473
- return typeof error === "object" && error !== null ? error.$metadata?.httpStatusCode : void 0;
474
- }
475
- __name(statusCode, "statusCode");
476
- function isNotFound2(error) {
477
- const name = errorName(error);
478
- return name === "NoSuchKey" || name === "NotFound" || statusCode(error) === 404;
479
- }
480
- __name(isNotFound2, "isNotFound");
481
- function isAccessDenied2(error) {
482
- return errorName(error) === "AccessDenied" || statusCode(error) === 403;
483
- }
484
- __name(isAccessDenied2, "isAccessDenied");
485
- function toRangeHeader(range) {
486
- return range ? `bytes=${range.start}-${range.end ?? ""}` : void 0;
487
- }
488
- __name(toRangeHeader, "toRangeHeader");
489
-
490
- // src/gcs.storage.provider.ts
491
- import { Injectable as Injectable4 } from "injectkit";
492
- import { Readable as Readable3 } from "stream";
493
- import { pipeline as pipeline2 } from "stream/promises";
494
- import { DateTime as DateTime3 } from "luxon";
495
- function _ts_decorate4(decorators, target, key, desc) {
496
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
497
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
498
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
499
- return c > 3 && r && Object.defineProperty(target, key, r), r;
500
- }
501
- __name(_ts_decorate4, "_ts_decorate");
502
- function _ts_metadata3(k, v) {
503
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
504
- }
505
- __name(_ts_metadata3, "_ts_metadata");
506
- var GcsStorageProviderOptions = class {
507
- static {
508
- __name(this, "GcsStorageProviderOptions");
509
- }
510
- /** Name of the bucket all keys live in. */
511
- bucket;
512
- constructor(init) {
513
- this.bucket = init.bucket;
514
- }
515
- };
516
- var GcsStorageProvider = class extends StorageProvider {
517
- static {
518
- __name(this, "GcsStorageProvider");
519
- }
520
- client;
521
- options;
522
- constructor(client, options) {
523
- super(), this.client = client, this.options = options;
524
- }
525
- async write(key, body, options) {
526
- const file = this.file(key);
527
- const metadata = {
528
- contentType: options?.contentType,
529
- cacheControl: options?.cacheControl,
530
- metadata: options?.metadata
531
- };
532
- if (body instanceof Readable3) {
533
- await pipeline2(body, file.createWriteStream({
534
- metadata
535
- }));
536
- return;
537
- }
538
- await file.save(body instanceof Buffer ? body : Buffer.from(body), {
539
- metadata
540
- });
541
- }
542
- async read(key, options) {
543
- const exists = await this.exists(key);
544
- if (!exists) {
545
- throw new StorageObjectNotFoundError(key);
546
- }
547
- return this.file(key).createReadStream(options?.range ? {
548
- start: options.range.start,
549
- end: options.range.end
550
- } : void 0);
551
- }
552
- async stat(key) {
553
- try {
554
- const [metadata] = await this.file(key).getMetadata();
555
- return {
556
- key,
557
- size: typeof metadata.size === "string" ? Number(metadata.size) : metadata.size ?? 0,
558
- contentType: metadata.contentType,
559
- etag: metadata.etag,
560
- lastModified: metadata.updated ? DateTime3.fromISO(metadata.updated) : void 0,
561
- metadata: metadata.metadata
562
- };
563
- } catch (error) {
564
- throw this.mapError(key, error);
565
- }
566
- }
567
- async exists(key) {
568
- try {
569
- const [exists] = await this.file(key).exists();
570
- return exists;
571
- } catch (error) {
572
- throw this.mapError(key, error);
573
- }
574
- }
575
- async delete(key) {
576
- await this.file(key).delete({
577
- ignoreNotFound: true
578
- });
579
- }
580
- async copy(sourceKey, destinationKey) {
581
- try {
582
- await this.file(sourceKey).copy(this.file(destinationKey));
583
- } catch (error) {
584
- throw this.mapError(sourceKey, error);
585
- }
586
- }
587
- async move(sourceKey, destinationKey) {
588
- try {
589
- await this.file(sourceKey).move(this.file(destinationKey));
590
- } catch (error) {
591
- throw this.mapError(sourceKey, error);
592
- }
593
- }
594
- async list(options) {
595
- const [files, nextQuery] = await this.client.bucket(this.options.bucket).getFiles({
596
- prefix: options?.prefix,
597
- maxResults: options?.limit,
598
- pageToken: options?.cursor,
599
- autoPaginate: false
600
- });
601
- const objects = files.map((file) => ({
602
- key: file.name,
603
- size: typeof file.metadata.size === "string" ? Number(file.metadata.size) : file.metadata.size ?? 0,
604
- contentType: file.metadata.contentType,
605
- etag: file.metadata.etag,
606
- lastModified: file.metadata.updated ? DateTime3.fromISO(file.metadata.updated) : void 0
607
- }));
608
- return {
609
- objects,
610
- cursor: nextQuery?.pageToken
611
- };
612
- }
613
- async getSignedUrl(key, options) {
614
- const [url] = await this.file(key).getSignedUrl({
615
- version: "v4",
616
- action: options.operation === "write" ? "write" : "read",
617
- expires: DateTime3.now().plus(options.expiresIn).toMillis(),
618
- contentType: options.operation === "write" ? options.contentType : void 0
619
- });
620
- return url;
621
- }
622
- file(key) {
623
- return this.client.bucket(this.options.bucket).file(key);
624
- }
625
- mapError(key, error) {
626
- if (isNotFound3(error)) {
627
- return new StorageObjectNotFoundError(key, {
628
- cause: error
629
- });
630
- }
631
- if (isAccessDenied3(error)) {
632
- return new StorageAccessDeniedError(key, {
633
- cause: error
634
- });
635
- }
636
- return error;
637
- }
638
- };
639
- GcsStorageProvider = _ts_decorate4([
640
- Injectable4(),
641
- _ts_metadata3("design:type", Function),
642
- _ts_metadata3("design:paramtypes", [
643
- typeof Storage === "undefined" ? Object : Storage,
644
- typeof GcsStorageProviderOptions === "undefined" ? Object : GcsStorageProviderOptions
645
- ])
646
- ], GcsStorageProvider);
647
- function statusCode2(error) {
648
- return typeof error === "object" && error !== null ? error.code : void 0;
649
- }
650
- __name(statusCode2, "statusCode");
651
- function isNotFound3(error) {
652
- return statusCode2(error) === 404;
653
- }
654
- __name(isNotFound3, "isNotFound");
655
- function isAccessDenied3(error) {
656
- return statusCode2(error) === 403;
657
- }
658
- __name(isAccessDenied3, "isAccessDenied");
659
225
  export {
660
226
  DiskStorageProvider,
661
227
  DiskStorageProviderOptions,
662
- GcsStorageProvider,
663
- GcsStorageProviderOptions,
664
- S3StorageProvider,
665
- S3StorageProviderOptions,
666
228
  StorageAccessDeniedError,
667
229
  StorageError,
668
230
  StorageObjectNotFoundError,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage.provider.ts","../src/storage.errors.ts","../src/disk.storage.provider.ts","../src/s3.storage.provider.ts","../src/gcs.storage.provider.ts"],"sourcesContent":["import { Injectable } from 'injectkit';\nimport { Readable } from 'node:stream';\nimport { DateTime, Duration } from 'luxon';\n\n/**\n * Options applied when writing an object. All fields are optional; backends\n * that cannot honour a given field ignore it (documented per provider).\n */\nexport interface StorageWriteOptions {\n /** MIME type stored alongside the object (e.g. `image/png`). */\n contentType?: string;\n /** Byte length of the body, when known ahead of time. */\n contentLength?: number;\n /** `Cache-Control` header persisted with the object (cloud backends). */\n cacheControl?: string;\n /** Arbitrary user metadata. Unsupported on the disk backend (ignored). */\n metadata?: Record<string, string>;\n}\n\n/**\n * Options for {@link StorageProvider.read}.\n */\nexport interface StorageReadOptions {\n /**\n * Restrict the read to a byte range. `start` and `end` are both inclusive\n * (HTTP `Range` semantics); omit `end` to read from `start` to the end of the\n * object. Useful for media streaming and resumable downloads.\n */\n range?: { start: number; end?: number };\n}\n\n/**\n * Metadata describing a stored object, returned by {@link StorageProvider.stat}\n * and {@link StorageProvider.list}.\n */\nexport interface StorageObjectMetadata {\n /** The object's key. */\n key: string;\n /** Size in bytes. */\n size: number;\n /** MIME type, when known. */\n contentType?: string;\n /** Backend entity tag, when provided. */\n etag?: string;\n /** Last modification time, when known. */\n lastModified?: DateTime;\n /** User metadata, when the backend supports and returns it. */\n metadata?: Record<string, string>;\n}\n\n/**\n * Options for {@link StorageProvider.list}.\n */\nexport interface StorageListOptions {\n /** Restrict results to keys beginning with this prefix. */\n prefix?: string;\n /** Maximum number of objects to return in this page. */\n limit?: number;\n /** Opaque continuation token from a previous {@link StorageListResult}. */\n cursor?: string;\n}\n\n/**\n * A single page of {@link StorageProvider.list} results.\n */\nexport interface StorageListResult {\n /** Objects in this page. */\n objects: StorageObjectMetadata[];\n /** Continuation token for the next page, or `undefined` when exhausted. */\n cursor?: string;\n}\n\n/** Whether a signed URL grants read (download) or write (upload) access. */\nexport type SignedUrlOperation = 'read' | 'write';\n\n/**\n * Options for {@link StorageProvider.getSignedUrl}.\n */\nexport interface SignedUrlOptions {\n /** The operation the URL authorises. */\n operation: SignedUrlOperation;\n /** How long the URL remains valid. */\n expiresIn: Duration;\n /** Content type the client must use when uploading (`write` URLs). */\n contentType?: string;\n}\n\n/**\n * Backend-agnostic object storage. Implementations wrap a concrete backend\n * (local disk, AWS S3, Google Cloud Storage). Bind a concrete provider to this\n * token in the DI container so consumers depend only on the abstraction:\n *\n * ```ts\n * container.bind(StorageProvider).toConstantValue(new DiskStorageProvider({ rootDir: '/var/data' }));\n * ```\n *\n * Keys are hierarchical, `/`-separated paths (e.g. `users/42/avatar.png`), not\n * flat filenames.\n *\n * ## Behaviour contract\n * - {@link read} / {@link stat} on a missing key throw `StorageObjectNotFoundError`.\n * - {@link exists} never throws for a missing key — it returns `false`.\n * - Operations that hit a permission failure throw `StorageAccessDeniedError`.\n * - {@link delete} is idempotent — deleting a missing key is a no-op.\n * - {@link copy} / {@link move} throw `StorageObjectNotFoundError` when the\n * source is missing, and overwrite the destination if it already exists. Both\n * operate within this backend only (same bucket / root) — cross-backend or\n * cross-bucket transfers are out of scope.\n * - {@link getSignedUrl} throws `StorageOperationNotSupportedError` on backends\n * that cannot sign URLs.\n */\n@Injectable()\nexport abstract class StorageProvider {\n /** Write `body` to `key`, overwriting any existing object. */\n abstract write(key: string, body: Readable | Buffer | string, options?: StorageWriteOptions): Promise<void>;\n /** Open a readable stream for `key`, optionally for a byte range. Throws if the key does not exist. */\n abstract read(key: string, options?: StorageReadOptions): Promise<Readable>;\n /** Fetch metadata for `key` without reading its body. Throws if absent. */\n abstract stat(key: string): Promise<StorageObjectMetadata>;\n /** Resolve to `true` if `key` exists, `false` otherwise. Never throws for absence. */\n abstract exists(key: string): Promise<boolean>;\n /** Delete `key`. A no-op if the key does not exist. */\n abstract delete(key: string): Promise<void>;\n /** Copy `sourceKey` to `destinationKey` within this backend, overwriting the destination. Throws if the source is missing. */\n abstract copy(sourceKey: string, destinationKey: string): Promise<void>;\n /** Move/rename `sourceKey` to `destinationKey` within this backend, overwriting the destination. Throws if the source is missing. */\n abstract move(sourceKey: string, destinationKey: string): Promise<void>;\n /** List a single page of objects, optionally filtered by prefix. */\n abstract list(options?: StorageListOptions): Promise<StorageListResult>;\n /** Generate a time-limited signed URL for direct client read/write access. */\n abstract getSignedUrl(key: string, options: SignedUrlOptions): Promise<string>;\n}\n","import { ServerkitError } from '@maroonedsoftware/errors';\n\n/**\n * Base class for all errors thrown by a {@link StorageProvider}.\n *\n * Catch this to handle any storage failure generically; catch a subclass to\n * distinguish specific conditions (missing object, unsupported operation).\n * Extends `ServerkitError`, so `errorMiddleware` renders it as a 500 with any\n * attached `details`.\n */\nexport class StorageError extends ServerkitError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'StorageError';\n }\n}\n\n/**\n * Thrown when {@link StorageProvider.read} or {@link StorageProvider.stat} is\n * called for a key that does not exist. The offending key is available on\n * `key` for callers that want to surface it.\n */\nexport class StorageObjectNotFoundError extends StorageError {\n constructor(\n readonly key: string,\n options?: { cause?: unknown },\n ) {\n super(`storage object '${key}' not found`, options);\n this.name = 'StorageObjectNotFoundError';\n }\n}\n\n/**\n * Thrown when the backend rejects an operation for permission reasons (an S3 or\n * GCS `403`, a local-filesystem `EACCES`/`EPERM`). The offending key is\n * available on `key`.\n */\nexport class StorageAccessDeniedError extends StorageError {\n constructor(\n readonly key: string,\n options?: { cause?: unknown },\n ) {\n super(`access denied for storage object '${key}'`, options);\n this.name = 'StorageAccessDeniedError';\n }\n}\n\n/**\n * Thrown when an operation is not supported by the active backend — for\n * example {@link StorageProvider.getSignedUrl} on a disk provider that has no\n * public base URL configured.\n */\nexport class StorageOperationNotSupportedError extends StorageError {\n constructor(operation: string, options?: { cause?: unknown }) {\n super(`storage operation '${operation}' is not supported by this backend`, options);\n this.name = 'StorageOperationNotSupportedError';\n }\n}\n","import { Injectable } from 'injectkit';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { copyFile, mkdir, readdir, rename, rm, stat as fsStat, unlink } from 'node:fs/promises';\nimport { dirname, join, posix, relative, resolve, sep } from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport { Readable } from 'node:stream';\nimport { DateTime } from 'luxon';\nimport mime from 'mime-types';\nimport { StorageAccessDeniedError, StorageObjectNotFoundError, StorageOperationNotSupportedError } from './storage.errors.js';\nimport type {\n SignedUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageObjectMetadata,\n StorageReadOptions,\n StorageWriteOptions,\n} from './storage.provider.js';\nimport { StorageProvider } from './storage.provider.js';\n\n/**\n * Construction options for {@link DiskStorageProvider}.\n *\n * A class (not an interface) so it can serve as an InjectKit token — register\n * an instance and the container can construct {@link DiskStorageProvider}.\n */\nexport class DiskStorageProviderOptions {\n /** Root directory the provider stores objects under. */\n readonly rootDir: string;\n /**\n * Base URL objects are publicly served from. When set, {@link DiskStorageProvider.getSignedUrl}\n * returns `${publicBaseUrl}/${key}`; when omitted, signing is unsupported.\n */\n readonly publicBaseUrl?: string;\n\n constructor(init: { rootDir: string; publicBaseUrl?: string }) {\n this.rootDir = init.rootDir;\n this.publicBaseUrl = init.publicBaseUrl;\n }\n}\n\n/**\n * {@link StorageProvider} backed by the local filesystem rooted at a directory.\n *\n * Keys map to paths under the root; nested keys create intermediate directories\n * on write. User metadata is not persisted (the filesystem has no native slot),\n * and `getSignedUrl` requires a configured `publicBaseUrl`. Useful for local\n * development and as an in-process test double.\n */\n@Injectable()\nexport class DiskStorageProvider extends StorageProvider {\n constructor(private readonly options: DiskStorageProviderOptions) {\n super();\n }\n\n async write(key: string, body: Readable | Buffer | string, _options?: StorageWriteOptions): Promise<void> {\n const path = this.resolveKey(key);\n await mkdir(dirname(path), { recursive: true });\n const source = body instanceof Readable ? body : Readable.from(body instanceof Buffer ? body : Buffer.from(body));\n await pipeline(source, createWriteStream(path));\n }\n\n async read(key: string, options?: StorageReadOptions): Promise<Readable> {\n const path = this.resolveKey(key);\n await this.assertExists(key, path);\n // `start`/`end` map directly to fs's inclusive byte offsets.\n return options?.range ? createReadStream(path, { start: options.range.start, end: options.range.end }) : createReadStream(path);\n }\n\n async stat(key: string): Promise<StorageObjectMetadata> {\n const path = this.resolveKey(key);\n const stats = await this.assertExists(key, path);\n const contentType = mime.lookup(path);\n return {\n key,\n size: stats.size,\n contentType: contentType === false ? undefined : contentType,\n lastModified: DateTime.fromJSDate(stats.mtime),\n };\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n const stats = await fsStat(this.resolveKey(key));\n return stats.isFile();\n } catch (error) {\n if (isNotFound(error)) {\n return false;\n }\n if (isAccessDenied(error)) {\n throw new StorageAccessDeniedError(key, { cause: error });\n }\n throw error;\n }\n }\n\n async delete(key: string): Promise<void> {\n // `force: true` makes this idempotent — no error when the path is absent.\n await rm(this.resolveKey(key), { force: true });\n }\n\n async copy(sourceKey: string, destinationKey: string): Promise<void> {\n const source = this.resolveKey(sourceKey);\n const destination = this.resolveKey(destinationKey);\n await this.assertExists(sourceKey, source);\n await mkdir(dirname(destination), { recursive: true });\n await copyFile(source, destination);\n }\n\n async move(sourceKey: string, destinationKey: string): Promise<void> {\n const source = this.resolveKey(sourceKey);\n const destination = this.resolveKey(destinationKey);\n await this.assertExists(sourceKey, source);\n await mkdir(dirname(destination), { recursive: true });\n try {\n // Atomic and cheap on the same filesystem.\n await rename(source, destination);\n } catch (error) {\n // `rename` cannot cross filesystem boundaries — fall back to copy + delete.\n if (typeof error === 'object' && error !== null && (error as NodeJS.ErrnoException).code === 'EXDEV') {\n await copyFile(source, destination);\n await unlink(source);\n return;\n }\n throw error;\n }\n }\n\n /**\n * Lists objects under the root, optionally filtered by prefix.\n *\n * Note: this walks the entire directory tree and `stat`s every matching file\n * on each call — pagination slices an in-memory list, it does not make paging\n * cheaper. Fine for development and modest trees (its intended use); avoid\n * pointing it at very large directories.\n */\n async list(options?: StorageListOptions): Promise<StorageListResult> {\n const prefix = options?.prefix ?? '';\n const keys: string[] = [];\n await this.walk(this.options.rootDir, keys);\n const matched = keys.filter(key => key.startsWith(prefix)).sort();\n\n // Cursor is the last key returned by the previous page; resume after it.\n const start = options?.cursor ? matched.findIndex(key => key > options.cursor!) : 0;\n const from = start === -1 ? matched.length : start;\n const limit = options?.limit ?? matched.length;\n const page = matched.slice(from, from + limit);\n\n const objects = await Promise.all(page.map(key => this.stat(key)));\n const hasMore = from + limit < matched.length;\n return {\n objects,\n cursor: hasMore && page.length > 0 ? page[page.length - 1] : undefined,\n };\n }\n\n async getSignedUrl(key: string, _options: SignedUrlOptions): Promise<string> {\n if (!this.options.publicBaseUrl) {\n throw new StorageOperationNotSupportedError('getSignedUrl');\n }\n // Normalise the key against the root for traversal safety, then join to the base URL.\n this.resolveKey(key);\n // Encode each path segment so keys with spaces or reserved characters yield a valid URL.\n const encodedPath = key\n .replace(/^\\/+/, '')\n .split('/')\n .map(encodeURIComponent)\n .join('/');\n return `${this.options.publicBaseUrl.replace(/\\/+$/, '')}/${encodedPath}`;\n }\n\n /** Resolve a key to an absolute path, rejecting traversal that escapes the root. */\n private resolveKey(key: string): string {\n const path = resolve(this.options.rootDir, key);\n const rel = relative(this.options.rootDir, path);\n if (rel === '' || rel.startsWith('..') || rel.startsWith(`..${sep}`)) {\n throw new StorageOperationNotSupportedError(`key '${key}' resolves outside the storage root`);\n }\n return path;\n }\n\n private async assertExists(key: string, path: string) {\n try {\n const stats = await fsStat(path);\n if (!stats.isFile()) {\n throw new StorageObjectNotFoundError(key);\n }\n return stats;\n } catch (error) {\n if (isNotFound(error)) {\n throw new StorageObjectNotFoundError(key, { cause: error });\n }\n if (isAccessDenied(error)) {\n throw new StorageAccessDeniedError(key, { cause: error });\n }\n throw error;\n }\n }\n\n /** Recursively collect file keys (root-relative, `/`-separated) under `dir`. */\n private async walk(dir: string, into: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n await this.walk(full, into);\n } else if (entry.isFile()) {\n into.push(relative(this.options.rootDir, full).split(sep).join(posix.sep));\n }\n }\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && (error as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nfunction isAccessDenied(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n const code = (error as NodeJS.ErrnoException).code;\n return code === 'EACCES' || code === 'EPERM';\n}\n","import { Injectable } from 'injectkit';\nimport { Readable } from 'node:stream';\nimport { DateTime } from 'luxon';\nimport {\n CopyObjectCommand,\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n type S3Client,\n} from '@aws-sdk/client-s3';\nimport { Upload } from '@aws-sdk/lib-storage';\nimport { getSignedUrl } from '@aws-sdk/s3-request-presigner';\nimport { StorageAccessDeniedError, StorageObjectNotFoundError } from './storage.errors.js';\nimport type {\n SignedUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageObjectMetadata,\n StorageReadOptions,\n StorageWriteOptions,\n} from './storage.provider.js';\nimport { StorageProvider } from './storage.provider.js';\n\n/**\n * Construction options for {@link S3StorageProvider}.\n *\n * A class (not an interface) so it can serve as an InjectKit token — register\n * an instance and the container can construct {@link S3StorageProvider}.\n */\nexport class S3StorageProviderOptions {\n /** Name of the bucket all keys live in. */\n readonly bucket: string;\n\n constructor(init: { bucket: string }) {\n this.bucket = init.bucket;\n }\n}\n\n/**\n * {@link StorageProvider} backed by an AWS S3 (or S3-compatible) bucket.\n *\n * Streaming writes go through `@aws-sdk/lib-storage`'s multipart `Upload`;\n * buffer/string writes use a single `PutObject`. Signed URLs are produced with\n * `@aws-sdk/s3-request-presigner`.\n */\n@Injectable()\nexport class S3StorageProvider extends StorageProvider {\n constructor(\n private readonly client: S3Client,\n private readonly options: S3StorageProviderOptions,\n ) {\n super();\n }\n\n async write(key: string, body: Readable | Buffer | string, options?: StorageWriteOptions): Promise<void> {\n const shared = {\n Bucket: this.options.bucket,\n Key: key,\n ContentType: options?.contentType,\n ContentLength: options?.contentLength,\n CacheControl: options?.cacheControl,\n Metadata: options?.metadata,\n };\n\n if (body instanceof Readable) {\n // lib-storage streams the body, automatically switching to multipart for large objects.\n const upload = new Upload({ client: this.client, params: { ...shared, Body: body } });\n await upload.done();\n return;\n }\n\n await this.client.send(new PutObjectCommand({ ...shared, Body: body }));\n }\n\n async read(key: string, options?: StorageReadOptions): Promise<Readable> {\n try {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.options.bucket, Key: key, Range: toRangeHeader(options?.range) }),\n );\n return response.Body as Readable;\n } catch (error) {\n throw this.mapError(key, error);\n }\n }\n\n async stat(key: string): Promise<StorageObjectMetadata> {\n try {\n const head = await this.client.send(new HeadObjectCommand({ Bucket: this.options.bucket, Key: key }));\n return {\n key,\n size: head.ContentLength ?? 0,\n contentType: head.ContentType,\n etag: head.ETag,\n lastModified: head.LastModified ? DateTime.fromJSDate(head.LastModified) : undefined,\n metadata: head.Metadata,\n };\n } catch (error) {\n throw this.mapError(key, error);\n }\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n await this.client.send(new HeadObjectCommand({ Bucket: this.options.bucket, Key: key }));\n return true;\n } catch (error) {\n if (isNotFound(error)) {\n return false;\n }\n // A 403 is a permission failure, not absence — surface it.\n throw this.mapError(key, error);\n }\n }\n\n async delete(key: string): Promise<void> {\n // S3 DeleteObject is idempotent — deleting a missing key succeeds.\n await this.client.send(new DeleteObjectCommand({ Bucket: this.options.bucket, Key: key }));\n }\n\n /**\n * Server-side copy via `CopyObjectCommand`.\n *\n * Note: S3's single-request `CopyObject` is capped at 5 GB. Copying a larger\n * object requires a multipart copy (`UploadPartCopy`), which this provider\n * does not yet implement — such copies will fail. {@link move} inherits the\n * same limit since it delegates to `copy`.\n */\n async copy(sourceKey: string, destinationKey: string): Promise<void> {\n try {\n await this.client.send(\n new CopyObjectCommand({\n Bucket: this.options.bucket,\n Key: destinationKey,\n // CopySource must be the URL-encoded `bucket/key` of the source object.\n CopySource: `${this.options.bucket}/${encodeURIComponent(sourceKey)}`,\n }),\n );\n } catch (error) {\n throw this.mapError(sourceKey, error);\n }\n }\n\n async move(sourceKey: string, destinationKey: string): Promise<void> {\n // S3 has no native move — copy then delete the source.\n await this.copy(sourceKey, destinationKey);\n await this.delete(sourceKey);\n }\n\n async list(options?: StorageListOptions): Promise<StorageListResult> {\n const response = await this.client.send(\n new ListObjectsV2Command({\n Bucket: this.options.bucket,\n Prefix: options?.prefix,\n MaxKeys: options?.limit,\n ContinuationToken: options?.cursor,\n }),\n );\n\n const objects: StorageObjectMetadata[] = (response.Contents ?? []).map(item => ({\n key: item.Key ?? '',\n size: item.Size ?? 0,\n etag: item.ETag,\n lastModified: item.LastModified ? DateTime.fromJSDate(item.LastModified) : undefined,\n }));\n\n return {\n objects,\n cursor: response.IsTruncated ? response.NextContinuationToken : undefined,\n };\n }\n\n async getSignedUrl(key: string, options: SignedUrlOptions): Promise<string> {\n const command =\n options.operation === 'write'\n ? new PutObjectCommand({ Bucket: this.options.bucket, Key: key, ContentType: options.contentType })\n : new GetObjectCommand({ Bucket: this.options.bucket, Key: key });\n return getSignedUrl(this.client, command, { expiresIn: Math.round(options.expiresIn.as('seconds')) });\n }\n\n private mapError(key: string, error: unknown): unknown {\n if (isNotFound(error)) {\n return new StorageObjectNotFoundError(key, { cause: error });\n }\n if (isAccessDenied(error)) {\n return new StorageAccessDeniedError(key, { cause: error });\n }\n return error;\n }\n}\n\nfunction errorName(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null ? (error as { name?: string }).name : undefined;\n}\n\nfunction statusCode(error: unknown): number | undefined {\n return typeof error === 'object' && error !== null ? (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode : undefined;\n}\n\nfunction isNotFound(error: unknown): boolean {\n const name = errorName(error);\n return name === 'NoSuchKey' || name === 'NotFound' || statusCode(error) === 404;\n}\n\nfunction isAccessDenied(error: unknown): boolean {\n return errorName(error) === 'AccessDenied' || statusCode(error) === 403;\n}\n\n/** Build an HTTP `Range` header (`bytes=start-end`) from an inclusive byte range. */\nfunction toRangeHeader(range?: { start: number; end?: number }): string | undefined {\n return range ? `bytes=${range.start}-${range.end ?? ''}` : undefined;\n}\n","import { Injectable } from 'injectkit';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport { DateTime } from 'luxon';\nimport type { File, Storage } from '@google-cloud/storage';\nimport { StorageAccessDeniedError, StorageObjectNotFoundError } from './storage.errors.js';\nimport type {\n SignedUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageObjectMetadata,\n StorageReadOptions,\n StorageWriteOptions,\n} from './storage.provider.js';\nimport { StorageProvider } from './storage.provider.js';\n\n/**\n * Construction options for {@link GcsStorageProvider}.\n *\n * A class (not an interface) so it can serve as an InjectKit token — register\n * an instance and the container can construct {@link GcsStorageProvider}.\n */\nexport class GcsStorageProviderOptions {\n /** Name of the bucket all keys live in. */\n readonly bucket: string;\n\n constructor(init: { bucket: string }) {\n this.bucket = init.bucket;\n }\n}\n\n/**\n * {@link StorageProvider} backed by a Google Cloud Storage bucket.\n */\n@Injectable()\nexport class GcsStorageProvider extends StorageProvider {\n constructor(\n private readonly client: Storage,\n private readonly options: GcsStorageProviderOptions,\n ) {\n super();\n }\n\n async write(key: string, body: Readable | Buffer | string, options?: StorageWriteOptions): Promise<void> {\n const file = this.file(key);\n const metadata = {\n contentType: options?.contentType,\n cacheControl: options?.cacheControl,\n metadata: options?.metadata,\n };\n\n if (body instanceof Readable) {\n await pipeline(body, file.createWriteStream({ metadata }));\n return;\n }\n\n await file.save(body instanceof Buffer ? body : Buffer.from(body), { metadata });\n }\n\n async read(key: string, options?: StorageReadOptions): Promise<Readable> {\n // GCS surfaces a missing object on the stream's 'error' event rather than as\n // a rejected promise, so pre-check existence to honour the not-found contract.\n const exists = await this.exists(key);\n if (!exists) {\n throw new StorageObjectNotFoundError(key);\n }\n // `start`/`end` are inclusive byte offsets, matching the read contract.\n return this.file(key).createReadStream(options?.range ? { start: options.range.start, end: options.range.end } : undefined);\n }\n\n async stat(key: string): Promise<StorageObjectMetadata> {\n try {\n const [metadata] = await this.file(key).getMetadata();\n return {\n key,\n size: typeof metadata.size === 'string' ? Number(metadata.size) : (metadata.size ?? 0),\n contentType: metadata.contentType,\n etag: metadata.etag,\n lastModified: metadata.updated ? DateTime.fromISO(metadata.updated) : undefined,\n metadata: metadata.metadata as Record<string, string> | undefined,\n };\n } catch (error) {\n throw this.mapError(key, error);\n }\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n const [exists] = await this.file(key).exists();\n return exists;\n } catch (error) {\n // `exists()` reports absence as `false`; a thrown error is a real failure\n // (e.g. a permission denial) and should surface.\n throw this.mapError(key, error);\n }\n }\n\n async delete(key: string): Promise<void> {\n await this.file(key).delete({ ignoreNotFound: true });\n }\n\n async copy(sourceKey: string, destinationKey: string): Promise<void> {\n try {\n await this.file(sourceKey).copy(this.file(destinationKey));\n } catch (error) {\n throw this.mapError(sourceKey, error);\n }\n }\n\n async move(sourceKey: string, destinationKey: string): Promise<void> {\n try {\n // GCS `move` is a server-side copy followed by a delete of the source.\n await this.file(sourceKey).move(this.file(destinationKey));\n } catch (error) {\n throw this.mapError(sourceKey, error);\n }\n }\n\n async list(options?: StorageListOptions): Promise<StorageListResult> {\n const [files, nextQuery] = await this.client.bucket(this.options.bucket).getFiles({\n prefix: options?.prefix,\n maxResults: options?.limit,\n pageToken: options?.cursor,\n autoPaginate: false,\n });\n\n const objects: StorageObjectMetadata[] = files.map(file => ({\n key: file.name,\n size: typeof file.metadata.size === 'string' ? Number(file.metadata.size) : (file.metadata.size ?? 0),\n contentType: file.metadata.contentType,\n etag: file.metadata.etag,\n lastModified: file.metadata.updated ? DateTime.fromISO(file.metadata.updated) : undefined,\n }));\n\n return {\n objects,\n cursor: (nextQuery as { pageToken?: string } | undefined)?.pageToken,\n };\n }\n\n async getSignedUrl(key: string, options: SignedUrlOptions): Promise<string> {\n const [url] = await this.file(key).getSignedUrl({\n version: 'v4',\n action: options.operation === 'write' ? 'write' : 'read',\n expires: DateTime.now().plus(options.expiresIn).toMillis(),\n contentType: options.operation === 'write' ? options.contentType : undefined,\n });\n return url;\n }\n\n private file(key: string): File {\n return this.client.bucket(this.options.bucket).file(key);\n }\n\n private mapError(key: string, error: unknown): unknown {\n if (isNotFound(error)) {\n return new StorageObjectNotFoundError(key, { cause: error });\n }\n if (isAccessDenied(error)) {\n return new StorageAccessDeniedError(key, { cause: error });\n }\n return error;\n }\n}\n\nfunction statusCode(error: unknown): number | undefined {\n return typeof error === 'object' && error !== null ? (error as { code?: number }).code : undefined;\n}\n\nfunction isNotFound(error: unknown): boolean {\n return statusCode(error) === 404;\n}\n\nfunction isAccessDenied(error: unknown): boolean {\n return statusCode(error) === 403;\n}\n"],"mappings":"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","names":["Injectable","StorageProvider","ServerkitError","StorageError","ServerkitError","message","options","name","StorageObjectNotFoundError","key","StorageAccessDeniedError","StorageOperationNotSupportedError","operation","Injectable","createReadStream","createWriteStream","copyFile","mkdir","readdir","rename","rm","stat","fsStat","unlink","dirname","join","posix","relative","resolve","sep","pipeline","Readable","DateTime","mime","DiskStorageProviderOptions","rootDir","publicBaseUrl","init","DiskStorageProvider","StorageProvider","options","write","key","body","_options","path","resolveKey","mkdir","dirname","recursive","source","Readable","from","Buffer","pipeline","createWriteStream","read","assertExists","range","createReadStream","start","end","stat","stats","contentType","mime","lookup","size","undefined","lastModified","DateTime","fromJSDate","mtime","exists","fsStat","isFile","error","isNotFound","isAccessDenied","StorageAccessDeniedError","cause","delete","rm","force","copy","sourceKey","destinationKey","destination","copyFile","move","rename","code","unlink","list","prefix","keys","walk","matched","filter","startsWith","sort","cursor","findIndex","length","limit","page","slice","objects","Promise","all","map","hasMore","getSignedUrl","StorageOperationNotSupportedError","encodedPath","replace","split","encodeURIComponent","join","resolve","rel","relative","sep","StorageObjectNotFoundError","dir","into","entries","readdir","withFileTypes","entry","full","name","isDirectory","push","posix","Injectable","Readable","DateTime","CopyObjectCommand","DeleteObjectCommand","GetObjectCommand","HeadObjectCommand","ListObjectsV2Command","PutObjectCommand","Upload","getSignedUrl","S3StorageProviderOptions","bucket","init","S3StorageProvider","StorageProvider","client","options","write","key","body","shared","Bucket","Key","ContentType","contentType","ContentLength","contentLength","CacheControl","cacheControl","Metadata","metadata","Readable","upload","Upload","params","Body","done","send","PutObjectCommand","read","response","GetObjectCommand","Range","toRangeHeader","range","error","mapError","stat","head","HeadObjectCommand","size","etag","ETag","lastModified","LastModified","DateTime","fromJSDate","undefined","exists","isNotFound","delete","DeleteObjectCommand","copy","sourceKey","destinationKey","CopyObjectCommand","CopySource","encodeURIComponent","move","list","ListObjectsV2Command","Prefix","prefix","MaxKeys","limit","ContinuationToken","cursor","objects","Contents","map","item","Size","IsTruncated","NextContinuationToken","getSignedUrl","command","operation","expiresIn","Math","round","as","StorageObjectNotFoundError","cause","isAccessDenied","StorageAccessDeniedError","errorName","name","statusCode","$metadata","httpStatusCode","start","end","Injectable","Readable","pipeline","DateTime","GcsStorageProviderOptions","bucket","init","GcsStorageProvider","StorageProvider","client","options","write","key","body","file","metadata","contentType","cacheControl","Readable","pipeline","createWriteStream","save","Buffer","from","read","exists","StorageObjectNotFoundError","createReadStream","range","start","end","undefined","stat","getMetadata","size","Number","etag","lastModified","updated","DateTime","fromISO","error","mapError","delete","ignoreNotFound","copy","sourceKey","destinationKey","move","list","files","nextQuery","getFiles","prefix","maxResults","limit","pageToken","cursor","autoPaginate","objects","map","name","getSignedUrl","url","version","action","operation","expires","now","plus","expiresIn","toMillis","isNotFound","cause","isAccessDenied","StorageAccessDeniedError","statusCode","code"]}
1
+ {"version":3,"sources":["../src/disk.storage.provider.ts"],"sourcesContent":["import { Injectable } from 'injectkit';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { copyFile, mkdir, readdir, rename, rm, stat as fsStat, unlink } from 'node:fs/promises';\nimport { dirname, join, posix, relative, resolve, sep } from 'node:path';\nimport { pipeline } from 'node:stream/promises';\nimport { Readable } from 'node:stream';\nimport { DateTime } from 'luxon';\nimport mime from 'mime-types';\nimport { StorageAccessDeniedError, StorageObjectNotFoundError, StorageOperationNotSupportedError } from './storage.errors.js';\nimport type {\n SignedUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageObjectMetadata,\n StorageReadOptions,\n StorageWriteOptions,\n} from './storage.provider.js';\nimport { StorageProvider } from './storage.provider.js';\n\n/**\n * Construction options for {@link DiskStorageProvider}.\n *\n * A class (not an interface) so it can serve as an InjectKit token — register\n * an instance and the container can construct {@link DiskStorageProvider}.\n */\nexport class DiskStorageProviderOptions {\n /** Root directory the provider stores objects under. */\n readonly rootDir: string;\n /**\n * Base URL objects are publicly served from. When set, {@link DiskStorageProvider.getSignedUrl}\n * returns `${publicBaseUrl}/${key}`; when omitted, signing is unsupported.\n */\n readonly publicBaseUrl?: string;\n\n constructor(init: { rootDir: string; publicBaseUrl?: string }) {\n this.rootDir = init.rootDir;\n this.publicBaseUrl = init.publicBaseUrl;\n }\n}\n\n/**\n * {@link StorageProvider} backed by the local filesystem rooted at a directory.\n *\n * Keys map to paths under the root; nested keys create intermediate directories\n * on write. User metadata is not persisted (the filesystem has no native slot),\n * and `getSignedUrl` requires a configured `publicBaseUrl`. Useful for local\n * development and as an in-process test double.\n */\n@Injectable()\nexport class DiskStorageProvider extends StorageProvider {\n constructor(private readonly options: DiskStorageProviderOptions) {\n super();\n }\n\n async write(key: string, body: Readable | Buffer | string, _options?: StorageWriteOptions): Promise<void> {\n const path = this.resolveKey(key);\n await mkdir(dirname(path), { recursive: true });\n const source = body instanceof Readable ? body : Readable.from(body instanceof Buffer ? body : Buffer.from(body));\n await pipeline(source, createWriteStream(path));\n }\n\n async read(key: string, options?: StorageReadOptions): Promise<Readable> {\n const path = this.resolveKey(key);\n await this.assertExists(key, path);\n // `start`/`end` map directly to fs's inclusive byte offsets.\n return options?.range ? createReadStream(path, { start: options.range.start, end: options.range.end }) : createReadStream(path);\n }\n\n async stat(key: string): Promise<StorageObjectMetadata> {\n const path = this.resolveKey(key);\n const stats = await this.assertExists(key, path);\n const contentType = mime.lookup(path);\n return {\n key,\n size: stats.size,\n contentType: contentType === false ? undefined : contentType,\n lastModified: DateTime.fromJSDate(stats.mtime),\n };\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n const stats = await fsStat(this.resolveKey(key));\n return stats.isFile();\n } catch (error) {\n if (isNotFound(error)) {\n return false;\n }\n if (isAccessDenied(error)) {\n throw new StorageAccessDeniedError(key, { cause: error });\n }\n throw error;\n }\n }\n\n async delete(key: string): Promise<void> {\n // `force: true` makes this idempotent — no error when the path is absent.\n await rm(this.resolveKey(key), { force: true });\n }\n\n async copy(sourceKey: string, destinationKey: string): Promise<void> {\n const source = this.resolveKey(sourceKey);\n const destination = this.resolveKey(destinationKey);\n await this.assertExists(sourceKey, source);\n await mkdir(dirname(destination), { recursive: true });\n await copyFile(source, destination);\n }\n\n async move(sourceKey: string, destinationKey: string): Promise<void> {\n const source = this.resolveKey(sourceKey);\n const destination = this.resolveKey(destinationKey);\n await this.assertExists(sourceKey, source);\n await mkdir(dirname(destination), { recursive: true });\n try {\n // Atomic and cheap on the same filesystem.\n await rename(source, destination);\n } catch (error) {\n // `rename` cannot cross filesystem boundaries — fall back to copy + delete.\n if (typeof error === 'object' && error !== null && (error as NodeJS.ErrnoException).code === 'EXDEV') {\n await copyFile(source, destination);\n await unlink(source);\n return;\n }\n throw error;\n }\n }\n\n /**\n * Lists objects under the root, optionally filtered by prefix.\n *\n * Note: this walks the entire directory tree and `stat`s every matching file\n * on each call — pagination slices an in-memory list, it does not make paging\n * cheaper. Fine for development and modest trees (its intended use); avoid\n * pointing it at very large directories.\n */\n async list(options?: StorageListOptions): Promise<StorageListResult> {\n const prefix = options?.prefix ?? '';\n const keys: string[] = [];\n await this.walk(this.options.rootDir, keys);\n const matched = keys.filter(key => key.startsWith(prefix)).sort();\n\n // Cursor is the last key returned by the previous page; resume after it.\n const start = options?.cursor ? matched.findIndex(key => key > options.cursor!) : 0;\n const from = start === -1 ? matched.length : start;\n const limit = options?.limit ?? matched.length;\n const page = matched.slice(from, from + limit);\n\n const objects = await Promise.all(page.map(key => this.stat(key)));\n const hasMore = from + limit < matched.length;\n return {\n objects,\n cursor: hasMore && page.length > 0 ? page[page.length - 1] : undefined,\n };\n }\n\n async getSignedUrl(key: string, _options: SignedUrlOptions): Promise<string> {\n if (!this.options.publicBaseUrl) {\n throw new StorageOperationNotSupportedError('getSignedUrl');\n }\n // Normalise the key against the root for traversal safety, then join to the base URL.\n this.resolveKey(key);\n // Encode each path segment so keys with spaces or reserved characters yield a valid URL.\n const encodedPath = key\n .replace(/^\\/+/, '')\n .split('/')\n .map(encodeURIComponent)\n .join('/');\n return `${this.options.publicBaseUrl.replace(/\\/+$/, '')}/${encodedPath}`;\n }\n\n /** Resolve a key to an absolute path, rejecting traversal that escapes the root. */\n private resolveKey(key: string): string {\n const path = resolve(this.options.rootDir, key);\n const rel = relative(this.options.rootDir, path);\n if (rel === '' || rel.startsWith('..') || rel.startsWith(`..${sep}`)) {\n throw new StorageOperationNotSupportedError(`key '${key}' resolves outside the storage root`);\n }\n return path;\n }\n\n private async assertExists(key: string, path: string) {\n try {\n const stats = await fsStat(path);\n if (!stats.isFile()) {\n throw new StorageObjectNotFoundError(key);\n }\n return stats;\n } catch (error) {\n if (isNotFound(error)) {\n throw new StorageObjectNotFoundError(key, { cause: error });\n }\n if (isAccessDenied(error)) {\n throw new StorageAccessDeniedError(key, { cause: error });\n }\n throw error;\n }\n }\n\n /** Recursively collect file keys (root-relative, `/`-separated) under `dir`. */\n private async walk(dir: string, into: string[]): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n await this.walk(full, into);\n } else if (entry.isFile()) {\n into.push(relative(this.options.rootDir, full).split(sep).join(posix.sep));\n }\n }\n }\n}\n\nfunction isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && (error as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nfunction isAccessDenied(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n const code = (error as NodeJS.ErrnoException).code;\n return code === 'EACCES' || code === 'EPERM';\n}\n"],"mappings":"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","names":["Injectable","createReadStream","createWriteStream","copyFile","mkdir","readdir","rename","rm","stat","fsStat","unlink","dirname","join","posix","relative","resolve","sep","pipeline","Readable","DateTime","mime","DiskStorageProviderOptions","rootDir","publicBaseUrl","init","DiskStorageProvider","StorageProvider","options","write","key","body","_options","path","resolveKey","mkdir","dirname","recursive","source","Readable","from","Buffer","pipeline","createWriteStream","read","assertExists","range","createReadStream","start","end","stat","stats","contentType","mime","lookup","size","undefined","lastModified","DateTime","fromJSDate","mtime","exists","fsStat","isFile","error","isNotFound","isAccessDenied","StorageAccessDeniedError","cause","delete","rm","force","copy","sourceKey","destinationKey","destination","copyFile","move","rename","code","unlink","list","prefix","keys","walk","matched","filter","startsWith","sort","cursor","findIndex","length","limit","page","slice","objects","Promise","all","map","hasMore","getSignedUrl","StorageOperationNotSupportedError","encodedPath","replace","split","encodeURIComponent","join","resolve","rel","relative","sep","StorageObjectNotFoundError","dir","into","entries","readdir","withFileTypes","entry","full","name","isDirectory","push","posix"]}
package/dist/s3.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './s3.storage.provider.js';
2
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../src/s3.ts"],"names":[],"mappings":"AAEA,cAAc,0BAA0B,CAAC"}