@glasstrace/sdk 1.8.0 → 1.9.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/async-context/index.cjs +15010 -0
- package/dist/async-context/index.cjs.map +1 -0
- package/dist/async-context/index.d.cts +174 -0
- package/dist/async-context/index.d.ts +174 -0
- package/dist/async-context/index.js +13 -0
- package/dist/{capture-error-Dzz7_-v4.d.ts → capture-error-BeuEXXJO.d.cts} +2 -2
- package/dist/{capture-error-C95zvSvF.d.cts → capture-error-D02pzB7q.d.ts} +2 -2
- package/dist/{chunk-JJL2M64Z.js → chunk-2F2MGFLO.js} +19 -10
- package/dist/chunk-2F2MGFLO.js.map +1 -0
- package/dist/chunk-CL3OVHPO.js +23 -0
- package/dist/chunk-CL3OVHPO.js.map +1 -0
- package/dist/{chunk-WL6BXEJ5.js → chunk-DKV53A2C.js} +2 -2
- package/dist/{chunk-3PJP5Y3U.js → chunk-GWIEUBFR.js} +3 -3
- package/dist/{chunk-H57MQGNU.js → chunk-H6WJ63X2.js} +2 -2
- package/dist/{chunk-NN5YCETI.js → chunk-HD6JIFKN.js} +2 -2
- package/dist/{chunk-P45NZR4J.js → chunk-JHUNLPSS.js} +35 -1
- package/dist/{chunk-P45NZR4J.js.map → chunk-JHUNLPSS.js.map} +1 -1
- package/dist/{chunk-UQKI476D.js → chunk-M6EWJCAT.js} +2 -2
- package/dist/chunk-QEXRCXSY.js +124 -0
- package/dist/chunk-QEXRCXSY.js.map +1 -0
- package/dist/chunk-QOHKZOKB.js +139 -0
- package/dist/chunk-QOHKZOKB.js.map +1 -0
- package/dist/{chunk-M2TLX6NM.js → chunk-QXITSNYM.js} +3 -3
- package/dist/cli/init.cjs +4 -4
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +7 -7
- package/dist/cli/mcp-add.cjs +1 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +3 -3
- package/dist/cli/uninit.js +3 -3
- package/dist/cli/upgrade-instructions.cjs +1 -1
- package/dist/cli/upgrade-instructions.js +3 -3
- package/dist/cli/validate.cjs.map +1 -1
- package/dist/cli/validate.js +2 -2
- package/dist/{edge-entry-AWO70gje.d.ts → correlation-id-B_K8adD6.d.ts} +1 -1
- package/dist/{edge-entry-DaeG7D7S.d.cts → correlation-id-NAapJ5jn.d.cts} +1 -1
- package/dist/edge-entry.cjs +309 -26
- package/dist/edge-entry.cjs.map +1 -1
- package/dist/edge-entry.d.cts +5 -2
- package/dist/edge-entry.d.ts +5 -2
- package/dist/edge-entry.js +12 -3
- package/dist/index.cjs +55 -5
- package/dist/index.cjs.map +1 -1
- package/dist/{index.d-Dq33YwFT.d.cts → index.d-CkTf_boH.d.cts} +1 -1
- package/dist/{index.d-Dq33YwFT.d.ts → index.d-CkTf_boH.d.ts} +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.cjs +15023 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +191 -0
- package/dist/middleware/index.d.ts +191 -0
- package/dist/middleware/index.js +13 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/node-entry.cjs +55 -5
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.d.cts +3 -3
- package/dist/node-entry.d.ts +3 -3
- package/dist/node-entry.js +8 -7
- package/dist/node-subpath.cjs.map +1 -1
- package/dist/node-subpath.d.cts +1 -1
- package/dist/node-subpath.d.ts +1 -1
- package/dist/node-subpath.js +3 -3
- package/dist/{source-map-uploader-XFUEVV7I.js → source-map-uploader-MMJ2WCL4.js} +3 -3
- package/dist/source-map-uploader-MMJ2WCL4.js.map +1 -0
- package/package.json +13 -1
- package/dist/chunk-JJL2M64Z.js.map +0 -1
- /package/dist/{source-map-uploader-XFUEVV7I.js.map → async-context/index.js.map} +0 -0
- /package/dist/{chunk-WL6BXEJ5.js.map → chunk-DKV53A2C.js.map} +0 -0
- /package/dist/{chunk-3PJP5Y3U.js.map → chunk-GWIEUBFR.js.map} +0 -0
- /package/dist/{chunk-H57MQGNU.js.map → chunk-H6WJ63X2.js.map} +0 -0
- /package/dist/{chunk-NN5YCETI.js.map → chunk-HD6JIFKN.js.map} +0 -0
- /package/dist/{chunk-UQKI476D.js.map → chunk-M6EWJCAT.js.map} +0 -0
- /package/dist/{chunk-M2TLX6NM.js.map → chunk-QXITSNYM.js.map} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { AttributeValue } from './common/Attributes';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request-middleware-ownership instrumentation for Glasstrace.
|
|
5
|
+
*
|
|
6
|
+
* Subpath: `@glasstrace/sdk/middleware`
|
|
7
|
+
*
|
|
8
|
+
* This module exposes {@link tracedRequestMiddleware}, a wrapper that
|
|
9
|
+
* turns a Next.js `middleware.ts` function (or any generic
|
|
10
|
+
* `Request → Response`-shaped handler) into a span-emitting middleware
|
|
11
|
+
* function. Each invocation opens a child span and tags it with the
|
|
12
|
+
* `glasstrace.causal.middleware_for_request` causal-evidence attribute
|
|
13
|
+
* carrying the originating request's normalized path so the
|
|
14
|
+
* product-side trace-summary transform can link the middleware span
|
|
15
|
+
* to the owning HTTP request trace (DISC-1537 / SDK-046).
|
|
16
|
+
*
|
|
17
|
+
* Edge-runtime safety
|
|
18
|
+
* -------------------
|
|
19
|
+
* The wrapper is included in the SDK's edge bundle
|
|
20
|
+
* (`packages/sdk/src/edge-entry.ts`). Its closure imports only the
|
|
21
|
+
* OTel API, the protocol constants, and the
|
|
22
|
+
* `./optional-lifecycle.js` bridge — none of which reach into
|
|
23
|
+
* `node:*` built-ins or the `process` global. The F003 closure scan
|
|
24
|
+
* (`packages/sdk/scripts/check-edge-bundle.mjs`) enforces this on
|
|
25
|
+
* every build.
|
|
26
|
+
*
|
|
27
|
+
* Causal-evidence form
|
|
28
|
+
* --------------------
|
|
29
|
+
* The wrapper attaches the originating request path as a span
|
|
30
|
+
* attribute (`glasstrace.causal.middleware_for_request`). It does NOT
|
|
31
|
+
* emit an OTel `Link`. Reasons:
|
|
32
|
+
*
|
|
33
|
+
* 1. The Next.js Edge runtime does not propagate AsyncLocalStorage
|
|
34
|
+
* into `middleware.ts`, so there is no in-process
|
|
35
|
+
* `SpanContext` to link to in that environment. Attribute-only
|
|
36
|
+
* causality works in both Node and Edge runtimes; a Link would
|
|
37
|
+
* degrade to a no-op (no parent context) on Edge.
|
|
38
|
+
* 2. The product-side trace-summary transform reconstructs
|
|
39
|
+
* ownership from `glasstrace.causal.*` attributes per
|
|
40
|
+
* DISC-1539 §51-58; it does not require a Link.
|
|
41
|
+
*
|
|
42
|
+
* Invariants
|
|
43
|
+
* ----------
|
|
44
|
+
*
|
|
45
|
+
* - The wrapped function preserves the user's call-site type so
|
|
46
|
+
* Next.js's `middleware` export contract (`(req: NextRequest) =>
|
|
47
|
+
* NextResponse | Response`) flows through unchanged.
|
|
48
|
+
* - The middleware span MUST NOT overwrite `glasstrace.route`,
|
|
49
|
+
* `glasstrace.http.status_code`, or `glasstrace.http.duration_ms`
|
|
50
|
+
* on the parent HTTP span — root-request semantics are owned by
|
|
51
|
+
* the enriching exporter (`packages/sdk/src/enriching-exporter.ts`).
|
|
52
|
+
* - On a thrown handler error: span ends with `ERROR` status +
|
|
53
|
+
* `recordException`; rethrows. The exception is normalized to
|
|
54
|
+
* `Error | string` first so non-Error throwables (number, plain
|
|
55
|
+
* object) do not crash `recordException`.
|
|
56
|
+
* - Always ends the span (`finally`), even on `throw`.
|
|
57
|
+
*
|
|
58
|
+
* @module @glasstrace/sdk/middleware
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* INTERNAL — clears the once-flag for the
|
|
63
|
+
* `middleware:skipped_uninstalled` lifecycle event. Invoked by
|
|
64
|
+
* Vitest fixtures only; not part of the public surface.
|
|
65
|
+
*/
|
|
66
|
+
declare function _resetForTesting(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Permissive structural bound for a request-middleware function. The
|
|
69
|
+
* shape is the intersection of Next.js's `middleware.ts` export
|
|
70
|
+
* (`(req: NextRequest, event?: NextFetchEvent) => NextResponse |
|
|
71
|
+
* Response | Promise<NextResponse | Response> | undefined`) and the
|
|
72
|
+
* generic Web Fetch API (`(req: Request, ...rest: any[]) => Response
|
|
73
|
+
* | Promise<Response>`). The parameter list is typed with `any[]` for
|
|
74
|
+
* the rest position so any caller-narrowed signature is assignable
|
|
75
|
+
* without the wrapper having to import `next/server` types.
|
|
76
|
+
*
|
|
77
|
+
* Exported so consumers can reference it for type-inference assertions
|
|
78
|
+
* (e.g., proving a strongly-typed handler fits the bound). The
|
|
79
|
+
* runtime contract is fixed by the Web Fetch API: the first argument
|
|
80
|
+
* is a `Request`-shaped object and the return is a `Response`-shaped
|
|
81
|
+
* value (or a Promise of one).
|
|
82
|
+
*/
|
|
83
|
+
type RequestMiddlewareFunction = (req: any, ...rest: any[]) => unknown;
|
|
84
|
+
/**
|
|
85
|
+
* Options for {@link tracedRequestMiddleware}.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { tracedRequestMiddleware } from "@glasstrace/sdk/middleware";
|
|
90
|
+
* import type { NextRequest } from "next/server";
|
|
91
|
+
*
|
|
92
|
+
* export const middleware = tracedRequestMiddleware(
|
|
93
|
+
* { name: "auth-middleware", attributes: { "auth.required": true } },
|
|
94
|
+
* async (req: NextRequest) => {
|
|
95
|
+
* // … your auth logic here …
|
|
96
|
+
* return NextResponse.next();
|
|
97
|
+
* },
|
|
98
|
+
* );
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
interface TracedRequestMiddlewareOptions {
|
|
102
|
+
/**
|
|
103
|
+
* Span name. Required, non-empty string. Used as the OTel span name
|
|
104
|
+
* and appears in trace timelines. Names should be stable across
|
|
105
|
+
* runs so the product-side transform can reason about middleware
|
|
106
|
+
* identity (e.g., "auth-middleware", "rate-limiter"); avoid
|
|
107
|
+
* embedding request data in the name.
|
|
108
|
+
*/
|
|
109
|
+
name: string;
|
|
110
|
+
/**
|
|
111
|
+
* Optional attributes attached to the span before the wrapped
|
|
112
|
+
* handler runs. Forwarded to OTel as-is via `span.setAttributes()`.
|
|
113
|
+
* The SDK does not redact, sanitize, or scan values here — callers
|
|
114
|
+
* MUST avoid placing tokens, credentials, or other sensitive data
|
|
115
|
+
* in `attributes`.
|
|
116
|
+
*
|
|
117
|
+
* Sensitive request/response data is captured through gated SDK
|
|
118
|
+
* paths (e.g., `glasstrace.error.response_body`), not through this
|
|
119
|
+
* surface.
|
|
120
|
+
*/
|
|
121
|
+
attributes?: Record<string, AttributeValue>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Wrap a Next.js / generic-fetch request-middleware function in an
|
|
125
|
+
* OTel span tagged with `glasstrace.causal.middleware_for_request`.
|
|
126
|
+
*
|
|
127
|
+
* **Privacy:** the value of
|
|
128
|
+
* `glasstrace.causal.middleware_for_request` is the raw URL
|
|
129
|
+
* pathname. Pathnames can carry user-controlled data (IDs, emails,
|
|
130
|
+
* opaque keys). The SDK does NOT redact this attribute. Callers MUST
|
|
131
|
+
* NOT place secrets, tokens, or other sensitive data in URL paths;
|
|
132
|
+
* the same general HTTP best practice that keeps secrets out of
|
|
133
|
+
* server logs keeps them out of Glasstrace trace evidence.
|
|
134
|
+
*
|
|
135
|
+
* Each call to the returned function:
|
|
136
|
+
*
|
|
137
|
+
* 1. Detects the SDK's registration state. When the OTel API is
|
|
138
|
+
* still on the noop tracer (SDK not registered, or
|
|
139
|
+
* `OtelState.UNCONFIGURED`), runs the wrapped handler directly
|
|
140
|
+
* and emits a `middleware:skipped_uninstalled` lifecycle event
|
|
141
|
+
* (at most once per process). No span is opened.
|
|
142
|
+
* 2. Otherwise opens a span named `options.name` under the active
|
|
143
|
+
* OTel context (typically the HTTP server span on Node;
|
|
144
|
+
* detached on Edge where AsyncLocalStorage is not available).
|
|
145
|
+
* Sets `options.attributes` first, then attaches the originating
|
|
146
|
+
* request's path (via {@link extractRequestPath}) as
|
|
147
|
+
* `glasstrace.causal.middleware_for_request`. The path is
|
|
148
|
+
* omitted when extraction returns `undefined` so absent evidence
|
|
149
|
+
* is preferred over guessed evidence.
|
|
150
|
+
* 3. Awaits the wrapped handler.
|
|
151
|
+
* 4. On a thrown error: normalizes the throwable to `Error | string`
|
|
152
|
+
* so `recordException` does not throw on non-Error values; sets
|
|
153
|
+
* `span.status` to `ERROR` with the error's message; rethrows the
|
|
154
|
+
* original (un-normalized) error verbatim.
|
|
155
|
+
* 5. On a successful return: leaves the span status `UNSET` per OTel
|
|
156
|
+
* instrumentation-library guidance (explicit `OK` would shadow
|
|
157
|
+
* downstream consumers' error transitions).
|
|
158
|
+
* 6. Always ends the span, even on `throw` or `return`.
|
|
159
|
+
*
|
|
160
|
+
* Type-inference: the returned function preserves the input function's
|
|
161
|
+
* type `H`, so caller-narrowed signatures (e.g., `(req: NextRequest)
|
|
162
|
+
* => NextResponse`) flow through unchanged.
|
|
163
|
+
*
|
|
164
|
+
* @param options - Span name and optional pre-start attributes.
|
|
165
|
+
* @param handler - The user's middleware handler. Must accept a
|
|
166
|
+
* request-shaped object as its first argument and return (or
|
|
167
|
+
* resolve to) a response-shaped value.
|
|
168
|
+
* @returns The wrapped handler with the same call signature and
|
|
169
|
+
* return type as `handler`.
|
|
170
|
+
*
|
|
171
|
+
* @example Next.js middleware.ts
|
|
172
|
+
* ```ts
|
|
173
|
+
* import { tracedRequestMiddleware } from "@glasstrace/sdk/middleware";
|
|
174
|
+
* import { NextResponse, type NextRequest } from "next/server";
|
|
175
|
+
*
|
|
176
|
+
* export const middleware = tracedRequestMiddleware(
|
|
177
|
+
* { name: "auth-middleware" },
|
|
178
|
+
* async (req: NextRequest) => {
|
|
179
|
+
* if (!req.cookies.get("session")) {
|
|
180
|
+
* return NextResponse.redirect(new URL("/login", req.url));
|
|
181
|
+
* }
|
|
182
|
+
* return NextResponse.next();
|
|
183
|
+
* },
|
|
184
|
+
* );
|
|
185
|
+
*
|
|
186
|
+
* export const config = { matcher: ["/dashboard/:path*"] };
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare function tracedRequestMiddleware<H extends RequestMiddlewareFunction>(options: TracedRequestMiddlewareOptions, handler: H): H;
|
|
190
|
+
|
|
191
|
+
export { type RequestMiddlewareFunction, type TracedRequestMiddlewareOptions, _resetForTesting, tracedRequestMiddleware };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { AttributeValue } from './common/Attributes';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Request-middleware-ownership instrumentation for Glasstrace.
|
|
5
|
+
*
|
|
6
|
+
* Subpath: `@glasstrace/sdk/middleware`
|
|
7
|
+
*
|
|
8
|
+
* This module exposes {@link tracedRequestMiddleware}, a wrapper that
|
|
9
|
+
* turns a Next.js `middleware.ts` function (or any generic
|
|
10
|
+
* `Request → Response`-shaped handler) into a span-emitting middleware
|
|
11
|
+
* function. Each invocation opens a child span and tags it with the
|
|
12
|
+
* `glasstrace.causal.middleware_for_request` causal-evidence attribute
|
|
13
|
+
* carrying the originating request's normalized path so the
|
|
14
|
+
* product-side trace-summary transform can link the middleware span
|
|
15
|
+
* to the owning HTTP request trace (DISC-1537 / SDK-046).
|
|
16
|
+
*
|
|
17
|
+
* Edge-runtime safety
|
|
18
|
+
* -------------------
|
|
19
|
+
* The wrapper is included in the SDK's edge bundle
|
|
20
|
+
* (`packages/sdk/src/edge-entry.ts`). Its closure imports only the
|
|
21
|
+
* OTel API, the protocol constants, and the
|
|
22
|
+
* `./optional-lifecycle.js` bridge — none of which reach into
|
|
23
|
+
* `node:*` built-ins or the `process` global. The F003 closure scan
|
|
24
|
+
* (`packages/sdk/scripts/check-edge-bundle.mjs`) enforces this on
|
|
25
|
+
* every build.
|
|
26
|
+
*
|
|
27
|
+
* Causal-evidence form
|
|
28
|
+
* --------------------
|
|
29
|
+
* The wrapper attaches the originating request path as a span
|
|
30
|
+
* attribute (`glasstrace.causal.middleware_for_request`). It does NOT
|
|
31
|
+
* emit an OTel `Link`. Reasons:
|
|
32
|
+
*
|
|
33
|
+
* 1. The Next.js Edge runtime does not propagate AsyncLocalStorage
|
|
34
|
+
* into `middleware.ts`, so there is no in-process
|
|
35
|
+
* `SpanContext` to link to in that environment. Attribute-only
|
|
36
|
+
* causality works in both Node and Edge runtimes; a Link would
|
|
37
|
+
* degrade to a no-op (no parent context) on Edge.
|
|
38
|
+
* 2. The product-side trace-summary transform reconstructs
|
|
39
|
+
* ownership from `glasstrace.causal.*` attributes per
|
|
40
|
+
* DISC-1539 §51-58; it does not require a Link.
|
|
41
|
+
*
|
|
42
|
+
* Invariants
|
|
43
|
+
* ----------
|
|
44
|
+
*
|
|
45
|
+
* - The wrapped function preserves the user's call-site type so
|
|
46
|
+
* Next.js's `middleware` export contract (`(req: NextRequest) =>
|
|
47
|
+
* NextResponse | Response`) flows through unchanged.
|
|
48
|
+
* - The middleware span MUST NOT overwrite `glasstrace.route`,
|
|
49
|
+
* `glasstrace.http.status_code`, or `glasstrace.http.duration_ms`
|
|
50
|
+
* on the parent HTTP span — root-request semantics are owned by
|
|
51
|
+
* the enriching exporter (`packages/sdk/src/enriching-exporter.ts`).
|
|
52
|
+
* - On a thrown handler error: span ends with `ERROR` status +
|
|
53
|
+
* `recordException`; rethrows. The exception is normalized to
|
|
54
|
+
* `Error | string` first so non-Error throwables (number, plain
|
|
55
|
+
* object) do not crash `recordException`.
|
|
56
|
+
* - Always ends the span (`finally`), even on `throw`.
|
|
57
|
+
*
|
|
58
|
+
* @module @glasstrace/sdk/middleware
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* INTERNAL — clears the once-flag for the
|
|
63
|
+
* `middleware:skipped_uninstalled` lifecycle event. Invoked by
|
|
64
|
+
* Vitest fixtures only; not part of the public surface.
|
|
65
|
+
*/
|
|
66
|
+
declare function _resetForTesting(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Permissive structural bound for a request-middleware function. The
|
|
69
|
+
* shape is the intersection of Next.js's `middleware.ts` export
|
|
70
|
+
* (`(req: NextRequest, event?: NextFetchEvent) => NextResponse |
|
|
71
|
+
* Response | Promise<NextResponse | Response> | undefined`) and the
|
|
72
|
+
* generic Web Fetch API (`(req: Request, ...rest: any[]) => Response
|
|
73
|
+
* | Promise<Response>`). The parameter list is typed with `any[]` for
|
|
74
|
+
* the rest position so any caller-narrowed signature is assignable
|
|
75
|
+
* without the wrapper having to import `next/server` types.
|
|
76
|
+
*
|
|
77
|
+
* Exported so consumers can reference it for type-inference assertions
|
|
78
|
+
* (e.g., proving a strongly-typed handler fits the bound). The
|
|
79
|
+
* runtime contract is fixed by the Web Fetch API: the first argument
|
|
80
|
+
* is a `Request`-shaped object and the return is a `Response`-shaped
|
|
81
|
+
* value (or a Promise of one).
|
|
82
|
+
*/
|
|
83
|
+
type RequestMiddlewareFunction = (req: any, ...rest: any[]) => unknown;
|
|
84
|
+
/**
|
|
85
|
+
* Options for {@link tracedRequestMiddleware}.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { tracedRequestMiddleware } from "@glasstrace/sdk/middleware";
|
|
90
|
+
* import type { NextRequest } from "next/server";
|
|
91
|
+
*
|
|
92
|
+
* export const middleware = tracedRequestMiddleware(
|
|
93
|
+
* { name: "auth-middleware", attributes: { "auth.required": true } },
|
|
94
|
+
* async (req: NextRequest) => {
|
|
95
|
+
* // … your auth logic here …
|
|
96
|
+
* return NextResponse.next();
|
|
97
|
+
* },
|
|
98
|
+
* );
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
interface TracedRequestMiddlewareOptions {
|
|
102
|
+
/**
|
|
103
|
+
* Span name. Required, non-empty string. Used as the OTel span name
|
|
104
|
+
* and appears in trace timelines. Names should be stable across
|
|
105
|
+
* runs so the product-side transform can reason about middleware
|
|
106
|
+
* identity (e.g., "auth-middleware", "rate-limiter"); avoid
|
|
107
|
+
* embedding request data in the name.
|
|
108
|
+
*/
|
|
109
|
+
name: string;
|
|
110
|
+
/**
|
|
111
|
+
* Optional attributes attached to the span before the wrapped
|
|
112
|
+
* handler runs. Forwarded to OTel as-is via `span.setAttributes()`.
|
|
113
|
+
* The SDK does not redact, sanitize, or scan values here — callers
|
|
114
|
+
* MUST avoid placing tokens, credentials, or other sensitive data
|
|
115
|
+
* in `attributes`.
|
|
116
|
+
*
|
|
117
|
+
* Sensitive request/response data is captured through gated SDK
|
|
118
|
+
* paths (e.g., `glasstrace.error.response_body`), not through this
|
|
119
|
+
* surface.
|
|
120
|
+
*/
|
|
121
|
+
attributes?: Record<string, AttributeValue>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Wrap a Next.js / generic-fetch request-middleware function in an
|
|
125
|
+
* OTel span tagged with `glasstrace.causal.middleware_for_request`.
|
|
126
|
+
*
|
|
127
|
+
* **Privacy:** the value of
|
|
128
|
+
* `glasstrace.causal.middleware_for_request` is the raw URL
|
|
129
|
+
* pathname. Pathnames can carry user-controlled data (IDs, emails,
|
|
130
|
+
* opaque keys). The SDK does NOT redact this attribute. Callers MUST
|
|
131
|
+
* NOT place secrets, tokens, or other sensitive data in URL paths;
|
|
132
|
+
* the same general HTTP best practice that keeps secrets out of
|
|
133
|
+
* server logs keeps them out of Glasstrace trace evidence.
|
|
134
|
+
*
|
|
135
|
+
* Each call to the returned function:
|
|
136
|
+
*
|
|
137
|
+
* 1. Detects the SDK's registration state. When the OTel API is
|
|
138
|
+
* still on the noop tracer (SDK not registered, or
|
|
139
|
+
* `OtelState.UNCONFIGURED`), runs the wrapped handler directly
|
|
140
|
+
* and emits a `middleware:skipped_uninstalled` lifecycle event
|
|
141
|
+
* (at most once per process). No span is opened.
|
|
142
|
+
* 2. Otherwise opens a span named `options.name` under the active
|
|
143
|
+
* OTel context (typically the HTTP server span on Node;
|
|
144
|
+
* detached on Edge where AsyncLocalStorage is not available).
|
|
145
|
+
* Sets `options.attributes` first, then attaches the originating
|
|
146
|
+
* request's path (via {@link extractRequestPath}) as
|
|
147
|
+
* `glasstrace.causal.middleware_for_request`. The path is
|
|
148
|
+
* omitted when extraction returns `undefined` so absent evidence
|
|
149
|
+
* is preferred over guessed evidence.
|
|
150
|
+
* 3. Awaits the wrapped handler.
|
|
151
|
+
* 4. On a thrown error: normalizes the throwable to `Error | string`
|
|
152
|
+
* so `recordException` does not throw on non-Error values; sets
|
|
153
|
+
* `span.status` to `ERROR` with the error's message; rethrows the
|
|
154
|
+
* original (un-normalized) error verbatim.
|
|
155
|
+
* 5. On a successful return: leaves the span status `UNSET` per OTel
|
|
156
|
+
* instrumentation-library guidance (explicit `OK` would shadow
|
|
157
|
+
* downstream consumers' error transitions).
|
|
158
|
+
* 6. Always ends the span, even on `throw` or `return`.
|
|
159
|
+
*
|
|
160
|
+
* Type-inference: the returned function preserves the input function's
|
|
161
|
+
* type `H`, so caller-narrowed signatures (e.g., `(req: NextRequest)
|
|
162
|
+
* => NextResponse`) flow through unchanged.
|
|
163
|
+
*
|
|
164
|
+
* @param options - Span name and optional pre-start attributes.
|
|
165
|
+
* @param handler - The user's middleware handler. Must accept a
|
|
166
|
+
* request-shaped object as its first argument and return (or
|
|
167
|
+
* resolve to) a response-shaped value.
|
|
168
|
+
* @returns The wrapped handler with the same call signature and
|
|
169
|
+
* return type as `handler`.
|
|
170
|
+
*
|
|
171
|
+
* @example Next.js middleware.ts
|
|
172
|
+
* ```ts
|
|
173
|
+
* import { tracedRequestMiddleware } from "@glasstrace/sdk/middleware";
|
|
174
|
+
* import { NextResponse, type NextRequest } from "next/server";
|
|
175
|
+
*
|
|
176
|
+
* export const middleware = tracedRequestMiddleware(
|
|
177
|
+
* { name: "auth-middleware" },
|
|
178
|
+
* async (req: NextRequest) => {
|
|
179
|
+
* if (!req.cookies.get("session")) {
|
|
180
|
+
* return NextResponse.redirect(new URL("/login", req.url));
|
|
181
|
+
* }
|
|
182
|
+
* return NextResponse.next();
|
|
183
|
+
* },
|
|
184
|
+
* );
|
|
185
|
+
*
|
|
186
|
+
* export const config = { matcher: ["/dashboard/:path*"] };
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare function tracedRequestMiddleware<H extends RequestMiddlewareFunction>(options: TracedRequestMiddlewareOptions, handler: H): H;
|
|
190
|
+
|
|
191
|
+
export { type RequestMiddlewareFunction, type TracedRequestMiddlewareOptions, _resetForTesting, tracedRequestMiddleware };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
_resetForTesting,
|
|
3
|
+
tracedRequestMiddleware
|
|
4
|
+
} from "../chunk-QOHKZOKB.js";
|
|
5
|
+
import "../chunk-CL3OVHPO.js";
|
|
6
|
+
import "../chunk-DQ25VOKK.js";
|
|
7
|
+
import "../chunk-JHUNLPSS.js";
|
|
8
|
+
import "../chunk-NSBPE2FW.js";
|
|
9
|
+
export {
|
|
10
|
+
_resetForTesting,
|
|
11
|
+
tracedRequestMiddleware
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/node-entry.cjs
CHANGED
|
@@ -14686,6 +14686,40 @@ var init_dist = __esm({
|
|
|
14686
14686
|
ELEMENT_FINGERPRINT: "glasstrace.element.fingerprint",
|
|
14687
14687
|
ELEMENT_CONFIDENCE: "glasstrace.element.confidence",
|
|
14688
14688
|
TAB_ID: "glasstrace.tab.id",
|
|
14689
|
+
// Causal evidence (SDK-046 / DISC-1537 + DISC-1539).
|
|
14690
|
+
//
|
|
14691
|
+
// The SDK emits `glasstrace.causal.*` attributes on spans that
|
|
14692
|
+
// carry instrumentation-time evidence about a span's relationship
|
|
14693
|
+
// to its owning request trace. Two families are defined here:
|
|
14694
|
+
//
|
|
14695
|
+
// - `MIDDLEWARE_FOR_REQUEST` — middleware-ownership marker. Set
|
|
14696
|
+
// on a middleware-only span by `tracedRequestMiddleware()` from
|
|
14697
|
+
// `@glasstrace/sdk/middleware`. Carries the originating
|
|
14698
|
+
// request's normalized path so the product-side trace-summary
|
|
14699
|
+
// transform can link the middleware span to the owning HTTP
|
|
14700
|
+
// request trace even when the middleware runs in an edge
|
|
14701
|
+
// runtime that does not propagate AsyncLocalStorage parents.
|
|
14702
|
+
//
|
|
14703
|
+
// - `POST_RESPONSE_ASYNC` — post-response async marker. Set on a
|
|
14704
|
+
// span emitted from inside `withAsyncCausality()` from
|
|
14705
|
+
// `@glasstrace/sdk/async-context`. Carries the originating
|
|
14706
|
+
// request's trace ID (32-character hex) captured at the call
|
|
14707
|
+
// site so async work scheduled via Next.js `after()`, queues,
|
|
14708
|
+
// or webhooks can be linked back to its originating request.
|
|
14709
|
+
// Companion booleans `CAUSAL_AFFECTS_HTTP_STATUS` /
|
|
14710
|
+
// `CAUSAL_AFFECTS_HTTP_DURATION` document whether the async
|
|
14711
|
+
// work participates in the root request's outcome (default
|
|
14712
|
+
// `false` — non-outcome async work).
|
|
14713
|
+
//
|
|
14714
|
+
// Wire keys live under the `glasstrace.causal.*` namespace so they
|
|
14715
|
+
// are namespace-distinct from `glasstrace.error.*`,
|
|
14716
|
+
// `glasstrace.http.*`, `glasstrace.trpc.*`, and the side-effect
|
|
14717
|
+
// family below. Adding these constants is a `@glasstrace/protocol`
|
|
14718
|
+
// minor bump; existing entries are untouched.
|
|
14719
|
+
CAUSAL_MIDDLEWARE_FOR_REQUEST: "glasstrace.causal.middleware_for_request",
|
|
14720
|
+
CAUSAL_POST_RESPONSE_ASYNC: "glasstrace.causal.post_response_async",
|
|
14721
|
+
CAUSAL_AFFECTS_HTTP_STATUS: "glasstrace.causal.affects_http_status",
|
|
14722
|
+
CAUSAL_AFFECTS_HTTP_DURATION: "glasstrace.causal.affects_http_duration",
|
|
14689
14723
|
// Side-effect evidence (SDK-049 / SCHEMA-036).
|
|
14690
14724
|
// Top-level operation attributes attached to the active span when a
|
|
14691
14725
|
// side-effect is recorded via `recordSideEffect()`. The wire-string
|
|
@@ -18932,6 +18966,16 @@ function getCoexistenceState() {
|
|
|
18932
18966
|
return coexistenceState;
|
|
18933
18967
|
}
|
|
18934
18968
|
|
|
18969
|
+
// src/optional-lifecycle.ts
|
|
18970
|
+
var EMIT_BRIDGE = /* @__PURE__ */ Symbol.for("glasstrace.lifecycle.emit-bridge");
|
|
18971
|
+
function _registerLifecycleEmitForBridge(emit) {
|
|
18972
|
+
try {
|
|
18973
|
+
const slot = { emit };
|
|
18974
|
+
globalThis[EMIT_BRIDGE] = slot;
|
|
18975
|
+
} catch {
|
|
18976
|
+
}
|
|
18977
|
+
}
|
|
18978
|
+
|
|
18935
18979
|
// src/lifecycle.ts
|
|
18936
18980
|
var CoreState = {
|
|
18937
18981
|
IDLE: "IDLE",
|
|
@@ -19026,6 +19070,12 @@ function initLifecycle(options) {
|
|
|
19026
19070
|
}
|
|
19027
19071
|
_logger = options.logger;
|
|
19028
19072
|
_initialized = true;
|
|
19073
|
+
_registerLifecycleEmitForBridge((event, payload) => {
|
|
19074
|
+
emitLifecycleEvent(
|
|
19075
|
+
event,
|
|
19076
|
+
payload
|
|
19077
|
+
);
|
|
19078
|
+
});
|
|
19029
19079
|
}
|
|
19030
19080
|
function warnIfNotInitialized() {
|
|
19031
19081
|
if (!_initialized && !_initWarned) {
|
|
@@ -23509,11 +23559,11 @@ function registerGlasstrace(options) {
|
|
|
23509
23559
|
setCoreState(CoreState.REGISTERING);
|
|
23510
23560
|
maybeWarnStaleAgentInstructions({
|
|
23511
23561
|
projectRoot: process.cwd(),
|
|
23512
|
-
sdkVersion: "1.
|
|
23562
|
+
sdkVersion: "1.9.1"
|
|
23513
23563
|
});
|
|
23514
23564
|
startRuntimeStateWriter({
|
|
23515
23565
|
projectRoot: process.cwd(),
|
|
23516
|
-
sdkVersion: "1.
|
|
23566
|
+
sdkVersion: "1.9.1"
|
|
23517
23567
|
});
|
|
23518
23568
|
const config2 = resolveConfig(options);
|
|
23519
23569
|
if (config2.verbose) {
|
|
@@ -23680,8 +23730,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
23680
23730
|
if (config2.verbose) {
|
|
23681
23731
|
console.info("[glasstrace] Background init firing.");
|
|
23682
23732
|
}
|
|
23683
|
-
const healthReport = collectHealthReport("1.
|
|
23684
|
-
const initResult = await performInit(config2, anonKeyForInit, "1.
|
|
23733
|
+
const healthReport = collectHealthReport("1.9.1");
|
|
23734
|
+
const initResult = await performInit(config2, anonKeyForInit, "1.9.1", healthReport);
|
|
23685
23735
|
if (generation !== registrationGeneration) return;
|
|
23686
23736
|
const currentState = getCoreState();
|
|
23687
23737
|
if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
|
|
@@ -23704,7 +23754,7 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
23704
23754
|
}
|
|
23705
23755
|
maybeInstallConsoleCapture();
|
|
23706
23756
|
if (didLastInitSucceed()) {
|
|
23707
|
-
startHeartbeat(config2, anonKeyForInit, "1.
|
|
23757
|
+
startHeartbeat(config2, anonKeyForInit, "1.9.1", generation, (newApiKey, accountId) => {
|
|
23708
23758
|
setAuthState(AuthState.CLAIMING);
|
|
23709
23759
|
emitLifecycleEvent("auth:claim_started", { accountId });
|
|
23710
23760
|
setResolvedApiKey(newApiKey);
|