@btravstack/storage 0.8.0 → 0.10.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 +34 -36
- package/dist/index.cjs +64 -70
- package/dist/index.d.cts +6 -15
- package/dist/index.d.mts +6 -15
- package/dist/index.mjs +65 -71
- package/dist/s3.cjs +6 -2
- package/dist/s3.mjs +6 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -57,35 +57,33 @@ class Invoices extends InvoicePort("ReadmeInvoices")<{
|
|
|
57
57
|
const keyFor = (tenantId: string, id: string) =>
|
|
58
58
|
`invoices/${tenantId}/${id}.pdf`;
|
|
59
59
|
|
|
60
|
-
export const invoices = InvoiceProvider(Invoices)(
|
|
61
|
-
{ storage: Storage },
|
|
62
|
-
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
);
|
|
60
|
+
export const invoices = InvoiceProvider(Invoices)({
|
|
61
|
+
inject: { storage: Storage },
|
|
62
|
+
sync: ({ storage }) => ({
|
|
63
|
+
store: (tenantId, id, pdf) =>
|
|
64
|
+
storage
|
|
65
|
+
.put(keyFor(tenantId, id), pdf, { contentType: "application/pdf" })
|
|
66
|
+
// A document that failed to store is YOUR decision to make; here it
|
|
67
|
+
// is swallowed because an observer already recorded it and
|
|
68
|
+
// counted it one layer down.
|
|
69
|
+
.recoverErrCases((matcher) =>
|
|
70
|
+
matcher.with(P.tag("StorageUnavailable"), () => undefined),
|
|
71
|
+
),
|
|
72
|
+
// Serving bytes through your own process is the anti-pattern this arm
|
|
73
|
+
// exists to avoid: the caller fetches the object directly, for a minute.
|
|
74
|
+
link: (tenantId, id) =>
|
|
75
|
+
storage
|
|
76
|
+
.presignedUrl(keyFor(tenantId, id), { ttlMs: 60_000 })
|
|
77
|
+
.recoverErrCases((matcher) =>
|
|
78
|
+
matcher
|
|
79
|
+
.with(P.tag("StorageUnavailable"), () => undefined)
|
|
80
|
+
.with(P.tag("PresignNotSupported"), () => undefined),
|
|
81
|
+
),
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
86
84
|
```
|
|
87
85
|
|
|
88
|
-
The composition — the adapter
|
|
86
|
+
The composition — the adapter is the only decision,
|
|
89
87
|
decided here and nowhere else:
|
|
90
88
|
|
|
91
89
|
```ts
|
|
@@ -95,7 +93,8 @@ import { s3Storage } from "@btravstack/storage/s3";
|
|
|
95
93
|
export const InvoicesApp = Module("InvoicesApp")({
|
|
96
94
|
imports: [
|
|
97
95
|
// Instrumented by default, which is why `observability()` and `otel()`
|
|
98
|
-
//
|
|
96
|
+
// Every call is reported through `Observers`. Drop the two modules
|
|
97
|
+
// below and this composition is unchanged — it just reports to nobody.
|
|
99
98
|
storage({ adapter: s3Storage() }),
|
|
100
99
|
observability(),
|
|
101
100
|
otel(),
|
|
@@ -107,14 +106,13 @@ export const InvoicesApp = Module("InvoicesApp")({
|
|
|
107
106
|
|
|
108
107
|
## Options
|
|
109
108
|
|
|
110
|
-
| Option | Where | What it is
|
|
111
|
-
| ----------------------------------------------------------- | ---------------------------------- |
|
|
112
|
-
| `adapter` | `storage({ adapter })` | the adapter module providing `StorageBackend` — required
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `STORAGE_S3_REGION` | environment | default `us-east-1` |
|
|
109
|
+
| Option | Where | What it is |
|
|
110
|
+
| ----------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------- |
|
|
111
|
+
| `adapter` | `storage({ adapter })` | the adapter module providing `StorageBackend` — required |
|
|
112
|
+
| `STORAGE_S3_ENDPOINT` | environment, read by `s3Storage()` | the store's endpoint — required |
|
|
113
|
+
| `STORAGE_S3_BUCKET` | environment | the bucket — required |
|
|
114
|
+
| `STORAGE_S3_ACCESS_KEY_ID` / `STORAGE_S3_SECRET_ACCESS_KEY` | environment | the credentials — required |
|
|
115
|
+
| `STORAGE_S3_REGION` | environment | default `us-east-1` |
|
|
118
116
|
|
|
119
117
|
The full table — defaults, semantics and the reasoning — lives on
|
|
120
118
|
[the reference page](https://btravstack.github.io/btravstack/reference/storage),
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,10 @@ const memoryStorageBackend = () => {
|
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
39
|
/** The adapter as a provider, which is the shape `@btravstack/testing`'s `overridden` takes. */
|
|
40
|
-
const memoryStorageProvider = () => (0, _btravstack_di.Provider)(require_storage.StorageBackend)({
|
|
40
|
+
const memoryStorageProvider = () => (0, _btravstack_di.Provider)(require_storage.StorageBackend)({
|
|
41
|
+
inject: {},
|
|
42
|
+
sync: () => memoryStorageBackend()
|
|
43
|
+
});
|
|
41
44
|
/** The adapter as a module, which is the shape `storage({ adapter })` takes. */
|
|
42
45
|
const memoryStorage = () => (0, _btravstack_di.Module)("MemoryStorage")({
|
|
43
46
|
provides: [memoryStorageProvider()],
|
|
@@ -46,60 +49,49 @@ const memoryStorage = () => (0, _btravstack_di.Module)("MemoryStorage")({
|
|
|
46
49
|
//#endregion
|
|
47
50
|
//#region src/instrument.ts
|
|
48
51
|
/**
|
|
49
|
-
* One operation,
|
|
50
|
-
* log line if it failed.
|
|
52
|
+
* One operation, handed to every observer the graph composed.
|
|
51
53
|
*
|
|
52
|
-
* **A missing object
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* **A missing object settles `ok` with `result: "not_found"`, not `error`.**
|
|
55
|
+
* Asking for something that is not there is an ordinary answer, and a dashboard
|
|
56
|
+
* that treats it as a fault teaches its readers to ignore the fault line.
|
|
57
|
+
* `StorageUnavailable` is what pages somebody, and it is the only one that
|
|
58
|
+
* settles as an error. The two ordinary answers keep their own words in
|
|
59
|
+
* `reason`, so an operator is not left hunting an object sitting where they put
|
|
60
|
+
* it.
|
|
56
61
|
*/
|
|
57
|
-
const observed = (call,
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
const observed = (call, observers, operation, key) => {
|
|
63
|
+
const settle = (0, _btravstack_core.observe)(observers, {
|
|
64
|
+
component: "storage",
|
|
65
|
+
name: operation,
|
|
66
|
+
attributes: { operation },
|
|
67
|
+
details: { "btravstack.storage.key": key }
|
|
68
|
+
});
|
|
69
|
+
return call().tap(() => settle({
|
|
70
|
+
outcome: "ok",
|
|
71
|
+
attributes: { result: "ok" }
|
|
72
|
+
})).tapFailure((failure) => {
|
|
67
73
|
const cause = failure.tag === "Err" ? failure.error : failure.cause;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
} else logger.info(message, {
|
|
80
|
-
operation,
|
|
81
|
-
key
|
|
74
|
+
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "ObjectNotFound" ? "the object was not there" : failure.error._tag === "PresignNotSupported" ? "this store cannot mint a url" : void 0;
|
|
75
|
+
settle(ordinary === void 0 ? {
|
|
76
|
+
outcome: "error",
|
|
77
|
+
attributes: { result: "error" },
|
|
78
|
+
cause
|
|
79
|
+
} : {
|
|
80
|
+
outcome: "ok",
|
|
81
|
+
attributes: {
|
|
82
|
+
result: "not_found",
|
|
83
|
+
reason: ordinary
|
|
84
|
+
}
|
|
82
85
|
});
|
|
83
|
-
span.end();
|
|
84
86
|
});
|
|
85
87
|
};
|
|
86
|
-
const instrument = (backend,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
operations
|
|
94
|
-
});
|
|
95
|
-
return {
|
|
96
|
-
put: (key, bytes, options) => observed(() => backend.put(key, bytes, options), context("put", key)),
|
|
97
|
-
get: (key) => observed(() => backend.get(key), context("get", key)),
|
|
98
|
-
delete: (key) => observed(() => backend.delete(key), context("delete", key)),
|
|
99
|
-
presignedUrl: (key, options) => observed(() => backend.presignedUrl(key, options), context("presigned_url", key)),
|
|
100
|
-
presignedUpload: (key, options) => observed(() => backend.presignedUpload(key, options), context("presigned_upload", key))
|
|
101
|
-
};
|
|
102
|
-
};
|
|
88
|
+
const instrument = (backend, observers) => ({
|
|
89
|
+
put: (key, bytes, options) => observed(() => backend.put(key, bytes, options), observers, "put", key),
|
|
90
|
+
get: (key) => observed(() => backend.get(key), observers, "get", key),
|
|
91
|
+
delete: (key) => observed(() => backend.delete(key), observers, "delete", key),
|
|
92
|
+
presignedUrl: (key, options) => observed(() => backend.presignedUrl(key, options), observers, "presigned_url", key),
|
|
93
|
+
presignedUpload: (key, options) => observed(() => backend.presignedUpload(key, options), observers, "presigned_upload", key)
|
|
94
|
+
});
|
|
103
95
|
//#endregion
|
|
104
96
|
//#region src/module.ts
|
|
105
97
|
/**
|
|
@@ -115,28 +107,30 @@ const instrument = (backend, logger, tracer, meter) => {
|
|
|
115
107
|
* application depends on must not be the one an adapter provides, which is also
|
|
116
108
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
117
109
|
*/
|
|
118
|
-
const storage = ({ adapter
|
|
119
|
-
const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
_btravstack_core.Tracer
|
|
128
|
-
],
|
|
129
|
-
imports: [adapter],
|
|
130
|
-
provides: [(0, _btravstack_di.Provider)(require_storage.Storage)({
|
|
131
|
-
backend: require_storage.StorageBackend,
|
|
132
|
-
logger: _btravstack_core.Logger,
|
|
133
|
-
tracer: _btravstack_core.Tracer,
|
|
134
|
-
meter: _btravstack_core.Meter
|
|
135
|
-
}, { sync: ({ backend, logger, tracer, meter }) => instrument(backend, logger, tracer, meter) }), healthCheck],
|
|
136
|
-
exports: [require_storage.Storage, _btravstack_core.HealthChecks]
|
|
137
|
-
}) : (0, _btravstack_di.Module)("Storage")({
|
|
110
|
+
const storage = ({ adapter }) => {
|
|
111
|
+
const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
|
|
112
|
+
inject: { backend: require_storage.StorageBackend },
|
|
113
|
+
sync: ({ backend }) => ({
|
|
114
|
+
name: "storage",
|
|
115
|
+
check: () => backend.get("btravstack:health").map(() => void 0).flatMapErrCases((matcher) => matcher.with(unthrown.P.tag("ObjectNotFound"), () => (0, unthrown.Ok)(void 0)).with(unthrown.P.tag("StorageUnavailable"), ({ reason }) => (0, unthrown.Err)(new _btravstack_core.HealthCheckFailed({ reason: `storage unavailable (${reason})` }))))
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
return (0, _btravstack_di.Module)("Storage")({
|
|
138
119
|
imports: [adapter],
|
|
139
|
-
provides: [
|
|
120
|
+
provides: [
|
|
121
|
+
_btravstack_di.Provider.member(_btravstack_core.Observers)({
|
|
122
|
+
inject: {},
|
|
123
|
+
value: _btravstack_core.noObserver
|
|
124
|
+
}),
|
|
125
|
+
(0, _btravstack_di.Provider)(require_storage.Storage)({
|
|
126
|
+
inject: {
|
|
127
|
+
backend: require_storage.StorageBackend,
|
|
128
|
+
observers: _btravstack_core.Observers
|
|
129
|
+
},
|
|
130
|
+
sync: ({ backend, observers }) => instrument(backend, observers)
|
|
131
|
+
}),
|
|
132
|
+
healthCheck
|
|
133
|
+
],
|
|
140
134
|
exports: [require_storage.Storage, _btravstack_core.HealthChecks]
|
|
141
135
|
});
|
|
142
136
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as StorageService, i as StorageBackend, n as PresignNotSupported, o as StorageUnavailable, r as Storage, s as StoredObject, t as ObjectNotFound } from "./storage-Cu27C7Oq.cjs";
|
|
2
2
|
import { Module, Provider } from "@btravstack/di";
|
|
3
|
-
import { HealthChecks
|
|
3
|
+
import { HealthChecks } from "@btravstack/core";
|
|
4
4
|
//#region src/memory.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* The in-process adapter: a `Map`, and an honest refusal to presign.
|
|
@@ -21,22 +21,13 @@ declare const memoryStorageProvider: () => Provider<StorageBackend, never, never
|
|
|
21
21
|
declare const memoryStorage: () => Module<StorageBackend, never, never>;
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/module.d.ts
|
|
24
|
-
type StorageOptions<E, N
|
|
24
|
+
type StorageOptions<E, N> = {
|
|
25
25
|
/**
|
|
26
|
-
* The adapter module: `memoryStorage()` from this entry point,
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* The adapter module: `memoryStorage()` from this entry point, `s3Storage()`
|
|
27
|
+
* from `@btravstack/storage/s3`, or one an application wrote itself over
|
|
28
|
+
* `StorageBackend`.
|
|
29
29
|
*/
|
|
30
30
|
readonly adapter: Module<StorageBackend, E, N>;
|
|
31
|
-
/**
|
|
32
|
-
* Span, count and log every operation. **Default `true`**, `false` opts out.
|
|
33
|
-
*
|
|
34
|
-
* On by default because telemetry that is missing is discovered during an
|
|
35
|
-
* incident. The cost is stated rather than hidden: instrumenting puts
|
|
36
|
-
* `Logger`, `Meter` and `Tracer` in this module's `Needs`, so a root without
|
|
37
|
-
* `observability()` and `otel()` gets a compile error naming all three.
|
|
38
|
-
*/
|
|
39
|
-
readonly instrumented?: Instrumented;
|
|
40
31
|
};
|
|
41
32
|
/**
|
|
42
33
|
* The storage starter: an adapter, and `Storage` provided from it —
|
|
@@ -51,6 +42,6 @@ type StorageOptions<E, N, Instrumented extends boolean> = {
|
|
|
51
42
|
* application depends on must not be the one an adapter provides, which is also
|
|
52
43
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
53
44
|
*/
|
|
54
|
-
declare const storage: <E, N
|
|
45
|
+
declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
|
|
55
46
|
//#endregion
|
|
56
47
|
export { ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject, memoryStorage, memoryStorageBackend, memoryStorageProvider, storage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as StorageService, i as StorageBackend, n as PresignNotSupported, o as StorageUnavailable, r as Storage, s as StoredObject, t as ObjectNotFound } from "./storage-Cu27C7Oq.mjs";
|
|
2
2
|
import { Module, Provider } from "@btravstack/di";
|
|
3
|
-
import { HealthChecks
|
|
3
|
+
import { HealthChecks } from "@btravstack/core";
|
|
4
4
|
//#region src/memory.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* The in-process adapter: a `Map`, and an honest refusal to presign.
|
|
@@ -21,22 +21,13 @@ declare const memoryStorageProvider: () => Provider<StorageBackend, never, never
|
|
|
21
21
|
declare const memoryStorage: () => Module<StorageBackend, never, never>;
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/module.d.ts
|
|
24
|
-
type StorageOptions<E, N
|
|
24
|
+
type StorageOptions<E, N> = {
|
|
25
25
|
/**
|
|
26
|
-
* The adapter module: `memoryStorage()` from this entry point,
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* The adapter module: `memoryStorage()` from this entry point, `s3Storage()`
|
|
27
|
+
* from `@btravstack/storage/s3`, or one an application wrote itself over
|
|
28
|
+
* `StorageBackend`.
|
|
29
29
|
*/
|
|
30
30
|
readonly adapter: Module<StorageBackend, E, N>;
|
|
31
|
-
/**
|
|
32
|
-
* Span, count and log every operation. **Default `true`**, `false` opts out.
|
|
33
|
-
*
|
|
34
|
-
* On by default because telemetry that is missing is discovered during an
|
|
35
|
-
* incident. The cost is stated rather than hidden: instrumenting puts
|
|
36
|
-
* `Logger`, `Meter` and `Tracer` in this module's `Needs`, so a root without
|
|
37
|
-
* `observability()` and `otel()` gets a compile error naming all three.
|
|
38
|
-
*/
|
|
39
|
-
readonly instrumented?: Instrumented;
|
|
40
31
|
};
|
|
41
32
|
/**
|
|
42
33
|
* The storage starter: an adapter, and `Storage` provided from it —
|
|
@@ -51,6 +42,6 @@ type StorageOptions<E, N, Instrumented extends boolean> = {
|
|
|
51
42
|
* application depends on must not be the one an adapter provides, which is also
|
|
52
43
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
53
44
|
*/
|
|
54
|
-
declare const storage: <E, N
|
|
45
|
+
declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
|
|
55
46
|
//#endregion
|
|
56
47
|
export { ObjectNotFound, PresignNotSupported, Storage, StorageBackend, type StorageOptions, type StorageService, StorageUnavailable, type StoredObject, memoryStorage, memoryStorageBackend, memoryStorageProvider, storage };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as StorageUnavailable, i as StorageBackend, n as PresignNotSupported, r as Storage, t as ObjectNotFound } from "./storage-CRkwIhac.mjs";
|
|
2
2
|
import { Module, Provider } from "@btravstack/di";
|
|
3
3
|
import { Err, ErrAsync, Ok, OkAsync, P } from "unthrown";
|
|
4
|
-
import { HealthCheckFailed, HealthChecks,
|
|
4
|
+
import { HealthCheckFailed, HealthChecks, Observers, noObserver, observe } from "@btravstack/core";
|
|
5
5
|
//#region src/memory.ts
|
|
6
6
|
/**
|
|
7
7
|
* The in-process adapter: a `Map`, and an honest refusal to presign.
|
|
@@ -36,7 +36,10 @@ const memoryStorageBackend = () => {
|
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
38
|
/** The adapter as a provider, which is the shape `@btravstack/testing`'s `overridden` takes. */
|
|
39
|
-
const memoryStorageProvider = () => Provider(StorageBackend)({
|
|
39
|
+
const memoryStorageProvider = () => Provider(StorageBackend)({
|
|
40
|
+
inject: {},
|
|
41
|
+
sync: () => memoryStorageBackend()
|
|
42
|
+
});
|
|
40
43
|
/** The adapter as a module, which is the shape `storage({ adapter })` takes. */
|
|
41
44
|
const memoryStorage = () => Module("MemoryStorage")({
|
|
42
45
|
provides: [memoryStorageProvider()],
|
|
@@ -45,60 +48,49 @@ const memoryStorage = () => Module("MemoryStorage")({
|
|
|
45
48
|
//#endregion
|
|
46
49
|
//#region src/instrument.ts
|
|
47
50
|
/**
|
|
48
|
-
* One operation,
|
|
49
|
-
* log line if it failed.
|
|
51
|
+
* One operation, handed to every observer the graph composed.
|
|
50
52
|
*
|
|
51
|
-
* **A missing object
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
53
|
+
* **A missing object settles `ok` with `result: "not_found"`, not `error`.**
|
|
54
|
+
* Asking for something that is not there is an ordinary answer, and a dashboard
|
|
55
|
+
* that treats it as a fault teaches its readers to ignore the fault line.
|
|
56
|
+
* `StorageUnavailable` is what pages somebody, and it is the only one that
|
|
57
|
+
* settles as an error. The two ordinary answers keep their own words in
|
|
58
|
+
* `reason`, so an operator is not left hunting an object sitting where they put
|
|
59
|
+
* it.
|
|
55
60
|
*/
|
|
56
|
-
const observed = (call,
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const observed = (call, observers, operation, key) => {
|
|
62
|
+
const settle = observe(observers, {
|
|
63
|
+
component: "storage",
|
|
64
|
+
name: operation,
|
|
65
|
+
attributes: { operation },
|
|
66
|
+
details: { "btravstack.storage.key": key }
|
|
67
|
+
});
|
|
68
|
+
return call().tap(() => settle({
|
|
69
|
+
outcome: "ok",
|
|
70
|
+
attributes: { result: "ok" }
|
|
71
|
+
})).tapFailure((failure) => {
|
|
66
72
|
const cause = failure.tag === "Err" ? failure.error : failure.cause;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} else logger.info(message, {
|
|
79
|
-
operation,
|
|
80
|
-
key
|
|
73
|
+
const ordinary = failure.tag !== "Err" ? void 0 : failure.error._tag === "ObjectNotFound" ? "the object was not there" : failure.error._tag === "PresignNotSupported" ? "this store cannot mint a url" : void 0;
|
|
74
|
+
settle(ordinary === void 0 ? {
|
|
75
|
+
outcome: "error",
|
|
76
|
+
attributes: { result: "error" },
|
|
77
|
+
cause
|
|
78
|
+
} : {
|
|
79
|
+
outcome: "ok",
|
|
80
|
+
attributes: {
|
|
81
|
+
result: "not_found",
|
|
82
|
+
reason: ordinary
|
|
83
|
+
}
|
|
81
84
|
});
|
|
82
|
-
span.end();
|
|
83
85
|
});
|
|
84
86
|
};
|
|
85
|
-
const instrument = (backend,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
operations
|
|
93
|
-
});
|
|
94
|
-
return {
|
|
95
|
-
put: (key, bytes, options) => observed(() => backend.put(key, bytes, options), context("put", key)),
|
|
96
|
-
get: (key) => observed(() => backend.get(key), context("get", key)),
|
|
97
|
-
delete: (key) => observed(() => backend.delete(key), context("delete", key)),
|
|
98
|
-
presignedUrl: (key, options) => observed(() => backend.presignedUrl(key, options), context("presigned_url", key)),
|
|
99
|
-
presignedUpload: (key, options) => observed(() => backend.presignedUpload(key, options), context("presigned_upload", key))
|
|
100
|
-
};
|
|
101
|
-
};
|
|
87
|
+
const instrument = (backend, observers) => ({
|
|
88
|
+
put: (key, bytes, options) => observed(() => backend.put(key, bytes, options), observers, "put", key),
|
|
89
|
+
get: (key) => observed(() => backend.get(key), observers, "get", key),
|
|
90
|
+
delete: (key) => observed(() => backend.delete(key), observers, "delete", key),
|
|
91
|
+
presignedUrl: (key, options) => observed(() => backend.presignedUrl(key, options), observers, "presigned_url", key),
|
|
92
|
+
presignedUpload: (key, options) => observed(() => backend.presignedUpload(key, options), observers, "presigned_upload", key)
|
|
93
|
+
});
|
|
102
94
|
//#endregion
|
|
103
95
|
//#region src/module.ts
|
|
104
96
|
/**
|
|
@@ -114,28 +106,30 @@ const instrument = (backend, logger, tracer, meter) => {
|
|
|
114
106
|
* application depends on must not be the one an adapter provides, which is also
|
|
115
107
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
116
108
|
*/
|
|
117
|
-
const storage = ({ adapter
|
|
118
|
-
const healthCheck = Provider.member(HealthChecks)({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Tracer
|
|
127
|
-
],
|
|
128
|
-
imports: [adapter],
|
|
129
|
-
provides: [Provider(Storage)({
|
|
130
|
-
backend: StorageBackend,
|
|
131
|
-
logger: Logger,
|
|
132
|
-
tracer: Tracer,
|
|
133
|
-
meter: Meter
|
|
134
|
-
}, { sync: ({ backend, logger, tracer, meter }) => instrument(backend, logger, tracer, meter) }), healthCheck],
|
|
135
|
-
exports: [Storage, HealthChecks]
|
|
136
|
-
}) : Module("Storage")({
|
|
109
|
+
const storage = ({ adapter }) => {
|
|
110
|
+
const healthCheck = Provider.member(HealthChecks)({
|
|
111
|
+
inject: { backend: StorageBackend },
|
|
112
|
+
sync: ({ backend }) => ({
|
|
113
|
+
name: "storage",
|
|
114
|
+
check: () => backend.get("btravstack:health").map(() => void 0).flatMapErrCases((matcher) => matcher.with(P.tag("ObjectNotFound"), () => Ok(void 0)).with(P.tag("StorageUnavailable"), ({ reason }) => Err(new HealthCheckFailed({ reason: `storage unavailable (${reason})` }))))
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
return Module("Storage")({
|
|
137
118
|
imports: [adapter],
|
|
138
|
-
provides: [
|
|
119
|
+
provides: [
|
|
120
|
+
Provider.member(Observers)({
|
|
121
|
+
inject: {},
|
|
122
|
+
value: noObserver
|
|
123
|
+
}),
|
|
124
|
+
Provider(Storage)({
|
|
125
|
+
inject: {
|
|
126
|
+
backend: StorageBackend,
|
|
127
|
+
observers: Observers
|
|
128
|
+
},
|
|
129
|
+
sync: ({ backend, observers }) => instrument(backend, observers)
|
|
130
|
+
}),
|
|
131
|
+
healthCheck
|
|
132
|
+
],
|
|
139
133
|
exports: [Storage, HealthChecks]
|
|
140
134
|
});
|
|
141
135
|
};
|
package/dist/s3.cjs
CHANGED
|
@@ -81,7 +81,8 @@ const s3Storage = () => (0, _btravstack_di.Module)("S3Storage")({
|
|
|
81
81
|
needs: [_btravstack_config.Env],
|
|
82
82
|
provides: [
|
|
83
83
|
_btravstack_config.Config.provider(StorageConfig)(s3Schema),
|
|
84
|
-
(0, _btravstack_di.Provider)(S3Connection)({
|
|
84
|
+
(0, _btravstack_di.Provider)(S3Connection)({
|
|
85
|
+
inject: { config: StorageConfig },
|
|
85
86
|
acquire: ({ config }) => (0, unthrown.fromSafePromise)(Promise.resolve({
|
|
86
87
|
client: new _aws_sdk_client_s3.S3Client({
|
|
87
88
|
endpoint: config.endpoint,
|
|
@@ -98,7 +99,10 @@ const s3Storage = () => (0, _btravstack_di.Module)("S3Storage")({
|
|
|
98
99
|
client.destroy();
|
|
99
100
|
}
|
|
100
101
|
}),
|
|
101
|
-
(0, _btravstack_di.Provider)(require_storage.StorageBackend)({
|
|
102
|
+
(0, _btravstack_di.Provider)(require_storage.StorageBackend)({
|
|
103
|
+
inject: { connection: S3Connection },
|
|
104
|
+
sync: ({ connection }) => s3StorageBackend(connection.client, connection.bucket)
|
|
105
|
+
})
|
|
102
106
|
],
|
|
103
107
|
exports: [require_storage.StorageBackend]
|
|
104
108
|
});
|
package/dist/s3.mjs
CHANGED
|
@@ -80,7 +80,8 @@ const s3Storage = () => Module("S3Storage")({
|
|
|
80
80
|
needs: [Env],
|
|
81
81
|
provides: [
|
|
82
82
|
Config.provider(StorageConfig)(s3Schema),
|
|
83
|
-
Provider(S3Connection)({
|
|
83
|
+
Provider(S3Connection)({
|
|
84
|
+
inject: { config: StorageConfig },
|
|
84
85
|
acquire: ({ config }) => fromSafePromise(Promise.resolve({
|
|
85
86
|
client: new S3Client({
|
|
86
87
|
endpoint: config.endpoint,
|
|
@@ -97,7 +98,10 @@ const s3Storage = () => Module("S3Storage")({
|
|
|
97
98
|
client.destroy();
|
|
98
99
|
}
|
|
99
100
|
}),
|
|
100
|
-
Provider(StorageBackend)({
|
|
101
|
+
Provider(StorageBackend)({
|
|
102
|
+
inject: { connection: S3Connection },
|
|
103
|
+
sync: ({ connection }) => s3StorageBackend(connection.client, connection.bucket)
|
|
104
|
+
})
|
|
101
105
|
],
|
|
102
106
|
exports: [StorageBackend]
|
|
103
107
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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",
|
|
@@ -67,19 +67,19 @@
|
|
|
67
67
|
"typescript": "7.0.2",
|
|
68
68
|
"unthrown": "5.7.0",
|
|
69
69
|
"vitest": "4.1.11",
|
|
70
|
-
"@btravstack/config": "0.
|
|
71
|
-
"@btravstack/core": "0.
|
|
72
|
-
"@btravstack/di": "0.
|
|
73
|
-
"@btravstack/
|
|
74
|
-
"@btravstack/
|
|
70
|
+
"@btravstack/config": "0.10.0",
|
|
71
|
+
"@btravstack/core": "0.10.0",
|
|
72
|
+
"@btravstack/di": "0.10.0",
|
|
73
|
+
"@btravstack/internal-test-infra": "0.0.0",
|
|
74
|
+
"@btravstack/observability": "0.10.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@aws-sdk/client-s3": "^3.1000.0",
|
|
78
78
|
"@aws-sdk/s3-request-presigner": "^3.1000.0",
|
|
79
79
|
"unthrown": "^5.0.0",
|
|
80
|
-
"@btravstack/config": "^0.
|
|
81
|
-
"@btravstack/core": "^0.
|
|
82
|
-
"@btravstack/di": "^0.
|
|
80
|
+
"@btravstack/config": "^0.10.0",
|
|
81
|
+
"@btravstack/core": "^0.10.0",
|
|
82
|
+
"@btravstack/di": "^0.10.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
85
85
|
"@aws-sdk/client-s3": {
|