@btravstack/storage 0.36.0 → 0.38.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 +1 -1
- package/dist/index.cjs +49 -8
- package/dist/index.d.cts +4 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +49 -9
- package/dist/s3.cjs +44 -4
- package/dist/s3.d.cts +1 -1
- package/dist/s3.d.mts +1 -1
- package/dist/s3.mjs +45 -5
- package/dist/{storage-CRkwIhac.mjs → storage-BIKstQ72.mjs} +4 -2
- package/dist/{storage-BLJtJ7KF.d.cts → storage-BavPXZpA.d.cts} +45 -6
- package/dist/{storage-BLJtJ7KF.d.mts → storage-BavPXZpA.d.mts} +45 -6
- package/dist/{storage-QVPXOcO6.cjs → storage-EA671z1R.cjs} +9 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ which is this list's one detailed home.
|
|
|
121
121
|
## What it decides, and what it does not
|
|
122
122
|
|
|
123
123
|
**It decides** that a missing object is an ordinary answer rather than a fault
|
|
124
|
-
(counted `not_found`, logged at `info`), that `delete` is idempotent, that
|
|
124
|
+
(counted `not_found`, logged at `info`), that a held key under `putIfAbsent` is an ordinary answer (`ObjectExists`, counted `exists`), that `delete` is idempotent, that `copy` and `head` never move the bytes through the process, that
|
|
125
125
|
presigning asks the store nothing — and that an adapter which cannot presign
|
|
126
126
|
says so instead of minting a URL that would fail only in production.
|
|
127
127
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_storage = require("./storage-
|
|
2
|
+
const require_storage = require("./storage-EA671z1R.cjs");
|
|
3
|
+
let node_crypto = require("node:crypto");
|
|
3
4
|
let _btravstack_di = require("@btravstack/di");
|
|
4
5
|
let unthrown = require("unthrown");
|
|
5
6
|
let _btravstack_core = require("@btravstack/core");
|
|
@@ -11,6 +12,8 @@ let _btravstack_core = require("@btravstack/core");
|
|
|
11
12
|
* which would be the worst kind of double — one that passes locally and fails in
|
|
12
13
|
* the deployment for a reason no test could have shown.
|
|
13
14
|
*
|
|
15
|
+
* `kmsKeyId` is accepted and ignored: nothing is encrypted at rest here.
|
|
16
|
+
*
|
|
14
17
|
* ponytail: no size limit, so a process storing unbounded objects grows
|
|
15
18
|
* unbounded. The upgrade path is the S3 adapter.
|
|
16
19
|
*/
|
|
@@ -19,14 +22,42 @@ const memoryStorageBackend = () => {
|
|
|
19
22
|
return {
|
|
20
23
|
put: (key, bytes, options) => {
|
|
21
24
|
objects.set(key, {
|
|
22
|
-
bytes,
|
|
25
|
+
bytes: bytes.slice(),
|
|
26
|
+
contentType: options.contentType
|
|
27
|
+
});
|
|
28
|
+
return (0, unthrown.OkAsync)();
|
|
29
|
+
},
|
|
30
|
+
putIfAbsent: (key, bytes, options) => {
|
|
31
|
+
if (objects.has(key)) return (0, unthrown.ErrAsync)(new require_storage.ObjectExists({ key }));
|
|
32
|
+
objects.set(key, {
|
|
33
|
+
bytes: bytes.slice(),
|
|
34
|
+
contentType: options.contentType
|
|
35
|
+
});
|
|
36
|
+
return (0, unthrown.OkAsync)();
|
|
37
|
+
},
|
|
38
|
+
copy: (from, to, options) => {
|
|
39
|
+
const object = objects.get(from);
|
|
40
|
+
if (object === void 0) return (0, unthrown.ErrAsync)(new require_storage.ObjectNotFound({ key: from }));
|
|
41
|
+
objects.set(to, {
|
|
42
|
+
bytes: object.bytes.slice(),
|
|
23
43
|
contentType: options.contentType
|
|
24
44
|
});
|
|
25
45
|
return (0, unthrown.OkAsync)();
|
|
26
46
|
},
|
|
47
|
+
head: (key) => {
|
|
48
|
+
const object = objects.get(key);
|
|
49
|
+
return object === void 0 ? (0, unthrown.ErrAsync)(new require_storage.ObjectNotFound({ key })) : (0, unthrown.OkAsync)({
|
|
50
|
+
size: object.bytes.byteLength,
|
|
51
|
+
contentType: object.contentType,
|
|
52
|
+
sha256: (0, node_crypto.createHash)("sha256").update(object.bytes).digest("hex")
|
|
53
|
+
});
|
|
54
|
+
},
|
|
27
55
|
get: (key) => {
|
|
28
56
|
const object = objects.get(key);
|
|
29
|
-
return object === void 0 ? (0, unthrown.ErrAsync)(new require_storage.ObjectNotFound({ key })) : (0, unthrown.OkAsync)(
|
|
57
|
+
return object === void 0 ? (0, unthrown.ErrAsync)(new require_storage.ObjectNotFound({ key })) : (0, unthrown.OkAsync)({
|
|
58
|
+
...object,
|
|
59
|
+
bytes: object.bytes.slice()
|
|
60
|
+
});
|
|
30
61
|
},
|
|
31
62
|
delete: (key) => {
|
|
32
63
|
objects.delete(key);
|
|
@@ -59,17 +90,23 @@ const memoryStorage = () => (0, _btravstack_di.Module)("MemoryStorage")({
|
|
|
59
90
|
*/
|
|
60
91
|
const failed = (failure) => {
|
|
61
92
|
const cause = failure.tag === "Err" ? failure.error : failure.cause;
|
|
62
|
-
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "
|
|
93
|
+
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "ObjectExists" ? {
|
|
94
|
+
result: "exists",
|
|
95
|
+
reason: "an object already held the key"
|
|
96
|
+
} : failure.error._tag === "ObjectNotFound" ? {
|
|
97
|
+
result: "not_found",
|
|
98
|
+
reason: "the object was not there"
|
|
99
|
+
} : failure.error._tag === "PresignNotSupported" ? {
|
|
100
|
+
result: "not_found",
|
|
101
|
+
reason: "this store cannot mint a url"
|
|
102
|
+
} : void 0;
|
|
63
103
|
return ordinary === void 0 ? {
|
|
64
104
|
outcome: "error",
|
|
65
105
|
attributes: { result: "error" },
|
|
66
106
|
cause
|
|
67
107
|
} : {
|
|
68
108
|
outcome: "ok",
|
|
69
|
-
attributes:
|
|
70
|
-
result: "not_found",
|
|
71
|
-
reason: ordinary
|
|
72
|
-
}
|
|
109
|
+
attributes: ordinary
|
|
73
110
|
};
|
|
74
111
|
};
|
|
75
112
|
/** The KEY is a detail, not a dimension: on the span and the line, never on an instrument. */
|
|
@@ -90,7 +127,10 @@ const instrument = (backend, observers) => {
|
|
|
90
127
|
const observe = through(observers);
|
|
91
128
|
return {
|
|
92
129
|
put: (key, bytes, options) => observe("put", key, () => backend.put(key, bytes, options)),
|
|
130
|
+
putIfAbsent: (key, bytes, options) => observe("put_if_absent", key, () => backend.putIfAbsent(key, bytes, options)),
|
|
93
131
|
get: (key) => observe("get", key, () => backend.get(key)),
|
|
132
|
+
copy: (from, to, options) => observe("copy", from, () => backend.copy(from, to, options)),
|
|
133
|
+
head: (key) => observe("head", key, () => backend.head(key)),
|
|
94
134
|
delete: (key) => observe("delete", key, () => backend.delete(key)),
|
|
95
135
|
presignedUrl: (key, options) => observe("presigned_url", key, () => backend.presignedUrl(key, options)),
|
|
96
136
|
presignedUpload: (key, options) => observe("presigned_upload", key, () => backend.presignedUpload(key, options))
|
|
@@ -139,6 +179,7 @@ const storage = ({ adapter }) => {
|
|
|
139
179
|
});
|
|
140
180
|
};
|
|
141
181
|
//#endregion
|
|
182
|
+
exports.ObjectExists = require_storage.ObjectExists;
|
|
142
183
|
exports.ObjectNotFound = require_storage.ObjectNotFound;
|
|
143
184
|
exports.PresignNotSupported = require_storage.PresignNotSupported;
|
|
144
185
|
exports.Storage = require_storage.Storage;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Storage, c as StorageUnavailable, i as PresignNotSupported, l as StoredObject, n as ObjectHead, o as StorageBackend, r as ObjectNotFound, s as StorageService, t as ObjectExists, u as WriteOptions } from "./storage-BavPXZpA.cjs";
|
|
2
2
|
import { Module, Provider } from "@btravstack/di";
|
|
3
3
|
import { HealthChecks } from "@btravstack/core";
|
|
4
4
|
//#region src/memory.d.ts
|
|
@@ -9,6 +9,8 @@ import { HealthChecks } from "@btravstack/core";
|
|
|
9
9
|
* which would be the worst kind of double — one that passes locally and fails in
|
|
10
10
|
* the deployment for a reason no test could have shown.
|
|
11
11
|
*
|
|
12
|
+
* `kmsKeyId` is accepted and ignored: nothing is encrypted at rest here.
|
|
13
|
+
*
|
|
12
14
|
* ponytail: no size limit, so a process storing unbounded objects grows
|
|
13
15
|
* unbounded. The upgrade path is the S3 adapter.
|
|
14
16
|
*/
|
|
@@ -47,4 +49,4 @@ type StorageOptions<E, N> = {
|
|
|
47
49
|
*/
|
|
48
50
|
export declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
|
|
49
51
|
//#endregion
|
|
50
|
-
export { ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject };
|
|
52
|
+
export { ObjectExists, type ObjectHead, ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject, type WriteOptions };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Storage, c as StorageUnavailable, i as PresignNotSupported, l as StoredObject, n as ObjectHead, o as StorageBackend, r as ObjectNotFound, s as StorageService, t as ObjectExists, u as WriteOptions } from "./storage-BavPXZpA.mjs";
|
|
2
2
|
import { Module, Provider } from "@btravstack/di";
|
|
3
3
|
import { HealthChecks } from "@btravstack/core";
|
|
4
4
|
//#region src/memory.d.ts
|
|
@@ -9,6 +9,8 @@ import { HealthChecks } from "@btravstack/core";
|
|
|
9
9
|
* which would be the worst kind of double — one that passes locally and fails in
|
|
10
10
|
* the deployment for a reason no test could have shown.
|
|
11
11
|
*
|
|
12
|
+
* `kmsKeyId` is accepted and ignored: nothing is encrypted at rest here.
|
|
13
|
+
*
|
|
12
14
|
* ponytail: no size limit, so a process storing unbounded objects grows
|
|
13
15
|
* unbounded. The upgrade path is the S3 adapter.
|
|
14
16
|
*/
|
|
@@ -47,4 +49,4 @@ type StorageOptions<E, N> = {
|
|
|
47
49
|
*/
|
|
48
50
|
export declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
|
|
49
51
|
//#endregion
|
|
50
|
-
export { ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject };
|
|
52
|
+
export { ObjectExists, type ObjectHead, ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject, type WriteOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as StorageBackend, i as Storage, n as ObjectNotFound, o as StorageUnavailable, r as PresignNotSupported, t as ObjectExists } from "./storage-BIKstQ72.mjs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { Module, Provider } from "@btravstack/di";
|
|
3
4
|
import { Err, ErrAsync, Ok, OkAsync, P } from "unthrown";
|
|
4
5
|
import { HealthCheckFailed, HealthChecks, Observers, noObserverMember, observed } from "@btravstack/core";
|
|
@@ -10,6 +11,8 @@ import { HealthCheckFailed, HealthChecks, Observers, noObserverMember, observed
|
|
|
10
11
|
* which would be the worst kind of double — one that passes locally and fails in
|
|
11
12
|
* the deployment for a reason no test could have shown.
|
|
12
13
|
*
|
|
14
|
+
* `kmsKeyId` is accepted and ignored: nothing is encrypted at rest here.
|
|
15
|
+
*
|
|
13
16
|
* ponytail: no size limit, so a process storing unbounded objects grows
|
|
14
17
|
* unbounded. The upgrade path is the S3 adapter.
|
|
15
18
|
*/
|
|
@@ -18,14 +21,42 @@ const memoryStorageBackend = () => {
|
|
|
18
21
|
return {
|
|
19
22
|
put: (key, bytes, options) => {
|
|
20
23
|
objects.set(key, {
|
|
21
|
-
bytes,
|
|
24
|
+
bytes: bytes.slice(),
|
|
25
|
+
contentType: options.contentType
|
|
26
|
+
});
|
|
27
|
+
return OkAsync();
|
|
28
|
+
},
|
|
29
|
+
putIfAbsent: (key, bytes, options) => {
|
|
30
|
+
if (objects.has(key)) return ErrAsync(new ObjectExists({ key }));
|
|
31
|
+
objects.set(key, {
|
|
32
|
+
bytes: bytes.slice(),
|
|
33
|
+
contentType: options.contentType
|
|
34
|
+
});
|
|
35
|
+
return OkAsync();
|
|
36
|
+
},
|
|
37
|
+
copy: (from, to, options) => {
|
|
38
|
+
const object = objects.get(from);
|
|
39
|
+
if (object === void 0) return ErrAsync(new ObjectNotFound({ key: from }));
|
|
40
|
+
objects.set(to, {
|
|
41
|
+
bytes: object.bytes.slice(),
|
|
22
42
|
contentType: options.contentType
|
|
23
43
|
});
|
|
24
44
|
return OkAsync();
|
|
25
45
|
},
|
|
46
|
+
head: (key) => {
|
|
47
|
+
const object = objects.get(key);
|
|
48
|
+
return object === void 0 ? ErrAsync(new ObjectNotFound({ key })) : OkAsync({
|
|
49
|
+
size: object.bytes.byteLength,
|
|
50
|
+
contentType: object.contentType,
|
|
51
|
+
sha256: createHash("sha256").update(object.bytes).digest("hex")
|
|
52
|
+
});
|
|
53
|
+
},
|
|
26
54
|
get: (key) => {
|
|
27
55
|
const object = objects.get(key);
|
|
28
|
-
return object === void 0 ? ErrAsync(new ObjectNotFound({ key })) : OkAsync(
|
|
56
|
+
return object === void 0 ? ErrAsync(new ObjectNotFound({ key })) : OkAsync({
|
|
57
|
+
...object,
|
|
58
|
+
bytes: object.bytes.slice()
|
|
59
|
+
});
|
|
29
60
|
},
|
|
30
61
|
delete: (key) => {
|
|
31
62
|
objects.delete(key);
|
|
@@ -58,17 +89,23 @@ const memoryStorage = () => Module("MemoryStorage")({
|
|
|
58
89
|
*/
|
|
59
90
|
const failed = (failure) => {
|
|
60
91
|
const cause = failure.tag === "Err" ? failure.error : failure.cause;
|
|
61
|
-
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "
|
|
92
|
+
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "ObjectExists" ? {
|
|
93
|
+
result: "exists",
|
|
94
|
+
reason: "an object already held the key"
|
|
95
|
+
} : failure.error._tag === "ObjectNotFound" ? {
|
|
96
|
+
result: "not_found",
|
|
97
|
+
reason: "the object was not there"
|
|
98
|
+
} : failure.error._tag === "PresignNotSupported" ? {
|
|
99
|
+
result: "not_found",
|
|
100
|
+
reason: "this store cannot mint a url"
|
|
101
|
+
} : void 0;
|
|
62
102
|
return ordinary === void 0 ? {
|
|
63
103
|
outcome: "error",
|
|
64
104
|
attributes: { result: "error" },
|
|
65
105
|
cause
|
|
66
106
|
} : {
|
|
67
107
|
outcome: "ok",
|
|
68
|
-
attributes:
|
|
69
|
-
result: "not_found",
|
|
70
|
-
reason: ordinary
|
|
71
|
-
}
|
|
108
|
+
attributes: ordinary
|
|
72
109
|
};
|
|
73
110
|
};
|
|
74
111
|
/** The KEY is a detail, not a dimension: on the span and the line, never on an instrument. */
|
|
@@ -89,7 +126,10 @@ const instrument = (backend, observers) => {
|
|
|
89
126
|
const observe = through(observers);
|
|
90
127
|
return {
|
|
91
128
|
put: (key, bytes, options) => observe("put", key, () => backend.put(key, bytes, options)),
|
|
129
|
+
putIfAbsent: (key, bytes, options) => observe("put_if_absent", key, () => backend.putIfAbsent(key, bytes, options)),
|
|
92
130
|
get: (key) => observe("get", key, () => backend.get(key)),
|
|
131
|
+
copy: (from, to, options) => observe("copy", from, () => backend.copy(from, to, options)),
|
|
132
|
+
head: (key) => observe("head", key, () => backend.head(key)),
|
|
93
133
|
delete: (key) => observe("delete", key, () => backend.delete(key)),
|
|
94
134
|
presignedUrl: (key, options) => observe("presigned_url", key, () => backend.presignedUrl(key, options)),
|
|
95
135
|
presignedUpload: (key, options) => observe("presigned_upload", key, () => backend.presignedUpload(key, options))
|
|
@@ -138,4 +178,4 @@ const storage = ({ adapter }) => {
|
|
|
138
178
|
});
|
|
139
179
|
};
|
|
140
180
|
//#endregion
|
|
141
|
-
export { ObjectNotFound, PresignNotSupported, Storage, StorageBackend, StorageUnavailable, memoryStorage, memoryStorageBackend, memoryStorageProvider, storage };
|
|
181
|
+
export { ObjectExists, ObjectNotFound, PresignNotSupported, Storage, StorageBackend, StorageUnavailable, memoryStorage, memoryStorageBackend, memoryStorageProvider, storage };
|
package/dist/s3.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_storage = require("./storage-
|
|
2
|
+
const require_storage = require("./storage-EA671z1R.cjs");
|
|
3
3
|
let _btravstack_di = require("@btravstack/di");
|
|
4
4
|
let unthrown = require("unthrown");
|
|
5
5
|
let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
|
|
@@ -35,8 +35,18 @@ const unavailable = (operation, key, cause) => new require_storage.StorageUnavai
|
|
|
35
35
|
key,
|
|
36
36
|
reason: cause instanceof Error ? cause.message : String(cause)
|
|
37
37
|
});
|
|
38
|
+
/** S3's answer to `If-None-Match: *` on a key that is held: HTTP 412. */
|
|
39
|
+
const isPreconditionFailed = (cause) => cause?.$metadata?.httpStatusCode === 412;
|
|
38
40
|
/** A missing key is S3's `NoSuchKey`, which is a different fact from "the store is down". */
|
|
39
41
|
const isMissing = (cause) => cause instanceof Error && (cause.name === "NoSuchKey" || cause.name === "NotFound");
|
|
42
|
+
/** SSE-KMS parameters when a key is named; none otherwise, so the bucket's default applies. */
|
|
43
|
+
const encryption = (options) => options.kmsKeyId === void 0 ? {} : {
|
|
44
|
+
ServerSideEncryption: "aws:kms",
|
|
45
|
+
SSEKMSKeyId: options.kmsKeyId
|
|
46
|
+
};
|
|
47
|
+
/** S3 reports and takes checksums in base64; the port speaks hex. */
|
|
48
|
+
const hexToBase64 = (hex) => Buffer.from(hex, "hex").toString("base64");
|
|
49
|
+
const base64ToHex = (base64) => Buffer.from(base64, "base64").toString("hex");
|
|
40
50
|
/**
|
|
41
51
|
* The adapter's service over a client and a bucket. A delete of a key nobody
|
|
42
52
|
* stored answers `Ok`, which is S3's own idempotent behaviour rather than a
|
|
@@ -47,8 +57,17 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
47
57
|
Bucket: bucket,
|
|
48
58
|
Key: key,
|
|
49
59
|
Body: bytes,
|
|
50
|
-
ContentType: options.contentType
|
|
60
|
+
ContentType: options.contentType,
|
|
61
|
+
...encryption(options)
|
|
51
62
|
})), (cause) => unavailable("put", key, cause)).map(() => void 0),
|
|
63
|
+
putIfAbsent: (key, bytes, options) => (0, unthrown.fromPromise)(client.send(new _aws_sdk_client_s3.PutObjectCommand({
|
|
64
|
+
Bucket: bucket,
|
|
65
|
+
Key: key,
|
|
66
|
+
Body: bytes,
|
|
67
|
+
ContentType: options.contentType,
|
|
68
|
+
IfNoneMatch: "*",
|
|
69
|
+
...encryption(options)
|
|
70
|
+
})), (cause) => isPreconditionFailed(cause) ? new require_storage.ObjectExists({ key }) : unavailable("putIfAbsent", key, cause)).map(() => void 0),
|
|
52
71
|
get: (key) => (0, unthrown.fromPromise)(client.send(new _aws_sdk_client_s3.GetObjectCommand({
|
|
53
72
|
Bucket: bucket,
|
|
54
73
|
Key: key
|
|
@@ -56,6 +75,25 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
56
75
|
bytes: await response.Body.transformToByteArray(),
|
|
57
76
|
contentType: response.ContentType ?? "application/octet-stream"
|
|
58
77
|
})), (cause) => isMissing(cause) ? new require_storage.ObjectNotFound({ key }) : unavailable("get", key, cause)),
|
|
78
|
+
copy: (from, to, options) => (0, unthrown.fromPromise)((async () => client.send(new _aws_sdk_client_s3.CopyObjectCommand({
|
|
79
|
+
Bucket: bucket,
|
|
80
|
+
Key: to,
|
|
81
|
+
CopySource: `${bucket}/${from.split("/").map(encodeURIComponent).join("/")}`,
|
|
82
|
+
MetadataDirective: "REPLACE",
|
|
83
|
+
TaggingDirective: "REPLACE",
|
|
84
|
+
AnnotationDirective: "EXCLUDE",
|
|
85
|
+
ContentType: options.contentType,
|
|
86
|
+
...encryption(options)
|
|
87
|
+
})))(), (cause) => isMissing(cause) ? new require_storage.ObjectNotFound({ key: from }) : unavailable("copy", from, cause)).map(() => void 0),
|
|
88
|
+
head: (key) => (0, unthrown.fromPromise)(client.send(new _aws_sdk_client_s3.HeadObjectCommand({
|
|
89
|
+
Bucket: bucket,
|
|
90
|
+
Key: key,
|
|
91
|
+
ChecksumMode: "ENABLED"
|
|
92
|
+
})).then((response) => ({
|
|
93
|
+
size: response.ContentLength ?? 0,
|
|
94
|
+
contentType: response.ContentType ?? "application/octet-stream",
|
|
95
|
+
...response.ChecksumSHA256 === void 0 || response.ChecksumType === "COMPOSITE" ? {} : { sha256: base64ToHex(response.ChecksumSHA256) }
|
|
96
|
+
})), (cause) => isMissing(cause) ? new require_storage.ObjectNotFound({ key }) : unavailable("head", key, cause)),
|
|
59
97
|
delete: (key) => (0, unthrown.fromPromise)(client.send(new _aws_sdk_client_s3.DeleteObjectCommand({
|
|
60
98
|
Bucket: bucket,
|
|
61
99
|
Key: key
|
|
@@ -68,10 +106,12 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
68
106
|
Bucket: bucket,
|
|
69
107
|
Key: key,
|
|
70
108
|
ContentType: options.contentType,
|
|
71
|
-
ContentLength: options.contentLength
|
|
109
|
+
ContentLength: options.contentLength,
|
|
110
|
+
...options.sha256 === void 0 ? {} : { ChecksumSHA256: hexToBase64(options.sha256) }
|
|
72
111
|
}), {
|
|
73
112
|
expiresIn: Math.ceil(options.ttlMs / 1e3),
|
|
74
|
-
signableHeaders: /* @__PURE__ */ new Set(["content-type"])
|
|
113
|
+
signableHeaders: /* @__PURE__ */ new Set(["content-type"]),
|
|
114
|
+
...options.sha256 === void 0 ? {} : { unhoistableHeaders: /* @__PURE__ */ new Set(["x-amz-checksum-sha256"]) }
|
|
75
115
|
}), (cause) => unavailable("presignedUpload", key, cause))
|
|
76
116
|
});
|
|
77
117
|
/**
|
package/dist/s3.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as StorageBackend, s as StorageService } from "./storage-BavPXZpA.cjs";
|
|
2
2
|
import { Module, Scope } from "@btravstack/di";
|
|
3
3
|
import { S3Client } from "@aws-sdk/client-s3";
|
|
4
4
|
import { ConfigInvalid, EnvReading } from "@btravstack/config";
|
package/dist/s3.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as StorageBackend, s as StorageService } from "./storage-BavPXZpA.mjs";
|
|
2
2
|
import { Module, Scope } from "@btravstack/di";
|
|
3
3
|
import { S3Client } from "@aws-sdk/client-s3";
|
|
4
4
|
import { ConfigInvalid, EnvReading } from "@btravstack/config";
|
package/dist/s3.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as StorageBackend, n as ObjectNotFound, o as StorageUnavailable, t as ObjectExists } from "./storage-BIKstQ72.mjs";
|
|
2
2
|
import { Module, Port, Provider } from "@btravstack/di";
|
|
3
3
|
import { Ok, fromPromise } from "unthrown";
|
|
4
|
-
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
4
|
+
import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
5
5
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
6
6
|
import { Config, Env } from "@btravstack/config";
|
|
7
7
|
//#region src/s3.ts
|
|
@@ -34,8 +34,18 @@ const unavailable = (operation, key, cause) => new StorageUnavailable({
|
|
|
34
34
|
key,
|
|
35
35
|
reason: cause instanceof Error ? cause.message : String(cause)
|
|
36
36
|
});
|
|
37
|
+
/** S3's answer to `If-None-Match: *` on a key that is held: HTTP 412. */
|
|
38
|
+
const isPreconditionFailed = (cause) => cause?.$metadata?.httpStatusCode === 412;
|
|
37
39
|
/** A missing key is S3's `NoSuchKey`, which is a different fact from "the store is down". */
|
|
38
40
|
const isMissing = (cause) => cause instanceof Error && (cause.name === "NoSuchKey" || cause.name === "NotFound");
|
|
41
|
+
/** SSE-KMS parameters when a key is named; none otherwise, so the bucket's default applies. */
|
|
42
|
+
const encryption = (options) => options.kmsKeyId === void 0 ? {} : {
|
|
43
|
+
ServerSideEncryption: "aws:kms",
|
|
44
|
+
SSEKMSKeyId: options.kmsKeyId
|
|
45
|
+
};
|
|
46
|
+
/** S3 reports and takes checksums in base64; the port speaks hex. */
|
|
47
|
+
const hexToBase64 = (hex) => Buffer.from(hex, "hex").toString("base64");
|
|
48
|
+
const base64ToHex = (base64) => Buffer.from(base64, "base64").toString("hex");
|
|
39
49
|
/**
|
|
40
50
|
* The adapter's service over a client and a bucket. A delete of a key nobody
|
|
41
51
|
* stored answers `Ok`, which is S3's own idempotent behaviour rather than a
|
|
@@ -46,8 +56,17 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
46
56
|
Bucket: bucket,
|
|
47
57
|
Key: key,
|
|
48
58
|
Body: bytes,
|
|
49
|
-
ContentType: options.contentType
|
|
59
|
+
ContentType: options.contentType,
|
|
60
|
+
...encryption(options)
|
|
50
61
|
})), (cause) => unavailable("put", key, cause)).map(() => void 0),
|
|
62
|
+
putIfAbsent: (key, bytes, options) => fromPromise(client.send(new PutObjectCommand({
|
|
63
|
+
Bucket: bucket,
|
|
64
|
+
Key: key,
|
|
65
|
+
Body: bytes,
|
|
66
|
+
ContentType: options.contentType,
|
|
67
|
+
IfNoneMatch: "*",
|
|
68
|
+
...encryption(options)
|
|
69
|
+
})), (cause) => isPreconditionFailed(cause) ? new ObjectExists({ key }) : unavailable("putIfAbsent", key, cause)).map(() => void 0),
|
|
51
70
|
get: (key) => fromPromise(client.send(new GetObjectCommand({
|
|
52
71
|
Bucket: bucket,
|
|
53
72
|
Key: key
|
|
@@ -55,6 +74,25 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
55
74
|
bytes: await response.Body.transformToByteArray(),
|
|
56
75
|
contentType: response.ContentType ?? "application/octet-stream"
|
|
57
76
|
})), (cause) => isMissing(cause) ? new ObjectNotFound({ key }) : unavailable("get", key, cause)),
|
|
77
|
+
copy: (from, to, options) => fromPromise((async () => client.send(new CopyObjectCommand({
|
|
78
|
+
Bucket: bucket,
|
|
79
|
+
Key: to,
|
|
80
|
+
CopySource: `${bucket}/${from.split("/").map(encodeURIComponent).join("/")}`,
|
|
81
|
+
MetadataDirective: "REPLACE",
|
|
82
|
+
TaggingDirective: "REPLACE",
|
|
83
|
+
AnnotationDirective: "EXCLUDE",
|
|
84
|
+
ContentType: options.contentType,
|
|
85
|
+
...encryption(options)
|
|
86
|
+
})))(), (cause) => isMissing(cause) ? new ObjectNotFound({ key: from }) : unavailable("copy", from, cause)).map(() => void 0),
|
|
87
|
+
head: (key) => fromPromise(client.send(new HeadObjectCommand({
|
|
88
|
+
Bucket: bucket,
|
|
89
|
+
Key: key,
|
|
90
|
+
ChecksumMode: "ENABLED"
|
|
91
|
+
})).then((response) => ({
|
|
92
|
+
size: response.ContentLength ?? 0,
|
|
93
|
+
contentType: response.ContentType ?? "application/octet-stream",
|
|
94
|
+
...response.ChecksumSHA256 === void 0 || response.ChecksumType === "COMPOSITE" ? {} : { sha256: base64ToHex(response.ChecksumSHA256) }
|
|
95
|
+
})), (cause) => isMissing(cause) ? new ObjectNotFound({ key }) : unavailable("head", key, cause)),
|
|
58
96
|
delete: (key) => fromPromise(client.send(new DeleteObjectCommand({
|
|
59
97
|
Bucket: bucket,
|
|
60
98
|
Key: key
|
|
@@ -67,10 +105,12 @@ const s3StorageBackend = (client, bucket) => ({
|
|
|
67
105
|
Bucket: bucket,
|
|
68
106
|
Key: key,
|
|
69
107
|
ContentType: options.contentType,
|
|
70
|
-
ContentLength: options.contentLength
|
|
108
|
+
ContentLength: options.contentLength,
|
|
109
|
+
...options.sha256 === void 0 ? {} : { ChecksumSHA256: hexToBase64(options.sha256) }
|
|
71
110
|
}), {
|
|
72
111
|
expiresIn: Math.ceil(options.ttlMs / 1e3),
|
|
73
|
-
signableHeaders: /* @__PURE__ */ new Set(["content-type"])
|
|
112
|
+
signableHeaders: /* @__PURE__ */ new Set(["content-type"]),
|
|
113
|
+
...options.sha256 === void 0 ? {} : { unhoistableHeaders: /* @__PURE__ */ new Set(["x-amz-checksum-sha256"]) }
|
|
74
114
|
}), (cause) => unavailable("presignedUpload", key, cause))
|
|
75
115
|
});
|
|
76
116
|
/**
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Port } from "@btravstack/di";
|
|
2
2
|
import { TaggedError } from "unthrown";
|
|
3
3
|
//#region src/storage.ts
|
|
4
|
-
/** The key is not there. Only `get`
|
|
4
|
+
/** The key is not there. Only `get`, `head` and `copy` (its source) answer it — see `delete` and `presignedUrl` below. */
|
|
5
5
|
var ObjectNotFound = class extends TaggedError("ObjectNotFound") {};
|
|
6
|
+
/** An object already holds the key. Only `putIfAbsent` answers it. */
|
|
7
|
+
var ObjectExists = class extends TaggedError("ObjectExists") {};
|
|
6
8
|
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
7
9
|
var StorageUnavailable = class extends TaggedError("StorageUnavailable") {};
|
|
8
10
|
/** The adapter cannot mint a time-limited URL, and says so rather than pretending. */
|
|
@@ -22,4 +24,4 @@ var Storage = class extends Port("Storage") {};
|
|
|
22
24
|
/** The port every adapter provides, and the one an application never depends on. */
|
|
23
25
|
var StorageBackend = class extends Port("StorageBackend") {};
|
|
24
26
|
//#endregion
|
|
25
|
-
export {
|
|
27
|
+
export { StorageBackend as a, Storage as i, ObjectNotFound as n, StorageUnavailable as o, PresignNotSupported as r, ObjectExists as t };
|
|
@@ -6,14 +6,19 @@ type StoredObject = {
|
|
|
6
6
|
readonly contentType: string;
|
|
7
7
|
};
|
|
8
8
|
declare const ObjectNotFound_base: import("unthrown").TaggedErrorConstructor<"ObjectNotFound">;
|
|
9
|
-
/** The key is not there. Only `get`
|
|
9
|
+
/** The key is not there. Only `get`, `head` and `copy` (its source) answer it — see `delete` and `presignedUrl` below. */
|
|
10
10
|
declare class ObjectNotFound extends ObjectNotFound_base<{
|
|
11
11
|
readonly key: string;
|
|
12
12
|
}> {}
|
|
13
|
+
declare const ObjectExists_base: import("unthrown").TaggedErrorConstructor<"ObjectExists">;
|
|
14
|
+
/** An object already holds the key. Only `putIfAbsent` answers it. */
|
|
15
|
+
declare class ObjectExists extends ObjectExists_base<{
|
|
16
|
+
readonly key: string;
|
|
17
|
+
}> {}
|
|
13
18
|
declare const StorageUnavailable_base: import("unthrown").TaggedErrorConstructor<"StorageUnavailable">;
|
|
14
19
|
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
15
20
|
declare class StorageUnavailable extends StorageUnavailable_base<{
|
|
16
|
-
readonly operation: "put" | "get" | "delete" | "presignedUrl" | "presignedUpload";
|
|
21
|
+
readonly operation: "put" | "putIfAbsent" | "get" | "copy" | "head" | "delete" | "presignedUrl" | "presignedUpload";
|
|
17
22
|
readonly key: string;
|
|
18
23
|
readonly reason: string;
|
|
19
24
|
}> {}
|
|
@@ -22,11 +27,39 @@ declare const PresignNotSupported_base: import("unthrown").TaggedErrorConstructo
|
|
|
22
27
|
declare class PresignNotSupported extends PresignNotSupported_base<{
|
|
23
28
|
readonly key: string;
|
|
24
29
|
}> {}
|
|
30
|
+
/** What the store knows about an object without sending its bytes. */
|
|
31
|
+
type ObjectHead = {
|
|
32
|
+
readonly size: number;
|
|
33
|
+
readonly contentType: string;
|
|
34
|
+
/** Lowercase hex of the SHA-256 the store holds for the object; absent when none was supplied at write time. */
|
|
35
|
+
readonly sha256?: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Written with `kmsKeyId`, an object is encrypted at rest with that KMS key
|
|
39
|
+
* (SSE-KMS); without it, the store's own default applies.
|
|
40
|
+
*/
|
|
41
|
+
type WriteOptions = {
|
|
42
|
+
readonly contentType: string;
|
|
43
|
+
readonly kmsKeyId?: string;
|
|
44
|
+
};
|
|
25
45
|
type StorageService = {
|
|
26
|
-
readonly put: (key: string, bytes: Uint8Array, options:
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
readonly put: (key: string, bytes: Uint8Array, options: WriteOptions) => AsyncResult<void, StorageUnavailable>;
|
|
47
|
+
/** Writes the object only if no object holds the key: the first writer wins, atomically, in the store. */
|
|
48
|
+
readonly putIfAbsent: (key: string, bytes: Uint8Array, options: WriteOptions) => AsyncResult<void, ObjectExists | StorageUnavailable>;
|
|
29
49
|
readonly get: (key: string) => AsyncResult<StoredObject, ObjectNotFound | StorageUnavailable>;
|
|
50
|
+
/**
|
|
51
|
+
* Copies `from` to `to` inside the store: no bytes pass through this process.
|
|
52
|
+
* The copy takes `contentType` (the source's is replaced) and, with
|
|
53
|
+
* `kmsKeyId`, is encrypted with that key. An existing `to` is overwritten.
|
|
54
|
+
*/
|
|
55
|
+
readonly copy: (from: string, to: string, options: WriteOptions) => AsyncResult<void, ObjectNotFound | StorageUnavailable>;
|
|
56
|
+
/**
|
|
57
|
+
* Size, type and checksum of an object, without reading it. `sha256` is
|
|
58
|
+
* present only if the store holds one: S3 keeps it for an object written with
|
|
59
|
+
* a SHA-256 checksum (such as through `presignedUpload` with `sha256`), and
|
|
60
|
+
* reports it base64-encoded, which the adapter converts to hex.
|
|
61
|
+
*/
|
|
62
|
+
readonly head: (key: string) => AsyncResult<ObjectHead, ObjectNotFound | StorageUnavailable>;
|
|
30
63
|
/** Deleting a key nobody stored is `Ok`: delete is idempotent, which is S3's own behaviour rather than a fiction over it. */
|
|
31
64
|
readonly delete: (key: string) => AsyncResult<void, StorageUnavailable>;
|
|
32
65
|
/**
|
|
@@ -53,6 +86,11 @@ type StorageService = {
|
|
|
53
86
|
* length would allow an unbounded single write, and there is no ceiling a
|
|
54
87
|
* presigned PUT can express other than the exact number.
|
|
55
88
|
*
|
|
89
|
+
* **`sha256` (lowercase hex, 64 characters) is optional.** When given, the
|
|
90
|
+
* checksum header is signed too, so the store refuses any body that does not
|
|
91
|
+
* hash to it, and keeps it for `head`. The client must send the same value as
|
|
92
|
+
* `x-amz-checksum-sha256` (base64 of the digest).
|
|
93
|
+
*
|
|
56
94
|
* The application still decides every input: whether this caller may write
|
|
57
95
|
* this key, how long the URL lives, and what size it is willing to accept.
|
|
58
96
|
* The adapter computes a signature over that decision and nothing more.
|
|
@@ -61,6 +99,7 @@ type StorageService = {
|
|
|
61
99
|
readonly ttlMs: number;
|
|
62
100
|
readonly contentType: string;
|
|
63
101
|
readonly contentLength: number;
|
|
102
|
+
readonly sha256?: string;
|
|
64
103
|
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
65
104
|
};
|
|
66
105
|
declare const Storage_base: import("@btravstack/di").PortClass<"Storage">;
|
|
@@ -80,4 +119,4 @@ declare const StorageBackend_base: import("@btravstack/di").PortClass<"StorageBa
|
|
|
80
119
|
/** The port every adapter provides, and the one an application never depends on. */
|
|
81
120
|
declare class StorageBackend extends StorageBackend_base<StorageService> {}
|
|
82
121
|
//#endregion
|
|
83
|
-
export {
|
|
122
|
+
export { Storage as a, StorageUnavailable as c, PresignNotSupported as i, StoredObject as l, ObjectHead as n, StorageBackend as o, ObjectNotFound as r, StorageService as s, ObjectExists as t, WriteOptions as u };
|
|
@@ -6,14 +6,19 @@ type StoredObject = {
|
|
|
6
6
|
readonly contentType: string;
|
|
7
7
|
};
|
|
8
8
|
declare const ObjectNotFound_base: import("unthrown").TaggedErrorConstructor<"ObjectNotFound">;
|
|
9
|
-
/** The key is not there. Only `get`
|
|
9
|
+
/** The key is not there. Only `get`, `head` and `copy` (its source) answer it — see `delete` and `presignedUrl` below. */
|
|
10
10
|
declare class ObjectNotFound extends ObjectNotFound_base<{
|
|
11
11
|
readonly key: string;
|
|
12
12
|
}> {}
|
|
13
|
+
declare const ObjectExists_base: import("unthrown").TaggedErrorConstructor<"ObjectExists">;
|
|
14
|
+
/** An object already holds the key. Only `putIfAbsent` answers it. */
|
|
15
|
+
declare class ObjectExists extends ObjectExists_base<{
|
|
16
|
+
readonly key: string;
|
|
17
|
+
}> {}
|
|
13
18
|
declare const StorageUnavailable_base: import("unthrown").TaggedErrorConstructor<"StorageUnavailable">;
|
|
14
19
|
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
15
20
|
declare class StorageUnavailable extends StorageUnavailable_base<{
|
|
16
|
-
readonly operation: "put" | "get" | "delete" | "presignedUrl" | "presignedUpload";
|
|
21
|
+
readonly operation: "put" | "putIfAbsent" | "get" | "copy" | "head" | "delete" | "presignedUrl" | "presignedUpload";
|
|
17
22
|
readonly key: string;
|
|
18
23
|
readonly reason: string;
|
|
19
24
|
}> {}
|
|
@@ -22,11 +27,39 @@ declare const PresignNotSupported_base: import("unthrown").TaggedErrorConstructo
|
|
|
22
27
|
declare class PresignNotSupported extends PresignNotSupported_base<{
|
|
23
28
|
readonly key: string;
|
|
24
29
|
}> {}
|
|
30
|
+
/** What the store knows about an object without sending its bytes. */
|
|
31
|
+
type ObjectHead = {
|
|
32
|
+
readonly size: number;
|
|
33
|
+
readonly contentType: string;
|
|
34
|
+
/** Lowercase hex of the SHA-256 the store holds for the object; absent when none was supplied at write time. */
|
|
35
|
+
readonly sha256?: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Written with `kmsKeyId`, an object is encrypted at rest with that KMS key
|
|
39
|
+
* (SSE-KMS); without it, the store's own default applies.
|
|
40
|
+
*/
|
|
41
|
+
type WriteOptions = {
|
|
42
|
+
readonly contentType: string;
|
|
43
|
+
readonly kmsKeyId?: string;
|
|
44
|
+
};
|
|
25
45
|
type StorageService = {
|
|
26
|
-
readonly put: (key: string, bytes: Uint8Array, options:
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
readonly put: (key: string, bytes: Uint8Array, options: WriteOptions) => AsyncResult<void, StorageUnavailable>;
|
|
47
|
+
/** Writes the object only if no object holds the key: the first writer wins, atomically, in the store. */
|
|
48
|
+
readonly putIfAbsent: (key: string, bytes: Uint8Array, options: WriteOptions) => AsyncResult<void, ObjectExists | StorageUnavailable>;
|
|
29
49
|
readonly get: (key: string) => AsyncResult<StoredObject, ObjectNotFound | StorageUnavailable>;
|
|
50
|
+
/**
|
|
51
|
+
* Copies `from` to `to` inside the store: no bytes pass through this process.
|
|
52
|
+
* The copy takes `contentType` (the source's is replaced) and, with
|
|
53
|
+
* `kmsKeyId`, is encrypted with that key. An existing `to` is overwritten.
|
|
54
|
+
*/
|
|
55
|
+
readonly copy: (from: string, to: string, options: WriteOptions) => AsyncResult<void, ObjectNotFound | StorageUnavailable>;
|
|
56
|
+
/**
|
|
57
|
+
* Size, type and checksum of an object, without reading it. `sha256` is
|
|
58
|
+
* present only if the store holds one: S3 keeps it for an object written with
|
|
59
|
+
* a SHA-256 checksum (such as through `presignedUpload` with `sha256`), and
|
|
60
|
+
* reports it base64-encoded, which the adapter converts to hex.
|
|
61
|
+
*/
|
|
62
|
+
readonly head: (key: string) => AsyncResult<ObjectHead, ObjectNotFound | StorageUnavailable>;
|
|
30
63
|
/** Deleting a key nobody stored is `Ok`: delete is idempotent, which is S3's own behaviour rather than a fiction over it. */
|
|
31
64
|
readonly delete: (key: string) => AsyncResult<void, StorageUnavailable>;
|
|
32
65
|
/**
|
|
@@ -53,6 +86,11 @@ type StorageService = {
|
|
|
53
86
|
* length would allow an unbounded single write, and there is no ceiling a
|
|
54
87
|
* presigned PUT can express other than the exact number.
|
|
55
88
|
*
|
|
89
|
+
* **`sha256` (lowercase hex, 64 characters) is optional.** When given, the
|
|
90
|
+
* checksum header is signed too, so the store refuses any body that does not
|
|
91
|
+
* hash to it, and keeps it for `head`. The client must send the same value as
|
|
92
|
+
* `x-amz-checksum-sha256` (base64 of the digest).
|
|
93
|
+
*
|
|
56
94
|
* The application still decides every input: whether this caller may write
|
|
57
95
|
* this key, how long the URL lives, and what size it is willing to accept.
|
|
58
96
|
* The adapter computes a signature over that decision and nothing more.
|
|
@@ -61,6 +99,7 @@ type StorageService = {
|
|
|
61
99
|
readonly ttlMs: number;
|
|
62
100
|
readonly contentType: string;
|
|
63
101
|
readonly contentLength: number;
|
|
102
|
+
readonly sha256?: string;
|
|
64
103
|
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
65
104
|
};
|
|
66
105
|
declare const Storage_base: import("@btravstack/di").PortClass<"Storage">;
|
|
@@ -80,4 +119,4 @@ declare const StorageBackend_base: import("@btravstack/di").PortClass<"StorageBa
|
|
|
80
119
|
/** The port every adapter provides, and the one an application never depends on. */
|
|
81
120
|
declare class StorageBackend extends StorageBackend_base<StorageService> {}
|
|
82
121
|
//#endregion
|
|
83
|
-
export {
|
|
122
|
+
export { Storage as a, StorageUnavailable as c, PresignNotSupported as i, StoredObject as l, ObjectHead as n, StorageBackend as o, ObjectNotFound as r, StorageService as s, ObjectExists as t, WriteOptions as u };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
let _btravstack_di = require("@btravstack/di");
|
|
2
2
|
let unthrown = require("unthrown");
|
|
3
3
|
//#region src/storage.ts
|
|
4
|
-
/** The key is not there. Only `get`
|
|
4
|
+
/** The key is not there. Only `get`, `head` and `copy` (its source) answer it — see `delete` and `presignedUrl` below. */
|
|
5
5
|
var ObjectNotFound = class extends (0, unthrown.TaggedError)("ObjectNotFound") {};
|
|
6
|
+
/** An object already holds the key. Only `putIfAbsent` answers it. */
|
|
7
|
+
var ObjectExists = class extends (0, unthrown.TaggedError)("ObjectExists") {};
|
|
6
8
|
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
7
9
|
var StorageUnavailable = class extends (0, unthrown.TaggedError)("StorageUnavailable") {};
|
|
8
10
|
/** The adapter cannot mint a time-limited URL, and says so rather than pretending. */
|
|
@@ -22,6 +24,12 @@ var Storage = class extends (0, _btravstack_di.Port)("Storage") {};
|
|
|
22
24
|
/** The port every adapter provides, and the one an application never depends on. */
|
|
23
25
|
var StorageBackend = class extends (0, _btravstack_di.Port)("StorageBackend") {};
|
|
24
26
|
//#endregion
|
|
27
|
+
Object.defineProperty(exports, "ObjectExists", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function() {
|
|
30
|
+
return ObjectExists;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
25
33
|
Object.defineProperty(exports, "ObjectNotFound", {
|
|
26
34
|
enumerable: true,
|
|
27
35
|
get: function() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "A Storage port for @btravstack/core: an in-memory adapter, an S3-compatible adapter with presigned reads, and a composition that spans, counts and logs every operation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dependency-injection",
|
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@aws-sdk/client-s3": "3.1149.0",
|
|
57
57
|
"@aws-sdk/s3-request-presigner": "3.1149.0",
|
|
58
|
-
"@btravstack/config": "0.
|
|
59
|
-
"@btravstack/core": "0.
|
|
60
|
-
"@btravstack/di": "0.
|
|
58
|
+
"@btravstack/config": "0.38.0",
|
|
59
|
+
"@btravstack/core": "0.38.0",
|
|
60
|
+
"@btravstack/di": "0.38.0",
|
|
61
61
|
"@btravstack/internal-test-infra": "0.0.0",
|
|
62
|
-
"@btravstack/observability": "0.
|
|
62
|
+
"@btravstack/observability": "0.38.0",
|
|
63
63
|
"@btravstack/tsconfig": "0.4.0",
|
|
64
64
|
"@opentelemetry/api": "1.9.1",
|
|
65
65
|
"@opentelemetry/sdk-metrics": "2.12.0",
|
|
@@ -76,9 +76,9 @@
|
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@aws-sdk/client-s3": "^3.1000.0",
|
|
78
78
|
"@aws-sdk/s3-request-presigner": "^3.1000.0",
|
|
79
|
-
"@btravstack/config": "^0.
|
|
80
|
-
"@btravstack/core": "^0.
|
|
81
|
-
"@btravstack/di": "^0.
|
|
79
|
+
"@btravstack/config": "^0.38.0",
|
|
80
|
+
"@btravstack/core": "^0.38.0",
|
|
81
|
+
"@btravstack/di": "^0.38.0",
|
|
82
82
|
"unthrown": "^5.0.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|