@bentocache/otel 0.1.2 → 0.2.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 CHANGED
@@ -8,6 +8,8 @@ Official OpenTelemetry instrumentation for Bentocache.
8
8
  pnpm add @bentocache/otel
9
9
  ```
10
10
 
11
+ Requires OpenTelemetry JS SDK 3 (`@opentelemetry/api` 1.10) and Node.js 22.15.0 or newer. For OpenTelemetry SDK 2, use `@bentocache/otel@0.1.x`.
12
+
11
13
  ## Usage
12
14
 
13
15
  ```ts
package/build/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Span } from '@opentelemetry/api';
2
1
  import { CacheOperationMessage } from 'bentocache';
2
+ import { Span } from '@opentelemetry/api';
3
3
  import { TracingChannelSubscribers } from 'node:diagnostics_channel';
4
4
  import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
5
5
  import { BentoCacheInstrumentationConfig, BentoCacheModuleExports } from './src/types.js';
package/build/index.js CHANGED
@@ -5,6 +5,105 @@ import {
5
5
  InstrumentationBase,
6
6
  InstrumentationNodeModuleDefinition
7
7
  } from "@opentelemetry/instrumentation";
8
+
9
+ // package.json
10
+ var package_default = {
11
+ name: "@bentocache/otel",
12
+ version: "0.2.0",
13
+ description: "OpenTelemetry instrumentation for Bentocache",
14
+ keywords: [
15
+ "bentocache",
16
+ "instrumentation",
17
+ "opentelemetry",
18
+ "otel",
19
+ "tracing"
20
+ ],
21
+ license: "MIT",
22
+ author: "Julien Ripouteau <julien@ripouteau.com>",
23
+ repository: {
24
+ type: "git",
25
+ url: "git+https://github.com/Julien-R44/bentocache.git",
26
+ directory: "packages/otel"
27
+ },
28
+ files: [
29
+ "build"
30
+ ],
31
+ type: "module",
32
+ main: "build/index.js",
33
+ exports: {
34
+ ".": "./build/index.js",
35
+ "./types": "./build/src/types.js"
36
+ },
37
+ publishConfig: {
38
+ access: "public",
39
+ tag: "latest"
40
+ },
41
+ scripts: {
42
+ test: "c8 pnpm quick:test",
43
+ "quick:test": "cross-env NODE_NO_WARNINGS=1 node --enable-source-maps --import=tsx bin/test.ts",
44
+ build: "tsup-node",
45
+ typecheck: "tsc --noEmit",
46
+ lint: "oxlint",
47
+ release: "pnpm build && pnpm release-it",
48
+ prepublishOnly: "pnpm build",
49
+ checks: "pnpm lint && pnpm typecheck"
50
+ },
51
+ dependencies: {
52
+ "@opentelemetry/core": "^3.1.1",
53
+ "@opentelemetry/instrumentation": "^0.301.1"
54
+ },
55
+ devDependencies: {
56
+ "@julr/utils": "^1.9.0",
57
+ "@opentelemetry/api": "^1.10.0",
58
+ "@opentelemetry/context-async-hooks": "^3.1.1",
59
+ "@opentelemetry/sdk-trace": "^3.1.1",
60
+ bentocache: "workspace:*"
61
+ },
62
+ peerDependencies: {
63
+ "@opentelemetry/api": "^1.10.0",
64
+ bentocache: "^1.6.0"
65
+ },
66
+ c8: {
67
+ exclude: [
68
+ "bin",
69
+ "tests/**",
70
+ "test_helpers/**",
71
+ "factories/**"
72
+ ],
73
+ reporter: [
74
+ "text",
75
+ "html"
76
+ ]
77
+ },
78
+ engines: {
79
+ node: ">=22.15.0"
80
+ },
81
+ "release-it": {
82
+ git: {
83
+ commitMessage: "chore(release): @bentocache/otel@${version}",
84
+ tagAnnotation: "release ${version}",
85
+ tagName: "@bentocache/otel@${version}"
86
+ },
87
+ github: {
88
+ release: true,
89
+ releaseName: "@bentocache/otel@${version}",
90
+ web: true
91
+ }
92
+ },
93
+ tsup: {
94
+ entry: [
95
+ "./index.ts",
96
+ "./src/types.ts"
97
+ ],
98
+ outDir: "./build",
99
+ clean: true,
100
+ format: "esm",
101
+ dts: true,
102
+ target: "esnext"
103
+ }
104
+ };
105
+
106
+ // src/instrumentation.ts
8
107
  var DEFAULT_CONFIG = {
9
108
  requireParentSpan: true,
10
109
  includeKeys: false,
@@ -16,10 +115,11 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
16
115
  spans = /* @__PURE__ */ new WeakMap();
17
116
  handlers;
18
117
  /**
19
- * Tracks active getOrSet contexts so child operations (factory, set)
20
- * can be parented to the getOrSet span. Keyed by `store:key`.
118
+ * Context tokens attached on `start` and disposed on the synchronous `end`
119
+ * event, so the operation span is the active context for everything the
120
+ * operation runs: nested cache operations and the user factory.
21
121
  */
22
- #activeContexts = /* @__PURE__ */ new Map();
122
+ #contextTokens = /* @__PURE__ */ new WeakMap();
23
123
  #cacheOperation;
24
124
  #originalBentoCache;
25
125
  /**
@@ -42,20 +142,22 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
42
142
  }
43
143
  if (moduleExports.BentoCache !== this.#originalBentoCache) return;
44
144
  const original = this.#originalBentoCache;
45
- const instrumentation = this;
145
+ const patchConfig = this.#patchConfig.bind(this);
46
146
  const BentoCachePatched = class BentoCachePatched extends original {
47
147
  /**
48
148
  * Wraps user config before delegating to the original BentoCache constructor.
49
149
  */
50
150
  constructor(config) {
51
- super(instrumentation.#patchConfig(config));
151
+ super(patchConfig(config));
52
152
  }
53
153
  };
54
154
  Object.setPrototypeOf(BentoCachePatched, original);
55
155
  try {
56
156
  moduleExports.BentoCache = BentoCachePatched;
57
157
  } catch {
58
- return;
158
+ this._diag.warn(
159
+ "Cannot patch the BentoCache constructor because the module exports are read-only (ESM namespace). suppressInternalOperations has no effect on caches created from these exports."
160
+ );
59
161
  }
60
162
  }
61
163
  /**
@@ -93,8 +195,7 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
93
195
  this.subscribed = true;
94
196
  this.handlers = {
95
197
  start: (message) => this.#handleStart(message),
96
- end: () => {
97
- },
198
+ end: (message) => this.#handleEnd(message),
98
199
  asyncStart: () => {
99
200
  },
100
201
  asyncEnd: (message) => this.#handleAsyncEnd(message),
@@ -111,17 +212,15 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
111
212
  this.subscribed = false;
112
213
  this.handlers = void 0;
113
214
  this.spans = /* @__PURE__ */ new WeakMap();
114
- this.#activeContexts.clear();
115
215
  }
116
216
  /**
117
- * Starts a span for each cache operation event and tracks getOrSet parent contexts.
217
+ * Starts a span for each cache operation and makes it the active context
218
+ * while the operation runs, so nested operations and the factory are parented to it.
118
219
  */
119
220
  #handleStart(message) {
120
221
  const config = this.getConfig();
121
- const contextKey = `${message.store}:${message.key}`;
122
- const parentContext = this.#activeContexts.get(contextKey) ?? context.active();
123
- const parentSpan = trace.getSpan(parentContext);
124
- if (config.requireParentSpan && !parentSpan) return;
222
+ const parentContext = context.active();
223
+ if (config.requireParentSpan && !trace.getSpan(parentContext)) return;
125
224
  const spanName = this.#getSpanName(message);
126
225
  const span = this.tracer.startSpan(
127
226
  spanName,
@@ -132,9 +231,15 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
132
231
  parentContext
133
232
  );
134
233
  this.spans.set(message, span);
135
- if (message.operation === "getOrSet") {
136
- this.#activeContexts.set(contextKey, trace.setSpan(parentContext, span));
137
- }
234
+ this.#contextTokens.set(message, context.attach(trace.setSpan(parentContext, span)));
235
+ }
236
+ /**
237
+ * Restores the caller context once the operation has returned its promise.
238
+ * Async continuations created by the operation keep the attached context.
239
+ */
240
+ #handleEnd(message) {
241
+ this.#contextTokens.get(message)?.dispose();
242
+ this.#contextTokens.delete(message);
138
243
  }
139
244
  /**
140
245
  * Finalizes successful async spans and records result attributes.
@@ -147,9 +252,6 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
147
252
  if (message.graced !== void 0) span.setAttribute("cache.graced", message.graced);
148
253
  span.end();
149
254
  this.spans.delete(message);
150
- if (message.operation === "getOrSet") {
151
- this.#activeContexts.delete(`${message.store}:${message.key}`);
152
- }
153
255
  }
154
256
  /**
155
257
  * Finalizes failed spans and records exception details when available.
@@ -163,9 +265,6 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
163
265
  }
164
266
  span.end();
165
267
  this.spans.delete(message);
166
- if (message.operation === "getOrSet") {
167
- this.#activeContexts.delete(`${message.store}:${message.key}`);
168
- }
169
268
  }
170
269
  /**
171
270
  * Builds the final span name using custom naming or the configured prefix.
@@ -201,7 +300,7 @@ var BentoCacheInstrumentation = class extends InstrumentationBase {
201
300
  * Creates a new instrumentation instance with default and user config merged.
202
301
  */
203
302
  constructor(config = {}) {
204
- super("@bentocache/otel", "0.1.0", { ...DEFAULT_CONFIG, ...config });
303
+ super(package_default.name, package_default.version, { ...DEFAULT_CONFIG, ...config });
205
304
  }
206
305
  /**
207
306
  * Applies runtime config updates while preserving default values.
package/package.json CHANGED
@@ -1,64 +1,63 @@
1
1
  {
2
2
  "name": "@bentocache/otel",
3
- "type": "module",
4
- "version": "0.1.2",
3
+ "version": "0.2.0",
5
4
  "description": "OpenTelemetry instrumentation for Bentocache",
6
- "author": "Julien Ripouteau <julien@ripouteau.com>",
7
- "license": "MIT",
8
5
  "keywords": [
9
6
  "bentocache",
7
+ "instrumentation",
10
8
  "opentelemetry",
11
9
  "otel",
12
- "instrumentation",
13
10
  "tracing"
14
11
  ],
15
- "exports": {
16
- ".": "./build/index.js",
17
- "./types": "./build/src/types.js"
12
+ "license": "MIT",
13
+ "author": "Julien Ripouteau <julien@ripouteau.com>",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Julien-R44/bentocache.git",
17
+ "directory": "packages/otel"
18
18
  },
19
- "main": "build/index.js",
20
19
  "files": [
21
20
  "build"
22
21
  ],
23
- "peerDependencies": {
24
- "bentocache": "^1.6.0"
22
+ "type": "module",
23
+ "main": "build/index.js",
24
+ "exports": {
25
+ ".": "./build/index.js",
26
+ "./types": "./build/src/types.js"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "tag": "latest"
25
31
  },
26
32
  "dependencies": {
27
- "@opentelemetry/api": "^1.9.0",
28
- "@opentelemetry/core": "^2.5.0",
29
- "@opentelemetry/instrumentation": "^0.211.0"
33
+ "@opentelemetry/core": "^3.1.1",
34
+ "@opentelemetry/instrumentation": "^0.301.1"
30
35
  },
31
36
  "devDependencies": {
32
37
  "@julr/utils": "^1.9.0",
33
- "@opentelemetry/sdk-trace-base": "^2.5.0",
38
+ "@opentelemetry/api": "^1.10.0",
39
+ "@opentelemetry/context-async-hooks": "^3.1.1",
40
+ "@opentelemetry/sdk-trace": "^3.1.1",
34
41
  "bentocache": "1.6.1"
35
42
  },
36
- "tsup": {
37
- "entry": [
38
- "./index.ts",
39
- "./src/types.ts"
40
- ],
41
- "outDir": "./build",
42
- "clean": true,
43
- "format": "esm",
44
- "dts": true,
45
- "target": "esnext"
43
+ "peerDependencies": {
44
+ "@opentelemetry/api": "^1.10.0",
45
+ "bentocache": "^1.6.0"
46
46
  },
47
47
  "c8": {
48
- "reporter": [
49
- "text",
50
- "html"
51
- ],
52
48
  "exclude": [
53
49
  "bin",
54
50
  "tests/**",
55
51
  "test_helpers/**",
56
52
  "factories/**"
53
+ ],
54
+ "reporter": [
55
+ "text",
56
+ "html"
57
57
  ]
58
58
  },
59
- "publishConfig": {
60
- "access": "public",
61
- "tag": "latest"
59
+ "engines": {
60
+ "node": ">=22.15.0"
62
61
  },
63
62
  "release-it": {
64
63
  "git": {
@@ -72,12 +71,23 @@
72
71
  "web": true
73
72
  }
74
73
  },
74
+ "tsup": {
75
+ "entry": [
76
+ "./index.ts",
77
+ "./src/types.ts"
78
+ ],
79
+ "outDir": "./build",
80
+ "clean": true,
81
+ "format": "esm",
82
+ "dts": true,
83
+ "target": "esnext"
84
+ },
75
85
  "scripts": {
76
86
  "test": "c8 pnpm quick:test",
77
- "quick:test": "cross-env NODE_NO_WARNINGS=1 node --enable-source-maps --loader=ts-node/esm bin/test.ts",
87
+ "quick:test": "cross-env NODE_NO_WARNINGS=1 node --enable-source-maps --import=tsx bin/test.ts",
78
88
  "build": "tsup-node",
79
89
  "typecheck": "tsc --noEmit",
80
- "lint": "eslint .",
90
+ "lint": "oxlint",
81
91
  "release": "pnpm build && pnpm release-it",
82
92
  "checks": "pnpm lint && pnpm typecheck"
83
93
  }