@glasstrace/sdk 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -773,6 +773,60 @@ declare function uploadSourceMapsAuto(apiKey: string, endpoint: string, buildHas
773
773
  */
774
774
  declare function captureError(error: unknown): void;
775
775
 
776
+ /**
777
+ * Minimal Fetch-API `Headers`-like interface supporting case-insensitive
778
+ * single-value lookup. Matches `Headers` from `undici` / the Web Fetch API.
779
+ */
780
+ interface FetchHeadersLike {
781
+ get(name: string): string | null;
782
+ }
783
+ /**
784
+ * Minimal Node `IncomingMessage.headers`-like shape: a dictionary mapping
785
+ * (typically lower-cased) header names to a value, a list of values, or
786
+ * `undefined`.
787
+ */
788
+ type NodeHeadersLike = Record<string, string | string[] | undefined>;
789
+ /**
790
+ * Accepted request shape for {@link captureCorrelationId}. Intentionally
791
+ * loose so callers can pass either a Fetch `Request` (or `NextRequest`)
792
+ * or a Node `IncomingMessage` without adapting the type.
793
+ */
794
+ interface CorrelationIdRequest {
795
+ headers: FetchHeadersLike | NodeHeadersLike | undefined;
796
+ }
797
+ /**
798
+ * Captures the Glasstrace correlation ID header (`x-gt-cid`) from an
799
+ * incoming request and materializes it as the
800
+ * `glasstrace.correlation.id` attribute on the currently active OTel span
801
+ * (DISC-1253).
802
+ *
803
+ * The SDK does not own any HTTP instrumentation, so it cannot read this
804
+ * header itself. Users opt in by calling this helper from a hook that
805
+ * runs inside the request's OTel context — typically a Next.js
806
+ * `middleware.ts` or a custom server request handler.
807
+ *
808
+ * The function is intentionally forgiving:
809
+ * - No active span → no-op.
810
+ * - Missing / empty header → no-op.
811
+ * - Array header values (Node IncomingMessage) → the first non-empty
812
+ * value is used; subsequent values are ignored because a correlation
813
+ * ID is a single logical value.
814
+ * - Malformed or unexpected `headers` shapes → caught and ignored; the
815
+ * helper never throws.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * // Next.js middleware.ts
820
+ * import { captureCorrelationId } from "@glasstrace/sdk";
821
+ *
822
+ * export function middleware(req: Request) {
823
+ * captureCorrelationId(req);
824
+ * return NextResponse.next();
825
+ * }
826
+ * ```
827
+ */
828
+ declare function captureCorrelationId(req: CorrelationIdRequest | null | undefined): void;
829
+
776
830
  /**
777
831
  * Discovers test files by scanning the project directory for conventional
778
832
  * test file patterns. Also reads vitest/jest config files for custom include
@@ -802,4 +856,4 @@ declare function extractImports(fileContent: string): string[];
802
856
  */
803
857
  declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
804
858
 
805
- export { type AutoUploadOptions, type BlobUploader, type ClaimState, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, type SourceMapFileInfo, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, createGlasstraceSpanProcessor, deriveSessionId, discoverSourceMapFiles, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, getStatus, isAnonymousMode, isProductionDisabled, isReady, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, waitForReady, withGlasstraceConfig };
859
+ export { type AutoUploadOptions, type BlobUploader, type ClaimState, type CorrelationIdRequest, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, type SourceMapFileInfo, buildImportGraph, captureCorrelationId, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, createGlasstraceSpanProcessor, deriveSessionId, discoverSourceMapFiles, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, getStatus, isAnonymousMode, isProductionDisabled, isReady, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, waitForReady, withGlasstraceConfig };
package/dist/index.d.ts CHANGED
@@ -773,6 +773,60 @@ declare function uploadSourceMapsAuto(apiKey: string, endpoint: string, buildHas
773
773
  */
774
774
  declare function captureError(error: unknown): void;
775
775
 
776
+ /**
777
+ * Minimal Fetch-API `Headers`-like interface supporting case-insensitive
778
+ * single-value lookup. Matches `Headers` from `undici` / the Web Fetch API.
779
+ */
780
+ interface FetchHeadersLike {
781
+ get(name: string): string | null;
782
+ }
783
+ /**
784
+ * Minimal Node `IncomingMessage.headers`-like shape: a dictionary mapping
785
+ * (typically lower-cased) header names to a value, a list of values, or
786
+ * `undefined`.
787
+ */
788
+ type NodeHeadersLike = Record<string, string | string[] | undefined>;
789
+ /**
790
+ * Accepted request shape for {@link captureCorrelationId}. Intentionally
791
+ * loose so callers can pass either a Fetch `Request` (or `NextRequest`)
792
+ * or a Node `IncomingMessage` without adapting the type.
793
+ */
794
+ interface CorrelationIdRequest {
795
+ headers: FetchHeadersLike | NodeHeadersLike | undefined;
796
+ }
797
+ /**
798
+ * Captures the Glasstrace correlation ID header (`x-gt-cid`) from an
799
+ * incoming request and materializes it as the
800
+ * `glasstrace.correlation.id` attribute on the currently active OTel span
801
+ * (DISC-1253).
802
+ *
803
+ * The SDK does not own any HTTP instrumentation, so it cannot read this
804
+ * header itself. Users opt in by calling this helper from a hook that
805
+ * runs inside the request's OTel context — typically a Next.js
806
+ * `middleware.ts` or a custom server request handler.
807
+ *
808
+ * The function is intentionally forgiving:
809
+ * - No active span → no-op.
810
+ * - Missing / empty header → no-op.
811
+ * - Array header values (Node IncomingMessage) → the first non-empty
812
+ * value is used; subsequent values are ignored because a correlation
813
+ * ID is a single logical value.
814
+ * - Malformed or unexpected `headers` shapes → caught and ignored; the
815
+ * helper never throws.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * // Next.js middleware.ts
820
+ * import { captureCorrelationId } from "@glasstrace/sdk";
821
+ *
822
+ * export function middleware(req: Request) {
823
+ * captureCorrelationId(req);
824
+ * return NextResponse.next();
825
+ * }
826
+ * ```
827
+ */
828
+ declare function captureCorrelationId(req: CorrelationIdRequest | null | undefined): void;
829
+
776
830
  /**
777
831
  * Discovers test files by scanning the project directory for conventional
778
832
  * test file patterns. Also reads vitest/jest config files for custom include
@@ -802,4 +856,4 @@ declare function extractImports(fileContent: string): string[];
802
856
  */
803
857
  declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
804
858
 
805
- export { type AutoUploadOptions, type BlobUploader, type ClaimState, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, type SourceMapFileInfo, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, createGlasstraceSpanProcessor, deriveSessionId, discoverSourceMapFiles, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, getStatus, isAnonymousMode, isProductionDisabled, isReady, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, waitForReady, withGlasstraceConfig };
859
+ export { type AutoUploadOptions, type BlobUploader, type ClaimState, type CorrelationIdRequest, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, type SourceMapFileInfo, buildImportGraph, captureCorrelationId, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, createGlasstraceSpanProcessor, deriveSessionId, discoverSourceMapFiles, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, getStatus, isAnonymousMode, isProductionDisabled, isReady, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, waitForReady, withGlasstraceConfig };
package/dist/index.js CHANGED
@@ -5,11 +5,12 @@ import {
5
5
  discoverSourceMapFiles,
6
6
  installConsoleCapture,
7
7
  maybeShowMcpNudge,
8
+ maybeShowServerActionNudge,
8
9
  sdkLog,
9
10
  uploadSourceMaps,
10
11
  uploadSourceMapsAuto,
11
12
  uploadSourceMapsPresigned
12
- } from "./chunk-BANTDXUT.js";
13
+ } from "./chunk-E33Y7BQH.js";
13
14
  import {
14
15
  _setCurrentConfig,
15
16
  buildImportGraph,
@@ -27,7 +28,7 @@ import {
27
28
  recordSpansExported,
28
29
  saveCachedConfig,
29
30
  sendInitRequest
30
- } from "./chunk-5C2TJFLB.js";
31
+ } from "./chunk-UUUKI65I.js";
31
32
  import {
32
33
  isAnonymousMode,
33
34
  isProductionDisabled,
@@ -37,11 +38,11 @@ import {
37
38
  import {
38
39
  getOrCreateAnonKey,
39
40
  readAnonKey
40
- } from "./chunk-TM5NKZTO.js";
41
+ } from "./chunk-J5BW7V2D.js";
41
42
  import {
42
43
  GLASSTRACE_ATTRIBUTE_NAMES,
43
44
  SessionIdSchema
44
- } from "./chunk-7JBKXSBU.js";
45
+ } from "./chunk-GSGX76Q5.js";
45
46
  import {
46
47
  DiagLogLevel,
47
48
  INVALID_SPAN_CONTEXT,
@@ -328,7 +329,8 @@ var GlasstraceExporter = class {
328
329
  if (typeof existingCid === "string") {
329
330
  extra[ATTR.CORRELATION_ID] = existingCid;
330
331
  }
331
- const route = attrs["http.route"] ?? name;
332
+ const rawRoute = attrs["http.route"];
333
+ const route = typeof rawRoute === "string" ? rawRoute : name;
332
334
  if (route) {
333
335
  extra[ATTR.ROUTE] = route;
334
336
  }
@@ -352,6 +354,17 @@ var GlasstraceExporter = class {
352
354
  if (method) {
353
355
  extra[ATTR.HTTP_METHOD] = method;
354
356
  }
357
+ const actionRoute = extractLeadingPath(route);
358
+ if (method === "POST" && actionRoute) {
359
+ const isApiRoute = actionRoute === "/api" || actionRoute.startsWith("/api/");
360
+ const isInternalRoute = actionRoute.startsWith("/_next/");
361
+ if (!isApiRoute && !isInternalRoute) {
362
+ extra[ATTR.NEXT_ACTION_DETECTED] = true;
363
+ if (typeof extra[ATTR.CORRELATION_ID] !== "string") {
364
+ maybeShowServerActionNudge();
365
+ }
366
+ }
367
+ }
355
368
  const statusCode = attrs["http.status_code"] ?? attrs["http.response.status_code"];
356
369
  if (statusCode !== void 0) {
357
370
  extra[ATTR.HTTP_STATUS_CODE] = statusCode;
@@ -560,6 +573,21 @@ function getExceptionEventDetails(span) {
560
573
  message: typeof message === "string" ? message : void 0
561
574
  };
562
575
  }
576
+ function extractLeadingPath(raw) {
577
+ if (!raw) return void 0;
578
+ const trimmed = raw.trim();
579
+ if (trimmed.length === 0) return void 0;
580
+ if (trimmed.startsWith("/")) {
581
+ const firstSpace = trimmed.indexOf(" ");
582
+ return firstSpace === -1 ? trimmed : trimmed.slice(0, firstSpace);
583
+ }
584
+ for (const token of trimmed.split(/\s+/)) {
585
+ if (token.startsWith("/")) {
586
+ return token;
587
+ }
588
+ }
589
+ return void 0;
590
+ }
563
591
  function deriveOrmProvider(instrumentationName) {
564
592
  const lower = instrumentationName.toLowerCase();
565
593
  if (lower.includes("prisma")) {
@@ -4176,7 +4204,7 @@ function registerGlasstrace(options) {
4176
4204
  setCoreState(CoreState.REGISTERING);
4177
4205
  startRuntimeStateWriter({
4178
4206
  projectRoot: process.cwd(),
4179
- sdkVersion: "0.16.0"
4207
+ sdkVersion: "0.17.1"
4180
4208
  });
4181
4209
  const config = resolveConfig(options);
4182
4210
  if (config.verbose) {
@@ -4341,8 +4369,8 @@ async function backgroundInit(config, anonKeyForInit, generation) {
4341
4369
  if (config.verbose) {
4342
4370
  console.info("[glasstrace] Background init firing.");
4343
4371
  }
4344
- const healthReport = collectHealthReport("0.16.0");
4345
- const initResult = await performInit(config, anonKeyForInit, "0.16.0", healthReport);
4372
+ const healthReport = collectHealthReport("0.17.1");
4373
+ const initResult = await performInit(config, anonKeyForInit, "0.17.1", healthReport);
4346
4374
  if (generation !== registrationGeneration) return;
4347
4375
  const currentState = getCoreState();
4348
4376
  if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
@@ -4365,7 +4393,7 @@ async function backgroundInit(config, anonKeyForInit, generation) {
4365
4393
  }
4366
4394
  maybeInstallConsoleCapture();
4367
4395
  if (didLastInitSucceed()) {
4368
- startHeartbeat(config, anonKeyForInit, "0.16.0", generation, (newApiKey, accountId) => {
4396
+ startHeartbeat(config, anonKeyForInit, "0.17.1", generation, (newApiKey, accountId) => {
4369
4397
  setAuthState(AuthState.CLAIMING);
4370
4398
  emitLifecycleEvent("auth:claim_started", { accountId });
4371
4399
  setResolvedApiKey(newApiKey);
@@ -4502,7 +4530,7 @@ async function handleSourceMapUpload(distDir) {
4502
4530
  );
4503
4531
  return;
4504
4532
  }
4505
- const { discoverSourceMapFiles: discoverSourceMapFiles2, computeBuildHash: computeBuildHash2, uploadSourceMaps: uploadSourceMaps2 } = await import("./source-map-uploader-MUZPI2S5.js");
4533
+ const { discoverSourceMapFiles: discoverSourceMapFiles2, computeBuildHash: computeBuildHash2, uploadSourceMaps: uploadSourceMaps2 } = await import("./source-map-uploader-26QPRSCG.js");
4506
4534
  const files = await discoverSourceMapFiles2(distDir);
4507
4535
  if (files.length === 0) {
4508
4536
  console.info("[glasstrace] No source map files found. Skipping upload.");
@@ -4540,6 +4568,67 @@ function captureError(error) {
4540
4568
  } catch {
4541
4569
  }
4542
4570
  }
4571
+
4572
+ // src/correlation-id.ts
4573
+ var ATTR2 = GLASSTRACE_ATTRIBUTE_NAMES;
4574
+ var HEADER_NAME = "x-gt-cid";
4575
+ var MAX_CID_LENGTH = 128;
4576
+ function captureCorrelationId(req) {
4577
+ try {
4578
+ if (!req || !req.headers) {
4579
+ return;
4580
+ }
4581
+ const value = readHeader(req.headers);
4582
+ if (!value) {
4583
+ return;
4584
+ }
4585
+ const span = trace.getActiveSpan();
4586
+ if (!span) {
4587
+ return;
4588
+ }
4589
+ span.setAttribute(ATTR2.CORRELATION_ID, value);
4590
+ } catch {
4591
+ }
4592
+ }
4593
+ function readHeader(headers) {
4594
+ const asFetch = headers;
4595
+ if (typeof asFetch.get === "function") {
4596
+ const raw = asFetch.get(HEADER_NAME);
4597
+ return firstToken(raw);
4598
+ }
4599
+ const dict = headers;
4600
+ const direct = dict[HEADER_NAME];
4601
+ if (direct !== void 0) {
4602
+ return firstValue(direct);
4603
+ }
4604
+ for (const key of Object.keys(dict)) {
4605
+ if (key.toLowerCase() === HEADER_NAME) {
4606
+ return firstValue(dict[key]);
4607
+ }
4608
+ }
4609
+ return void 0;
4610
+ }
4611
+ function firstValue(value) {
4612
+ if (Array.isArray(value)) {
4613
+ for (const entry of value) {
4614
+ const token = firstToken(entry);
4615
+ if (token) return token;
4616
+ }
4617
+ return void 0;
4618
+ }
4619
+ return firstToken(value);
4620
+ }
4621
+ function firstToken(value) {
4622
+ if (typeof value !== "string") return void 0;
4623
+ const parts = value.split(",");
4624
+ for (const part of parts) {
4625
+ const trimmed = part.trim();
4626
+ if (trimmed.length === 0) continue;
4627
+ if (trimmed.length > MAX_CID_LENGTH) return void 0;
4628
+ return trimmed;
4629
+ }
4630
+ return void 0;
4631
+ }
4543
4632
  export {
4544
4633
  GlasstraceExporter,
4545
4634
  GlasstraceSpanProcessor,
@@ -4547,6 +4636,7 @@ export {
4547
4636
  SdkError,
4548
4637
  SessionManager,
4549
4638
  buildImportGraph,
4639
+ captureCorrelationId,
4550
4640
  captureError,
4551
4641
  classifyFetchTarget,
4552
4642
  collectSourceMaps,