@byline/storage-s3 3.17.1 → 3.19.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.
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import path from 'node:path';
9
9
  import { Readable } from 'node:stream';
10
- import { DeleteObjectCommand, S3Client, } from '@aws-sdk/client-s3';
10
+ import { CopyObjectCommand, DeleteObjectCommand, HeadObjectCommand, S3Client, } from '@aws-sdk/client-s3';
11
11
  import { Upload } from '@aws-sdk/lib-storage';
12
12
  import { v4 as uuidv4 } from 'uuid';
13
13
  // ---------------------------------------------------------------------------
@@ -128,6 +128,49 @@ class S3StorageProvider {
128
128
  Key: storagePath,
129
129
  }));
130
130
  }
131
+ async move(fromPath, toPath) {
132
+ // S3 has no native rename — copy to the new key, then delete the
133
+ // original. CopyObject throws NoSuchKey when the source is missing,
134
+ // satisfying the interface's "throws if the source does not exist".
135
+ // The default MetadataDirective (COPY) carries the source object's
136
+ // Content-Type, Cache-Control, and user metadata to the new key —
137
+ // exactly what a rename should do. ACLs are not copied by S3, so
138
+ // re-apply the configured canned ACL when one is set.
139
+ // Note: CopySource must be URL-encoded (keys may contain characters
140
+ // that are significant in the `<bucket>/<key>` source string).
141
+ await this.client.send(new CopyObjectCommand({
142
+ Bucket: this.bucket,
143
+ CopySource: encodeURIComponent(`${this.bucket}/${fromPath}`),
144
+ Key: toPath,
145
+ ...(this.acl ? { ACL: this.acl } : {}),
146
+ }));
147
+ await this.client.send(new DeleteObjectCommand({
148
+ Bucket: this.bucket,
149
+ Key: fromPath,
150
+ }));
151
+ return {
152
+ storageProvider: this.providerName,
153
+ storagePath: toPath,
154
+ storageUrl: this.getUrl(toPath),
155
+ };
156
+ }
157
+ async exists(storagePath) {
158
+ try {
159
+ await this.client.send(new HeadObjectCommand({
160
+ Bucket: this.bucket,
161
+ Key: storagePath,
162
+ }));
163
+ return true;
164
+ }
165
+ catch (err) {
166
+ const name = err.name;
167
+ const status = err.$metadata?.httpStatusCode;
168
+ if (name === 'NotFound' || name === 'NoSuchKey' || status === 404) {
169
+ return false;
170
+ }
171
+ throw err;
172
+ }
173
+ }
131
174
  getUrl(storagePath) {
132
175
  return `${this.publicUrl}/${storagePath}`;
133
176
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/storage-s3",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "3.17.1",
5
+ "version": "3.19.0",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -39,20 +39,20 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@aws-sdk/client-s3": "^3.1053.0",
43
- "@aws-sdk/lib-storage": "^3.1053.0",
44
- "uuid": "^14.0.0",
45
- "@byline/core": "3.17.1"
42
+ "@aws-sdk/client-s3": "^3.1081.0",
43
+ "@aws-sdk/lib-storage": "^3.1081.0",
44
+ "uuid": "^14.0.1",
45
+ "@byline/core": "3.19.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@biomejs/biome": "2.4.15",
49
- "@types/node": "^25.9.1",
48
+ "@biomejs/biome": "2.5.2",
49
+ "@types/node": "^26.1.0",
50
50
  "chokidar": "^5.0.0",
51
51
  "chokidar-cli": "^3.0.0",
52
52
  "npm-run-all": "^4.1.5",
53
- "tsc-alias": "^1.8.17",
54
- "tsx": "^4.22.3",
55
- "typescript": "6.0.3"
53
+ "tsc-alias": "^1.9.0",
54
+ "tsx": "^4.23.0",
55
+ "typescript": "^7.0.2"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"