@btravstack/storage 0.37.0 → 0.39.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 +12 -9
- package/dist/index.cjs +79 -30
- package/dist/index.d.cts +4 -2
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +64 -17
- package/dist/{storage-DlAQXJ1t.cjs → options-BhML_i5p.cjs} +36 -1
- package/dist/{storage-BmbyYPIE.mjs → options-DYPXkWb9.mjs} +20 -3
- package/dist/s3.cjs +87 -27
- package/dist/s3.d.cts +1 -1
- package/dist/s3.d.mts +1 -1
- package/dist/s3.mjs +84 -24
- package/dist/storage-CfN3KqWm.d.cts +154 -0
- package/dist/storage-CfN3KqWm.d.mts +154 -0
- package/package.json +8 -8
- package/dist/storage-uSBWFyB2.d.cts +0 -92
- package/dist/storage-uSBWFyB2.d.mts +0 -92
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.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.39.0",
|
|
59
|
+
"@btravstack/core": "0.39.0",
|
|
60
|
+
"@btravstack/di": "0.39.0",
|
|
61
61
|
"@btravstack/internal-test-infra": "0.0.0",
|
|
62
|
-
"@btravstack/observability": "0.
|
|
62
|
+
"@btravstack/observability": "0.39.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.39.0",
|
|
80
|
+
"@btravstack/core": "^0.39.0",
|
|
81
|
+
"@btravstack/di": "^0.39.0",
|
|
82
82
|
"unthrown": "^5.0.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { AsyncResult } from "unthrown";
|
|
2
|
-
//#region src/storage.d.ts
|
|
3
|
-
/** One stored object: its bytes and what they are. */
|
|
4
|
-
type StoredObject = {
|
|
5
|
-
readonly bytes: Uint8Array;
|
|
6
|
-
readonly contentType: string;
|
|
7
|
-
};
|
|
8
|
-
declare const ObjectNotFound_base: import("unthrown").TaggedErrorConstructor<"ObjectNotFound">;
|
|
9
|
-
/** The key is not there. Only `get` answers it — see `delete` and `presignedUrl` below. */
|
|
10
|
-
declare class ObjectNotFound extends ObjectNotFound_base<{
|
|
11
|
-
readonly key: string;
|
|
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
|
-
}> {}
|
|
18
|
-
declare const StorageUnavailable_base: import("unthrown").TaggedErrorConstructor<"StorageUnavailable">;
|
|
19
|
-
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
20
|
-
declare class StorageUnavailable extends StorageUnavailable_base<{
|
|
21
|
-
readonly operation: "put" | "putIfAbsent" | "get" | "delete" | "presignedUrl" | "presignedUpload";
|
|
22
|
-
readonly key: string;
|
|
23
|
-
readonly reason: string;
|
|
24
|
-
}> {}
|
|
25
|
-
declare const PresignNotSupported_base: import("unthrown").TaggedErrorConstructor<"PresignNotSupported">;
|
|
26
|
-
/** The adapter cannot mint a time-limited URL, and says so rather than pretending. */
|
|
27
|
-
declare class PresignNotSupported extends PresignNotSupported_base<{
|
|
28
|
-
readonly key: string;
|
|
29
|
-
}> {}
|
|
30
|
-
type StorageService = {
|
|
31
|
-
readonly put: (key: string, bytes: Uint8Array, options: {
|
|
32
|
-
readonly contentType: string;
|
|
33
|
-
}) => AsyncResult<void, StorageUnavailable>;
|
|
34
|
-
/** Writes the object only if no object holds the key: the first writer wins, atomically, in the store. */
|
|
35
|
-
readonly putIfAbsent: (key: string, bytes: Uint8Array, options: {
|
|
36
|
-
readonly contentType: string;
|
|
37
|
-
}) => AsyncResult<void, ObjectExists | StorageUnavailable>;
|
|
38
|
-
readonly get: (key: string) => AsyncResult<StoredObject, ObjectNotFound | StorageUnavailable>;
|
|
39
|
-
/** Deleting a key nobody stored is `Ok`: delete is idempotent, which is S3's own behaviour rather than a fiction over it. */
|
|
40
|
-
readonly delete: (key: string) => AsyncResult<void, StorageUnavailable>;
|
|
41
|
-
/**
|
|
42
|
-
* A URL that reads the object for `ttlMs`, without the caller holding
|
|
43
|
-
* credentials.
|
|
44
|
-
*
|
|
45
|
-
* There is **no `ObjectNotFound` arm**: presigning is a signature computation
|
|
46
|
-
* that asks the store nothing, so a URL for an absent key is minted happily
|
|
47
|
-
* and 404s when followed. Checking would cost a HEAD per call.
|
|
48
|
-
*/
|
|
49
|
-
readonly presignedUrl: (key: string, options: {
|
|
50
|
-
readonly ttlMs: number;
|
|
51
|
-
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
52
|
-
/**
|
|
53
|
-
* A URL that writes the object for `ttlMs`, without the caller holding
|
|
54
|
-
* credentials — so the bytes go from the client to the store and never
|
|
55
|
-
* through this process.
|
|
56
|
-
*
|
|
57
|
-
* **`contentType` and `contentLength` are signed, not advisory.** They are
|
|
58
|
-
* part of the signature, so a client sending different ones is refused by the
|
|
59
|
-
* store. The same URL can write the key repeatedly until expiry, replacing
|
|
60
|
-
* the previous object with different bytes of that size and type. Neither
|
|
61
|
-
* signed header proves the bytes. `contentLength` is required — an unsigned
|
|
62
|
-
* length would allow an unbounded single write, and there is no ceiling a
|
|
63
|
-
* presigned PUT can express other than the exact number.
|
|
64
|
-
*
|
|
65
|
-
* The application still decides every input: whether this caller may write
|
|
66
|
-
* this key, how long the URL lives, and what size it is willing to accept.
|
|
67
|
-
* The adapter computes a signature over that decision and nothing more.
|
|
68
|
-
*/
|
|
69
|
-
readonly presignedUpload: (key: string, options: {
|
|
70
|
-
readonly ttlMs: number;
|
|
71
|
-
readonly contentType: string;
|
|
72
|
-
readonly contentLength: number;
|
|
73
|
-
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
74
|
-
};
|
|
75
|
-
declare const Storage_base: import("@btravstack/di").PortClass<"Storage">;
|
|
76
|
-
/**
|
|
77
|
-
* The port an application depends on.
|
|
78
|
-
*
|
|
79
|
-
* Keys are plain strings the caller composes, tenant included: a store is an
|
|
80
|
-
* application service, and the framework has no concept of a tenant to put in a
|
|
81
|
-
* slot.
|
|
82
|
-
*
|
|
83
|
-
* **Bytes, not streams.** An object here is a document. Streaming would change
|
|
84
|
-
* every signature, adapter and test to serve a case that wants a different
|
|
85
|
-
* design anyway — a stated non-goal, not an oversight.
|
|
86
|
-
*/
|
|
87
|
-
declare class Storage extends Storage_base<StorageService> {}
|
|
88
|
-
declare const StorageBackend_base: import("@btravstack/di").PortClass<"StorageBackend">;
|
|
89
|
-
/** The port every adapter provides, and the one an application never depends on. */
|
|
90
|
-
declare class StorageBackend extends StorageBackend_base<StorageService> {}
|
|
91
|
-
//#endregion
|
|
92
|
-
export { StorageBackend as a, StoredObject as c, Storage as i, ObjectNotFound as n, StorageService as o, PresignNotSupported as r, StorageUnavailable as s, ObjectExists as t };
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { AsyncResult } from "unthrown";
|
|
2
|
-
//#region src/storage.d.ts
|
|
3
|
-
/** One stored object: its bytes and what they are. */
|
|
4
|
-
type StoredObject = {
|
|
5
|
-
readonly bytes: Uint8Array;
|
|
6
|
-
readonly contentType: string;
|
|
7
|
-
};
|
|
8
|
-
declare const ObjectNotFound_base: import("unthrown").TaggedErrorConstructor<"ObjectNotFound">;
|
|
9
|
-
/** The key is not there. Only `get` answers it — see `delete` and `presignedUrl` below. */
|
|
10
|
-
declare class ObjectNotFound extends ObjectNotFound_base<{
|
|
11
|
-
readonly key: string;
|
|
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
|
-
}> {}
|
|
18
|
-
declare const StorageUnavailable_base: import("unthrown").TaggedErrorConstructor<"StorageUnavailable">;
|
|
19
|
-
/** The store could not answer — the endpoint is down, the credentials were refused, the bucket is gone. */
|
|
20
|
-
declare class StorageUnavailable extends StorageUnavailable_base<{
|
|
21
|
-
readonly operation: "put" | "putIfAbsent" | "get" | "delete" | "presignedUrl" | "presignedUpload";
|
|
22
|
-
readonly key: string;
|
|
23
|
-
readonly reason: string;
|
|
24
|
-
}> {}
|
|
25
|
-
declare const PresignNotSupported_base: import("unthrown").TaggedErrorConstructor<"PresignNotSupported">;
|
|
26
|
-
/** The adapter cannot mint a time-limited URL, and says so rather than pretending. */
|
|
27
|
-
declare class PresignNotSupported extends PresignNotSupported_base<{
|
|
28
|
-
readonly key: string;
|
|
29
|
-
}> {}
|
|
30
|
-
type StorageService = {
|
|
31
|
-
readonly put: (key: string, bytes: Uint8Array, options: {
|
|
32
|
-
readonly contentType: string;
|
|
33
|
-
}) => AsyncResult<void, StorageUnavailable>;
|
|
34
|
-
/** Writes the object only if no object holds the key: the first writer wins, atomically, in the store. */
|
|
35
|
-
readonly putIfAbsent: (key: string, bytes: Uint8Array, options: {
|
|
36
|
-
readonly contentType: string;
|
|
37
|
-
}) => AsyncResult<void, ObjectExists | StorageUnavailable>;
|
|
38
|
-
readonly get: (key: string) => AsyncResult<StoredObject, ObjectNotFound | StorageUnavailable>;
|
|
39
|
-
/** Deleting a key nobody stored is `Ok`: delete is idempotent, which is S3's own behaviour rather than a fiction over it. */
|
|
40
|
-
readonly delete: (key: string) => AsyncResult<void, StorageUnavailable>;
|
|
41
|
-
/**
|
|
42
|
-
* A URL that reads the object for `ttlMs`, without the caller holding
|
|
43
|
-
* credentials.
|
|
44
|
-
*
|
|
45
|
-
* There is **no `ObjectNotFound` arm**: presigning is a signature computation
|
|
46
|
-
* that asks the store nothing, so a URL for an absent key is minted happily
|
|
47
|
-
* and 404s when followed. Checking would cost a HEAD per call.
|
|
48
|
-
*/
|
|
49
|
-
readonly presignedUrl: (key: string, options: {
|
|
50
|
-
readonly ttlMs: number;
|
|
51
|
-
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
52
|
-
/**
|
|
53
|
-
* A URL that writes the object for `ttlMs`, without the caller holding
|
|
54
|
-
* credentials — so the bytes go from the client to the store and never
|
|
55
|
-
* through this process.
|
|
56
|
-
*
|
|
57
|
-
* **`contentType` and `contentLength` are signed, not advisory.** They are
|
|
58
|
-
* part of the signature, so a client sending different ones is refused by the
|
|
59
|
-
* store. The same URL can write the key repeatedly until expiry, replacing
|
|
60
|
-
* the previous object with different bytes of that size and type. Neither
|
|
61
|
-
* signed header proves the bytes. `contentLength` is required — an unsigned
|
|
62
|
-
* length would allow an unbounded single write, and there is no ceiling a
|
|
63
|
-
* presigned PUT can express other than the exact number.
|
|
64
|
-
*
|
|
65
|
-
* The application still decides every input: whether this caller may write
|
|
66
|
-
* this key, how long the URL lives, and what size it is willing to accept.
|
|
67
|
-
* The adapter computes a signature over that decision and nothing more.
|
|
68
|
-
*/
|
|
69
|
-
readonly presignedUpload: (key: string, options: {
|
|
70
|
-
readonly ttlMs: number;
|
|
71
|
-
readonly contentType: string;
|
|
72
|
-
readonly contentLength: number;
|
|
73
|
-
}) => AsyncResult<string, PresignNotSupported | StorageUnavailable>;
|
|
74
|
-
};
|
|
75
|
-
declare const Storage_base: import("@btravstack/di").PortClass<"Storage">;
|
|
76
|
-
/**
|
|
77
|
-
* The port an application depends on.
|
|
78
|
-
*
|
|
79
|
-
* Keys are plain strings the caller composes, tenant included: a store is an
|
|
80
|
-
* application service, and the framework has no concept of a tenant to put in a
|
|
81
|
-
* slot.
|
|
82
|
-
*
|
|
83
|
-
* **Bytes, not streams.** An object here is a document. Streaming would change
|
|
84
|
-
* every signature, adapter and test to serve a case that wants a different
|
|
85
|
-
* design anyway — a stated non-goal, not an oversight.
|
|
86
|
-
*/
|
|
87
|
-
declare class Storage extends Storage_base<StorageService> {}
|
|
88
|
-
declare const StorageBackend_base: import("@btravstack/di").PortClass<"StorageBackend">;
|
|
89
|
-
/** The port every adapter provides, and the one an application never depends on. */
|
|
90
|
-
declare class StorageBackend extends StorageBackend_base<StorageService> {}
|
|
91
|
-
//#endregion
|
|
92
|
-
export { StorageBackend as a, StoredObject as c, Storage as i, ObjectNotFound as n, StorageService as o, PresignNotSupported as r, StorageUnavailable as s, ObjectExists as t };
|