@pingops/sdk 0.2.0 → 0.2.2

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
@@ -120,33 +120,33 @@ Configuration can be provided via:
120
120
 
121
121
  ### Required fields
122
122
 
123
- | Field | Env var | Description |
124
- |----------------|-----------------------|--------------------------------|
125
- | `baseUrl` | `PINGOPS_BASE_URL` | PingOps backend base URL |
126
- | `serviceName` | `PINGOPS_SERVICE_NAME`| Service name for resource |
123
+ | Field | Env var | Description |
124
+ | ------------- | ---------------------- | ------------------------- |
125
+ | `baseUrl` | `PINGOPS_BASE_URL` | PingOps backend base URL |
126
+ | `serviceName` | `PINGOPS_SERVICE_NAME` | Service name for resource |
127
127
 
128
128
  `apiKey` is optional at config level; if your backend requires it, set `apiKey` or `PINGOPS_API_KEY`.
129
129
 
130
130
  ### Full configuration reference
131
131
 
132
- | Option | Type | Default | Description |
133
- |-------------------------|-------------------------|------------|-------------|
134
- | `apiKey` | `string` | — | API key (or `PINGOPS_API_KEY`) |
135
- | `baseUrl` | `string` | **required** | Backend base URL |
136
- | `serviceName` | `string` | **required** | Service name |
137
- | `debug` | `boolean` | `false` | Enable debug logs (`PINGOPS_DEBUG=true`) |
138
- | `headersAllowList` | `string[]` | — | Headers to include (case-insensitive) |
139
- | `headersDenyList` | `string[]` | — | Headers to exclude (overrides allow) |
140
- | `captureRequestBody` | `boolean` | `false` | Capture request bodies (global) |
141
- | `captureResponseBody` | `boolean` | `false` | Capture response bodies (global) |
142
- | `maxRequestBodySize` | `number` | `4096` | Max request body size in bytes |
143
- | `maxResponseBodySize` | `number` | `4096` | Max response body size in bytes |
144
- | `domainAllowList` | `DomainRule[]` | — | Domains (and optional rules) to allow |
145
- | `domainDenyList` | `DomainRule[]` | — | Domains to exclude |
146
- | `headerRedaction` | `HeaderRedactionConfig` | — | Custom header redaction |
147
- | `batchSize` | `number` | `50` | Spans per batch (`PINGOPS_BATCH_SIZE`) |
148
- | `batchTimeout` | `number` | `5000` | Flush interval in ms (`PINGOPS_BATCH_TIMEOUT`) |
149
- | `exportMode` | `"batched"` \| `"immediate"` | `"batched"` | `PINGOPS_EXPORT_MODE` |
132
+ | Option | Type | Default | Description |
133
+ | --------------------- | ---------------------------- | ------------ | ---------------------------------------------- |
134
+ | `apiKey` | `string` | — | API key (or `PINGOPS_API_KEY`) |
135
+ | `baseUrl` | `string` | **required** | Backend base URL |
136
+ | `serviceName` | `string` | **required** | Service name |
137
+ | `debug` | `boolean` | `false` | Enable debug logs (`PINGOPS_DEBUG=true`) |
138
+ | `headersAllowList` | `string[]` | — | Headers to include (case-insensitive) |
139
+ | `headersDenyList` | `string[]` | — | Headers to exclude (overrides allow) |
140
+ | `captureRequestBody` | `boolean` | `false` | Capture request bodies (global) |
141
+ | `captureResponseBody` | `boolean` | `false` | Capture response bodies (global) |
142
+ | `maxRequestBodySize` | `number` | `4096` | Max request body size in bytes |
143
+ | `maxResponseBodySize` | `number` | `4096` | Max response body size in bytes |
144
+ | `domainAllowList` | `DomainRule[]` | — | Domains (and optional rules) to allow |
145
+ | `domainDenyList` | `DomainRule[]` | — | Domains to exclude |
146
+ | `headerRedaction` | `HeaderRedactionConfig` | — | Custom header redaction |
147
+ | `batchSize` | `number` | `50` | Spans per batch (`PINGOPS_BATCH_SIZE`) |
148
+ | `batchTimeout` | `number` | `5000` | Flush interval in ms (`PINGOPS_BATCH_TIMEOUT`) |
149
+ | `exportMode` | `"batched"` \| `"immediate"` | `"batched"` | `PINGOPS_EXPORT_MODE` |
150
150
 
151
151
  **Config file path:** Set `PINGOPS_CONFIG_FILE` to the path of your JSON or YAML file when using the register entry.
152
152
 
@@ -237,6 +237,10 @@ process.on("SIGTERM", async () => {
237
237
 
238
238
  Starts a new trace, sets PingOps attributes (e.g. `userId`, `sessionId`, tags, metadata) in context, runs the given function inside that context, and returns the function’s result. Any spans created inside the function (including automatic HTTP/fetch spans) are part of this trace and carry the same context.
239
239
 
240
+ If `startTrace` is called while the current OpenTelemetry context is tracing-suppressed, the SDK now starts the trace from a clean unsuppressed base context. This prevents leaked suppression flags from silently disabling outbound instrumentation for user traffic.
241
+
242
+ When using `initializePingops` without `startTrace`, SDK instrumentations also guard outbound user requests against leaked suppression context. Exporter traffic remains suppressed to avoid self-instrumentation recursion.
243
+
240
244
  **Parameters:**
241
245
 
242
246
  - `options.attributes` — Optional [PingopsTraceAttributes](#pingopstraceattributes) to attach to the trace and propagate to spans.
@@ -273,6 +277,24 @@ const data = await startTrace(
273
277
 
274
278
  ---
275
279
 
280
+ ### `runUnsuppressed(fn)`
281
+
282
+ Runs `fn` in a clean unsuppressed OpenTelemetry context. Use this at async task/job boundaries when you are not using `startTrace`, but still need outbound HTTP/fetch instrumentation to capture spans.
283
+
284
+ **Example:**
285
+
286
+ ```typescript
287
+ import { runUnsuppressed } from "@pingops/sdk";
288
+
289
+ await runUnsuppressed(async () => {
290
+ await fetch("https://api.example.com/work");
291
+ });
292
+ ```
293
+
294
+ This helper is scoped to the callback only and does not globally disable OpenTelemetry suppression.
295
+
296
+ ---
297
+
276
298
  ### `getActiveTraceId()`
277
299
 
278
300
  Returns the trace ID of the currently active span, or `undefined` if there is none.
@@ -306,15 +328,15 @@ const spanId = getActiveSpanId();
306
328
 
307
329
  Type for attributes you can pass into `startTrace({ attributes })`:
308
330
 
309
- | Field | Type | Description |
310
- |-------------------------|--------------------------|-------------|
311
- | `traceId` | `string` | Override trace ID (otherwise one is generated or derived from `seed`) |
312
- | `userId` | `string` | User identifier |
313
- | `sessionId` | `string` | Session identifier |
314
- | `tags` | `string[]` | Tags for the trace |
315
- | `metadata` | `Record<string, string>` | Key-value metadata |
316
- | `captureRequestBody` | `boolean` | Override request body capture for spans in this trace |
317
- | `captureResponseBody` | `boolean` | Override response body capture for spans in this trace |
331
+ | Field | Type | Description |
332
+ | --------------------- | ------------------------ | --------------------------------------------------------------------- |
333
+ | `traceId` | `string` | Override trace ID (otherwise one is generated or derived from `seed`) |
334
+ | `userId` | `string` | User identifier |
335
+ | `sessionId` | `string` | Session identifier |
336
+ | `tags` | `string[]` | Tags for the trace |
337
+ | `metadata` | `Record<string, string>` | Key-value metadata |
338
+ | `captureRequestBody` | `boolean` | Override request body capture for spans in this trace |
339
+ | `captureResponseBody` | `boolean` | Override response body capture for spans in this trace |
318
340
 
319
341
  ---
320
342
 
@@ -381,9 +403,7 @@ initializePingops({
381
403
  captureResponseBody: true,
382
404
  },
383
405
  ],
384
- domainDenyList: [
385
- { domain: "internal.corp.local" },
386
- ],
406
+ domainDenyList: [{ domain: "internal.corp.local" }],
387
407
  });
388
408
  ```
389
409
 
@@ -465,14 +485,15 @@ Only **CLIENT** spans with HTTP (or supported semantic) attributes are exported
465
485
 
466
486
  ## Summary
467
487
 
468
- | Goal | What to do |
469
- |------|------------|
470
- | Install | `pnpm add @pingops/sdk` |
471
- | Auto-init from env | `node --require @pingops/sdk/register your-app.js` or `import "@pingops/sdk/register"` first |
472
- | Manual init | `initializePingops({ baseUrl, serviceName, ... })` before any HTTP usage |
473
- | Config from file | `PINGOPS_CONFIG_FILE=./pingops.config.yaml` or `initializePingops("./pingops.config.json")` |
488
+ | Goal | What to do |
489
+ | ------------------ | ----------------------------------------------------------------------------------------------- |
490
+ | Install | `pnpm add @pingops/sdk` |
491
+ | Auto-init from env | `node --require @pingops/sdk/register your-app.js` or `import "@pingops/sdk/register"` first |
492
+ | Manual init | `initializePingops({ baseUrl, serviceName, ... })` before any HTTP usage |
493
+ | Config from file | `PINGOPS_CONFIG_FILE=./pingops.config.yaml` or `initializePingops("./pingops.config.json")` |
474
494
  | Trace with context | `startTrace({ attributes: { userId, sessionId, tags, metadata }, seed? }, async () => { ... })` |
475
- | Get current IDs | `getActiveTraceId()`, `getActiveSpanId()` |
476
- | Graceful shutdown | `await shutdownPingops()` |
495
+ | Unsuppress a scope | `runUnsuppressed(async () => { ... })` |
496
+ | Get current IDs | `getActiveTraceId()`, `getActiveSpanId()` |
497
+ | Graceful shutdown | `await shutdownPingops()` |
477
498
 
478
499
  For more detail on types and options, see the [Configuration](#configuration) and [API Reference](#api-reference) sections above.
package/dist/index.cjs CHANGED
@@ -1,7 +1,8 @@
1
- const require_pingops = require('./pingops-Bbm0Xd0z.cjs');
1
+ const require_pingops = require('./pingops-BM2jW6Bi.cjs');
2
2
 
3
3
  exports.getActiveSpanId = require_pingops.getActiveSpanId;
4
4
  exports.getActiveTraceId = require_pingops.getActiveTraceId;
5
5
  exports.initializePingops = require_pingops.initializePingops;
6
+ exports.runUnsuppressed = require_pingops.runUnsuppressed;
6
7
  exports.shutdownPingops = require_pingops.shutdownPingops;
7
8
  exports.startTrace = require_pingops.startTrace;
package/dist/index.d.cts CHANGED
@@ -67,6 +67,11 @@ declare function startTrace<T>(options: {
67
67
  attributes?: PingopsTraceAttributes$1;
68
68
  seed?: string;
69
69
  }, fn: () => T | Promise<T>): Promise<T>;
70
+ /**
71
+ * Runs a callback in a context that is guaranteed to be unsuppressed.
72
+ * Useful for task/job boundaries where suppression may have leaked.
73
+ */
74
+ declare function runUnsuppressed<T>(fn: () => T): T;
70
75
  //#endregion
71
- export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace };
76
+ export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, runUnsuppressed, shutdownPingops, startTrace };
72
77
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AA+SA;AAOA;AAiCA;;;;;;;;;;;;iBArRgB,iBAAA,SACN;iBAGM,iBAAA;iBAIA,iBAAA;;;;;;iBAuGM,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCM;eACI;;aACd,IAAI,QAAQ,KACrB,QAAQ"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AAiTA;AAOA;AAiCA;;;;;;;;AA6FA;;;;iBAlXgB,iBAAA,SACN;iBAGM,iBAAA;iBAIA,iBAAA;;;;;;iBAuGM,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCM;eACI;;aACd,IAAI,QAAQ,KACrB,QAAQ;;;;;iBA0FK,6BAA6B,IAAI"}
package/dist/index.d.mts CHANGED
@@ -67,6 +67,11 @@ declare function startTrace<T>(options: {
67
67
  attributes?: PingopsTraceAttributes$1;
68
68
  seed?: string;
69
69
  }, fn: () => T | Promise<T>): Promise<T>;
70
+ /**
71
+ * Runs a callback in a context that is guaranteed to be unsuppressed.
72
+ * Useful for task/job boundaries where suppression may have leaked.
73
+ */
74
+ declare function runUnsuppressed<T>(fn: () => T): T;
70
75
  //#endregion
71
- export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace };
76
+ export { type PingopsTraceAttributes, getActiveSpanId, getActiveTraceId, initializePingops, runUnsuppressed, shutdownPingops, startTrace };
72
77
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AA+SA;AAOA;AAiCA;;;;;;;;;;;;iBArRgB,iBAAA,SACN;iBAGM,iBAAA;iBAIA,iBAAA;;;;;;iBAuGM,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCM;eACI;;aACd,IAAI,QAAQ,KACrB,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/pingops.ts"],"sourcesContent":[],"mappings":";;;;;AAiTA;AAOA;AAiCA;;;;;;;;AA6FA;;;;iBAlXgB,iBAAA,SACN;iBAGM,iBAAA;iBAIA,iBAAA;;;;;;iBAuGM,eAAA,CAAA,GAAmB;;;;iBA8HzB,gBAAA,CAAA;;;;iBAOA,eAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCM;eACI;;aACd,IAAI,QAAQ,KACrB,QAAQ;;;;;iBA0FK,6BAA6B,IAAI"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as startTrace, i as shutdownPingops, n as getActiveTraceId, r as initializePingops, t as getActiveSpanId } from "./pingops-DJsItvUR.mjs";
1
+ import { a as shutdownPingops, i as runUnsuppressed, n as getActiveTraceId, o as startTrace, r as initializePingops, t as getActiveSpanId } from "./pingops-SV21E4bp.mjs";
2
2
 
3
- export { getActiveSpanId, getActiveTraceId, initializePingops, shutdownPingops, startTrace };
3
+ export { getActiveSpanId, getActiveTraceId, initializePingops, runUnsuppressed, shutdownPingops, startTrace };
@@ -1,4 +1,5 @@
1
1
  let _opentelemetry_api = require("@opentelemetry/api");
2
+ let _opentelemetry_core = require("@opentelemetry/core");
2
3
  let _opentelemetry_sdk_node = require("@opentelemetry/sdk-node");
3
4
  let _opentelemetry_resources = require("@opentelemetry/resources");
4
5
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
@@ -57,10 +58,17 @@ function mergeConfigWithEnv(fileConfig) {
57
58
  //#endregion
58
59
  //#region src/init-state.ts
59
60
  /**
61
+ * Shared state for tracking SDK initialization
62
+ * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts
63
+ */
64
+ let isSdkInitializedFlag$1 = false;
65
+ /**
60
66
  * Sets the SDK initialization flag.
61
67
  * Called by initializePingops when the SDK is initialized.
62
68
  */
63
- function setSdkInitialized(initialized) {}
69
+ function setSdkInitialized(initialized) {
70
+ isSdkInitializedFlag$1 = initialized;
71
+ }
64
72
 
65
73
  //#endregion
66
74
  //#region src/pingops.ts
@@ -80,6 +88,7 @@ let isSdkInitializedFlag = false;
80
88
  */
81
89
  let isInitialized = false;
82
90
  let initializationPromise = null;
91
+ let hasLoggedSuppressedStartTraceWarning = false;
83
92
  function initializePingops(config, explicit = true) {
84
93
  const resolvedConfig = typeof config === "string" ? resolveConfigFromFile(config) : "configFile" in config ? resolveConfigFromFile(config.configFile) : config;
85
94
  if (isSdkInitializedFlag) {
@@ -97,7 +106,7 @@ function initializePingops(config, explicit = true) {
97
106
  nodeSdk.start();
98
107
  sdkInstance = nodeSdk;
99
108
  isSdkInitializedFlag = true;
100
- /* @__PURE__ */ setSdkInitialized(true);
109
+ setSdkInitialized(true);
101
110
  try {
102
111
  const isolatedProvider = new _opentelemetry_sdk_trace_node.NodeTracerProvider({
103
112
  resource,
@@ -127,7 +136,7 @@ async function shutdownPingops() {
127
136
  await sdkInstance.shutdown();
128
137
  sdkInstance = null;
129
138
  isSdkInitializedFlag = false;
130
- /* @__PURE__ */ setSdkInitialized(false);
139
+ setSdkInitialized(false);
131
140
  }
132
141
  /**
133
142
  * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available
@@ -258,7 +267,12 @@ async function startTrace(options, fn) {
258
267
  traceFlags: TRACE_FLAG_SAMPLED
259
268
  };
260
269
  const activeContext = _opentelemetry_api.context.active();
261
- const contextWithSpanContext = _opentelemetry_api.trace.setSpanContext(activeContext, spanContext);
270
+ const traceExecutionBaseContext = (0, _opentelemetry_core.isTracingSuppressed)(activeContext) ? _opentelemetry_api.ROOT_CONTEXT : activeContext;
271
+ if (traceExecutionBaseContext === _opentelemetry_api.ROOT_CONTEXT) if (!hasLoggedSuppressedStartTraceWarning) {
272
+ logger.warn("startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation");
273
+ hasLoggedSuppressedStartTraceWarning = true;
274
+ } else logger.debug("startTrace received a suppressed active context; running trace on ROOT_CONTEXT");
275
+ const contextWithSpanContext = _opentelemetry_api.trace.setSpanContext(traceExecutionBaseContext, spanContext);
262
276
  const tracer = (0, _pingops_otel.getPingopsTracerProvider)().getTracer("pingops-sdk", "1.0.0");
263
277
  return new Promise((resolve$1, reject) => {
264
278
  tracer.startActiveSpan("pingops-trace", {}, contextWithSpanContext, (span) => {
@@ -274,7 +288,7 @@ async function startTrace(options, fn) {
274
288
  resolve$1(v);
275
289
  }).catch((err) => {
276
290
  span.end();
277
- reject(err);
291
+ reject(err instanceof Error ? err : new Error(String(err)));
278
292
  });
279
293
  else {
280
294
  span.end();
@@ -282,11 +296,20 @@ async function startTrace(options, fn) {
282
296
  }
283
297
  } catch (err) {
284
298
  span.end();
285
- reject(err);
299
+ reject(err instanceof Error ? err : new Error(String(err)));
286
300
  }
287
301
  });
288
302
  });
289
303
  }
304
+ /**
305
+ * Runs a callback in a context that is guaranteed to be unsuppressed.
306
+ * Useful for task/job boundaries where suppression may have leaked.
307
+ */
308
+ function runUnsuppressed(fn) {
309
+ const activeContext = _opentelemetry_api.context.active();
310
+ const unsuppressedContext = (0, _opentelemetry_core.isTracingSuppressed)(activeContext) ? _opentelemetry_api.ROOT_CONTEXT : activeContext;
311
+ return _opentelemetry_api.context.with(unsuppressedContext, fn);
312
+ }
290
313
  function setAttributesInContext(ctx, attrs) {
291
314
  if (attrs.userId !== void 0) ctx = ctx.setValue(_pingops_core.PINGOPS_USER_ID, attrs.userId);
292
315
  if (attrs.sessionId !== void 0) ctx = ctx.setValue(_pingops_core.PINGOPS_SESSION_ID, attrs.sessionId);
@@ -328,6 +351,12 @@ Object.defineProperty(exports, 'mergeConfigWithEnv', {
328
351
  return mergeConfigWithEnv;
329
352
  }
330
353
  });
354
+ Object.defineProperty(exports, 'runUnsuppressed', {
355
+ enumerable: true,
356
+ get: function () {
357
+ return runUnsuppressed;
358
+ }
359
+ });
331
360
  Object.defineProperty(exports, 'shutdownPingops', {
332
361
  enumerable: true,
333
362
  get: function () {
@@ -340,4 +369,4 @@ Object.defineProperty(exports, 'startTrace', {
340
369
  return startTrace;
341
370
  }
342
371
  });
343
- //# sourceMappingURL=pingops-Bbm0Xd0z.cjs.map
372
+ //# sourceMappingURL=pingops-BM2jW6Bi.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pingops-BM2jW6Bi.cjs","names":["isSdkInitializedFlag","ATTR_SERVICE_NAME","PingopsSpanProcessor","NodeSDK","NodeTracerProvider","trace","context","ROOT_CONTEXT","PINGOPS_TRACE_ID","PINGOPS_USER_ID","PINGOPS_SESSION_ID","PINGOPS_TAGS","PINGOPS_METADATA","PINGOPS_CAPTURE_REQUEST_BODY","PINGOPS_CAPTURE_RESPONSE_BODY"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const traceExecutionBaseContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const contextWithSpanContext = trace.setSpanContext(\n traceExecutionBaseContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n );\n });\n}\n\n/**\n * Runs a callback in a context that is guaranteed to be unsuppressed.\n * Useful for task/job boundaries where suppression may have leaked.\n */\nexport function runUnsuppressed<T>(fn: () => T): T {\n const activeContext = context.active();\n const unsuppressedContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n return context.with(unsuppressedContext, fn);\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { context, trace } from "@opentelemetry/api";
1
+ import { ROOT_CONTEXT, context, trace } from "@opentelemetry/api";
2
+ import { isTracingSuppressed } from "@opentelemetry/core";
2
3
  import { NodeSDK } from "@opentelemetry/sdk-node";
3
4
  import { resourceFromAttributes } from "@opentelemetry/resources";
4
5
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
@@ -57,10 +58,17 @@ function mergeConfigWithEnv(fileConfig) {
57
58
  //#endregion
58
59
  //#region src/init-state.ts
59
60
  /**
61
+ * Shared state for tracking SDK initialization
62
+ * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts
63
+ */
64
+ let isSdkInitializedFlag$1 = false;
65
+ /**
60
66
  * Sets the SDK initialization flag.
61
67
  * Called by initializePingops when the SDK is initialized.
62
68
  */
63
- function setSdkInitialized(initialized) {}
69
+ function setSdkInitialized(initialized) {
70
+ isSdkInitializedFlag$1 = initialized;
71
+ }
64
72
 
65
73
  //#endregion
66
74
  //#region src/pingops.ts
@@ -80,6 +88,7 @@ let isSdkInitializedFlag = false;
80
88
  */
81
89
  let isInitialized = false;
82
90
  let initializationPromise = null;
91
+ let hasLoggedSuppressedStartTraceWarning = false;
83
92
  function initializePingops(config, explicit = true) {
84
93
  const resolvedConfig = typeof config === "string" ? resolveConfigFromFile(config) : "configFile" in config ? resolveConfigFromFile(config.configFile) : config;
85
94
  if (isSdkInitializedFlag) {
@@ -97,7 +106,7 @@ function initializePingops(config, explicit = true) {
97
106
  nodeSdk.start();
98
107
  sdkInstance = nodeSdk;
99
108
  isSdkInitializedFlag = true;
100
- /* @__PURE__ */ setSdkInitialized(true);
109
+ setSdkInitialized(true);
101
110
  try {
102
111
  const isolatedProvider = new NodeTracerProvider({
103
112
  resource,
@@ -127,7 +136,7 @@ async function shutdownPingops() {
127
136
  await sdkInstance.shutdown();
128
137
  sdkInstance = null;
129
138
  isSdkInitializedFlag = false;
130
- /* @__PURE__ */ setSdkInitialized(false);
139
+ setSdkInitialized(false);
131
140
  }
132
141
  /**
133
142
  * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available
@@ -258,7 +267,12 @@ async function startTrace(options, fn) {
258
267
  traceFlags: TRACE_FLAG_SAMPLED
259
268
  };
260
269
  const activeContext = context.active();
261
- const contextWithSpanContext = trace.setSpanContext(activeContext, spanContext);
270
+ const traceExecutionBaseContext = isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;
271
+ if (traceExecutionBaseContext === ROOT_CONTEXT) if (!hasLoggedSuppressedStartTraceWarning) {
272
+ logger.warn("startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation");
273
+ hasLoggedSuppressedStartTraceWarning = true;
274
+ } else logger.debug("startTrace received a suppressed active context; running trace on ROOT_CONTEXT");
275
+ const contextWithSpanContext = trace.setSpanContext(traceExecutionBaseContext, spanContext);
262
276
  const tracer = getPingopsTracerProvider().getTracer("pingops-sdk", "1.0.0");
263
277
  return new Promise((resolve$1, reject) => {
264
278
  tracer.startActiveSpan("pingops-trace", {}, contextWithSpanContext, (span) => {
@@ -274,7 +288,7 @@ async function startTrace(options, fn) {
274
288
  resolve$1(v);
275
289
  }).catch((err) => {
276
290
  span.end();
277
- reject(err);
291
+ reject(err instanceof Error ? err : new Error(String(err)));
278
292
  });
279
293
  else {
280
294
  span.end();
@@ -282,11 +296,20 @@ async function startTrace(options, fn) {
282
296
  }
283
297
  } catch (err) {
284
298
  span.end();
285
- reject(err);
299
+ reject(err instanceof Error ? err : new Error(String(err)));
286
300
  }
287
301
  });
288
302
  });
289
303
  }
304
+ /**
305
+ * Runs a callback in a context that is guaranteed to be unsuppressed.
306
+ * Useful for task/job boundaries where suppression may have leaked.
307
+ */
308
+ function runUnsuppressed(fn) {
309
+ const activeContext = context.active();
310
+ const unsuppressedContext = isTracingSuppressed(activeContext) ? ROOT_CONTEXT : activeContext;
311
+ return context.with(unsuppressedContext, fn);
312
+ }
290
313
  function setAttributesInContext(ctx, attrs) {
291
314
  if (attrs.userId !== void 0) ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);
292
315
  if (attrs.sessionId !== void 0) ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);
@@ -298,5 +321,5 @@ function setAttributesInContext(ctx, attrs) {
298
321
  }
299
322
 
300
323
  //#endregion
301
- export { startTrace as a, shutdownPingops as i, getActiveTraceId as n, loadConfigFromFile as o, initializePingops as r, mergeConfigWithEnv as s, getActiveSpanId as t };
302
- //# sourceMappingURL=pingops-DJsItvUR.mjs.map
324
+ export { shutdownPingops as a, mergeConfigWithEnv as c, runUnsuppressed as i, getActiveTraceId as n, startTrace as o, initializePingops as r, loadConfigFromFile as s, getActiveSpanId as t };
325
+ //# sourceMappingURL=pingops-SV21E4bp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pingops-SV21E4bp.mjs","names":["loadYaml","isSdkInitializedFlag"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Returns whether the SDK has been initialized.\n */\nexport function isSdkInitialized(): boolean {\n return isSdkInitializedFlag;\n}\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { ROOT_CONTEXT, context, trace } from \"@opentelemetry/api\";\nimport { isTracingSuppressed } from \"@opentelemetry/core\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\nlet hasLoggedSuppressedStartTraceWarning = false;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const traceExecutionBaseContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n if (traceExecutionBaseContext === ROOT_CONTEXT) {\n if (!hasLoggedSuppressedStartTraceWarning) {\n logger.warn(\n \"startTrace detected a suppressed active context and is running on ROOT_CONTEXT to prevent suppression leakage into user outbound instrumentation\"\n );\n hasLoggedSuppressedStartTraceWarning = true;\n } else {\n logger.debug(\n \"startTrace received a suppressed active context; running trace on ROOT_CONTEXT\"\n );\n }\n }\n const contextWithSpanContext = trace.setSpanContext(\n traceExecutionBaseContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n );\n });\n}\n\n/**\n * Runs a callback in a context that is guaranteed to be unsuppressed.\n * Useful for task/job boundaries where suppression may have leaked.\n */\nexport function runUnsuppressed<T>(fn: () => T): T {\n const activeContext = context.active();\n const unsuppressedContext = isTracingSuppressed(activeContext)\n ? ROOT_CONTEXT\n : activeContext;\n return context.with(unsuppressedContext, fn);\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
package/dist/register.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_pingops = require('./pingops-Bbm0Xd0z.cjs');
1
+ const require_pingops = require('./pingops-BM2jW6Bi.cjs');
2
2
 
3
3
  //#region src/register.ts
4
4
  /**
package/dist/register.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { o as loadConfigFromFile, r as initializePingops, s as mergeConfigWithEnv } from "./pingops-DJsItvUR.mjs";
1
+ import { c as mergeConfigWithEnv, r as initializePingops, s as loadConfigFromFile } from "./pingops-SV21E4bp.mjs";
2
2
 
3
3
  //#region src/register.ts
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pingops/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -39,24 +39,27 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@opentelemetry/api": "^1.9.0",
42
+ "@opentelemetry/core": "^2.2.0",
42
43
  "@opentelemetry/resources": "^2.2.0",
43
44
  "@opentelemetry/sdk-node": "^0.208.0",
44
45
  "@opentelemetry/sdk-trace-node": "^2.2.0",
45
46
  "@opentelemetry/semantic-conventions": "^1.38.0",
46
47
  "js-yaml": "^4.1.0",
47
- "@pingops/core": "^0.2.0",
48
- "@pingops/otel": "^0.2.0"
48
+ "@pingops/otel": "^0.2.2",
49
+ "@pingops/core": "^0.2.2"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/node": "^20.11.0",
52
53
  "@types/js-yaml": "^4.0.9",
53
54
  "axios": "^1.6.2",
55
+ "vitest": "^3.2.4",
54
56
  "tsx": "^4.7.0",
55
57
  "typescript": "^5.6.0"
56
58
  },
57
59
  "scripts": {
58
60
  "build": "tsdown",
59
61
  "dev": "tsdown --watch",
60
- "clean": "rm -rf dist"
62
+ "clean": "rm -rf dist",
63
+ "test": "vitest run"
61
64
  }
62
65
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"pingops-Bbm0Xd0z.cjs","names":["ATTR_SERVICE_NAME","PingopsSpanProcessor","NodeSDK","NodeTracerProvider","trace","context","PINGOPS_TRACE_ID","PINGOPS_USER_ID","PINGOPS_SESSION_ID","PINGOPS_TAGS","PINGOPS_METADATA","PINGOPS_CAPTURE_REQUEST_BODY","PINGOPS_CAPTURE_RESPONSE_BODY"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { context, trace } from \"@opentelemetry/api\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const contextWithSpanContext = trace.setSpanContext(\n activeContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err);\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err);\n }\n }\n );\n });\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pingops-DJsItvUR.mjs","names":["loadYaml"],"sources":["../src/config-loader.ts","../src/init-state.ts","../src/pingops.ts"],"sourcesContent":["/**\n * Configuration loader for reading PingOps config from JSON/YAML files\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { load as loadYaml } from \"js-yaml\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\n\n/**\n * Loads configuration from a JSON or YAML file\n *\n * @param filePath - Path to the config file (JSON or YAML)\n * @returns Parsed configuration object\n * @throws Error if file cannot be read or parsed\n */\nexport function loadConfigFromFile(\n filePath: string\n): Partial<PingopsProcessorConfig> {\n const resolvedPath = resolve(filePath);\n const fileContent = readFileSync(resolvedPath, \"utf-8\");\n\n const ext = resolvedPath.toLowerCase();\n if (ext.endsWith(\".yaml\") || ext.endsWith(\".yml\")) {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n } else if (ext.endsWith(\".json\")) {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } else {\n // Try to parse as JSON first, then YAML\n try {\n return JSON.parse(fileContent) as Partial<PingopsProcessorConfig>;\n } catch {\n return (loadYaml(fileContent) as Partial<PingopsProcessorConfig>) || {};\n }\n }\n}\n\n/**\n * Merges configuration from file and environment variables.\n * Environment variables take precedence over file config.\n *\n * @param fileConfig - Configuration loaded from file\n * @returns Merged configuration with env vars taking precedence\n */\nexport function mergeConfigWithEnv(\n fileConfig: Partial<PingopsProcessorConfig>\n): Partial<PingopsProcessorConfig> {\n const envConfig: Partial<PingopsProcessorConfig> = {};\n\n // Read from environment variables\n if (process.env.PINGOPS_API_KEY) {\n envConfig.apiKey = process.env.PINGOPS_API_KEY;\n }\n if (process.env.PINGOPS_BASE_URL) {\n envConfig.baseUrl = process.env.PINGOPS_BASE_URL;\n }\n if (process.env.PINGOPS_SERVICE_NAME) {\n envConfig.serviceName = process.env.PINGOPS_SERVICE_NAME;\n }\n if (process.env.PINGOPS_DEBUG) {\n envConfig.debug = process.env.PINGOPS_DEBUG === \"true\";\n }\n if (process.env.PINGOPS_BATCH_SIZE) {\n envConfig.batchSize = parseInt(process.env.PINGOPS_BATCH_SIZE, 10);\n }\n if (process.env.PINGOPS_BATCH_TIMEOUT) {\n envConfig.batchTimeout = parseInt(process.env.PINGOPS_BATCH_TIMEOUT, 10);\n }\n if (process.env.PINGOPS_EXPORT_MODE) {\n envConfig.exportMode = process.env.PINGOPS_EXPORT_MODE as\n | \"batched\"\n | \"immediate\";\n }\n\n // Merge: env vars override file config\n return {\n ...fileConfig,\n ...envConfig,\n };\n}\n","/**\n * Shared state for tracking SDK initialization\n * This module exists to avoid circular dependencies between pingops.ts and instrumentation.ts\n */\n\nlet isSdkInitializedFlag = false;\n\n/**\n * Sets the SDK initialization flag.\n * Called by initializePingops when the SDK is initialized.\n */\nexport function setSdkInitialized(initialized: boolean): void {\n isSdkInitializedFlag = initialized;\n}\n","/**\n * PingOps SDK singleton for manual instrumentation\n *\n * Provides initializePingops, shutdownPingops, startTrace, getActiveTraceId,\n * and getActiveSpanId. startTrace can auto-initialize from environment variables if needed.\n */\n\nimport { context, trace } from \"@opentelemetry/api\";\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport { NodeTracerProvider } from \"@opentelemetry/sdk-trace-node\";\nimport type { PingopsProcessorConfig } from \"@pingops/otel\";\nimport {\n setPingopsTracerProvider,\n shutdownTracerProvider,\n PingopsSpanProcessor,\n} from \"@pingops/otel\";\nimport {\n createLogger,\n createTraceId,\n uint8ArrayToHex,\n type PingopsTraceAttributes,\n} from \"@pingops/core\";\nimport {\n PINGOPS_TRACE_ID,\n PINGOPS_USER_ID,\n PINGOPS_SESSION_ID,\n PINGOPS_TAGS,\n PINGOPS_METADATA,\n PINGOPS_CAPTURE_REQUEST_BODY,\n PINGOPS_CAPTURE_RESPONSE_BODY,\n} from \"@pingops/core\";\nimport { loadConfigFromFile, mergeConfigWithEnv } from \"./config-loader\";\nimport { setSdkInitialized } from \"./init-state\";\nimport { getPingopsTracerProvider } from \"@pingops/otel\";\nimport { getInstrumentations } from \"@pingops/otel\";\n\nconst TRACE_FLAG_SAMPLED = 1;\n\nconst initLogger = createLogger(\"[PingOps Initialize]\");\nconst logger = createLogger(\"[PingOps Pingops]\");\n\nlet sdkInstance: NodeSDK | null = null;\nlet isSdkInitializedFlag = false;\n\n/**\n * Global state to track initialization\n */\nlet isInitialized = false;\nlet initializationPromise: Promise<void> | null = null;\n\n/**\n * Initializes PingOps SDK\n *\n * This function:\n * 1. Creates an OpenTelemetry NodeSDK instance\n * 2. Configures Resource with service.name\n * 3. Registers PingopsSpanProcessor\n * 4. Enables HTTP/fetch/GenAI instrumentation\n * 5. Starts the SDK\n *\n * @param config - Configuration object, config file path, or config file wrapper\n * @param explicit - Whether this is an explicit call (default: true).\n * Set to false when called internally by startTrace auto-initialization.\n */\nexport function initializePingops(\n config: PingopsProcessorConfig,\n explicit?: boolean\n): void;\nexport function initializePingops(\n configFilePath: string,\n explicit?: boolean\n): void;\nexport function initializePingops(\n config: { configFile: string },\n explicit?: boolean\n): void;\nexport function initializePingops(\n config:\n | PingopsProcessorConfig\n | string\n | {\n configFile: string;\n },\n explicit: boolean = true\n): void {\n void explicit; // Ignored: SDK always uses global instrumentation\n const resolvedConfig: PingopsProcessorConfig =\n typeof config === \"string\"\n ? resolveConfigFromFile(config)\n : \"configFile\" in config\n ? resolveConfigFromFile(config.configFile)\n : config;\n\n if (isSdkInitializedFlag) {\n if (resolvedConfig.debug) {\n initLogger.warn(\"[PingOps] SDK already initialized, skipping\");\n }\n return;\n }\n\n // Create resource with service name\n const resource = resourceFromAttributes({\n [ATTR_SERVICE_NAME]: resolvedConfig.serviceName,\n });\n\n const processor = new PingopsSpanProcessor(resolvedConfig);\n const instrumentations = getInstrumentations();\n\n // Node.js SDK\n const nodeSdk = new NodeSDK({\n resource,\n spanProcessors: [processor],\n instrumentations,\n });\n\n nodeSdk.start();\n sdkInstance = nodeSdk;\n\n // Mark SDK as initialized\n isSdkInitializedFlag = true;\n\n setSdkInitialized(true);\n\n // Initialize isolated TracerProvider for manual spans AFTER NodeSDK starts\n // This ensures manual spans created via startSpan are processed by the same processor\n // We register it after NodeSDK so it takes precedence as the global provider\n try {\n // In version 2.2.0, span processors are passed in the constructor\n const isolatedProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n // Register the provider globally\n isolatedProvider.register();\n\n // Set it in global state\n setPingopsTracerProvider(isolatedProvider);\n } catch (error) {\n if (resolvedConfig.debug) {\n initLogger.error(\n \"[PingOps] Failed to create isolated TracerProvider:\",\n error instanceof Error ? error.message : String(error)\n );\n }\n // Continue without isolated provider - manual spans will use global provider\n }\n\n if (resolvedConfig.debug) {\n initLogger.info(\"[PingOps] SDK initialized\");\n }\n}\n\nfunction resolveConfigFromFile(configFilePath: string): PingopsProcessorConfig {\n const fileConfig = loadConfigFromFile(configFilePath);\n const mergedConfig = mergeConfigWithEnv(fileConfig);\n\n if (!mergedConfig.baseUrl || !mergedConfig.serviceName) {\n const missing = [\n !mergedConfig.baseUrl && \"baseUrl (or PINGOPS_BASE_URL)\",\n !mergedConfig.serviceName && \"serviceName (or PINGOPS_SERVICE_NAME)\",\n ].filter(Boolean);\n\n throw new Error(\n `initializePingops(configFile) requires ${missing.join(\" and \")}. ` +\n `Provide them in the config file or via environment variables.`\n );\n }\n\n return mergedConfig as PingopsProcessorConfig;\n}\n\n/**\n * Shuts down the SDK and flushes remaining spans\n */\nexport async function shutdownPingops(): Promise<void> {\n // Shutdown isolated TracerProvider first\n await shutdownTracerProvider();\n\n if (!sdkInstance) {\n return;\n }\n\n await sdkInstance.shutdown();\n sdkInstance = null;\n isSdkInitializedFlag = false;\n setSdkInitialized(false);\n}\n\n/**\n * Checks if the SDK is already initialized by checking if a NodeTracerProvider is available\n */\nfunction isSdkInitialized(): boolean {\n try {\n const provider = getPingopsTracerProvider();\n // If we have a NodeTracerProvider (not the default NoOpTracerProvider), SDK is initialized\n const initialized = provider instanceof NodeTracerProvider;\n logger.debug(\"Checked SDK initialization status\", {\n initialized,\n providerType: provider.constructor.name,\n });\n return initialized;\n } catch (error) {\n logger.debug(\"Error checking SDK initialization status\", {\n error: error instanceof Error ? error.message : String(error),\n });\n return false;\n }\n}\n\n/**\n * Auto-initializes the SDK from environment variables if not already initialized\n */\nasync function ensureInitialized(): Promise<void> {\n // Check if SDK is already initialized (e.g., by calling initializePingops directly)\n if (isSdkInitialized()) {\n logger.debug(\"SDK already initialized, skipping auto-initialization\");\n isInitialized = true;\n return;\n }\n\n if (isInitialized) {\n logger.debug(\"SDK initialization flag already set, skipping\");\n return;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n logger.debug(\"SDK initialization already in progress, waiting...\");\n return initializationPromise;\n }\n\n // Start initialization\n logger.info(\"Starting SDK auto-initialization from environment variables\");\n initializationPromise = Promise.resolve().then(() => {\n const apiKey = process.env.PINGOPS_API_KEY;\n const baseUrl = process.env.PINGOPS_BASE_URL;\n const serviceName = process.env.PINGOPS_SERVICE_NAME;\n const debug = process.env.PINGOPS_DEBUG === \"true\";\n\n logger.debug(\"Reading environment variables\", {\n hasApiKey: !!apiKey,\n hasBaseUrl: !!baseUrl,\n hasServiceName: !!serviceName,\n debug,\n });\n\n if (!apiKey || !baseUrl || !serviceName) {\n const missing = [\n !apiKey && \"PINGOPS_API_KEY\",\n !baseUrl && \"PINGOPS_BASE_URL\",\n !serviceName && \"PINGOPS_SERVICE_NAME\",\n ].filter(Boolean);\n\n logger.error(\n \"Missing required environment variables for auto-initialization\",\n {\n missing,\n }\n );\n\n throw new Error(\n `PingOps SDK auto-initialization requires PINGOPS_API_KEY, PINGOPS_BASE_URL, and PINGOPS_SERVICE_NAME environment variables. Missing: ${missing.join(\", \")}`\n );\n }\n\n const config: PingopsProcessorConfig = {\n apiKey,\n baseUrl,\n serviceName,\n debug,\n };\n\n logger.info(\"Initializing SDK with config\", {\n baseUrl,\n serviceName,\n debug,\n });\n\n // Call initializePingops with explicit=false since this is auto-initialization\n initializePingops(config, false);\n isInitialized = true;\n\n logger.info(\"SDK auto-initialization completed successfully\");\n });\n\n try {\n await initializationPromise;\n } catch (error) {\n logger.error(\"SDK auto-initialization failed\", {\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n } finally {\n initializationPromise = null;\n }\n}\n\n/**\n * Returns the trace ID of the currently active span, if any.\n */\nexport function getActiveTraceId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().traceId;\n}\n\n/**\n * Returns the span ID of the currently active span, if any.\n */\nexport function getActiveSpanId(): string | undefined {\n return trace.getActiveSpan()?.spanContext().spanId;\n}\n\n/**\n * Starts a new trace using the PingOps tracer provider and runs the callback within that trace.\n * Sets attributes (traceId, userId, sessionId, tags, metadata, etc.) in context so they are\n * propagated to spans created within the callback.\n *\n * @param options - Options including optional attributes and optional seed for deterministic traceId\n * @param fn - Function to execute within the trace and attribute context\n * @returns Promise resolving to the result of the function\n *\n * @example\n * ```typescript\n * import { startTrace, initializePingops } from '@pingops/sdk';\n *\n * initializePingops({ ... });\n *\n * const result = await startTrace({\n * attributes: {\n * userId: 'user-123',\n * sessionId: 'session-456',\n * tags: ['production', 'api'],\n * metadata: { environment: 'prod', version: '1.0.0' }\n * },\n * seed: 'request-123' // optional: deterministic traceId from this seed\n * }, async () => {\n * const response = await fetch('https://api.example.com/users/123');\n * return response.json();\n * });\n * ```\n */\nexport async function startTrace<T>(\n options: { attributes?: PingopsTraceAttributes; seed?: string },\n fn: () => T | Promise<T>\n): Promise<T> {\n if (!isSdkInitialized()) {\n await ensureInitialized();\n }\n\n const traceId =\n options.attributes?.traceId ?? (await createTraceId(options?.seed));\n const parentSpanId = uint8ArrayToHex(\n crypto.getRandomValues(new Uint8Array(8))\n );\n\n const spanContext = {\n traceId,\n spanId: parentSpanId,\n traceFlags: TRACE_FLAG_SAMPLED,\n };\n\n const activeContext = context.active();\n const contextWithSpanContext = trace.setSpanContext(\n activeContext,\n spanContext\n );\n\n const tracer = getPingopsTracerProvider().getTracer(\"pingops-sdk\", \"1.0.0\");\n\n return new Promise((resolve, reject) => {\n tracer.startActiveSpan(\n \"pingops-trace\",\n {},\n contextWithSpanContext,\n (span) => {\n let contextWithAttributes = context.active();\n const attrs = options.attributes;\n if (attrs) {\n contextWithAttributes = setAttributesInContext(\n contextWithAttributes,\n attrs\n );\n }\n contextWithAttributes = contextWithAttributes.setValue(\n PINGOPS_TRACE_ID,\n traceId\n );\n\n const run = () => fn();\n\n try {\n const result = context.with(contextWithAttributes, run);\n if (result instanceof Promise) {\n result\n .then((v) => {\n span.end();\n resolve(v);\n })\n .catch((err) => {\n span.end();\n reject(err);\n });\n } else {\n span.end();\n resolve(result);\n }\n } catch (err) {\n span.end();\n reject(err);\n }\n }\n );\n });\n}\n\nfunction setAttributesInContext(\n ctx: ReturnType<typeof context.active>,\n attrs: PingopsTraceAttributes\n): ReturnType<typeof context.active> {\n if (attrs.userId !== undefined) {\n ctx = ctx.setValue(PINGOPS_USER_ID, attrs.userId);\n }\n if (attrs.sessionId !== undefined) {\n ctx = ctx.setValue(PINGOPS_SESSION_ID, attrs.sessionId);\n }\n if (attrs.tags !== undefined) {\n ctx = ctx.setValue(PINGOPS_TAGS, attrs.tags);\n }\n if (attrs.metadata !== undefined) {\n ctx = ctx.setValue(PINGOPS_METADATA, attrs.metadata);\n }\n if (attrs.captureRequestBody !== undefined) {\n ctx = ctx.setValue(PINGOPS_CAPTURE_REQUEST_BODY, attrs.captureRequestBody);\n }\n if (attrs.captureResponseBody !== undefined) {\n ctx = ctx.setValue(\n PINGOPS_CAPTURE_RESPONSE_BODY,\n attrs.captureResponseBody\n );\n }\n return ctx;\n}\n"],"mappings":"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"}