@storyshelf/observability 0.7.0 → 0.8.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/config.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/config.d.ts
4
3
  /**
5
4
  * Environment-driven observability configuration.
6
5
  *
@@ -33,6 +32,5 @@ interface ObservabilityConfig {
33
32
  * @returns The resolved config; `enabled` is false without an endpoint.
34
33
  */
35
34
  declare function resolveObservabilityConfig(env?: NodeJS.ProcessEnv, serviceVersion?: string): ObservabilityConfig;
36
- //#endregion
37
35
  export { ObservabilityConfig, resolveObservabilityConfig };
38
36
  //# sourceMappingURL=config.d.mts.map
package/dist/config.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/config.ts
4
+ // src/config.ts
5
5
  const DEFAULT_SERVICE_NAME = "storyshelf";
6
6
  const DEFAULT_METRICS_INTERVAL_MS = 6e4;
7
7
  /** Whether the current runtime is Deno (native OTEL owns the SDK there). */
@@ -56,9 +56,8 @@ function resolveObservabilityConfig(env = process.env, serviceVersion) {
56
56
  * runs, where the identifier is undeclared, from throwing.
57
57
  */
58
58
  function packageVersion() {
59
- return "0.7.0";
59
+ return "0.8.0";
60
60
  }
61
- //#endregion
62
61
  export { resolveObservabilityConfig };
63
62
 
64
63
  //# sourceMappingURL=config.mjs.map
package/dist/index.mjs CHANGED
@@ -9,16 +9,4 @@ import { createHttpMiddleware } from "./middleware.mjs";
9
9
  import { otelLogMixin } from "./logs.mjs";
10
10
  import { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext } from "./propagate.mjs";
11
11
  import { resolveObservabilityConfig } from "./config.mjs";
12
- //#region src/index.ts
13
- /**
14
- * StoryShelf observability: runtime-agnostic OTEL instrumentation.
15
- *
16
- * This entrypoint is cross-runtime (`@opentelemetry/api` only — Node SDK or
17
- * Deno's native bridge supplies the provider). The Node SDK lifecycle
18
- * (`initObservabilityFromEnv`) lives under `@storyshelf/observability/node`
19
- * and must never be imported here.
20
- */
21
- //#endregion
22
12
  export { TRACEPARENT_HEADER, captureMetrics, createHttpMiddleware, createInstrumentedDatabase, createInstrumentedStorage, currentTraceparent, dbMetrics, injectTraceContext, otelLogMixin, parentContext, resolveObservabilityConfig, storageMetrics, withSpan };
23
-
24
- //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
4
- //#region src/instrument-db.d.ts
5
4
  /**
6
5
  * Wrap a database adapter with per-operation spans and metrics.
7
6
  *
@@ -9,6 +8,5 @@ import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
9
8
  * @returns A transparent adapter emitting `db.*` telemetry.
10
9
  */
11
10
  declare function createInstrumentedDatabase(db: DatabaseAdapter): DatabaseAdapter;
12
- //#endregion
13
11
  export { createInstrumentedDatabase };
14
12
  //# sourceMappingURL=instrument-db.d.mts.map
@@ -5,7 +5,7 @@ import { dbMetrics } from "./metrics.mjs";
5
5
  import { withSpan } from "./tracing.mjs";
6
6
  import { addTiming } from "@storyshelf/core/utils";
7
7
  import { getTableName } from "drizzle-orm";
8
- //#region src/instrument-db.ts
8
+ // src/instrument-db.ts
9
9
  /**
10
10
  * Wrap a database adapter with per-operation spans and metrics.
11
11
  *
@@ -17,8 +17,11 @@ function createInstrumentedDatabase(db) {
17
17
  }
18
18
  var InstrumentedDatabase = class {
19
19
  inner;
20
+ transact;
20
21
  constructor(inner) {
21
22
  this.inner = inner;
23
+ const transact = inner.transact?.bind(inner);
24
+ this.transact = transact ? async (fn) => await trackQuery("db.transact", async () => await transact(fn)) : void 0;
22
25
  }
23
26
  get metadata() {
24
27
  return this.inner.metadata;
@@ -26,6 +29,9 @@ var InstrumentedDatabase = class {
26
29
  get lifecycle() {
27
30
  return this.inner.lifecycle;
28
31
  }
32
+ get buildReferenceLifecycle() {
33
+ return this.inner.buildReferenceLifecycle;
34
+ }
29
35
  get tables() {
30
36
  return this.inner.tables;
31
37
  }
@@ -38,6 +44,9 @@ var InstrumentedDatabase = class {
38
44
  async update(table, id, values) {
39
45
  return await track("db.update", table, async () => await this.inner.update(table, id, values));
40
46
  }
47
+ async updateWhere(table, id, values, where) {
48
+ return await track("db.updateWhere", table, async () => await this.inner.updateWhere(table, id, values, where));
49
+ }
41
50
  async get(table, id) {
42
51
  return await track("db.get", table, async () => await this.inner.get(table, id));
43
52
  }
@@ -82,7 +91,6 @@ async function trackQuery(operation, fn) {
82
91
  dbMetrics().operationDuration.record(durationMs, { "db.operation": operation });
83
92
  }
84
93
  }
85
- //#endregion
86
94
  export { createInstrumentedDatabase };
87
95
 
88
96
  //# sourceMappingURL=instrument-db.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-db.mjs","names":[],"sources":["../src/instrument-db.ts"],"sourcesContent":["/**\n * Database adapter wrapper: one span + duration metric per operation.\n *\n * Span names are `db.<operation>` with the table as an attribute (never in\n * the name). Adapter identity (`metadata`, `lifecycle`, `tables`) and the\n * host-bound logger delegate straight through, so health/setup behave\n * identically with or without observability.\n */\nimport type { DatabaseAdapter, ListOptions, Tables } from \"@storyshelf/core/adapter/database\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { addTiming } from \"@storyshelf/core/utils\";\nimport { getTableName, type SQL, type Table } from \"drizzle-orm\";\nimport { dbMetrics } from \"./metrics.ts\";\nimport { withSpan } from \"./tracing.ts\";\n\n/**\n * Wrap a database adapter with per-operation spans and metrics.\n *\n * @param db - The adapter to wrap (never mutated).\n * @returns A transparent adapter emitting `db.*` telemetry.\n */\nexport function createInstrumentedDatabase(db: DatabaseAdapter): DatabaseAdapter {\n return new InstrumentedDatabase(db);\n}\n\nclass InstrumentedDatabase implements DatabaseAdapter {\n private readonly inner: DatabaseAdapter;\n\n constructor(inner: DatabaseAdapter) {\n this.inner = inner;\n }\n\n get metadata(): DatabaseAdapter[\"metadata\"] {\n return this.inner.metadata;\n }\n\n get lifecycle(): DatabaseAdapter[\"lifecycle\"] {\n return this.inner.lifecycle;\n }\n\n get tables(): Tables {\n return this.inner.tables;\n }\n\n setLogger(logger: Logger): void {\n this.inner.setLogger?.(logger);\n }\n\n async insert<T extends Table>(table: T, values: T[\"$inferInsert\"]): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.insert\", table, async () => await this.inner.insert(table, values));\n }\n\n async update<T extends Table>(\n table: T,\n id: string,\n values: Partial<T[\"$inferInsert\"]>,\n ): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.update\", table, async () => await this.inner.update(table, id, values));\n }\n\n async get<T extends Table>(table: T, id: string): Promise<T[\"$inferSelect\"] | null> {\n return await track(\"db.get\", table, async () => await this.inner.get(table, id));\n }\n\n async remove(table: Table, id: string): Promise<void> {\n await track(\"db.remove\", table, async () => {\n await this.inner.remove(table, id);\n });\n }\n\n async list<T extends Table>(table: T, opts?: ListOptions): Promise<T[\"$inferSelect\"][]> {\n return await track(\"db.list\", table, async () => await this.inner.list(table, opts));\n }\n\n async count(table: Table, where?: SQL): Promise<number> {\n return await track(\"db.count\", table, async () => await this.inner.count(table, where));\n }\n\n async all<T>(query: SQL): Promise<T[]> {\n return await trackQuery(\"db.all\", async () => await this.inner.all<T>(query));\n }\n}\n\n/** Run a table operation inside a span with a duration measurement. */\nasync function track<T>(operation: string, table: Table, fn: () => Promise<T>): Promise<T> {\n const tableName = getTableName(table);\n const start = performance.now();\n try {\n return await withSpan(operation, fn, { \"db.table\": tableName });\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n \"db.table\": tableName,\n });\n }\n}\n\n/** Run a table-less query inside a span with a duration measurement. */\nasync function trackQuery<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n try {\n return await withSpan(operation, fn);\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,SAAgB,2BAA2B,IAAsC;CAC/E,OAAO,IAAI,qBAAqB,EAAE;AACpC;AAEA,IAAM,uBAAN,MAAsD;CACpD;CAEA,YAAY,OAAwB;EAClC,KAAK,QAAQ;CACf;CAEA,IAAI,WAAwC;EAC1C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,YAA0C;EAC5C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,SAAiB;EACnB,OAAO,KAAK,MAAM;CACpB;CAEA,UAAU,QAAsB;EAC9B,KAAK,MAAM,YAAY,MAAM;CAC/B;CAEA,MAAM,OAAwB,OAAU,QAAuD;EAC7F,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC;CAC3F;CAEA,MAAM,OACJ,OACA,IACA,QAC4B;EAC5B,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,IAAI,MAAM,CAAC;CAC/F;CAEA,MAAM,IAAqB,OAAU,IAA+C;EAClF,OAAO,MAAM,MAAM,UAAU,OAAO,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;CACjF;CAEA,MAAM,OAAO,OAAc,IAA2B;EACpD,MAAM,MAAM,aAAa,OAAO,YAAY;GAC1C,MAAM,KAAK,MAAM,OAAO,OAAO,EAAE;EACnC,CAAC;CACH;CAEA,MAAM,KAAsB,OAAU,MAAkD;EACtF,OAAO,MAAM,MAAM,WAAW,OAAO,YAAY,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC;CACrF;CAEA,MAAM,MAAM,OAAc,OAA8B;EACtD,OAAO,MAAM,MAAM,YAAY,OAAO,YAAY,MAAM,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC;CACxF;CAEA,MAAM,IAAO,OAA0B;EACrC,OAAO,MAAM,WAAW,UAAU,YAAY,MAAM,KAAK,MAAM,IAAO,KAAK,CAAC;CAC9E;AACF;;AAGA,eAAe,MAAS,WAAmB,OAAc,IAAkC;CACzF,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,IAAI,EAAE,YAAY,UAAU,CAAC;CAChE,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY;GAC/C,gBAAgB;GAChB,YAAY;EACd,CAAC;CACH;AACF;;AAGA,eAAe,WAAc,WAAmB,IAAkC;CAChF,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,EAAE;CACrC,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY,EAC/C,gBAAgB,UAClB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"instrument-db.mjs","names":[],"sources":["../src/instrument-db.ts"],"sourcesContent":["/**\n * Database adapter wrapper: one span + duration metric per operation.\n *\n * Span names are `db.<operation>` with the table as an attribute (never in\n * the name). Adapter identity (`metadata`, `lifecycle`, `tables`) and the\n * host-bound logger delegate straight through, so health/setup behave\n * identically with or without observability.\n */\nimport type {\n DatabaseAdapter,\n ListOptions,\n Tables,\n TxStore,\n} from \"@storyshelf/core/adapter/database\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { addTiming } from \"@storyshelf/core/utils\";\nimport { getTableName, type SQL, type Table } from \"drizzle-orm\";\nimport { dbMetrics } from \"./metrics.ts\";\nimport { withSpan } from \"./tracing.ts\";\n\n/**\n * Wrap a database adapter with per-operation spans and metrics.\n *\n * @param db - The adapter to wrap (never mutated).\n * @returns A transparent adapter emitting `db.*` telemetry.\n */\nexport function createInstrumentedDatabase(db: DatabaseAdapter): DatabaseAdapter {\n return new InstrumentedDatabase(db);\n}\n\nclass InstrumentedDatabase implements DatabaseAdapter {\n private readonly inner: DatabaseAdapter;\n readonly transact: DatabaseAdapter[\"transact\"];\n\n constructor(inner: DatabaseAdapter) {\n this.inner = inner;\n const transact = inner.transact?.bind(inner);\n this.transact = transact\n ? async <Result>(fn: (tx: TxStore) => Promise<Result>): Promise<Result> =>\n await trackQuery(\"db.transact\", async () => await transact(fn))\n : undefined;\n }\n\n get metadata(): DatabaseAdapter[\"metadata\"] {\n return this.inner.metadata;\n }\n\n get lifecycle(): DatabaseAdapter[\"lifecycle\"] {\n return this.inner.lifecycle;\n }\n\n get buildReferenceLifecycle(): DatabaseAdapter[\"buildReferenceLifecycle\"] {\n return this.inner.buildReferenceLifecycle;\n }\n\n get tables(): Tables {\n return this.inner.tables;\n }\n\n setLogger(logger: Logger): void {\n this.inner.setLogger?.(logger);\n }\n\n async insert<T extends Table>(table: T, values: T[\"$inferInsert\"]): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.insert\", table, async () => await this.inner.insert(table, values));\n }\n\n async update<T extends Table>(\n table: T,\n id: string,\n values: Partial<T[\"$inferInsert\"]>,\n ): Promise<T[\"$inferSelect\"]> {\n return await track(\"db.update\", table, async () => await this.inner.update(table, id, values));\n }\n\n async updateWhere<T extends Table>(\n table: T,\n id: string,\n values: Partial<T[\"$inferInsert\"]>,\n where: SQL,\n ): Promise<T[\"$inferSelect\"] | null> {\n return await track(\n \"db.updateWhere\",\n table,\n async () => await this.inner.updateWhere(table, id, values, where),\n );\n }\n\n async get<T extends Table>(table: T, id: string): Promise<T[\"$inferSelect\"] | null> {\n return await track(\"db.get\", table, async () => await this.inner.get(table, id));\n }\n\n async remove(table: Table, id: string): Promise<void> {\n await track(\"db.remove\", table, async () => {\n await this.inner.remove(table, id);\n });\n }\n\n async list<T extends Table>(table: T, opts?: ListOptions): Promise<T[\"$inferSelect\"][]> {\n return await track(\"db.list\", table, async () => await this.inner.list(table, opts));\n }\n\n async count(table: Table, where?: SQL): Promise<number> {\n return await track(\"db.count\", table, async () => await this.inner.count(table, where));\n }\n\n async all<T>(query: SQL): Promise<T[]> {\n return await trackQuery(\"db.all\", async () => await this.inner.all<T>(query));\n }\n}\n\n/** Run a table operation inside a span with a duration measurement. */\nasync function track<T>(operation: string, table: Table, fn: () => Promise<T>): Promise<T> {\n const tableName = getTableName(table);\n const start = performance.now();\n try {\n return await withSpan(operation, fn, { \"db.table\": tableName });\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n \"db.table\": tableName,\n });\n }\n}\n\n/** Run a table-less query inside a span with a duration measurement. */\nasync function trackQuery<T>(operation: string, fn: () => Promise<T>): Promise<T> {\n const start = performance.now();\n try {\n return await withSpan(operation, fn);\n } finally {\n const durationMs = performance.now() - start;\n addTiming(\"db\", durationMs);\n dbMetrics().operationDuration.record(durationMs, {\n \"db.operation\": operation,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AA0BA,SAAgB,2BAA2B,IAAsC;CAC/E,OAAO,IAAI,qBAAqB,EAAE;AACpC;AAEA,IAAM,uBAAN,MAAsD;CACpD;CACA;CAEA,YAAY,OAAwB;EAClC,KAAK,QAAQ;EACb,MAAM,WAAW,MAAM,UAAU,KAAK,KAAK;EAC3C,KAAK,WAAW,WACZ,OAAe,OACb,MAAM,WAAW,eAAe,YAAY,MAAM,SAAS,EAAE,CAAC,IAChE,KAAA;CACN;CAEA,IAAI,WAAwC;EAC1C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,YAA0C;EAC5C,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,0BAAsE;EACxE,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,SAAiB;EACnB,OAAO,KAAK,MAAM;CACpB;CAEA,UAAU,QAAsB;EAC9B,KAAK,MAAM,YAAY,MAAM;CAC/B;CAEA,MAAM,OAAwB,OAAU,QAAuD;EAC7F,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC;CAC3F;CAEA,MAAM,OACJ,OACA,IACA,QAC4B;EAC5B,OAAO,MAAM,MAAM,aAAa,OAAO,YAAY,MAAM,KAAK,MAAM,OAAO,OAAO,IAAI,MAAM,CAAC;CAC/F;CAEA,MAAM,YACJ,OACA,IACA,QACA,OACmC;EACnC,OAAO,MAAM,MACX,kBACA,OACA,YAAY,MAAM,KAAK,MAAM,YAAY,OAAO,IAAI,QAAQ,KAAK,CACnE;CACF;CAEA,MAAM,IAAqB,OAAU,IAA+C;EAClF,OAAO,MAAM,MAAM,UAAU,OAAO,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC;CACjF;CAEA,MAAM,OAAO,OAAc,IAA2B;EACpD,MAAM,MAAM,aAAa,OAAO,YAAY;GAC1C,MAAM,KAAK,MAAM,OAAO,OAAO,EAAE;EACnC,CAAC;CACH;CAEA,MAAM,KAAsB,OAAU,MAAkD;EACtF,OAAO,MAAM,MAAM,WAAW,OAAO,YAAY,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC;CACrF;CAEA,MAAM,MAAM,OAAc,OAA8B;EACtD,OAAO,MAAM,MAAM,YAAY,OAAO,YAAY,MAAM,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC;CACxF;CAEA,MAAM,IAAO,OAA0B;EACrC,OAAO,MAAM,WAAW,UAAU,YAAY,MAAM,KAAK,MAAM,IAAO,KAAK,CAAC;CAC9E;AACF;;AAGA,eAAe,MAAS,WAAmB,OAAc,IAAkC;CACzF,MAAM,YAAY,aAAa,KAAK;CACpC,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,IAAI,EAAE,YAAY,UAAU,CAAC;CAChE,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY;GAC/C,gBAAgB;GAChB,YAAY;EACd,CAAC;CACH;AACF;;AAGA,eAAe,WAAc,WAAmB,IAAkC;CAChF,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;EACF,OAAO,MAAM,SAAS,WAAW,EAAE;CACrC,UAAU;EACR,MAAM,aAAa,YAAY,IAAI,IAAI;EACvC,UAAU,MAAM,UAAU;EAC1B,UAAU,CAAC,CAAC,kBAAkB,OAAO,YAAY,EAC/C,gBAAgB,UAClB,CAAC;CACH;AACF"}
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { StorageAdapter } from "@storyshelf/core/adapter/storage";
4
- //#region src/instrument-storage.d.ts
5
4
  /**
6
5
  * Wrap a storage adapter with per-operation spans and metrics.
7
6
  *
@@ -9,6 +8,5 @@ import { StorageAdapter } from "@storyshelf/core/adapter/storage";
9
8
  * @returns A transparent adapter emitting `storage.*` telemetry.
10
9
  */
11
10
  declare function createInstrumentedStorage(storage: StorageAdapter): StorageAdapter;
12
- //#endregion
13
11
  export { createInstrumentedStorage };
14
12
  //# sourceMappingURL=instrument-storage.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { storageMetrics } from "./metrics.mjs";
5
5
  import { withSpan } from "./tracing.mjs";
6
6
  import { addTiming } from "@storyshelf/core/utils";
7
- //#region src/instrument-storage.ts
7
+ // src/instrument-storage.ts
8
8
  /**
9
9
  * Wrap a storage adapter with per-operation spans and metrics.
10
10
  *
@@ -67,7 +67,6 @@ async function track(operation, path, fn) {
67
67
  storageMetrics().operationDuration.record(durationMs, { "storage.operation": operation });
68
68
  }
69
69
  }
70
- //#endregion
71
70
  export { createInstrumentedStorage };
72
71
 
73
72
  //# sourceMappingURL=instrument-storage.mjs.map
package/dist/logs.d.mts CHANGED
@@ -1,12 +1,10 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/logs.d.ts
4
3
  /**
5
4
  * Build the trace-correlation fields for one log line.
6
5
  *
7
6
  * @returns `{ trace_id, span_id }` when a valid span is active, else `{}`.
8
7
  */
9
8
  declare function otelLogMixin(): Record<string, string>;
10
- //#endregion
11
9
  export { otelLogMixin };
12
10
  //# sourceMappingURL=logs.d.mts.map
package/dist/logs.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { context, trace } from "@opentelemetry/api";
5
- //#region src/logs.ts
5
+ // src/logs.ts
6
6
  /**
7
7
  * Pino log correlation: inject the active trace context into every log line.
8
8
  *
@@ -24,7 +24,6 @@ function otelLogMixin() {
24
24
  span_id: spanContext.spanId
25
25
  };
26
26
  }
27
- //#endregion
28
27
  export { otelLogMixin };
29
28
 
30
29
  //# sourceMappingURL=logs.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Counter, Histogram } from "@opentelemetry/api";
4
- //#region src/metrics.d.ts
5
4
  /** Capture-pipeline instruments. */
6
5
  interface CaptureInstruments {
7
6
  /** Capture job wall duration in milliseconds (`capture.job.status`). */
@@ -27,6 +26,5 @@ declare function captureMetrics(): CaptureInstruments;
27
26
  declare function dbMetrics(): DbInstruments;
28
27
  /** Storage-operation instruments (created once). */
29
28
  declare function storageMetrics(): StorageInstruments;
30
- //#endregion
31
29
  export { CaptureInstruments, DbInstruments, StorageInstruments, captureMetrics, dbMetrics, storageMetrics };
32
30
  //# sourceMappingURL=metrics.d.mts.map
package/dist/metrics.mjs CHANGED
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { metrics } from "@opentelemetry/api";
5
- //#region src/metrics.ts
5
+ // src/metrics.ts
6
6
  /**
7
7
  * Memoized OTEL instruments (global meter, `storyshelf` scope).
8
8
  *
@@ -38,7 +38,6 @@ function storageMetrics() {
38
38
  if (!storage) storage = { operationDuration: metrics.getMeter(METER_NAME).createHistogram("storage.operation.duration", { unit: "ms" }) };
39
39
  return storage;
40
40
  }
41
- //#endregion
42
41
  export { captureMetrics, dbMetrics, storageMetrics };
43
42
 
44
43
  //# sourceMappingURL=metrics.mjs.map
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { HttpMiddlewareOptions } from "./types.mjs";
4
4
  import { MiddlewareHandler } from "hono";
5
- //#region src/middleware.d.ts
6
5
  /**
7
6
  * Create the instrumenting middleware.
8
7
  *
@@ -13,6 +12,5 @@ import { MiddlewareHandler } from "hono";
13
12
  * @returns Hono middleware producing one `http.server` span per request.
14
13
  */
15
14
  declare function createHttpMiddleware(options?: HttpMiddlewareOptions): MiddlewareHandler;
16
- //#endregion
17
15
  export { createHttpMiddleware };
18
16
  //# sourceMappingURL=middleware.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { redactSecrets } from "@storyshelf/core/utils";
5
5
  import { context, trace } from "@opentelemetry/api";
6
6
  import { httpInstrumentationMiddleware } from "@hono/otel";
7
- //#region src/middleware.ts
7
+ // src/middleware.ts
8
8
  /**
9
9
  * Hono HTTP middleware: `@hono/otel` server spans plus StoryShelf context.
10
10
  *
@@ -49,7 +49,6 @@ function createHttpMiddleware(options = {}) {
49
49
  });
50
50
  };
51
51
  }
52
- //#endregion
53
52
  export { createHttpMiddleware };
54
53
 
55
54
  //# sourceMappingURL=middleware.mjs.map
package/dist/node.d.mts CHANGED
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ObservabilityHandle } from "./types.mjs";
4
4
  import { ObservabilityConfig, resolveObservabilityConfig } from "./config.mjs";
5
- //#region src/node.d.ts
6
5
  /**
7
6
  * Initialize observability from the environment.
8
7
  *
@@ -15,6 +14,5 @@ import { ObservabilityConfig, resolveObservabilityConfig } from "./config.mjs";
15
14
  * @returns The SDK handle (check `enabled`, always call `shutdown`).
16
15
  */
17
16
  declare function initObservabilityFromEnv(env?: NodeJS.ProcessEnv): Promise<ObservabilityHandle>;
18
- //#endregion
19
17
  export { type ObservabilityConfig, type ObservabilityHandle, initObservabilityFromEnv, resolveObservabilityConfig };
20
18
  //# sourceMappingURL=node.d.mts.map
package/dist/node.mjs CHANGED
@@ -8,7 +8,7 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
8
8
  import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
9
9
  import { NodeSDK } from "@opentelemetry/sdk-node";
10
10
  import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME, ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
11
- //#region src/node.ts
11
+ // src/node.ts
12
12
  /**
13
13
  * Node-only OTEL SDK lifecycle.
14
14
  *
@@ -77,7 +77,6 @@ async function initObservabilityFromEnv(env = process.env) {
77
77
  if (!config.enabled) return noopHandle();
78
78
  return startSdk(config);
79
79
  }
80
- //#endregion
81
80
  export { initObservabilityFromEnv, resolveObservabilityConfig };
82
81
 
83
82
  //# sourceMappingURL=node.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Context } from "@opentelemetry/api";
4
- //#region src/propagate.d.ts
5
4
  /** W3C traceparent header name. */
6
5
  declare const TRACEPARENT_HEADER = "traceparent";
7
6
  /**
@@ -19,6 +18,5 @@ declare function injectTraceContext(headers: Record<string, string>): void;
19
18
  * @returns The header value, or undefined when no span is active.
20
19
  */
21
20
  declare function currentTraceparent(): string | undefined;
22
- //#endregion
23
21
  export { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext };
24
22
  //# sourceMappingURL=propagate.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { context, propagation } from "@opentelemetry/api";
5
- //#region src/propagate.ts
5
+ // src/propagate.ts
6
6
  /**
7
7
  * W3C trace-context propagation helpers.
8
8
  *
@@ -36,7 +36,6 @@ function currentTraceparent() {
36
36
  propagation.inject(context.active(), carrier);
37
37
  return carrier[TRACEPARENT_HEADER];
38
38
  }
39
- //#endregion
40
39
  export { TRACEPARENT_HEADER, currentTraceparent, injectTraceContext, parentContext };
41
40
 
42
41
  //# sourceMappingURL=propagate.mjs.map
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Attributes, Context, Span } from "@opentelemetry/api";
4
- //#region src/tracing.d.ts
5
4
  /**
6
5
  * Run `fn` inside an internal span.
7
6
  *
@@ -15,6 +14,5 @@ import { Attributes, Context, Span } from "@opentelemetry/api";
15
14
  * @returns Whatever `fn` returns.
16
15
  */
17
16
  declare function withSpan<T>(name: string, fn: (span: Span) => Promise<T>, attributes?: Attributes, parent?: Context): Promise<T>;
18
- //#endregion
19
17
  export { withSpan };
20
18
  //# sourceMappingURL=tracing.d.mts.map
package/dist/tracing.mjs CHANGED
@@ -1,13 +1,30 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { HttpError, redactSecrets } from "@storyshelf/core/utils";
4
5
  import { SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
5
- //#region src/tracing.ts
6
+ // src/tracing.ts
6
7
  /**
7
8
  * Span helpers over `@opentelemetry/api` (runtime-agnostic: Node SDK or
8
9
  * Deno's native bridge). Without a configured provider these are cheap
9
10
  * noops, so call sites need no enabled-checks.
10
11
  */
12
+ /**
13
+ * What a span records of a caught error: a copy with the same `name` and a `redactSecrets` message
14
+ * (connection strings, tokens and keys stay out of exported spans), and for an `HttpError` its
15
+ * status only, never the response body. The caller still gets the original error rethrown.
16
+ * Intentional copy of core's `spanException` (ADR 0015 §5 no-cycle rule: do not deduplicate).
17
+ */
18
+ function recordedError(error) {
19
+ if (!(error instanceof Error)) return redactSecrets(String(error));
20
+ const message = error instanceof HttpError ? `HTTP ${String(error.status)}` : redactSecrets(error.message);
21
+ const copy = new Error(message);
22
+ copy.name = error.name;
23
+ const header = `${error.name}: ${error.message}`;
24
+ const frames = error.stack?.startsWith(header) ? error.stack.slice(header.length) : "";
25
+ copy.stack = `${error.name}: ${message}${redactSecrets(frames)}`;
26
+ return copy;
27
+ }
11
28
  /** Instrumentation scope for observability-package spans. */
12
29
  const SCOPE = "storyshelf.observability";
13
30
  /**
@@ -30,7 +47,7 @@ async function withSpan(name, fn, attributes, parent) {
30
47
  try {
31
48
  return await fn(span);
32
49
  } catch (error) {
33
- span.recordException(error instanceof Error ? error : String(error));
50
+ span.recordException(recordedError(error));
34
51
  span.setStatus({ code: SpanStatusCode.ERROR });
35
52
  throw error;
36
53
  } finally {
@@ -38,7 +55,6 @@ async function withSpan(name, fn, attributes, parent) {
38
55
  }
39
56
  });
40
57
  }
41
- //#endregion
42
58
  export { withSpan };
43
59
 
44
60
  //# sourceMappingURL=tracing.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Span helpers over `@opentelemetry/api` (runtime-agnostic: Node SDK or\n * Deno's native bridge). Without a configured provider these are cheap\n * noops, so call sites need no enabled-checks.\n */\nimport {\n SpanKind,\n SpanStatusCode,\n context,\n trace,\n type Attributes,\n type Context,\n type Span,\n} from \"@opentelemetry/api\";\n\n/** Instrumentation scope for observability-package spans. */\nconst SCOPE = \"storyshelf.observability\";\n\n/**\n * Run `fn` inside an internal span.\n *\n * On throw the span records the exception, is marked ERROR, and the error\n * is rethrown — mirroring the pino `{ err }` convention.\n *\n * @param name - Span name (`domain.operation`, never a high-cardinality value).\n * @param fn - Work to run with the span active.\n * @param attributes - Fixed span attributes (no high-cardinality values in names).\n * @param parent - Parent context (defaults to the active context).\n * @returns Whatever `fn` returns.\n */\nexport async function withSpan<T>(\n name: string,\n fn: (span: Span) => Promise<T>,\n attributes?: Attributes,\n parent?: Context,\n): Promise<T> {\n const tracer = trace.getTracer(SCOPE);\n return await tracer.startActiveSpan(\n name,\n { kind: SpanKind.INTERNAL, attributes },\n parent ?? context.active(),\n async (span) => {\n try {\n return await fn(span);\n } catch (error) {\n span.recordException(error instanceof Error ? error : String(error));\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAM,QAAQ;;;;;;;;;;;;;AAcd,eAAsB,SACpB,MACA,IACA,YACA,QACY;CAEZ,OAAO,MADQ,MAAM,UAAU,KACb,CAAC,CAAC,gBAClB,MACA;EAAE,MAAM,SAAS;EAAU;CAAW,GACtC,UAAU,QAAQ,OAAO,GACzB,OAAO,SAAS;EACd,IAAI;GACF,OAAO,MAAM,GAAG,IAAI;EACtB,SAAS,OAAO;GACd,KAAK,gBAAgB,iBAAiB,QAAQ,QAAQ,OAAO,KAAK,CAAC;GACnE,KAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;GAC7C,MAAM;EACR,UAAU;GACR,KAAK,IAAI;EACX;CACF,CACF;AACF"}
1
+ {"version":3,"file":"tracing.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Span helpers over `@opentelemetry/api` (runtime-agnostic: Node SDK or\n * Deno's native bridge). Without a configured provider these are cheap\n * noops, so call sites need no enabled-checks.\n */\nimport {\n SpanKind,\n SpanStatusCode,\n context,\n trace,\n type Attributes,\n type Context,\n type Span,\n} from \"@opentelemetry/api\";\nimport { HttpError, redactSecrets } from \"@storyshelf/core/utils\";\n\n/**\n * What a span records of a caught error: a copy with the same `name` and a `redactSecrets` message\n * (connection strings, tokens and keys stay out of exported spans), and for an `HttpError` its\n * status only, never the response body. The caller still gets the original error rethrown.\n * Intentional copy of core's `spanException` (ADR 0015 §5 no-cycle rule: do not deduplicate).\n */\nfunction recordedError(error: unknown): Error | string {\n if (!(error instanceof Error)) {\n return redactSecrets(String(error));\n }\n const message =\n error instanceof HttpError ? `HTTP ${String(error.status)}` : redactSecrets(error.message);\n const copy = new Error(message);\n copy.name = error.name;\n const header = `${error.name}: ${error.message}`;\n const frames = error.stack?.startsWith(header) ? error.stack.slice(header.length) : \"\";\n copy.stack = `${error.name}: ${message}${redactSecrets(frames)}`;\n return copy;\n}\n\n/** Instrumentation scope for observability-package spans. */\nconst SCOPE = \"storyshelf.observability\";\n\n/**\n * Run `fn` inside an internal span.\n *\n * On throw the span records the exception, is marked ERROR, and the error\n * is rethrown — mirroring the pino `{ err }` convention.\n *\n * @param name - Span name (`domain.operation`, never a high-cardinality value).\n * @param fn - Work to run with the span active.\n * @param attributes - Fixed span attributes (no high-cardinality values in names).\n * @param parent - Parent context (defaults to the active context).\n * @returns Whatever `fn` returns.\n */\nexport async function withSpan<T>(\n name: string,\n fn: (span: Span) => Promise<T>,\n attributes?: Attributes,\n parent?: Context,\n): Promise<T> {\n const tracer = trace.getTracer(SCOPE);\n return await tracer.startActiveSpan(\n name,\n { kind: SpanKind.INTERNAL, attributes },\n parent ?? context.active(),\n async (span) => {\n try {\n return await fn(span);\n } catch (error) {\n span.recordException(recordedError(error));\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw error;\n } finally {\n span.end();\n }\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAsBA,SAAS,cAAc,OAAgC;CACrD,IAAI,EAAE,iBAAiB,QACrB,OAAO,cAAc,OAAO,KAAK,CAAC;CAEpC,MAAM,UACJ,iBAAiB,YAAY,QAAQ,OAAO,MAAM,MAAM,MAAM,cAAc,MAAM,OAAO;CAC3F,MAAM,OAAO,IAAI,MAAM,OAAO;CAC9B,KAAK,OAAO,MAAM;CAClB,MAAM,SAAS,GAAG,MAAM,KAAK,IAAI,MAAM;CACvC,MAAM,SAAS,MAAM,OAAO,WAAW,MAAM,IAAI,MAAM,MAAM,MAAM,OAAO,MAAM,IAAI;CACpF,KAAK,QAAQ,GAAG,MAAM,KAAK,IAAI,UAAU,cAAc,MAAM;CAC7D,OAAO;AACT;;AAGA,MAAM,QAAQ;;;;;;;;;;;;;AAcd,eAAsB,SACpB,MACA,IACA,YACA,QACY;CAEZ,OAAO,MADQ,MAAM,UAAU,KACb,CAAC,CAAC,gBAClB,MACA;EAAE,MAAM,SAAS;EAAU;CAAW,GACtC,UAAU,QAAQ,OAAO,GACzB,OAAO,SAAS;EACd,IAAI;GACF,OAAO,MAAM,GAAG,IAAI;EACtB,SAAS,OAAO;GACd,KAAK,gBAAgB,cAAc,KAAK,CAAC;GACzC,KAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;GAC7C,MAAM;EACR,UAAU;GACR,KAAK,IAAI;EACX;CACF,CACF;AACF"}
package/dist/types.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Attributes } from "@opentelemetry/api";
4
- //#region src/types.d.ts
5
4
  /** Attributes attached to a span or metric measurement. */
6
5
  type SpanAttributes = Attributes;
7
6
  /** Shutdown handle for the observability SDK (see `node.ts`). */
@@ -18,6 +17,5 @@ interface HttpMiddlewareOptions {
18
17
  /** Service version in spans/metrics. Defaults to the package version. */
19
18
  serviceVersion?: string;
20
19
  }
21
- //#endregion
22
20
  export { HttpMiddlewareOptions, ObservabilityHandle, SpanAttributes };
23
21
  //# sourceMappingURL=types.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storyshelf/observability",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "StoryShelf observability: OpenTelemetry tracing, metrics, and log correlation over @storyshelf/core adapters.",
5
5
  "keywords": [
6
6
  "metrics",
@@ -65,8 +65,8 @@
65
65
  "@opentelemetry/sdk-metrics": "^2.11.0",
66
66
  "@opentelemetry/sdk-node": "^0.222.0",
67
67
  "@opentelemetry/semantic-conventions": "^1.43.0",
68
- "@storyshelf/core": "0.7.0",
69
- "drizzle-orm": "^0.44.0",
68
+ "@storyshelf/core": "0.8.0",
69
+ "drizzle-orm": "^0.45.4",
70
70
  "hono": "^4.11.0"
71
71
  },
72
72
  "devDependencies": {
@@ -82,7 +82,7 @@
82
82
  "oxlint-tsgolint": "^7.0.2001",
83
83
  "tsdown": "^0.22.14",
84
84
  "typescript": "^7.0.2",
85
- "vitest": "^5.0.0"
85
+ "vitest": "^5.0.5"
86
86
  },
87
87
  "engines": {
88
88
  "node": ">=24"
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * StoryShelf observability: runtime-agnostic OTEL instrumentation.\n *\n * This entrypoint is cross-runtime (`@opentelemetry/api` only — Node SDK or\n * Deno's native bridge supplies the provider). The Node SDK lifecycle\n * (`initObservabilityFromEnv`) lives under `@storyshelf/observability/node`\n * and must never be imported here.\n */\nimport { createInstrumentedDatabase } from \"./instrument-db.ts\";\nimport { createInstrumentedStorage } from \"./instrument-storage.ts\";\nimport { createHttpMiddleware } from \"./middleware.ts\";\n\n/** Wrap a database adapter with `db.*` spans and metrics. */\nexport { createInstrumentedDatabase };\n/** Wrap a storage adapter with `storage.*` spans and metrics. */\nexport { createInstrumentedStorage };\n/** Hono middleware emitting one `http.server` span per request. */\nexport { createHttpMiddleware };\nexport { captureMetrics, dbMetrics, storageMetrics } from \"./metrics.ts\";\nexport type { CaptureInstruments, DbInstruments, StorageInstruments } from \"./metrics.ts\";\nexport { otelLogMixin } from \"./logs.ts\";\nexport {\n TRACEPARENT_HEADER,\n currentTraceparent,\n injectTraceContext,\n parentContext,\n} from \"./propagate.ts\";\nexport { resolveObservabilityConfig } from \"./config.ts\";\nexport type { ObservabilityConfig } from \"./config.ts\";\nexport { withSpan } from \"./tracing.ts\";\nexport type { HttpMiddlewareOptions, ObservabilityHandle, SpanAttributes } from \"./types.ts\";\n"],"mappings":""}