@glasstrace/sdk 1.1.3 → 1.2.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.
Files changed (49) hide show
  1. package/README.md +54 -1
  2. package/dist/{chunk-6RNBUUBR.js → chunk-2GCN27SI.js} +2 -2
  3. package/dist/{chunk-4EZ6JTDG.js → chunk-47B2G3FE.js} +2 -2
  4. package/dist/{chunk-FGDS33I2.js → chunk-CTJO7PUZ.js} +71 -17
  5. package/dist/chunk-CTJO7PUZ.js.map +1 -0
  6. package/dist/{chunk-X5MAXP5T.js → chunk-EBYISKQP.js} +5 -3
  7. package/dist/{chunk-X5MAXP5T.js.map → chunk-EBYISKQP.js.map} +1 -1
  8. package/dist/{chunk-JKI4OCFV.js → chunk-HV5ID2WJ.js} +3 -3
  9. package/dist/{chunk-TWTWRJ25.js → chunk-MD5XPCTQ.js} +2 -2
  10. package/dist/{chunk-TWHCJKRS.js → chunk-NFPDYME5.js} +3 -3
  11. package/dist/{chunk-DST4UBXU.js → chunk-YMLMZCPR.js} +2 -2
  12. package/dist/cli/init.cjs +5 -3
  13. package/dist/cli/init.cjs.map +1 -1
  14. package/dist/cli/init.js +6 -6
  15. package/dist/cli/mcp-add.cjs +4 -2
  16. package/dist/cli/mcp-add.cjs.map +1 -1
  17. package/dist/cli/mcp-add.js +2 -2
  18. package/dist/cli/uninit.js +3 -3
  19. package/dist/cli/validate.cjs +4 -2
  20. package/dist/cli/validate.cjs.map +1 -1
  21. package/dist/cli/validate.js +2 -2
  22. package/dist/edge-entry.cjs +4 -2
  23. package/dist/edge-entry.cjs.map +1 -1
  24. package/dist/edge-entry.js +2 -2
  25. package/dist/index.cjs +70 -14
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +5 -5
  28. package/dist/node-entry.cjs +70 -14
  29. package/dist/node-entry.cjs.map +1 -1
  30. package/dist/node-entry.js +7 -7
  31. package/dist/node-subpath.cjs +4 -2
  32. package/dist/node-subpath.cjs.map +1 -1
  33. package/dist/node-subpath.js +3 -3
  34. package/dist/{source-map-uploader-DPUUCLNW.js → source-map-uploader-RA4Z7TWA.js} +3 -3
  35. package/dist/trpc/index.cjs +809 -0
  36. package/dist/trpc/index.cjs.map +1 -0
  37. package/dist/trpc/index.d.cts +165 -0
  38. package/dist/trpc/index.d.ts +165 -0
  39. package/dist/trpc/index.js +65 -0
  40. package/dist/trpc/index.js.map +1 -0
  41. package/package.json +12 -1
  42. package/dist/chunk-FGDS33I2.js.map +0 -1
  43. /package/dist/{chunk-6RNBUUBR.js.map → chunk-2GCN27SI.js.map} +0 -0
  44. /package/dist/{chunk-4EZ6JTDG.js.map → chunk-47B2G3FE.js.map} +0 -0
  45. /package/dist/{chunk-JKI4OCFV.js.map → chunk-HV5ID2WJ.js.map} +0 -0
  46. /package/dist/{chunk-TWTWRJ25.js.map → chunk-MD5XPCTQ.js.map} +0 -0
  47. /package/dist/{chunk-TWHCJKRS.js.map → chunk-NFPDYME5.js.map} +0 -0
  48. /package/dist/{chunk-DST4UBXU.js.map → chunk-YMLMZCPR.js.map} +0 -0
  49. /package/dist/{source-map-uploader-DPUUCLNW.js.map → source-map-uploader-RA4Z7TWA.js.map} +0 -0
package/README.md CHANGED
@@ -280,7 +280,7 @@ file directly and no longer needs the runtime handler.
280
280
 
281
281
  ## Subpath exports
282
282
 
283
- `@glasstrace/sdk` ships three public entries:
283
+ `@glasstrace/sdk` ships four public entries:
284
284
 
285
285
  - **`@glasstrace/sdk`** — primary import site. Use from
286
286
  `instrumentation.ts` (runtime instrumentation) and `next.config.ts`
@@ -298,6 +298,8 @@ file directly and no longer needs the runtime handler.
298
298
  condition; non-Node runtimes (workerd, edge-light) fail cleanly at
299
299
  module resolution rather than at evaluation.
300
300
  - **`@glasstrace/sdk/drizzle`** — Drizzle ORM adapter.
301
+ - **`@glasstrace/sdk/trpc`** — tRPC middleware-chain instrumentation.
302
+ See "tRPC middleware instrumentation" below.
301
303
 
302
304
  The source-map and import-graph helpers previously reachable from the
303
305
  `@glasstrace/sdk` root specifier have moved to `@glasstrace/sdk/node`
@@ -383,6 +385,57 @@ on the Node-only side to become edge-safe, the right move is to remove
383
385
  the `process` and Node built-in reaches from the symbol's transitive
384
386
  closure, not to add a runtime guard.
385
387
 
388
+ ## tRPC middleware instrumentation
389
+
390
+ The `@glasstrace/sdk/trpc` subpath exposes `tracedMiddleware`, a thin
391
+ wrapper that turns a user-supplied tRPC middleware function into a
392
+ span-emitting middleware function. Each invocation opens a child span
393
+ named `options.name` under the active OTel context (typically the HTTP
394
+ server span), so middleware steps land as children of the HTTP span
395
+ without manual context plumbing. Errors thrown from the middleware
396
+ body are recorded via `span.recordException` and propagate unchanged;
397
+ short-circuit `{ ok: false, error }` results mark the span `ERROR`
398
+ without recording an exception.
399
+
400
+ `@trpc/server` is declared as an optional peer dependency
401
+ (`^10.0.0 || ^11.0.0`); projects that do not use tRPC pay no runtime
402
+ cost because the subpath is excluded from the root barrel and is
403
+ tree-shakeable.
404
+
405
+ ```ts
406
+ // trpc.ts — your project
407
+ import { initTRPC, TRPCError } from "@trpc/server";
408
+ import { tracedMiddleware } from "@glasstrace/sdk/trpc";
409
+
410
+ interface MyContext { session?: { userId: string }; tier?: string }
411
+ const t = initTRPC.context<MyContext>().create();
412
+
413
+ const isAuthed = t.middleware(
414
+ tracedMiddleware({ name: "isAuthed" }, async ({ ctx, next }) => {
415
+ if (!ctx.session) throw new TRPCError({ code: "UNAUTHORIZED" });
416
+ return next({ ctx: { ...ctx, session: ctx.session } });
417
+ }),
418
+ );
419
+
420
+ const isPro = t.middleware(
421
+ tracedMiddleware({ name: "isPro" }, async ({ ctx, next }) => {
422
+ if (ctx.tier !== "pro") throw new TRPCError({ code: "FORBIDDEN" });
423
+ return next();
424
+ }),
425
+ );
426
+
427
+ export const proProcedure = t.procedure.use(isAuthed).use(isPro);
428
+ ```
429
+
430
+ The wrapped function preserves the original middleware's call-site type,
431
+ so tRPC's procedure-builder context narrowing flows through unchanged.
432
+ The existing `glasstrace.trpc.procedure` attribute (set on the parent
433
+ HTTP span) is not duplicated on the middleware child spans — middleware
434
+ spans carry only `trpc.path`, `trpc.type`, and any caller-supplied
435
+ `options.attributes`. Caller-supplied attributes are forwarded as-is;
436
+ the SDK does not redact them, so callers must avoid placing tokens or
437
+ credentials in `options.attributes`.
438
+
386
439
  ## Security
387
440
 
388
441
  The SDK transmits your API key exclusively via the `Authorization: Bearer`
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-DQ25VOKK.js";
4
4
  import {
5
5
  GLASSTRACE_ATTRIBUTE_NAMES
6
- } from "./chunk-X5MAXP5T.js";
6
+ } from "./chunk-EBYISKQP.js";
7
7
 
8
8
  // src/errors.ts
9
9
  var SdkError = class extends Error {
@@ -102,4 +102,4 @@ export {
102
102
  GlasstraceSpanProcessor,
103
103
  captureCorrelationId
104
104
  };
105
- //# sourceMappingURL=chunk-6RNBUUBR.js.map
105
+ //# sourceMappingURL=chunk-2GCN27SI.js.map
@@ -5,7 +5,7 @@ import {
5
5
  PresignedUploadResponseSchema,
6
6
  SourceMapManifestResponseSchema,
7
7
  SourceMapUploadResponseSchema
8
- } from "./chunk-X5MAXP5T.js";
8
+ } from "./chunk-EBYISKQP.js";
9
9
 
10
10
  // src/source-map-uploader.ts
11
11
  import * as fs from "node:fs/promises";
@@ -331,4 +331,4 @@ export {
331
331
  uploadSourceMapsPresigned,
332
332
  uploadSourceMapsAuto
333
333
  };
334
- //# sourceMappingURL=chunk-4EZ6JTDG.js.map
334
+ //# sourceMappingURL=chunk-47B2G3FE.js.map
@@ -33,7 +33,7 @@ import {
33
33
  performInit,
34
34
  recordSpansDropped,
35
35
  recordSpansExported
36
- } from "./chunk-JKI4OCFV.js";
36
+ } from "./chunk-HV5ID2WJ.js";
37
37
  import {
38
38
  isAnonymousMode,
39
39
  isProductionDisabled,
@@ -43,11 +43,11 @@ import {
43
43
  atomicWriteFileSync,
44
44
  getOrCreateAnonKey,
45
45
  readAnonKey
46
- } from "./chunk-TWTWRJ25.js";
46
+ } from "./chunk-MD5XPCTQ.js";
47
47
  import {
48
48
  GLASSTRACE_ATTRIBUTE_NAMES,
49
49
  deriveSessionId
50
- } from "./chunk-X5MAXP5T.js";
50
+ } from "./chunk-EBYISKQP.js";
51
51
  import {
52
52
  __require
53
53
  } from "./chunk-NSBPE2FW.js";
@@ -144,16 +144,22 @@ var ERROR_RESPONSE_BODY_TRUNCATION_MARKER = "...[truncated]";
144
144
  var REDACTED = "[REDACTED]";
145
145
  var ERROR_STATUS_MIN = 400;
146
146
  var ERROR_STATUS_MAX = 599;
147
- function isHttpErrorStatus(status) {
147
+ function coerceHttpStatus(value) {
148
148
  let numeric;
149
- if (typeof status === "number") {
150
- numeric = status;
151
- } else if (typeof status === "string" && status.length > 0) {
152
- numeric = Number(status);
149
+ if (typeof value === "number") {
150
+ numeric = value;
151
+ } else if (typeof value === "string") {
152
+ const trimmed = value.trim();
153
+ if (trimmed.length === 0) return void 0;
154
+ numeric = Number(trimmed);
153
155
  } else {
154
- return false;
156
+ return void 0;
155
157
  }
156
- if (!Number.isFinite(numeric)) return false;
158
+ return Number.isFinite(numeric) ? numeric : void 0;
159
+ }
160
+ function isHttpErrorStatus(status) {
161
+ const numeric = coerceHttpStatus(status);
162
+ if (numeric === void 0) return false;
157
163
  return numeric >= ERROR_STATUS_MIN && numeric <= ERROR_STATUS_MAX;
158
164
  }
159
165
  var REDACTION_PATTERNS = [
@@ -413,7 +419,7 @@ var GlasstraceExporter = class {
413
419
  }
414
420
  }
415
421
  }
416
- const statusCode = attrs["http.status_code"] ?? attrs["http.response.status_code"];
422
+ const statusCode = coerceHttpStatus(attrs["http.status_code"]) ?? coerceHttpStatus(attrs["http.response.status_code"]);
417
423
  if (statusCode !== void 0) {
418
424
  extra[ATTR.HTTP_STATUS_CODE] = statusCode;
419
425
  }
@@ -3906,8 +3912,51 @@ function createDiscoveryHandler(getAnonKey, getSessionId, getClaimState) {
3906
3912
 
3907
3913
  // src/context-manager.ts
3908
3914
  import { AsyncLocalStorage } from "node:async_hooks";
3915
+ var GLASSTRACE_BRAND = 1;
3916
+ var GUARD = /* @__PURE__ */ Symbol.for("glasstrace.context-manager.installed");
3917
+ var OTEL_API_KEY = /* @__PURE__ */ Symbol.for("opentelemetry.js.api.1");
3918
+ function isOtelContextManager(value) {
3919
+ if (typeof value !== "object" || value === null) return false;
3920
+ const candidate = value;
3921
+ return typeof candidate.active === "function" && typeof candidate.with === "function" && typeof candidate.bind === "function" && typeof candidate.enable === "function" && typeof candidate.disable === "function";
3922
+ }
3923
+ function isInstallationRecord(value) {
3924
+ if (typeof value !== "object" || value === null) return false;
3925
+ const candidate = value;
3926
+ if (candidate.glasstraceContextManagerBrand !== GLASSTRACE_BRAND) return false;
3927
+ return candidate.manager === null || isOtelContextManager(candidate.manager);
3928
+ }
3929
+ function getOtelRegisteredContextManager() {
3930
+ const otelSlot = globalThis[OTEL_API_KEY];
3931
+ if (typeof otelSlot !== "object" || otelSlot === null) return void 0;
3932
+ const ctx = otelSlot.context;
3933
+ return isOtelContextManager(ctx) ? ctx : void 0;
3934
+ }
3909
3935
  function installContextManager() {
3910
3936
  try {
3937
+ const slot = globalThis;
3938
+ const existing = slot[GUARD];
3939
+ const otelCurrent = getOtelRegisteredContextManager();
3940
+ if (isInstallationRecord(existing) && existing.manager !== null && existing.manager === otelCurrent) {
3941
+ return true;
3942
+ }
3943
+ if (isInstallationRecord(existing) && existing.manager === null && otelCurrent !== void 0) {
3944
+ return false;
3945
+ }
3946
+ if (isInstallationRecord(existing) && existing.manager !== null) {
3947
+ const reSuccess = context.setGlobalContextManager(existing.manager);
3948
+ if (!reSuccess) {
3949
+ console.warn(
3950
+ "[glasstrace] Another context manager is already registered. Trace context propagation may not work as expected."
3951
+ );
3952
+ }
3953
+ const reRecord = {
3954
+ glasstraceContextManagerBrand: GLASSTRACE_BRAND,
3955
+ manager: reSuccess ? existing.manager : null
3956
+ };
3957
+ slot[GUARD] = reRecord;
3958
+ return reSuccess;
3959
+ }
3911
3960
  const als = new AsyncLocalStorage();
3912
3961
  const contextManager = {
3913
3962
  active: () => als.getStore() ?? ROOT_CONTEXT,
@@ -3928,6 +3977,11 @@ function installContextManager() {
3928
3977
  "[glasstrace] Another context manager is already registered. Trace context propagation may not work as expected."
3929
3978
  );
3930
3979
  }
3980
+ const record = {
3981
+ glasstraceContextManagerBrand: GLASSTRACE_BRAND,
3982
+ manager: success ? contextManager : null
3983
+ };
3984
+ slot[GUARD] = record;
3931
3985
  return success;
3932
3986
  } catch {
3933
3987
  return false;
@@ -4153,7 +4207,7 @@ function registerGlasstrace(options) {
4153
4207
  setCoreState(CoreState.REGISTERING);
4154
4208
  startRuntimeStateWriter({
4155
4209
  projectRoot: process.cwd(),
4156
- sdkVersion: "1.1.3"
4210
+ sdkVersion: "1.2.1"
4157
4211
  });
4158
4212
  const config = resolveConfig(options);
4159
4213
  if (config.verbose) {
@@ -4319,8 +4373,8 @@ async function backgroundInit(config, anonKeyForInit, generation) {
4319
4373
  if (config.verbose) {
4320
4374
  console.info("[glasstrace] Background init firing.");
4321
4375
  }
4322
- const healthReport = collectHealthReport("1.1.3");
4323
- const initResult = await performInit(config, anonKeyForInit, "1.1.3", healthReport);
4376
+ const healthReport = collectHealthReport("1.2.1");
4377
+ const initResult = await performInit(config, anonKeyForInit, "1.2.1", healthReport);
4324
4378
  if (generation !== registrationGeneration) return;
4325
4379
  const currentState = getCoreState();
4326
4380
  if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
@@ -4343,7 +4397,7 @@ async function backgroundInit(config, anonKeyForInit, generation) {
4343
4397
  }
4344
4398
  maybeInstallConsoleCapture();
4345
4399
  if (didLastInitSucceed()) {
4346
- startHeartbeat(config, anonKeyForInit, "1.1.3", generation, (newApiKey, accountId) => {
4400
+ startHeartbeat(config, anonKeyForInit, "1.2.1", generation, (newApiKey, accountId) => {
4347
4401
  setAuthState(AuthState.CLAIMING);
4348
4402
  emitLifecycleEvent("auth:claim_started", { accountId });
4349
4403
  setResolvedApiKey(newApiKey);
@@ -4638,7 +4692,7 @@ async function handleSourceMapUpload(distDir) {
4638
4692
  );
4639
4693
  return;
4640
4694
  }
4641
- const { discoverSourceMapFiles, computeBuildHash, uploadSourceMaps } = await import("./source-map-uploader-DPUUCLNW.js");
4695
+ const { discoverSourceMapFiles, computeBuildHash, uploadSourceMaps } = await import("./source-map-uploader-RA4Z7TWA.js");
4642
4696
  const files = await discoverSourceMapFiles(distDir);
4643
4697
  if (files.length === 0) {
4644
4698
  console.info("[glasstrace] No source map files found. Skipping upload.");
@@ -4692,4 +4746,4 @@ export {
4692
4746
  withGlasstraceConfig,
4693
4747
  captureError
4694
4748
  };
4695
- //# sourceMappingURL=chunk-FGDS33I2.js.map
4749
+ //# sourceMappingURL=chunk-CTJO7PUZ.js.map