@btravstack/storage 0.14.0 → 0.16.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/dist/index.cjs CHANGED
@@ -49,8 +49,6 @@ const memoryStorage = () => (0, _btravstack_di.Module)("MemoryStorage")({
49
49
  //#endregion
50
50
  //#region src/instrument.ts
51
51
  /**
52
- * One operation, handed to every observer the graph composed.
53
- *
54
52
  * **A missing object settles `ok` with `result: "not_found"`, not `error`.**
55
53
  * Asking for something that is not there is an ordinary answer, and a dashboard
56
54
  * that treats it as a fault teaches its readers to ignore the fault line.
@@ -59,53 +57,62 @@ const memoryStorage = () => (0, _btravstack_di.Module)("MemoryStorage")({
59
57
  * `reason`, so an operator is not left hunting an object sitting where they put
60
58
  * it.
61
59
  */
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({
60
+ const failed = (failure) => {
61
+ const cause = failure.tag === "Err" ? failure.error : failure.cause;
62
+ 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;
63
+ return ordinary === void 0 ? {
64
+ outcome: "error",
65
+ attributes: { result: "error" },
66
+ cause
67
+ } : {
70
68
  outcome: "ok",
71
- attributes: { result: "ok" }
72
- })).tapFailure((failure) => {
73
- const cause = failure.tag === "Err" ? failure.error : failure.cause;
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
- }
85
- });
86
- });
69
+ attributes: {
70
+ result: "not_found",
71
+ reason: ordinary
72
+ }
73
+ };
87
74
  };
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)
75
+ /** The KEY is a detail, not a dimension: on the span and the line, never on an instrument. */
76
+ const through = (observers) => (operation, key, call) => (0, _btravstack_core.observed)(observers, {
77
+ component: "storage",
78
+ name: operation,
79
+ attributes: { operation },
80
+ details: { "btravstack.storage.key": key }
81
+ }, call, {
82
+ ok: () => ({
83
+ outcome: "ok",
84
+ attributes: { result: "ok" }
85
+ }),
86
+ failure: failed
94
87
  });
88
+ /** Every operation handed to the observers the graph composed, transparent to the `Result`. */
89
+ const instrument = (backend, observers) => {
90
+ const observe = through(observers);
91
+ return {
92
+ put: (key, bytes, options) => observe("put", key, () => backend.put(key, bytes, options)),
93
+ get: (key) => observe("get", key, () => backend.get(key)),
94
+ delete: (key) => observe("delete", key, () => backend.delete(key)),
95
+ presignedUrl: (key, options) => observe("presigned_url", key, () => backend.presignedUrl(key, options)),
96
+ presignedUpload: (key, options) => observe("presigned_upload", key, () => backend.presignedUpload(key, options))
97
+ };
98
+ };
95
99
  //#endregion
96
100
  //#region src/module.ts
97
101
  /**
98
- * The storage starter: an adapter, and `Storage` provided from it —
99
- * instrumented or not, decided here at the composition root.
102
+ * The storage starter: an adapter, and `Storage` provided from it.
100
103
  *
101
104
  * ```ts
102
- * storage({ adapter: s3Storage() }); // spans, counts, log lines
103
- * storage({ adapter: s3Storage(), instrumented: false }); // just a store
105
+ * storage({ adapter: s3Storage() });
104
106
  * ```
105
107
  *
106
108
  * **Two ports, because di allows one provider per port per graph**: the port an
107
- * application depends on must not be the one an adapter provides, which is also
108
- * why instrumentation is a flag on the composition rather than a wrapper.
109
+ * application depends on must not be the one an adapter provides, which is what
110
+ * lets this function be the seam.
111
+ *
112
+ * **There is no `instrumented` flag.** Every operation is handed to whatever
113
+ * contributed to `Observers`; a graph that composed no observability has only
114
+ * this module's own no-op member, so it costs one call per operation and nothing
115
+ * else.
109
116
  */
110
117
  const storage = ({ adapter }) => {
111
118
  const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
package/dist/index.d.cts CHANGED
@@ -30,17 +30,20 @@ type StorageOptions<E, N> = {
30
30
  readonly adapter: Module<StorageBackend, E, N>;
31
31
  };
32
32
  /**
33
- * The storage starter: an adapter, and `Storage` provided from it —
34
- * instrumented or not, decided here at the composition root.
33
+ * The storage starter: an adapter, and `Storage` provided from it.
35
34
  *
36
35
  * ```ts
37
- * storage({ adapter: s3Storage() }); // spans, counts, log lines
38
- * storage({ adapter: s3Storage(), instrumented: false }); // just a store
36
+ * storage({ adapter: s3Storage() });
39
37
  * ```
40
38
  *
41
39
  * **Two ports, because di allows one provider per port per graph**: the port an
42
- * application depends on must not be the one an adapter provides, which is also
43
- * why instrumentation is a flag on the composition rather than a wrapper.
40
+ * application depends on must not be the one an adapter provides, which is what
41
+ * lets this function be the seam.
42
+ *
43
+ * **There is no `instrumented` flag.** Every operation is handed to whatever
44
+ * contributed to `Observers`; a graph that composed no observability has only
45
+ * this module's own no-op member, so it costs one call per operation and nothing
46
+ * else.
44
47
  */
45
48
  export declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
46
49
  //#endregion
package/dist/index.d.mts CHANGED
@@ -30,17 +30,20 @@ type StorageOptions<E, N> = {
30
30
  readonly adapter: Module<StorageBackend, E, N>;
31
31
  };
32
32
  /**
33
- * The storage starter: an adapter, and `Storage` provided from it —
34
- * instrumented or not, decided here at the composition root.
33
+ * The storage starter: an adapter, and `Storage` provided from it.
35
34
  *
36
35
  * ```ts
37
- * storage({ adapter: s3Storage() }); // spans, counts, log lines
38
- * storage({ adapter: s3Storage(), instrumented: false }); // just a store
36
+ * storage({ adapter: s3Storage() });
39
37
  * ```
40
38
  *
41
39
  * **Two ports, because di allows one provider per port per graph**: the port an
42
- * application depends on must not be the one an adapter provides, which is also
43
- * why instrumentation is a flag on the composition rather than a wrapper.
40
+ * application depends on must not be the one an adapter provides, which is what
41
+ * lets this function be the seam.
42
+ *
43
+ * **There is no `instrumented` flag.** Every operation is handed to whatever
44
+ * contributed to `Observers`; a graph that composed no observability has only
45
+ * this module's own no-op member, so it costs one call per operation and nothing
46
+ * else.
44
47
  */
45
48
  export declare const storage: <E, N>({ adapter }: StorageOptions<E, N>) => Module<Storage | HealthChecks, E, N>;
46
49
  //#endregion
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, Observers, noObserver, observe } from "@btravstack/core";
4
+ import { HealthCheckFailed, HealthChecks, Observers, noObserver, observed } 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.
@@ -48,8 +48,6 @@ const memoryStorage = () => Module("MemoryStorage")({
48
48
  //#endregion
49
49
  //#region src/instrument.ts
50
50
  /**
51
- * One operation, handed to every observer the graph composed.
52
- *
53
51
  * **A missing object settles `ok` with `result: "not_found"`, not `error`.**
54
52
  * Asking for something that is not there is an ordinary answer, and a dashboard
55
53
  * that treats it as a fault teaches its readers to ignore the fault line.
@@ -58,53 +56,62 @@ const memoryStorage = () => Module("MemoryStorage")({
58
56
  * `reason`, so an operator is not left hunting an object sitting where they put
59
57
  * it.
60
58
  */
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({
59
+ const failed = (failure) => {
60
+ const cause = failure.tag === "Err" ? failure.error : failure.cause;
61
+ 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;
62
+ return ordinary === void 0 ? {
63
+ outcome: "error",
64
+ attributes: { result: "error" },
65
+ cause
66
+ } : {
69
67
  outcome: "ok",
70
- attributes: { result: "ok" }
71
- })).tapFailure((failure) => {
72
- const cause = failure.tag === "Err" ? failure.error : failure.cause;
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
- }
84
- });
85
- });
68
+ attributes: {
69
+ result: "not_found",
70
+ reason: ordinary
71
+ }
72
+ };
86
73
  };
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)
74
+ /** The KEY is a detail, not a dimension: on the span and the line, never on an instrument. */
75
+ const through = (observers) => (operation, key, call) => observed(observers, {
76
+ component: "storage",
77
+ name: operation,
78
+ attributes: { operation },
79
+ details: { "btravstack.storage.key": key }
80
+ }, call, {
81
+ ok: () => ({
82
+ outcome: "ok",
83
+ attributes: { result: "ok" }
84
+ }),
85
+ failure: failed
93
86
  });
87
+ /** Every operation handed to the observers the graph composed, transparent to the `Result`. */
88
+ const instrument = (backend, observers) => {
89
+ const observe = through(observers);
90
+ return {
91
+ put: (key, bytes, options) => observe("put", key, () => backend.put(key, bytes, options)),
92
+ get: (key) => observe("get", key, () => backend.get(key)),
93
+ delete: (key) => observe("delete", key, () => backend.delete(key)),
94
+ presignedUrl: (key, options) => observe("presigned_url", key, () => backend.presignedUrl(key, options)),
95
+ presignedUpload: (key, options) => observe("presigned_upload", key, () => backend.presignedUpload(key, options))
96
+ };
97
+ };
94
98
  //#endregion
95
99
  //#region src/module.ts
96
100
  /**
97
- * The storage starter: an adapter, and `Storage` provided from it —
98
- * instrumented or not, decided here at the composition root.
101
+ * The storage starter: an adapter, and `Storage` provided from it.
99
102
  *
100
103
  * ```ts
101
- * storage({ adapter: s3Storage() }); // spans, counts, log lines
102
- * storage({ adapter: s3Storage(), instrumented: false }); // just a store
104
+ * storage({ adapter: s3Storage() });
103
105
  * ```
104
106
  *
105
107
  * **Two ports, because di allows one provider per port per graph**: the port an
106
- * application depends on must not be the one an adapter provides, which is also
107
- * why instrumentation is a flag on the composition rather than a wrapper.
108
+ * application depends on must not be the one an adapter provides, which is what
109
+ * lets this function be the seam.
110
+ *
111
+ * **There is no `instrumented` flag.** Every operation is handed to whatever
112
+ * contributed to `Observers`; a graph that composed no observability has only
113
+ * this module's own no-op member, so it costs one call per operation and nothing
114
+ * else.
108
115
  */
109
116
  const storage = ({ adapter }) => {
110
117
  const healthCheck = Provider.member(HealthChecks)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/storage",
3
- "version": "0.14.0",
3
+ "version": "0.16.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,6 +55,11 @@
55
55
  "devDependencies": {
56
56
  "@aws-sdk/client-s3": "3.1127.0",
57
57
  "@aws-sdk/s3-request-presigner": "3.1127.0",
58
+ "@btravstack/config": "0.16.0",
59
+ "@btravstack/core": "0.16.0",
60
+ "@btravstack/di": "0.16.0",
61
+ "@btravstack/internal-test-infra": "0.0.0",
62
+ "@btravstack/observability": "0.16.0",
58
63
  "@btravstack/tsconfig": "0.4.0",
59
64
  "@opentelemetry/api": "1.9.1",
60
65
  "@opentelemetry/sdk-metrics": "2.11.0",
@@ -66,20 +71,15 @@
66
71
  "tsdown": "0.23.0",
67
72
  "typescript": "7.0.2",
68
73
  "unthrown": "5.8.0",
69
- "vitest": "4.1.11",
70
- "@btravstack/config": "0.14.0",
71
- "@btravstack/core": "0.14.0",
72
- "@btravstack/di": "0.14.0",
73
- "@btravstack/internal-test-infra": "0.0.0",
74
- "@btravstack/observability": "0.14.0"
74
+ "vitest": "4.1.11"
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
- "unthrown": "^5.0.0",
80
- "@btravstack/config": "^0.14.0",
81
- "@btravstack/core": "^0.14.0",
82
- "@btravstack/di": "^0.14.0"
79
+ "@btravstack/config": "^0.16.0",
80
+ "@btravstack/core": "^0.16.0",
81
+ "@btravstack/di": "^0.16.0",
82
+ "unthrown": "^5.0.0"
83
83
  },
84
84
  "peerDependenciesMeta": {
85
85
  "@aws-sdk/client-s3": {
@@ -96,7 +96,6 @@
96
96
  "build": "tsdown src/index.ts src/s3.ts --format cjs,esm --dts --clean",
97
97
  "dev": "tsdown src/index.ts src/s3.ts --format cjs,esm --dts --watch",
98
98
  "test": "vitest run --coverage",
99
- "test:types": "tsc -p tsconfig.test-d.json --noEmit",
100
- "typecheck": "tsc --noEmit"
99
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test-d.json"
101
100
  }
102
101
  }