@glasstrace/sdk 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -1
- package/dist/{chunk-Z35HKVSO.js → chunk-6RIH6SFM.js} +58 -11
- package/dist/{chunk-Z35HKVSO.js.map → chunk-6RIH6SFM.js.map} +1 -1
- package/dist/{chunk-C567H5EQ.js → chunk-JKI4OCFV.js} +4 -14
- package/dist/chunk-JKI4OCFV.js.map +1 -0
- package/dist/{chunk-UJ2JC7PZ.js → chunk-TWHCJKRS.js} +17 -16
- package/dist/chunk-TWHCJKRS.js.map +1 -0
- package/dist/{chunk-3LILTM3T.js → chunk-TWTWRJ25.js} +233 -9
- package/dist/chunk-TWTWRJ25.js.map +1 -0
- package/dist/cli/init.cjs +156 -17
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +4 -4
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +1 -1
- package/dist/cli/uninit.cjs +113 -11
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.js +2 -2
- package/dist/cli/validate.cjs.map +1 -1
- package/dist/cli/validate.js +1 -1
- package/dist/index.cjs +257 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -9
- package/dist/index.d.ts +12 -9
- package/dist/index.js +3 -3
- package/dist/node-entry.cjs +257 -27
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.js +3 -3
- package/dist/trpc/index.cjs +809 -0
- package/dist/trpc/index.cjs.map +1 -0
- package/dist/trpc/index.d.cts +165 -0
- package/dist/trpc/index.d.ts +165 -0
- package/dist/trpc/index.js +65 -0
- package/dist/trpc/index.js.map +1 -0
- package/package.json +12 -1
- package/dist/chunk-3LILTM3T.js.map +0 -1
- package/dist/chunk-C567H5EQ.js.map +0 -1
- package/dist/chunk-UJ2JC7PZ.js.map +0 -1
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
|
|
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`
|
|
@@ -33,16 +33,17 @@ import {
|
|
|
33
33
|
performInit,
|
|
34
34
|
recordSpansDropped,
|
|
35
35
|
recordSpansExported
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-JKI4OCFV.js";
|
|
37
37
|
import {
|
|
38
38
|
isAnonymousMode,
|
|
39
39
|
isProductionDisabled,
|
|
40
40
|
resolveConfig
|
|
41
41
|
} from "./chunk-VUZCLMIX.js";
|
|
42
42
|
import {
|
|
43
|
+
atomicWriteFileSync,
|
|
43
44
|
getOrCreateAnonKey,
|
|
44
45
|
readAnonKey
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-TWTWRJ25.js";
|
|
46
47
|
import {
|
|
47
48
|
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
48
49
|
deriveSessionId
|
|
@@ -3905,8 +3906,51 @@ function createDiscoveryHandler(getAnonKey, getSessionId, getClaimState) {
|
|
|
3905
3906
|
|
|
3906
3907
|
// src/context-manager.ts
|
|
3907
3908
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3909
|
+
var GLASSTRACE_BRAND = 1;
|
|
3910
|
+
var GUARD = /* @__PURE__ */ Symbol.for("glasstrace.context-manager.installed");
|
|
3911
|
+
var OTEL_API_KEY = /* @__PURE__ */ Symbol.for("opentelemetry.js.api.1");
|
|
3912
|
+
function isOtelContextManager(value) {
|
|
3913
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3914
|
+
const candidate = value;
|
|
3915
|
+
return typeof candidate.active === "function" && typeof candidate.with === "function" && typeof candidate.bind === "function" && typeof candidate.enable === "function" && typeof candidate.disable === "function";
|
|
3916
|
+
}
|
|
3917
|
+
function isInstallationRecord(value) {
|
|
3918
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3919
|
+
const candidate = value;
|
|
3920
|
+
if (candidate.glasstraceContextManagerBrand !== GLASSTRACE_BRAND) return false;
|
|
3921
|
+
return candidate.manager === null || isOtelContextManager(candidate.manager);
|
|
3922
|
+
}
|
|
3923
|
+
function getOtelRegisteredContextManager() {
|
|
3924
|
+
const otelSlot = globalThis[OTEL_API_KEY];
|
|
3925
|
+
if (typeof otelSlot !== "object" || otelSlot === null) return void 0;
|
|
3926
|
+
const ctx = otelSlot.context;
|
|
3927
|
+
return isOtelContextManager(ctx) ? ctx : void 0;
|
|
3928
|
+
}
|
|
3908
3929
|
function installContextManager() {
|
|
3909
3930
|
try {
|
|
3931
|
+
const slot = globalThis;
|
|
3932
|
+
const existing = slot[GUARD];
|
|
3933
|
+
const otelCurrent = getOtelRegisteredContextManager();
|
|
3934
|
+
if (isInstallationRecord(existing) && existing.manager !== null && existing.manager === otelCurrent) {
|
|
3935
|
+
return true;
|
|
3936
|
+
}
|
|
3937
|
+
if (isInstallationRecord(existing) && existing.manager === null && otelCurrent !== void 0) {
|
|
3938
|
+
return false;
|
|
3939
|
+
}
|
|
3940
|
+
if (isInstallationRecord(existing) && existing.manager !== null) {
|
|
3941
|
+
const reSuccess = context.setGlobalContextManager(existing.manager);
|
|
3942
|
+
if (!reSuccess) {
|
|
3943
|
+
console.warn(
|
|
3944
|
+
"[glasstrace] Another context manager is already registered. Trace context propagation may not work as expected."
|
|
3945
|
+
);
|
|
3946
|
+
}
|
|
3947
|
+
const reRecord = {
|
|
3948
|
+
glasstraceContextManagerBrand: GLASSTRACE_BRAND,
|
|
3949
|
+
manager: reSuccess ? existing.manager : null
|
|
3950
|
+
};
|
|
3951
|
+
slot[GUARD] = reRecord;
|
|
3952
|
+
return reSuccess;
|
|
3953
|
+
}
|
|
3910
3954
|
const als = new AsyncLocalStorage();
|
|
3911
3955
|
const contextManager = {
|
|
3912
3956
|
active: () => als.getStore() ?? ROOT_CONTEXT,
|
|
@@ -3927,6 +3971,11 @@ function installContextManager() {
|
|
|
3927
3971
|
"[glasstrace] Another context manager is already registered. Trace context propagation may not work as expected."
|
|
3928
3972
|
);
|
|
3929
3973
|
}
|
|
3974
|
+
const record = {
|
|
3975
|
+
glasstraceContextManagerBrand: GLASSTRACE_BRAND,
|
|
3976
|
+
manager: success ? contextManager : null
|
|
3977
|
+
};
|
|
3978
|
+
slot[GUARD] = record;
|
|
3930
3979
|
return success;
|
|
3931
3980
|
} catch {
|
|
3932
3981
|
return false;
|
|
@@ -4058,7 +4107,7 @@ function registerHeartbeatShutdownHook(config, anonKey, sdkVersion) {
|
|
|
4058
4107
|
}
|
|
4059
4108
|
|
|
4060
4109
|
// src/runtime-state.ts
|
|
4061
|
-
import {
|
|
4110
|
+
import { mkdirSync } from "node:fs";
|
|
4062
4111
|
import { join } from "node:path";
|
|
4063
4112
|
var _projectRoot = null;
|
|
4064
4113
|
var _sdkVersion = "unknown";
|
|
@@ -4110,12 +4159,10 @@ function writeStateNow() {
|
|
|
4110
4159
|
};
|
|
4111
4160
|
const dir = join(_projectRoot, ".glasstrace");
|
|
4112
4161
|
const filePath = join(dir, "runtime-state.json");
|
|
4113
|
-
const tmpPath = join(dir, "runtime-state.json.tmp");
|
|
4114
4162
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4115
|
-
|
|
4163
|
+
atomicWriteFileSync(filePath, JSON.stringify(runtimeState, null, 2) + "\n", {
|
|
4116
4164
|
mode: 384
|
|
4117
4165
|
});
|
|
4118
|
-
renameSync(tmpPath, filePath);
|
|
4119
4166
|
} catch (err) {
|
|
4120
4167
|
sdkLog(
|
|
4121
4168
|
"warn",
|
|
@@ -4154,7 +4201,7 @@ function registerGlasstrace(options) {
|
|
|
4154
4201
|
setCoreState(CoreState.REGISTERING);
|
|
4155
4202
|
startRuntimeStateWriter({
|
|
4156
4203
|
projectRoot: process.cwd(),
|
|
4157
|
-
sdkVersion: "1.
|
|
4204
|
+
sdkVersion: "1.2.0"
|
|
4158
4205
|
});
|
|
4159
4206
|
const config = resolveConfig(options);
|
|
4160
4207
|
if (config.verbose) {
|
|
@@ -4320,8 +4367,8 @@ async function backgroundInit(config, anonKeyForInit, generation) {
|
|
|
4320
4367
|
if (config.verbose) {
|
|
4321
4368
|
console.info("[glasstrace] Background init firing.");
|
|
4322
4369
|
}
|
|
4323
|
-
const healthReport = collectHealthReport("1.
|
|
4324
|
-
const initResult = await performInit(config, anonKeyForInit, "1.
|
|
4370
|
+
const healthReport = collectHealthReport("1.2.0");
|
|
4371
|
+
const initResult = await performInit(config, anonKeyForInit, "1.2.0", healthReport);
|
|
4325
4372
|
if (generation !== registrationGeneration) return;
|
|
4326
4373
|
const currentState = getCoreState();
|
|
4327
4374
|
if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
|
|
@@ -4344,7 +4391,7 @@ async function backgroundInit(config, anonKeyForInit, generation) {
|
|
|
4344
4391
|
}
|
|
4345
4392
|
maybeInstallConsoleCapture();
|
|
4346
4393
|
if (didLastInitSucceed()) {
|
|
4347
|
-
startHeartbeat(config, anonKeyForInit, "1.
|
|
4394
|
+
startHeartbeat(config, anonKeyForInit, "1.2.0", generation, (newApiKey, accountId) => {
|
|
4348
4395
|
setAuthState(AuthState.CLAIMING);
|
|
4349
4396
|
emitLifecycleEvent("auth:claim_started", { accountId });
|
|
4350
4397
|
setResolvedApiKey(newApiKey);
|
|
@@ -4693,4 +4740,4 @@ export {
|
|
|
4693
4740
|
withGlasstraceConfig,
|
|
4694
4741
|
captureError
|
|
4695
4742
|
};
|
|
4696
|
-
//# sourceMappingURL=chunk-
|
|
4743
|
+
//# sourceMappingURL=chunk-6RIH6SFM.js.map
|