@michaelthielemann/kestrel-blobstore-s3 5.2.0 → 5.4.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/README.md +18 -0
- package/dist/impl.js +6 -5
- package/dist/module.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -19,3 +19,21 @@ is missing. Once the SDK's own `maxAttempts` are spent, `put`/`get`/`remove`/`mo
|
|
|
19
19
|
`TimeoutError`/`NetworkingError`/`AbortError`, or a Node `code` of
|
|
20
20
|
`ECONNRESET`/`ECONNREFUSED`/`ETIMEDOUT`/`EPIPE`/`EAI_AGAIN`. Everything else (an invalid key,
|
|
21
21
|
`AccessDenied`, a misconfigured bucket) throws — it is a wiring bug, not an expected failure.
|
|
22
|
+
|
|
23
|
+
<!-- kestrel-docs:start -->
|
|
24
|
+
## Generated from the manifest
|
|
25
|
+
`@michaelthielemann/kestrel-blobstore-s3` – module `blobstore/s3`: provides `blobstore@1`.
|
|
26
|
+
|
|
27
|
+
| Config | Type | Required | Default |
|
|
28
|
+
|---|---|---|---|
|
|
29
|
+
| `bucket` | string | yes | – |
|
|
30
|
+
| `prefix` | string | no | `""` |
|
|
31
|
+
| `region` | string | no | – |
|
|
32
|
+
| `endpoint` | string | no | – |
|
|
33
|
+
| `forcePathStyle` | boolean | no | – |
|
|
34
|
+
| `accessKeyId` | string | no | *(secret)* |
|
|
35
|
+
| `secretAccessKey` | string | no | *(secret)* |
|
|
36
|
+
| `timeoutMs` | integer | no | `10000` |
|
|
37
|
+
| `maxAttempts` | integer | no | `3` |
|
|
38
|
+
|
|
39
|
+
<!-- kestrel-docs:end -->
|
package/dist/impl.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, ListObjectsV2Command, NoSuchKey, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
2
|
+
import { boundaryCast } from "@michaelthielemann/kestrel/cast";
|
|
2
3
|
import { failure } from "@michaelthielemann/kestrel/errors";
|
|
3
4
|
import { err, ok } from "@michaelthielemann/kestrel/result";
|
|
4
5
|
export const DEFAULT_TIMEOUT_MS = 10000;
|
|
@@ -27,7 +28,7 @@ const TRANSIENT_NAMES = new Set(["TimeoutError", "NetworkingError", "AbortError"
|
|
|
27
28
|
const TRANSIENT_CODES = new Set(["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "EPIPE", "EAI_AGAIN"]);
|
|
28
29
|
/** After the SDK's own `maxAttempts` are spent, this is the only line separating "retry the caller" from "the operation is broken". */
|
|
29
30
|
function isTransient(cause) {
|
|
30
|
-
const e = cause;
|
|
31
|
+
const e = boundaryCast(cause, "host");
|
|
31
32
|
const status = e.$metadata?.httpStatusCode;
|
|
32
33
|
if (status !== undefined && (status >= 500 || status === 429))
|
|
33
34
|
return true;
|
|
@@ -63,13 +64,13 @@ export function createBlobstoreS3(config, client = createClient(config)) {
|
|
|
63
64
|
},
|
|
64
65
|
async get(key) {
|
|
65
66
|
try {
|
|
66
|
-
const out = (await client.send(new GetObjectCommand({ Bucket: config.bucket, Key: fullKey(key) })));
|
|
67
|
+
const out = boundaryCast(await client.send(new GetObjectCommand({ Bucket: config.bucket, Key: fullKey(key) })), "host");
|
|
67
68
|
if (!out.Body)
|
|
68
69
|
return ok(null);
|
|
69
70
|
return ok(await out.Body.transformToByteArray());
|
|
70
71
|
}
|
|
71
72
|
catch (cause) {
|
|
72
|
-
if (cause instanceof NoSuchKey || cause.name === "NoSuchKey")
|
|
73
|
+
if (cause instanceof NoSuchKey || boundaryCast(cause, "host").name === "NoSuchKey")
|
|
73
74
|
return ok(null);
|
|
74
75
|
if (isTransient(cause))
|
|
75
76
|
return err(failure("TRANSIENT", `blobstore/s3: get ${JSON.stringify(key)} failed`, { cause }));
|
|
@@ -95,7 +96,7 @@ export function createBlobstoreS3(config, client = createClient(config)) {
|
|
|
95
96
|
await client.send(new CopyObjectCommand({ Bucket: config.bucket, Key: fullKey(to), CopySource: copySource, MetadataDirective: "COPY" }));
|
|
96
97
|
}
|
|
97
98
|
catch (cause) {
|
|
98
|
-
const info = cause;
|
|
99
|
+
const info = boundaryCast(cause, "host");
|
|
99
100
|
if (info.name === "NoSuchKey" || info.$metadata?.httpStatusCode === 404)
|
|
100
101
|
return err(failure("NOT_FOUND", `blobstore/s3: ${JSON.stringify(from)} not found`, { cause }));
|
|
101
102
|
if (isTransient(cause))
|
|
@@ -117,7 +118,7 @@ export function createBlobstoreS3(config, client = createClient(config)) {
|
|
|
117
118
|
let token;
|
|
118
119
|
try {
|
|
119
120
|
do {
|
|
120
|
-
const page = (await client.send(new ListObjectsV2Command({ Bucket: config.bucket, Prefix: config.prefix + prefix, ContinuationToken: token })));
|
|
121
|
+
const page = boundaryCast(await client.send(new ListObjectsV2Command({ Bucket: config.bucket, Prefix: config.prefix + prefix, ContinuationToken: token })), "host");
|
|
121
122
|
for (const item of page.Contents ?? []) {
|
|
122
123
|
if (item.Key === undefined)
|
|
123
124
|
continue;
|
package/dist/module.js
CHANGED
|
@@ -9,8 +9,8 @@ export const configSchema = z
|
|
|
9
9
|
region: z.string().min(1).optional(),
|
|
10
10
|
endpoint: z.string().url().optional(),
|
|
11
11
|
forcePathStyle: z.boolean().optional(),
|
|
12
|
-
accessKeyId: z.string().min(1).optional(),
|
|
13
|
-
secretAccessKey: z.string().min(1).optional(),
|
|
12
|
+
accessKeyId: z.string().min(1).describe("secret").optional(),
|
|
13
|
+
secretAccessKey: z.string().min(1).describe("secret").optional(),
|
|
14
14
|
timeoutMs: z.number().int().positive().default(DEFAULT_TIMEOUT_MS),
|
|
15
15
|
maxAttempts: z.number().int().min(1).default(DEFAULT_MAX_ATTEMPTS),
|
|
16
16
|
})
|