@storyshelf/storage-s3 0.1.0 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Siddhant Gupta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.mts CHANGED
@@ -16,6 +16,7 @@ interface S3StorageOptions {
16
16
  /** Pre-configured S3 client. Defaults to a client built from the other options. */
17
17
  client?: S3Client;
18
18
  }
19
+ /** Join an optional key prefix with a storage path. */
19
20
  declare function s3Key(prefix: string, path: string): string;
20
21
  /**
21
22
  * Create an S3-compatible StorageAdapter (AWS S3, R2, MinIO, etc.).
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
5
5
  //#region src/index.ts
6
+ /** Join an optional key prefix with a storage path. */
6
7
  function s3Key(prefix, path) {
7
8
  return prefix === "" ? path : `${prefix}/${path}`;
8
9
  }
@@ -12,6 +13,32 @@ function s3Rel(prefix, key) {
12
13
  function isNotFound(error) {
13
14
  return error instanceof S3ServiceException && error.$metadata.httpStatusCode === 404;
14
15
  }
16
+ async function s3Read(ctx, path) {
17
+ const body = (await ctx.client.send(new GetObjectCommand({
18
+ Bucket: ctx.bucket,
19
+ Key: s3Key(ctx.prefix, path)
20
+ }))).Body;
21
+ if (body === void 0) return Buffer.alloc(0);
22
+ return Buffer.from(await body.transformToByteArray());
23
+ }
24
+ async function s3Exists(ctx, path) {
25
+ try {
26
+ await ctx.client.send(new HeadObjectCommand({
27
+ Bucket: ctx.bucket,
28
+ Key: s3Key(ctx.prefix, path)
29
+ }));
30
+ return true;
31
+ } catch (error) {
32
+ if (isNotFound(error)) return false;
33
+ throw error;
34
+ }
35
+ }
36
+ async function s3List(ctx, listPrefix) {
37
+ return ((await ctx.client.send(new ListObjectsV2Command({
38
+ Bucket: ctx.bucket,
39
+ Prefix: s3Key(ctx.prefix, listPrefix)
40
+ }))).Contents ?? []).map((item) => item.Key).filter((key) => key !== void 0).map((key) => s3Rel(ctx.prefix, key));
41
+ }
15
42
  /**
16
43
  * Create an S3-compatible StorageAdapter (AWS S3, R2, MinIO, etc.).
17
44
  *
@@ -25,20 +52,20 @@ function createS3Storage(options) {
25
52
  region,
26
53
  forcePathStyle: true
27
54
  });
55
+ const ctx = {
56
+ client,
57
+ bucket,
58
+ prefix
59
+ };
28
60
  return {
29
61
  metadata: {
30
62
  name: "S3 Storage",
31
- version: "0.1.0",
63
+ version: globalThis.__PKG_VERSION__ ?? "0.0.0",
32
64
  description: "S3-compatible storage adapter",
33
65
  kind: "s3"
34
66
  },
35
67
  async read(path) {
36
- const body = (await client.send(new GetObjectCommand({
37
- Bucket: bucket,
38
- Key: s3Key(prefix, path)
39
- }))).Body;
40
- if (body === void 0) return Buffer.alloc(0);
41
- return Buffer.from(await body.transformToByteArray());
68
+ return await s3Read(ctx, path);
42
69
  },
43
70
  async write(path, data) {
44
71
  await client.send(new PutObjectCommand({
@@ -54,22 +81,10 @@ function createS3Storage(options) {
54
81
  }));
55
82
  },
56
83
  async exists(path) {
57
- try {
58
- await client.send(new HeadObjectCommand({
59
- Bucket: bucket,
60
- Key: s3Key(prefix, path)
61
- }));
62
- return true;
63
- } catch (error) {
64
- if (isNotFound(error)) return false;
65
- throw error;
66
- }
84
+ return await s3Exists(ctx, path);
67
85
  },
68
86
  async list(listPrefix) {
69
- return ((await client.send(new ListObjectsV2Command({
70
- Bucket: bucket,
71
- Prefix: s3Key(prefix, listPrefix)
72
- }))).Contents ?? []).map((item) => item.Key).filter((key) => key !== void 0).map((key) => s3Rel(prefix, key));
87
+ return await s3List(ctx, listPrefix);
73
88
  }
74
89
  };
75
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n S3ServiceException,\n} from \"@aws-sdk/client-s3\";\n\nimport type { StorageAdapter } from \"@storyshelf/core/adapter/storage\";\n\ndeclare const __PKG_VERSION__: string;\n\n/** Options for configuring an S3-compatible storage adapter. */\nexport interface S3StorageOptions {\n /** S3 bucket name. */\n bucket: string;\n /** Optional key prefix applied to all stored objects. */\n prefix?: string;\n /** Custom endpoint for S3-compatible services (e.g. MinIO, R2). */\n endpoint?: string;\n /** AWS region. Defaults to `us-east-1`. */\n region?: string;\n /** Pre-configured S3 client. Defaults to a client built from the other options. */\n client?: S3Client;\n}\n\nexport function s3Key(prefix: string, path: string): string {\n return prefix === \"\" ? path : `${prefix}/${path}`;\n}\n\nfunction s3Rel(prefix: string, key: string): string {\n return prefix === \"\" ? key : key.slice(prefix.length + 1);\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof S3ServiceException && error.$metadata.httpStatusCode === 404;\n}\n\n/**\n * Create an S3-compatible StorageAdapter (AWS S3, R2, MinIO, etc.).\n *\n * @param options - S3 configuration options.\n * @returns A StorageAdapter backed by the configured S3 bucket.\n */\nexport function createS3Storage(options: S3StorageOptions): StorageAdapter {\n const { bucket, prefix = \"\", endpoint, region = \"us-east-1\", client: injectedClient } = options;\n const client = injectedClient ?? new S3Client({ endpoint, region, forcePathStyle: true });\n\n return {\n metadata: {\n name: \"S3 Storage\",\n version: typeof __PKG_VERSION__ === \"undefined\" ? \"0.0.0\" : __PKG_VERSION__, // oxlint-disable-line unicorn/no-typeof-undefined\n description: \"S3-compatible storage adapter\",\n kind: \"s3\",\n },\n async read(path) {\n const response = await client.send(\n new GetObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path) }),\n );\n const body = response.Body;\n if (body === undefined) {\n return Buffer.alloc(0);\n }\n return Buffer.from(await body.transformToByteArray());\n },\n async write(path, data) {\n await client.send(\n new PutObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path), Body: data }),\n );\n },\n async delete(path) {\n await client.send(\n new DeleteObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path) }),\n );\n },\n async exists(path) {\n try {\n await client.send(new HeadObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path) }));\n return true;\n } catch (error) {\n if (isNotFound(error)) {\n return false;\n }\n throw error;\n }\n },\n async list(listPrefix) {\n const response = await client.send(\n new ListObjectsV2Command({ Bucket: bucket, Prefix: s3Key(prefix, listPrefix) }),\n );\n return (response.Contents ?? [])\n .map((item) => item.Key)\n .filter((key): key is string => key !== undefined)\n .map((key) => s3Rel(prefix, key));\n },\n };\n}\n"],"mappings":";;;;;AA4BA,SAAgB,MAAM,QAAgB,MAAsB;CAC1D,OAAO,WAAW,KAAK,OAAO,GAAG,OAAO,GAAG;AAC7C;AAEA,SAAS,MAAM,QAAgB,KAAqB;CAClD,OAAO,WAAW,KAAK,MAAM,IAAI,MAAM,OAAO,SAAS,CAAC;AAC1D;AAEA,SAAS,WAAW,OAAyB;CAC3C,OAAO,iBAAiB,sBAAsB,MAAM,UAAU,mBAAmB;AACnF;;;;;;;AAQA,SAAgB,gBAAgB,SAA2C;CACzE,MAAM,EAAE,QAAQ,SAAS,IAAI,UAAU,SAAS,aAAa,QAAQ,mBAAmB;CACxF,MAAM,SAAS,kBAAkB,IAAI,SAAS;EAAE;EAAU;EAAQ,gBAAgB;CAAK,CAAC;CAExF,OAAO;EACL,UAAU;GACR,MAAM;GACN,SAAA;GACA,aAAa;GACb,MAAM;EACR;EACA,MAAM,KAAK,MAAM;GAIf,MAAM,QAAO,MAHU,OAAO,KAC5B,IAAI,iBAAiB;IAAE,QAAQ;IAAQ,KAAK,MAAM,QAAQ,IAAI;GAAE,CAAC,CACnE,EAAA,CACsB;GACtB,IAAI,SAAS,KAAA,GACX,OAAO,OAAO,MAAM,CAAC;GAEvB,OAAO,OAAO,KAAK,MAAM,KAAK,qBAAqB,CAAC;EACtD;EACA,MAAM,MAAM,MAAM,MAAM;GACtB,MAAM,OAAO,KACX,IAAI,iBAAiB;IAAE,QAAQ;IAAQ,KAAK,MAAM,QAAQ,IAAI;IAAG,MAAM;GAAK,CAAC,CAC/E;EACF;EACA,MAAM,OAAO,MAAM;GACjB,MAAM,OAAO,KACX,IAAI,oBAAoB;IAAE,QAAQ;IAAQ,KAAK,MAAM,QAAQ,IAAI;GAAE,CAAC,CACtE;EACF;EACA,MAAM,OAAO,MAAM;GACjB,IAAI;IACF,MAAM,OAAO,KAAK,IAAI,kBAAkB;KAAE,QAAQ;KAAQ,KAAK,MAAM,QAAQ,IAAI;IAAE,CAAC,CAAC;IACrF,OAAO;GACT,SAAS,OAAO;IACd,IAAI,WAAW,KAAK,GAClB,OAAO;IAET,MAAM;GACR;EACF;EACA,MAAM,KAAK,YAAY;GAIrB,SAAQ,MAHe,OAAO,KAC5B,IAAI,qBAAqB;IAAE,QAAQ;IAAQ,QAAQ,MAAM,QAAQ,UAAU;GAAE,CAAC,CAChF,EAAA,CACiB,YAAY,CAAC,EAAA,CAC3B,KAAK,SAAS,KAAK,GAAG,CAAC,CACvB,QAAQ,QAAuB,QAAQ,KAAA,CAAS,CAAC,CACjD,KAAK,QAAQ,MAAM,QAAQ,GAAG,CAAC;EACpC;CACF;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n DeleteObjectCommand,\n GetObjectCommand,\n HeadObjectCommand,\n ListObjectsV2Command,\n PutObjectCommand,\n S3Client,\n S3ServiceException,\n} from \"@aws-sdk/client-s3\";\n\nimport type { StorageAdapter } from \"@storyshelf/core/adapter/storage\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/** Options for configuring an S3-compatible storage adapter. */\nexport interface S3StorageOptions {\n /** S3 bucket name. */\n bucket: string;\n /** Optional key prefix applied to all stored objects. */\n prefix?: string;\n /** Custom endpoint for S3-compatible services (e.g. MinIO, R2). */\n endpoint?: string;\n /** AWS region. Defaults to `us-east-1`. */\n region?: string;\n /** Pre-configured S3 client. Defaults to a client built from the other options. */\n client?: S3Client;\n}\n\n/** Join an optional key prefix with a storage path. */\nexport function s3Key(prefix: string, path: string): string {\n return prefix === \"\" ? path : `${prefix}/${path}`;\n}\n\nfunction s3Rel(prefix: string, key: string): string {\n return prefix === \"\" ? key : key.slice(prefix.length + 1);\n}\n\nfunction isNotFound(error: unknown): boolean {\n return error instanceof S3ServiceException && error.$metadata.httpStatusCode === 404;\n}\n\ninterface S3Context {\n client: S3Client;\n bucket: string;\n prefix: string;\n}\n\nasync function s3Read(ctx: S3Context, path: string): Promise<Buffer> {\n const response = await ctx.client.send(\n new GetObjectCommand({ Bucket: ctx.bucket, Key: s3Key(ctx.prefix, path) }),\n );\n const body = response.Body;\n if (body === undefined) {\n return Buffer.alloc(0);\n }\n return Buffer.from(await body.transformToByteArray());\n}\n\nasync function s3Exists(ctx: S3Context, path: string): Promise<boolean> {\n try {\n await ctx.client.send(new HeadObjectCommand({ Bucket: ctx.bucket, Key: s3Key(ctx.prefix, path) }));\n return true;\n } catch (error) {\n if (isNotFound(error)) {\n return false;\n }\n throw error;\n }\n}\n\nasync function s3List(ctx: S3Context, listPrefix: string): Promise<string[]> {\n const response = await ctx.client.send(\n new ListObjectsV2Command({ Bucket: ctx.bucket, Prefix: s3Key(ctx.prefix, listPrefix) }),\n );\n return (response.Contents ?? [])\n .map((item) => item.Key)\n .filter((key): key is string => key !== undefined)\n .map((key) => s3Rel(ctx.prefix, key));\n}\n\n/**\n * Create an S3-compatible StorageAdapter (AWS S3, R2, MinIO, etc.).\n *\n * @param options - S3 configuration options.\n * @returns A StorageAdapter backed by the configured S3 bucket.\n */\nexport function createS3Storage(options: S3StorageOptions): StorageAdapter {\n const { bucket, prefix = \"\", endpoint, region = \"us-east-1\", client: injectedClient } = options;\n const client = injectedClient ?? new S3Client({ endpoint, region, forcePathStyle: true });\n const ctx: S3Context = { client, bucket, prefix };\n\n return {\n metadata: {\n name: \"S3 Storage\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"S3-compatible storage adapter\",\n kind: \"s3\",\n },\n async read(path) {\n return await s3Read(ctx, path);\n },\n async write(path, data) {\n await client.send(\n new PutObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path), Body: data }),\n );\n },\n async delete(path) {\n await client.send(\n new DeleteObjectCommand({ Bucket: bucket, Key: s3Key(prefix, path) }),\n );\n },\n async exists(path) {\n return await s3Exists(ctx, path);\n },\n async list(listPrefix) {\n return await s3List(ctx, listPrefix);\n },\n };\n}\n"],"mappings":";;;;;;AA6BA,SAAgB,MAAM,QAAgB,MAAsB;CAC1D,OAAO,WAAW,KAAK,OAAO,GAAG,OAAO,GAAG;AAC7C;AAEA,SAAS,MAAM,QAAgB,KAAqB;CAClD,OAAO,WAAW,KAAK,MAAM,IAAI,MAAM,OAAO,SAAS,CAAC;AAC1D;AAEA,SAAS,WAAW,OAAyB;CAC3C,OAAO,iBAAiB,sBAAsB,MAAM,UAAU,mBAAmB;AACnF;AAQA,eAAe,OAAO,KAAgB,MAA+B;CAInE,MAAM,QAAO,MAHU,IAAI,OAAO,KAChC,IAAI,iBAAiB;EAAE,QAAQ,IAAI;EAAQ,KAAK,MAAM,IAAI,QAAQ,IAAI;CAAE,CAAC,CAC3E,EAAA,CACsB;CACtB,IAAI,SAAS,KAAA,GACX,OAAO,OAAO,MAAM,CAAC;CAEvB,OAAO,OAAO,KAAK,MAAM,KAAK,qBAAqB,CAAC;AACtD;AAEA,eAAe,SAAS,KAAgB,MAAgC;CACtE,IAAI;EACF,MAAM,IAAI,OAAO,KAAK,IAAI,kBAAkB;GAAE,QAAQ,IAAI;GAAQ,KAAK,MAAM,IAAI,QAAQ,IAAI;EAAE,CAAC,CAAC;EACjG,OAAO;CACT,SAAS,OAAO;EACd,IAAI,WAAW,KAAK,GAClB,OAAO;EAET,MAAM;CACR;AACF;AAEA,eAAe,OAAO,KAAgB,YAAuC;CAI3E,SAAQ,MAHe,IAAI,OAAO,KAChC,IAAI,qBAAqB;EAAE,QAAQ,IAAI;EAAQ,QAAQ,MAAM,IAAI,QAAQ,UAAU;CAAE,CAAC,CACxF,EAAA,CACiB,YAAY,CAAC,EAAA,CAC3B,KAAK,SAAS,KAAK,GAAG,CAAC,CACvB,QAAQ,QAAuB,QAAQ,KAAA,CAAS,CAAC,CACjD,KAAK,QAAQ,MAAM,IAAI,QAAQ,GAAG,CAAC;AACxC;;;;;;;AAQA,SAAgB,gBAAgB,SAA2C;CACzE,MAAM,EAAE,QAAQ,SAAS,IAAI,UAAU,SAAS,aAAa,QAAQ,mBAAmB;CACxF,MAAM,SAAS,kBAAkB,IAAI,SAAS;EAAE;EAAU;EAAQ,gBAAgB;CAAK,CAAC;CACxF,MAAM,MAAiB;EAAE;EAAQ;EAAQ;CAAO;CAEhD,OAAO;EACL,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;EACR;EACA,MAAM,KAAK,MAAM;GACf,OAAO,MAAM,OAAO,KAAK,IAAI;EAC/B;EACA,MAAM,MAAM,MAAM,MAAM;GACtB,MAAM,OAAO,KACX,IAAI,iBAAiB;IAAE,QAAQ;IAAQ,KAAK,MAAM,QAAQ,IAAI;IAAG,MAAM;GAAK,CAAC,CAC/E;EACF;EACA,MAAM,OAAO,MAAM;GACjB,MAAM,OAAO,KACX,IAAI,oBAAoB;IAAE,QAAQ;IAAQ,KAAK,MAAM,QAAQ,IAAI;GAAE,CAAC,CACtE;EACF;EACA,MAAM,OAAO,MAAM;GACjB,OAAO,MAAM,SAAS,KAAK,IAAI;EACjC;EACA,MAAM,KAAK,YAAY;GACrB,OAAO,MAAM,OAAO,KAAK,UAAU;EACrC;CACF;AACF"}
package/package.json CHANGED
@@ -1,44 +1,52 @@
1
1
  {
2
2
  "name": "@storyshelf/storage-s3",
3
- "version": "0.1.0",
4
- "type": "module",
3
+ "version": "0.1.2",
5
4
  "description": "S3-compatible storage adapter for StoryShelf (AWS SDK v3).",
5
+ "homepage": "https://github.com/GuptaSiddhant/StoryShelf#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/GuptaSiddhant/StoryShelf/issues"
8
+ },
9
+ "license": "MIT",
6
10
  "author": {
7
11
  "name": "Siddhant Gupta",
8
12
  "url": "https://guptasiddhant.com"
9
13
  },
10
- "license": "MIT",
11
- "sideEffects": false,
12
14
  "repository": {
13
15
  "type": "git",
14
- "url": "git+https://github.com/GuptaSiddhant/storyshelf.git",
16
+ "url": "git+https://github.com/GuptaSiddhant/StoryShelf.git",
15
17
  "directory": "packages/storage-s3"
16
18
  },
17
- "homepage": "https://github.com/GuptaSiddhant/storyshelf#readme",
18
- "bugs": {
19
- "url": "https://github.com/GuptaSiddhant/storyshelf/issues"
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "type": "module",
23
+ "sideEffects": false,
24
+ "types": "./dist/index.d.mts",
25
+ "exports": {
26
+ ".": {
27
+ "source": "./src/index.ts",
28
+ "default": "./dist/index.mjs"
29
+ },
30
+ "./package.json": "./package.json"
20
31
  },
21
32
  "publishConfig": {
22
- "access": "public",
23
33
  "exports": {
24
34
  ".": "./dist/index.mjs",
25
35
  "./package.json": "./package.json"
26
- }
36
+ },
37
+ "access": "public"
27
38
  },
28
- "files": [
29
- "dist"
30
- ],
31
39
  "scripts": {
32
40
  "build": "tsdown",
33
41
  "dev": "tsdown -w",
34
- "fmt": "oxfmt -c ../../.oxfmtrc.json ./src",
42
+ "fmt": "oxfmt ./src",
35
43
  "lint": "oxlint --type-aware --type-check ./src",
36
44
  "test": "vitest run",
37
45
  "prepublishOnly": "nub run build"
38
46
  },
39
47
  "dependencies": {
40
48
  "@aws-sdk/client-s3": "^3.700.0",
41
- "@storyshelf/core": "workspace:*"
49
+ "@storyshelf/core": "^0.1.2"
42
50
  },
43
51
  "devDependencies": {
44
52
  "@types/node": "catalog:",
@@ -50,12 +58,13 @@
50
58
  "typescript": "catalog:",
51
59
  "vitest": "catalog:"
52
60
  },
53
- "types": "./dist/index.d.mts",
54
- "exports": {
55
- ".": {
56
- "source": "./src/index.ts",
57
- "default": "./dist/index.mjs"
58
- },
59
- "./package.json": "./package.json"
61
+ "jsr": {
62
+ "runtimeCompat": {
63
+ "node": true,
64
+ "deno": true,
65
+ "browser": false,
66
+ "workerd": false,
67
+ "bun": true
68
+ }
60
69
  }
61
70
  }