@btravstack/storage 0.8.0 → 0.9.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 +24 -26
- package/dist/index.cjs +23 -11
- package/dist/index.mjs +23 -11
- package/dist/s3.cjs +6 -2
- package/dist/s3.mjs +6 -2
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -57,32 +57,30 @@ 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 the instrumented store already logged 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
86
|
The composition — the adapter, and whether operations are instrumented,
|
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()],
|
|
@@ -116,10 +119,13 @@ const instrument = (backend, logger, tracer, meter) => {
|
|
|
116
119
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
117
120
|
*/
|
|
118
121
|
const storage = ({ adapter, instrumented }) => {
|
|
119
|
-
const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
|
|
123
|
+
inject: { backend: require_storage.StorageBackend },
|
|
124
|
+
sync: ({ backend }) => ({
|
|
125
|
+
name: "storage",
|
|
126
|
+
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})` }))))
|
|
127
|
+
})
|
|
128
|
+
});
|
|
123
129
|
return instrumented !== false ? (0, _btravstack_di.Module)("InstrumentedStorage")({
|
|
124
130
|
needs: [
|
|
125
131
|
_btravstack_core.Logger,
|
|
@@ -128,15 +134,21 @@ const storage = ({ adapter, instrumented }) => {
|
|
|
128
134
|
],
|
|
129
135
|
imports: [adapter],
|
|
130
136
|
provides: [(0, _btravstack_di.Provider)(require_storage.Storage)({
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
inject: {
|
|
138
|
+
backend: require_storage.StorageBackend,
|
|
139
|
+
logger: _btravstack_core.Logger,
|
|
140
|
+
tracer: _btravstack_core.Tracer,
|
|
141
|
+
meter: _btravstack_core.Meter
|
|
142
|
+
},
|
|
143
|
+
sync: ({ backend, logger, tracer, meter }) => instrument(backend, logger, tracer, meter)
|
|
144
|
+
}), healthCheck],
|
|
136
145
|
exports: [require_storage.Storage, _btravstack_core.HealthChecks]
|
|
137
146
|
}) : (0, _btravstack_di.Module)("Storage")({
|
|
138
147
|
imports: [adapter],
|
|
139
|
-
provides: [(0, _btravstack_di.Provider)(require_storage.Storage)({
|
|
148
|
+
provides: [(0, _btravstack_di.Provider)(require_storage.Storage)({
|
|
149
|
+
inject: { backend: require_storage.StorageBackend },
|
|
150
|
+
sync: ({ backend }) => backend
|
|
151
|
+
}), healthCheck],
|
|
140
152
|
exports: [require_storage.Storage, _btravstack_core.HealthChecks]
|
|
141
153
|
});
|
|
142
154
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -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()],
|
|
@@ -115,10 +118,13 @@ const instrument = (backend, logger, tracer, meter) => {
|
|
|
115
118
|
* why instrumentation is a flag on the composition rather than a wrapper.
|
|
116
119
|
*/
|
|
117
120
|
const storage = ({ adapter, instrumented }) => {
|
|
118
|
-
const healthCheck = Provider.member(HealthChecks)({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
const healthCheck = Provider.member(HealthChecks)({
|
|
122
|
+
inject: { backend: StorageBackend },
|
|
123
|
+
sync: ({ backend }) => ({
|
|
124
|
+
name: "storage",
|
|
125
|
+
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})` }))))
|
|
126
|
+
})
|
|
127
|
+
});
|
|
122
128
|
return instrumented !== false ? Module("InstrumentedStorage")({
|
|
123
129
|
needs: [
|
|
124
130
|
Logger,
|
|
@@ -127,15 +133,21 @@ const storage = ({ adapter, instrumented }) => {
|
|
|
127
133
|
],
|
|
128
134
|
imports: [adapter],
|
|
129
135
|
provides: [Provider(Storage)({
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
inject: {
|
|
137
|
+
backend: StorageBackend,
|
|
138
|
+
logger: Logger,
|
|
139
|
+
tracer: Tracer,
|
|
140
|
+
meter: Meter
|
|
141
|
+
},
|
|
142
|
+
sync: ({ backend, logger, tracer, meter }) => instrument(backend, logger, tracer, meter)
|
|
143
|
+
}), healthCheck],
|
|
135
144
|
exports: [Storage, HealthChecks]
|
|
136
145
|
}) : Module("Storage")({
|
|
137
146
|
imports: [adapter],
|
|
138
|
-
provides: [Provider(Storage)({
|
|
147
|
+
provides: [Provider(Storage)({
|
|
148
|
+
inject: { backend: StorageBackend },
|
|
149
|
+
sync: ({ backend }) => backend
|
|
150
|
+
}), healthCheck],
|
|
139
151
|
exports: [Storage, HealthChecks]
|
|
140
152
|
});
|
|
141
153
|
};
|
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.9.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/
|
|
72
|
-
"@btravstack/
|
|
73
|
-
"@btravstack/observability": "0.
|
|
74
|
-
"@btravstack/
|
|
70
|
+
"@btravstack/config": "0.9.0",
|
|
71
|
+
"@btravstack/di": "0.9.0",
|
|
72
|
+
"@btravstack/internal-test-infra": "0.0.0",
|
|
73
|
+
"@btravstack/observability": "0.9.0",
|
|
74
|
+
"@btravstack/core": "0.9.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.9.0",
|
|
81
|
+
"@btravstack/core": "^0.9.0",
|
|
82
|
+
"@btravstack/di": "^0.9.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependenciesMeta": {
|
|
85
85
|
"@aws-sdk/client-s3": {
|