@glasstrace/sdk 1.7.0 → 1.9.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/dist/async-context/index.cjs +15005 -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-CTgSYxek.d.ts → capture-error-BeuEXXJO.d.cts} +40 -2
- package/dist/{capture-error-BmQz7xF6.d.cts → capture-error-D02pzB7q.d.ts} +40 -2
- 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-QHV7NFON.js +130 -0
- package/dist/chunk-QHV7NFON.js.map +1 -0
- package/dist/{chunk-M2TLX6NM.js → chunk-QXITSNYM.js} +3 -3
- package/dist/chunk-RQ5BIWDT.js +119 -0
- package/dist/chunk-RQ5BIWDT.js.map +1 -0
- package/dist/{chunk-OWPA7GHV.js → chunk-XEPC4NFL.js} +1398 -1037
- package/dist/chunk-XEPC4NFL.js.map +1 -0
- 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 +295 -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 +3613 -3211
- 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 +15014 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +183 -0
- package/dist/middleware/index.d.ts +183 -0
- package/dist/middleware/index.js +13 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/node-entry.cjs +3613 -3211
- 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-OWPA7GHV.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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
_registerLifecycleEmitForBridge
|
|
3
|
+
} from "./chunk-CL3OVHPO.js";
|
|
1
4
|
import {
|
|
2
5
|
DiagLogLevel,
|
|
3
6
|
INVALID_SPAN_CONTEXT,
|
|
@@ -33,7 +36,7 @@ import {
|
|
|
33
36
|
performInit,
|
|
34
37
|
recordSpansDropped,
|
|
35
38
|
recordSpansExported
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-QXITSNYM.js";
|
|
37
40
|
import {
|
|
38
41
|
isAnonymousMode,
|
|
39
42
|
isProductionDisabled,
|
|
@@ -44,11 +47,11 @@ import {
|
|
|
44
47
|
getOrCreateAnonKey,
|
|
45
48
|
isSyncFsAvailable,
|
|
46
49
|
readAnonKey
|
|
47
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-DKV53A2C.js";
|
|
48
51
|
import {
|
|
49
52
|
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
50
53
|
deriveSessionId
|
|
51
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-JHUNLPSS.js";
|
|
52
55
|
import {
|
|
53
56
|
isEndMarkerLine,
|
|
54
57
|
parseStartMarkerLine
|
|
@@ -143,1114 +146,1428 @@ function classifyFetchTarget(url) {
|
|
|
143
146
|
return "unknown";
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
// src/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
let numeric;
|
|
154
|
-
if (typeof value === "number") {
|
|
155
|
-
numeric = value;
|
|
156
|
-
} else if (typeof value === "string") {
|
|
157
|
-
const trimmed = value.trim();
|
|
158
|
-
if (trimmed.length === 0) return void 0;
|
|
159
|
-
numeric = Number(trimmed);
|
|
160
|
-
} else {
|
|
161
|
-
return void 0;
|
|
162
|
-
}
|
|
163
|
-
return Number.isFinite(numeric) ? numeric : void 0;
|
|
149
|
+
// src/lifecycle.ts
|
|
150
|
+
import { EventEmitter } from "node:events";
|
|
151
|
+
|
|
152
|
+
// src/signal-handler.ts
|
|
153
|
+
var coexistenceState = "unknown";
|
|
154
|
+
function setCoexistenceState(s) {
|
|
155
|
+
coexistenceState = s;
|
|
164
156
|
}
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
if (numeric === void 0) return false;
|
|
168
|
-
return numeric >= ERROR_STATUS_MIN && numeric <= ERROR_STATUS_MAX;
|
|
157
|
+
function getCoexistenceState() {
|
|
158
|
+
return coexistenceState;
|
|
169
159
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
]
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
160
|
+
|
|
161
|
+
// src/lifecycle.ts
|
|
162
|
+
var CoreState = {
|
|
163
|
+
IDLE: "IDLE",
|
|
164
|
+
REGISTERING: "REGISTERING",
|
|
165
|
+
KEY_PENDING: "KEY_PENDING",
|
|
166
|
+
KEY_RESOLVED: "KEY_RESOLVED",
|
|
167
|
+
ACTIVE: "ACTIVE",
|
|
168
|
+
ACTIVE_DEGRADED: "ACTIVE_DEGRADED",
|
|
169
|
+
SHUTTING_DOWN: "SHUTTING_DOWN",
|
|
170
|
+
SHUTDOWN: "SHUTDOWN",
|
|
171
|
+
PRODUCTION_DISABLED: "PRODUCTION_DISABLED",
|
|
172
|
+
REGISTRATION_FAILED: "REGISTRATION_FAILED"
|
|
173
|
+
};
|
|
174
|
+
var AuthState = {
|
|
175
|
+
ANONYMOUS: "ANONYMOUS",
|
|
176
|
+
AUTHENTICATED: "AUTHENTICATED",
|
|
177
|
+
CLAIMING: "CLAIMING",
|
|
178
|
+
CLAIMED: "CLAIMED"
|
|
179
|
+
};
|
|
180
|
+
var OtelState = {
|
|
181
|
+
UNCONFIGURED: "UNCONFIGURED",
|
|
182
|
+
CONFIGURING: "CONFIGURING",
|
|
183
|
+
OWNS_PROVIDER: "OWNS_PROVIDER",
|
|
184
|
+
AUTO_ATTACHED: "AUTO_ATTACHED",
|
|
185
|
+
PROCESSOR_PRESENT: "PROCESSOR_PRESENT",
|
|
186
|
+
COEXISTENCE_FAILED: "COEXISTENCE_FAILED"
|
|
187
|
+
};
|
|
188
|
+
var VALID_CORE_TRANSITIONS = {
|
|
189
|
+
[CoreState.IDLE]: [CoreState.REGISTERING, CoreState.REGISTRATION_FAILED, CoreState.SHUTTING_DOWN],
|
|
190
|
+
[CoreState.REGISTERING]: [
|
|
191
|
+
CoreState.KEY_PENDING,
|
|
192
|
+
CoreState.PRODUCTION_DISABLED,
|
|
193
|
+
CoreState.REGISTRATION_FAILED,
|
|
194
|
+
CoreState.SHUTTING_DOWN
|
|
195
|
+
],
|
|
196
|
+
[CoreState.KEY_PENDING]: [
|
|
197
|
+
CoreState.KEY_RESOLVED,
|
|
198
|
+
CoreState.REGISTRATION_FAILED,
|
|
199
|
+
CoreState.SHUTTING_DOWN
|
|
200
|
+
],
|
|
201
|
+
[CoreState.KEY_RESOLVED]: [
|
|
202
|
+
CoreState.ACTIVE,
|
|
203
|
+
CoreState.ACTIVE_DEGRADED,
|
|
204
|
+
CoreState.SHUTTING_DOWN
|
|
205
|
+
],
|
|
206
|
+
[CoreState.ACTIVE]: [
|
|
207
|
+
CoreState.ACTIVE_DEGRADED,
|
|
208
|
+
CoreState.SHUTTING_DOWN
|
|
209
|
+
],
|
|
210
|
+
[CoreState.ACTIVE_DEGRADED]: [
|
|
211
|
+
CoreState.ACTIVE,
|
|
212
|
+
CoreState.SHUTTING_DOWN
|
|
213
|
+
],
|
|
214
|
+
[CoreState.SHUTTING_DOWN]: [CoreState.SHUTDOWN],
|
|
215
|
+
[CoreState.SHUTDOWN]: [],
|
|
216
|
+
[CoreState.PRODUCTION_DISABLED]: [],
|
|
217
|
+
[CoreState.REGISTRATION_FAILED]: []
|
|
218
|
+
};
|
|
219
|
+
var VALID_AUTH_TRANSITIONS = {
|
|
220
|
+
[AuthState.ANONYMOUS]: [AuthState.CLAIMING],
|
|
221
|
+
[AuthState.AUTHENTICATED]: [AuthState.CLAIMING],
|
|
222
|
+
[AuthState.CLAIMING]: [AuthState.CLAIMED],
|
|
223
|
+
[AuthState.CLAIMED]: [AuthState.CLAIMING]
|
|
224
|
+
};
|
|
225
|
+
var VALID_OTEL_TRANSITIONS = {
|
|
226
|
+
[OtelState.UNCONFIGURED]: [OtelState.CONFIGURING],
|
|
227
|
+
[OtelState.CONFIGURING]: [
|
|
228
|
+
OtelState.OWNS_PROVIDER,
|
|
229
|
+
OtelState.AUTO_ATTACHED,
|
|
230
|
+
OtelState.PROCESSOR_PRESENT,
|
|
231
|
+
OtelState.COEXISTENCE_FAILED
|
|
232
|
+
],
|
|
233
|
+
[OtelState.OWNS_PROVIDER]: [],
|
|
234
|
+
[OtelState.AUTO_ATTACHED]: [],
|
|
235
|
+
[OtelState.PROCESSOR_PRESENT]: [],
|
|
236
|
+
[OtelState.COEXISTENCE_FAILED]: []
|
|
237
|
+
};
|
|
238
|
+
var _coreState = CoreState.IDLE;
|
|
239
|
+
var _authState = AuthState.ANONYMOUS;
|
|
240
|
+
var _otelState = OtelState.UNCONFIGURED;
|
|
241
|
+
var _emitter = new EventEmitter();
|
|
242
|
+
var _logger = null;
|
|
243
|
+
var _initialized = false;
|
|
244
|
+
var _initWarned = false;
|
|
245
|
+
var _coreReadyEmitted = false;
|
|
246
|
+
var _authInitialized = false;
|
|
247
|
+
var _emitting = false;
|
|
248
|
+
function initLifecycle(options) {
|
|
249
|
+
if (_initialized) {
|
|
250
|
+
options.logger("warn", "[glasstrace] initLifecycle() called twice \u2014 ignored.");
|
|
251
|
+
return;
|
|
230
252
|
}
|
|
231
|
-
|
|
253
|
+
_logger = options.logger;
|
|
254
|
+
_initialized = true;
|
|
255
|
+
_registerLifecycleEmitForBridge((event, payload) => {
|
|
256
|
+
emitLifecycleEvent(
|
|
257
|
+
event,
|
|
258
|
+
payload
|
|
259
|
+
);
|
|
260
|
+
});
|
|
232
261
|
}
|
|
233
|
-
function
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
262
|
+
function warnIfNotInitialized() {
|
|
263
|
+
if (!_initialized && !_initWarned) {
|
|
264
|
+
_initWarned = true;
|
|
265
|
+
console.warn(
|
|
266
|
+
"[glasstrace] Lifecycle state changed before initLifecycle() was called. Logger not available \u2014 errors will be silent."
|
|
267
|
+
);
|
|
238
268
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
269
|
+
}
|
|
270
|
+
function setCoreState(to) {
|
|
271
|
+
warnIfNotInitialized();
|
|
272
|
+
const from = _coreState;
|
|
273
|
+
if (from === to) return;
|
|
274
|
+
const valid = VALID_CORE_TRANSITIONS[from];
|
|
275
|
+
if (!valid.includes(to)) {
|
|
276
|
+
_logger?.(
|
|
277
|
+
"warn",
|
|
278
|
+
`[glasstrace] Invalid core state transition: ${from} \u2192 ${to}. Ignored.`
|
|
279
|
+
);
|
|
280
|
+
return;
|
|
243
281
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
} else if ((leading & 248) === 240) {
|
|
254
|
-
expected = 4;
|
|
282
|
+
_coreState = to;
|
|
283
|
+
if (_emitting) return;
|
|
284
|
+
_emitting = true;
|
|
285
|
+
try {
|
|
286
|
+
emitSafe("core:state_changed", { from, to });
|
|
287
|
+
const current = _coreState;
|
|
288
|
+
if (!_coreReadyEmitted && (current === CoreState.ACTIVE || current === CoreState.ACTIVE_DEGRADED)) {
|
|
289
|
+
_coreReadyEmitted = true;
|
|
290
|
+
emitSafe("core:ready", {});
|
|
255
291
|
}
|
|
256
|
-
if (
|
|
257
|
-
|
|
292
|
+
if (current === CoreState.SHUTTING_DOWN) {
|
|
293
|
+
emitSafe("core:shutdown_started", {});
|
|
258
294
|
}
|
|
295
|
+
if (current === CoreState.SHUTDOWN) {
|
|
296
|
+
emitSafe("core:shutdown_completed", {});
|
|
297
|
+
}
|
|
298
|
+
} finally {
|
|
299
|
+
_emitting = false;
|
|
300
|
+
}
|
|
301
|
+
if (to === CoreState.ACTIVE && _degradationSources.size > 0) {
|
|
302
|
+
recomputeCoreFromDegradationSources();
|
|
259
303
|
}
|
|
260
|
-
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
261
|
-
const sliced = encoded.subarray(0, cut);
|
|
262
|
-
const decoded = decoder.decode(sliced);
|
|
263
|
-
return decoded + ERROR_RESPONSE_BODY_TRUNCATION_MARKER;
|
|
264
|
-
}
|
|
265
|
-
function prepareErrorResponseBody(body) {
|
|
266
|
-
if (body.length === 0) return null;
|
|
267
|
-
if (body.trim().length === 0) return null;
|
|
268
|
-
const sanitized = sanitizeErrorResponseBody(body);
|
|
269
|
-
return truncateErrorResponseBody(sanitized);
|
|
270
304
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
".next",
|
|
279
|
-
".glasstrace",
|
|
280
|
-
"src",
|
|
281
|
-
"dist",
|
|
282
|
-
"build",
|
|
283
|
-
"lib",
|
|
284
|
-
"app",
|
|
285
|
-
"pages"
|
|
286
|
-
];
|
|
287
|
-
var PATH_TOKEN_RE = /(?<=^|[\s(])(\/[^\s()<>]+|[A-Za-z]:\\[^\s()<>]+|file:\/\/\/[^\s()<>]+|webpack-internal:\/\/[^\s()<>]+|node:[^\s()<>]+)/g;
|
|
288
|
-
var URL_QUERY_FRAGMENT_RE = /(\bhttps?:\/\/[^\s?#()<>]+)([?#][^\s()<>]*)/g;
|
|
289
|
-
function normalizePathToken(token) {
|
|
290
|
-
let work = token;
|
|
291
|
-
if (work.startsWith("file:///")) {
|
|
292
|
-
work = work.slice("file://".length);
|
|
293
|
-
}
|
|
294
|
-
if (work.startsWith("webpack-internal:") || work.startsWith("node:")) {
|
|
295
|
-
return { token, changed: false };
|
|
296
|
-
}
|
|
297
|
-
const isPosixAbs = work.startsWith("/");
|
|
298
|
-
const isWinAbs = /^[A-Za-z]:\\/.test(work);
|
|
299
|
-
if (!isPosixAbs && !isWinAbs) {
|
|
300
|
-
return { token, changed: false };
|
|
305
|
+
function initAuthState(state) {
|
|
306
|
+
if (_authInitialized) {
|
|
307
|
+
_logger?.(
|
|
308
|
+
"warn",
|
|
309
|
+
"[glasstrace] initAuthState() called after auth state already initialized. Ignored."
|
|
310
|
+
);
|
|
311
|
+
return;
|
|
301
312
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
_authInitialized = true;
|
|
314
|
+
_authState = state;
|
|
315
|
+
}
|
|
316
|
+
function setAuthState(to) {
|
|
317
|
+
warnIfNotInitialized();
|
|
318
|
+
const from = _authState;
|
|
319
|
+
if (from === to) return;
|
|
320
|
+
const valid = VALID_AUTH_TRANSITIONS[from];
|
|
321
|
+
if (!valid.includes(to)) {
|
|
322
|
+
_logger?.(
|
|
323
|
+
"warn",
|
|
324
|
+
`[glasstrace] Invalid auth state transition: ${from} \u2192 ${to}. Ignored.`
|
|
325
|
+
);
|
|
326
|
+
return;
|
|
311
327
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
_authState = to;
|
|
329
|
+
}
|
|
330
|
+
function setOtelState(to) {
|
|
331
|
+
warnIfNotInitialized();
|
|
332
|
+
const from = _otelState;
|
|
333
|
+
if (from === to) return;
|
|
334
|
+
const valid = VALID_OTEL_TRANSITIONS[from];
|
|
335
|
+
if (!valid.includes(to)) {
|
|
336
|
+
_logger?.(
|
|
337
|
+
"warn",
|
|
338
|
+
`[glasstrace] Invalid OTel state transition: ${from} \u2192 ${to}. Ignored.`
|
|
339
|
+
);
|
|
340
|
+
return;
|
|
316
341
|
}
|
|
317
|
-
|
|
318
|
-
const lineMatch = colonLineRe.exec(work);
|
|
319
|
-
const pathBody = lineMatch ? work.slice(0, lineMatch.index) : work;
|
|
320
|
-
const lineSuffix = lineMatch ? work.slice(lineMatch.index) : "";
|
|
321
|
-
const lastSep = Math.max(pathBody.lastIndexOf("/"), pathBody.lastIndexOf("\\"));
|
|
322
|
-
const basename = lastSep >= 0 ? pathBody.slice(lastSep + 1) : pathBody;
|
|
323
|
-
const rebuilt = `${PATH_REDACTED}/${basename}${lineSuffix}`;
|
|
324
|
-
return { token: rebuilt, changed: true };
|
|
342
|
+
_otelState = to;
|
|
325
343
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (out.changed) changed = true;
|
|
331
|
-
return out.token;
|
|
332
|
-
});
|
|
333
|
-
const urlStripped = pathNormalized.replace(URL_QUERY_FRAGMENT_RE, (match, prefix) => {
|
|
334
|
-
if (match !== prefix) changed = true;
|
|
335
|
-
return prefix;
|
|
336
|
-
});
|
|
337
|
-
const credentialRedacted = sanitizeErrorResponseBody(urlStripped);
|
|
338
|
-
if (credentialRedacted !== urlStripped) changed = true;
|
|
339
|
-
return { stack: credentialRedacted, redacted: changed };
|
|
344
|
+
var _degradationSources = /* @__PURE__ */ new Set();
|
|
345
|
+
function pushDegradationSource(key) {
|
|
346
|
+
_degradationSources.add(key);
|
|
347
|
+
recomputeCoreFromDegradationSources();
|
|
340
348
|
}
|
|
341
|
-
function
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
scan -= 1;
|
|
349
|
+
function clearDegradationSource(key) {
|
|
350
|
+
_degradationSources.delete(key);
|
|
351
|
+
recomputeCoreFromDegradationSources();
|
|
352
|
+
}
|
|
353
|
+
function recomputeCoreFromDegradationSources() {
|
|
354
|
+
const hasDegradation = _degradationSources.size > 0;
|
|
355
|
+
if (hasDegradation && _coreState === CoreState.ACTIVE) {
|
|
356
|
+
setCoreState(CoreState.ACTIVE_DEGRADED);
|
|
357
|
+
return;
|
|
351
358
|
}
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
let expected = 1;
|
|
355
|
-
if ((leading & 128) === 0) {
|
|
356
|
-
expected = 1;
|
|
357
|
-
} else if ((leading & 224) === 192) {
|
|
358
|
-
expected = 2;
|
|
359
|
-
} else if ((leading & 240) === 224) {
|
|
360
|
-
expected = 3;
|
|
361
|
-
} else if ((leading & 248) === 240) {
|
|
362
|
-
expected = 4;
|
|
363
|
-
}
|
|
364
|
-
if (scan + expected > cut) {
|
|
365
|
-
cut = scan;
|
|
366
|
-
}
|
|
359
|
+
if (!hasDegradation && _coreState === CoreState.ACTIVE_DEGRADED) {
|
|
360
|
+
setCoreState(CoreState.ACTIVE);
|
|
367
361
|
}
|
|
368
|
-
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
369
|
-
const sliced = encoded.subarray(0, cut);
|
|
370
|
-
const decoded = decoder.decode(sliced);
|
|
371
|
-
return { stack: decoded + ERROR_STACK_TRUNCATION_MARKER, truncated: true };
|
|
372
362
|
}
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const truncated = truncateStack(sanitized.stack);
|
|
363
|
+
function getCoreState() {
|
|
364
|
+
return _coreState;
|
|
365
|
+
}
|
|
366
|
+
function getSdkState() {
|
|
378
367
|
return {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
368
|
+
core: _coreState,
|
|
369
|
+
auth: _authState,
|
|
370
|
+
otel: _otelState
|
|
382
371
|
};
|
|
383
372
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
var UNSET = "";
|
|
387
|
-
var SHA_SHAPE = /^[0-9a-f]{7,64}$/i;
|
|
388
|
-
function redactBuildHash(value) {
|
|
389
|
-
const sanitize = (s) => s.replace(/[\x00-\x1F\x7F]/g, "?");
|
|
390
|
-
if (value.length <= 12) return sanitize(value.slice(0, 4)) + "...";
|
|
391
|
-
return sanitize(value.slice(0, 8)) + "..." + sanitize(value.slice(-4));
|
|
373
|
+
function onLifecycleEvent(event, listener) {
|
|
374
|
+
_emitter.on(event, listener);
|
|
392
375
|
}
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
if (typeof raw !== "string") return UNSET;
|
|
396
|
-
const trimmed = raw.trim();
|
|
397
|
-
if (trimmed.length === 0) return UNSET;
|
|
398
|
-
if (!SHA_SHAPE.test(trimmed)) {
|
|
399
|
-
sdkLog(
|
|
400
|
-
"warn",
|
|
401
|
-
`[glasstrace] warning: GLASSTRACE_BUILD_HASH=${redactBuildHash(trimmed)} does not match expected SHA shape (7-64 hex characters); source-map enrichment may not work as expected.`
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
return trimmed;
|
|
376
|
+
function emitLifecycleEvent(event, payload) {
|
|
377
|
+
emitSafe(event, payload);
|
|
405
378
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
379
|
+
function offLifecycleEvent(event, listener) {
|
|
380
|
+
_emitter.off(event, listener);
|
|
381
|
+
}
|
|
382
|
+
function emitSafe(event, payload) {
|
|
383
|
+
const listeners = _emitter.listeners(event);
|
|
384
|
+
for (const listener of listeners) {
|
|
385
|
+
try {
|
|
386
|
+
const result = listener(payload);
|
|
387
|
+
if (result && typeof result.catch === "function") {
|
|
388
|
+
result.catch((err) => {
|
|
389
|
+
_logger?.(
|
|
390
|
+
"error",
|
|
391
|
+
`[glasstrace] Async error in lifecycle event listener for "${event}": ${err instanceof Error ? err.message : String(err)}`
|
|
392
|
+
);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
} catch (err) {
|
|
396
|
+
_logger?.(
|
|
397
|
+
"error",
|
|
398
|
+
`[glasstrace] Error in lifecycle event listener for "${event}": ${err instanceof Error ? err.message : String(err)}`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
410
401
|
}
|
|
411
|
-
return cachedBuildHash === UNSET ? void 0 : cachedBuildHash;
|
|
412
402
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
getApiKey;
|
|
420
|
-
sessionManager;
|
|
421
|
-
getConfig;
|
|
422
|
-
environment;
|
|
423
|
-
endpointUrl;
|
|
424
|
-
createDelegateFn;
|
|
425
|
-
verbose;
|
|
426
|
-
delegate = null;
|
|
427
|
-
delegateKey = null;
|
|
428
|
-
pendingBatches = [];
|
|
429
|
-
pendingSpanCount = 0;
|
|
430
|
-
overflowLogged = false;
|
|
431
|
-
constructor(options) {
|
|
432
|
-
this.getApiKey = options.getApiKey;
|
|
433
|
-
this.sessionManager = options.sessionManager;
|
|
434
|
-
this.getConfig = options.getConfig;
|
|
435
|
-
this.environment = options.environment;
|
|
436
|
-
this.endpointUrl = options.endpointUrl;
|
|
437
|
-
this.createDelegateFn = options.createDelegate;
|
|
438
|
-
this.verbose = options.verbose ?? false;
|
|
439
|
-
this[/* @__PURE__ */ Symbol.for("glasstrace.exporter")] = true;
|
|
403
|
+
function isReady() {
|
|
404
|
+
return _coreState === CoreState.ACTIVE || _coreState === CoreState.ACTIVE_DEGRADED;
|
|
405
|
+
}
|
|
406
|
+
function waitForReady(timeoutMs = 3e4) {
|
|
407
|
+
if (isReady()) {
|
|
408
|
+
return Promise.resolve();
|
|
440
409
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (currentKey === API_KEY_PENDING) {
|
|
444
|
-
this.bufferSpans(spans, resultCallback);
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
const enrichedSpans = spans.map((span) => this.enrichSpan(span));
|
|
448
|
-
const exporter = this.ensureDelegate();
|
|
449
|
-
if (exporter) {
|
|
450
|
-
exporter.export(enrichedSpans, (result) => {
|
|
451
|
-
if (result.code !== 0) {
|
|
452
|
-
sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
|
|
453
|
-
}
|
|
454
|
-
resultCallback(result);
|
|
455
|
-
});
|
|
456
|
-
recordSpansExported(enrichedSpans.length);
|
|
457
|
-
} else {
|
|
458
|
-
recordSpansDropped(enrichedSpans.length);
|
|
459
|
-
resultCallback({ code: 0 });
|
|
460
|
-
}
|
|
410
|
+
if (_coreState === CoreState.PRODUCTION_DISABLED || _coreState === CoreState.REGISTRATION_FAILED || _coreState === CoreState.SHUTTING_DOWN || _coreState === CoreState.SHUTDOWN) {
|
|
411
|
+
return Promise.reject(new Error(`SDK is in terminal state: ${_coreState}`));
|
|
461
412
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
413
|
+
return new Promise((resolve2, reject) => {
|
|
414
|
+
let settled = false;
|
|
415
|
+
const listener = ({ to }) => {
|
|
416
|
+
if (settled) return;
|
|
417
|
+
if (to === CoreState.ACTIVE || to === CoreState.ACTIVE_DEGRADED) {
|
|
418
|
+
settled = true;
|
|
419
|
+
offLifecycleEvent("core:state_changed", listener);
|
|
420
|
+
resolve2();
|
|
421
|
+
} else if (to === CoreState.PRODUCTION_DISABLED || to === CoreState.REGISTRATION_FAILED || to === CoreState.SHUTTING_DOWN || to === CoreState.SHUTDOWN) {
|
|
422
|
+
settled = true;
|
|
423
|
+
offLifecycleEvent("core:state_changed", listener);
|
|
424
|
+
reject(new Error(`SDK reached terminal state: ${to}`));
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
onLifecycleEvent("core:state_changed", listener);
|
|
428
|
+
if (timeoutMs > 0) {
|
|
429
|
+
const timer = setTimeout(() => {
|
|
430
|
+
if (settled) return;
|
|
431
|
+
settled = true;
|
|
432
|
+
offLifecycleEvent("core:state_changed", listener);
|
|
433
|
+
reject(new Error(`waitForReady timed out after ${timeoutMs}ms (state: ${_coreState})`));
|
|
434
|
+
}, timeoutMs);
|
|
435
|
+
if (typeof timer === "object" && "unref" in timer) {
|
|
436
|
+
timer.unref();
|
|
480
437
|
}
|
|
481
|
-
this.pendingBatches = [];
|
|
482
|
-
this.pendingSpanCount = 0;
|
|
483
|
-
}
|
|
484
|
-
if (this.delegate) {
|
|
485
|
-
return this.delegate.shutdown();
|
|
486
438
|
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function getStatus() {
|
|
442
|
+
let mode;
|
|
443
|
+
if (_coreState === CoreState.PRODUCTION_DISABLED) {
|
|
444
|
+
mode = "disabled";
|
|
445
|
+
} else if (_authState === AuthState.CLAIMING || _authState === AuthState.CLAIMED) {
|
|
446
|
+
mode = "claiming";
|
|
447
|
+
} else if (_authState === AuthState.AUTHENTICATED) {
|
|
448
|
+
mode = "authenticated";
|
|
449
|
+
} else {
|
|
450
|
+
mode = "anonymous";
|
|
487
451
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return this.delegate.forceFlush();
|
|
498
|
-
}
|
|
499
|
-
return Promise.resolve();
|
|
452
|
+
let tracing;
|
|
453
|
+
if (_otelState === OtelState.COEXISTENCE_FAILED || _otelState === OtelState.UNCONFIGURED || _otelState === OtelState.CONFIGURING) {
|
|
454
|
+
tracing = "not-configured";
|
|
455
|
+
} else if (_coreState === CoreState.ACTIVE_DEGRADED) {
|
|
456
|
+
tracing = "degraded";
|
|
457
|
+
} else if (_otelState === OtelState.AUTO_ATTACHED || _otelState === OtelState.PROCESSOR_PRESENT) {
|
|
458
|
+
tracing = "coexistence";
|
|
459
|
+
} else {
|
|
460
|
+
tracing = "active";
|
|
500
461
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
462
|
+
return {
|
|
463
|
+
ready: isReady(),
|
|
464
|
+
mode,
|
|
465
|
+
tracing
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
var _shutdownHooks = [];
|
|
469
|
+
var _signalHandlersRegistered = false;
|
|
470
|
+
var _signalHandler = null;
|
|
471
|
+
var _beforeExitRegistered = false;
|
|
472
|
+
var _beforeExitHandler = null;
|
|
473
|
+
var _shutdownExecuted = false;
|
|
474
|
+
function registerShutdownHook(hook) {
|
|
475
|
+
_shutdownHooks.push(hook);
|
|
476
|
+
_shutdownHooks.sort((a, b) => a.priority - b.priority);
|
|
477
|
+
}
|
|
478
|
+
async function executeShutdown(timeoutMs = 5e3) {
|
|
479
|
+
if (_shutdownExecuted) return;
|
|
480
|
+
_shutdownExecuted = true;
|
|
481
|
+
setCoreState(CoreState.SHUTTING_DOWN);
|
|
482
|
+
for (const hook of _shutdownHooks) {
|
|
515
483
|
try {
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const env = this.environment ?? process.env.GLASSTRACE_ENV;
|
|
526
|
-
if (env) {
|
|
527
|
-
extra[ATTR.ENVIRONMENT] = env;
|
|
528
|
-
}
|
|
529
|
-
const buildHash = getBuildHash();
|
|
530
|
-
if (buildHash) {
|
|
531
|
-
extra[ATTR.BUILD_HASH] = buildHash;
|
|
532
|
-
}
|
|
533
|
-
const existingCid = attrs["glasstrace.correlation.id"];
|
|
534
|
-
if (typeof existingCid === "string") {
|
|
535
|
-
extra[ATTR.CORRELATION_ID] = existingCid;
|
|
536
|
-
}
|
|
537
|
-
const rawRoute = attrs["http.route"];
|
|
538
|
-
const route = typeof rawRoute === "string" ? rawRoute : name;
|
|
539
|
-
if (route) {
|
|
540
|
-
extra[ATTR.ROUTE] = route;
|
|
541
|
-
}
|
|
542
|
-
const rawUrlAttr = attrs["http.url"] ?? attrs["url.full"] ?? attrs["http.target"];
|
|
543
|
-
const rawHttpUrl = typeof rawUrlAttr === "string" ? rawUrlAttr : void 0;
|
|
544
|
-
if (rawHttpUrl) {
|
|
545
|
-
const trpcMatch = rawHttpUrl.match(/\/api\/trpc\/([^/?#]+)/);
|
|
546
|
-
if (trpcMatch) {
|
|
547
|
-
let procedure;
|
|
548
|
-
try {
|
|
549
|
-
procedure = decodeURIComponent(trpcMatch[1]);
|
|
550
|
-
} catch {
|
|
551
|
-
procedure = trpcMatch[1];
|
|
552
|
-
}
|
|
553
|
-
if (procedure) {
|
|
554
|
-
extra[ATTR.TRPC_PROCEDURE] = procedure;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
const method = attrs["http.method"] ?? attrs["http.request.method"];
|
|
559
|
-
if (method) {
|
|
560
|
-
extra[ATTR.HTTP_METHOD] = method;
|
|
561
|
-
}
|
|
562
|
-
const actionRoute = extractLeadingPath(route);
|
|
563
|
-
if (method === "POST" && actionRoute) {
|
|
564
|
-
const isApiRoute = actionRoute === "/api" || actionRoute.startsWith("/api/");
|
|
565
|
-
const isInternalRoute = actionRoute.startsWith("/_next/");
|
|
566
|
-
if (!isApiRoute && !isInternalRoute) {
|
|
567
|
-
extra[ATTR.NEXT_ACTION_DETECTED] = true;
|
|
568
|
-
if (typeof extra[ATTR.CORRELATION_ID] !== "string") {
|
|
569
|
-
maybeShowServerActionNudge();
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const statusCode = coerceHttpStatus(attrs["http.status_code"]) ?? coerceHttpStatus(attrs["http.response.status_code"]);
|
|
574
|
-
if (statusCode !== void 0) {
|
|
575
|
-
extra[ATTR.HTTP_STATUS_CODE] = statusCode;
|
|
576
|
-
}
|
|
577
|
-
const isErrorByStatus = span.status?.code === SpanStatusCode.ERROR;
|
|
578
|
-
const isErrorByEvent = hasExceptionEvent(span);
|
|
579
|
-
const isErrorByAttrs = typeof attrs["exception.type"] === "string" || typeof attrs["exception.message"] === "string";
|
|
580
|
-
const statusNotExplicitlyOK = span.status?.code !== SpanStatusCode.OK;
|
|
581
|
-
if (this.verbose && method) {
|
|
582
|
-
sdkLog(
|
|
583
|
-
"info",
|
|
584
|
-
`[glasstrace] enrichSpan "${name}": status.code=${span.status?.code}, http.status_code=${statusCode}, isErrorByStatus=${isErrorByStatus}, isErrorByEvent=${isErrorByEvent}, isErrorByAttrs=${isErrorByAttrs}`
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
if (method && statusNotExplicitlyOK && (isErrorByStatus || isErrorByEvent || isErrorByAttrs)) {
|
|
588
|
-
if (statusCode === void 0 || statusCode === 0 || statusCode === 200) {
|
|
589
|
-
const httpErrorType = attrs["error.type"];
|
|
590
|
-
if (typeof httpErrorType === "string") {
|
|
591
|
-
const parsed = parseInt(httpErrorType, 10);
|
|
592
|
-
if (!isNaN(parsed) && parsed >= 400 && parsed <= 599) {
|
|
593
|
-
extra[ATTR.HTTP_STATUS_CODE] = parsed;
|
|
594
|
-
} else {
|
|
595
|
-
extra[ATTR.HTTP_STATUS_CODE] = 500;
|
|
596
|
-
}
|
|
597
|
-
} else {
|
|
598
|
-
extra[ATTR.HTTP_STATUS_CODE] = 500;
|
|
599
|
-
}
|
|
600
|
-
if (this.verbose) {
|
|
601
|
-
sdkLog(
|
|
602
|
-
"info",
|
|
603
|
-
`[glasstrace] enrichSpan "${name}": inferred status_code=${extra[ATTR.HTTP_STATUS_CODE]} (was ${statusCode}), error.type=${attrs["error.type"]}`
|
|
604
|
-
);
|
|
484
|
+
const hookPromise = hook.fn();
|
|
485
|
+
hookPromise.catch(() => {
|
|
486
|
+
});
|
|
487
|
+
await Promise.race([
|
|
488
|
+
hookPromise,
|
|
489
|
+
new Promise((_, reject) => {
|
|
490
|
+
const timer = setTimeout(() => reject(new Error(`Shutdown hook "${hook.name}" timed out`)), timeoutMs);
|
|
491
|
+
if (typeof timer === "object" && "unref" in timer) {
|
|
492
|
+
timer.unref();
|
|
605
493
|
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
494
|
+
})
|
|
495
|
+
]);
|
|
496
|
+
} catch (err) {
|
|
497
|
+
_logger?.(
|
|
498
|
+
"warn",
|
|
499
|
+
`[glasstrace] Shutdown hook "${hook.name}" failed: ${err instanceof Error ? err.message : String(err)}`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
setCoreState(CoreState.SHUTDOWN);
|
|
504
|
+
}
|
|
505
|
+
function registerSignalHandlers() {
|
|
506
|
+
if (_signalHandlersRegistered) return;
|
|
507
|
+
if (typeof process === "undefined" || typeof process.once !== "function") return;
|
|
508
|
+
_signalHandlersRegistered = true;
|
|
509
|
+
const otherSigtermListeners = process.listenerCount("SIGTERM");
|
|
510
|
+
const otherSigintListeners = process.listenerCount("SIGINT");
|
|
511
|
+
const handler = (signal) => {
|
|
512
|
+
void executeShutdown().finally(() => {
|
|
513
|
+
if (_signalHandler) {
|
|
514
|
+
process.removeListener("SIGTERM", _signalHandler);
|
|
515
|
+
process.removeListener("SIGINT", _signalHandler);
|
|
615
516
|
}
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
extra[ATTR.ERROR_MESSAGE] = eventDetails.message;
|
|
621
|
-
errorSource = "otel_exception";
|
|
622
|
-
} else if (typeof attrMessage === "string") {
|
|
623
|
-
extra[ATTR.ERROR_MESSAGE] = attrMessage;
|
|
624
|
-
errorSource = "otel_event";
|
|
517
|
+
const otherListeners = signal === "SIGTERM" ? otherSigtermListeners : otherSigintListeners;
|
|
518
|
+
const otherProviderOwnsSignal = getCoexistenceState() === "coexisting" && otherListeners > 0;
|
|
519
|
+
if (!otherProviderOwnsSignal) {
|
|
520
|
+
process.kill(process.pid, signal);
|
|
625
521
|
}
|
|
626
|
-
const attrType = attrs["exception.type"];
|
|
627
|
-
if (eventDetails.type) {
|
|
628
|
-
extra[ATTR.ERROR_CODE] = eventDetails.type;
|
|
629
|
-
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(eventDetails.type);
|
|
630
|
-
errorSource = errorSource ?? "otel_exception";
|
|
631
|
-
} else if (typeof attrType === "string") {
|
|
632
|
-
extra[ATTR.ERROR_CODE] = attrType;
|
|
633
|
-
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(attrType);
|
|
634
|
-
errorSource = errorSource ?? "otel_event";
|
|
635
|
-
}
|
|
636
|
-
if (statusNotExplicitlyOK) {
|
|
637
|
-
const rawStack = eventDetails.stacktrace ?? (typeof attrs["exception.stacktrace"] === "string" ? attrs["exception.stacktrace"] : void 0);
|
|
638
|
-
if (rawStack) {
|
|
639
|
-
const prepared = prepareStack(rawStack);
|
|
640
|
-
if (prepared !== null) {
|
|
641
|
-
extra[ATTR.ERROR_STACK] = prepared.stack;
|
|
642
|
-
extra[ATTR.ERROR_STACK_TRUNCATED] = prepared.truncated;
|
|
643
|
-
extra[ATTR.ERROR_STACK_REDACTED] = prepared.redacted;
|
|
644
|
-
errorSource = errorSource ?? (eventDetails.stacktrace ? "otel_exception" : "otel_event");
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
const routeIsFallback = route === "/_error" || route === "/_not-found" || route === "/_404" || route === "/_500";
|
|
649
|
-
if (routeIsFallback && rawHttpUrl) {
|
|
650
|
-
const originalPath = extractPathOnly(rawHttpUrl);
|
|
651
|
-
const normOriginal = stripTrailingSlash(originalPath);
|
|
652
|
-
const normRoute = stripTrailingSlash(route);
|
|
653
|
-
if (normOriginal && normOriginal !== normRoute) {
|
|
654
|
-
extra[ATTR.ERROR_ORIGINAL_PATH] = normOriginal;
|
|
655
|
-
extra[ATTR.ERROR_FALLBACK_ROUTE] = route;
|
|
656
|
-
extra[ATTR.ERROR_FRAMEWORK_KIND] = "fallback";
|
|
657
|
-
errorSource = errorSource ?? "framework_fallback";
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (errorSource !== void 0) {
|
|
661
|
-
extra[ATTR.ERROR_SOURCE] = errorSource;
|
|
662
|
-
}
|
|
663
|
-
if (this.verbose && (extra[ATTR.ERROR_MESSAGE] || extra[ATTR.ERROR_CODE])) {
|
|
664
|
-
const msgSource = eventDetails.message ? "event" : typeof attrMessage === "string" ? "attrs" : "none";
|
|
665
|
-
const typeSource = eventDetails.type ? "event" : typeof attrType === "string" ? "attrs" : "none";
|
|
666
|
-
sdkLog(
|
|
667
|
-
"info",
|
|
668
|
-
`[glasstrace] enrichSpan "${name}": error.message source=${msgSource}, error.code source=${typeSource}`
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
const errorField = attrs["error.field"];
|
|
672
|
-
if (typeof errorField === "string") {
|
|
673
|
-
extra[ATTR.ERROR_FIELD] = errorField;
|
|
674
|
-
}
|
|
675
|
-
if (this.getConfig().errorResponseBodies) {
|
|
676
|
-
const responseBody = attrs["glasstrace.internal.response_body"];
|
|
677
|
-
if (typeof responseBody === "string") {
|
|
678
|
-
const enrichedStatus = extra[ATTR.HTTP_STATUS_CODE];
|
|
679
|
-
const effectiveStatus = typeof enrichedStatus === "number" ? enrichedStatus : statusCode;
|
|
680
|
-
if (isHttpErrorStatus(effectiveStatus)) {
|
|
681
|
-
const prepared = prepareErrorResponseBody(responseBody);
|
|
682
|
-
if (prepared !== null) {
|
|
683
|
-
extra[ATTR.ERROR_RESPONSE_BODY] = prepared;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
const spanAny = span;
|
|
689
|
-
const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
|
|
690
|
-
const ormProvider = deriveOrmProvider(instrumentationName);
|
|
691
|
-
if (ormProvider) {
|
|
692
|
-
extra[ATTR.ORM_PROVIDER] = ormProvider;
|
|
693
|
-
const table = attrs["db.sql.table"];
|
|
694
|
-
const prismaModel = attrs["db.prisma.model"];
|
|
695
|
-
const model = typeof table === "string" ? table : typeof prismaModel === "string" ? prismaModel : void 0;
|
|
696
|
-
if (model) {
|
|
697
|
-
extra[ATTR.ORM_MODEL] = model;
|
|
698
|
-
}
|
|
699
|
-
const operation = attrs["db.operation"];
|
|
700
|
-
if (typeof operation === "string") {
|
|
701
|
-
extra[ATTR.ORM_OPERATION] = operation;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
const httpUrl = attrs["http.url"];
|
|
705
|
-
const fullUrl = attrs["url.full"];
|
|
706
|
-
const url = typeof httpUrl === "string" ? httpUrl : typeof fullUrl === "string" ? fullUrl : void 0;
|
|
707
|
-
if (url && span.kind === SpanKind.CLIENT) {
|
|
708
|
-
extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
|
|
709
|
-
}
|
|
710
|
-
return createEnrichedSpan(span, extra);
|
|
711
|
-
} catch {
|
|
712
|
-
return span;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Lazily creates the delegate OTLP exporter once the API key is resolved.
|
|
717
|
-
* Recreates the delegate if the key has changed (e.g., after key rotation)
|
|
718
|
-
* so the Authorization header stays current.
|
|
719
|
-
*/
|
|
720
|
-
ensureDelegate() {
|
|
721
|
-
if (!this.createDelegateFn) return null;
|
|
722
|
-
const currentKey = this.getApiKey();
|
|
723
|
-
if (currentKey === API_KEY_PENDING) return null;
|
|
724
|
-
if (this.delegate && this.delegateKey === currentKey) {
|
|
725
|
-
return this.delegate;
|
|
726
|
-
}
|
|
727
|
-
if (this.delegate) {
|
|
728
|
-
void this.delegate.shutdown?.().catch(() => {
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
this.delegate = this.createDelegateFn(this.endpointUrl, {
|
|
732
|
-
Authorization: `Bearer ${currentKey}`
|
|
733
522
|
});
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
* Buffers raw (unenriched) spans while the API key is pending.
|
|
739
|
-
* Evicts oldest batches if the buffer exceeds MAX_PENDING_SPANS.
|
|
740
|
-
* Re-checks the key after buffering to close the race window where
|
|
741
|
-
* the key resolves between the caller's check and this buffer call.
|
|
742
|
-
*/
|
|
743
|
-
bufferSpans(spans, resultCallback) {
|
|
744
|
-
this.pendingBatches.push({ spans, resultCallback });
|
|
745
|
-
this.pendingSpanCount += spans.length;
|
|
746
|
-
while (this.pendingSpanCount > MAX_PENDING_SPANS && this.pendingBatches.length > 1) {
|
|
747
|
-
const evicted = this.pendingBatches.shift();
|
|
748
|
-
this.pendingSpanCount -= evicted.spans.length;
|
|
749
|
-
recordSpansDropped(evicted.spans.length);
|
|
750
|
-
evicted.resultCallback({ code: 0 });
|
|
751
|
-
if (!this.overflowLogged) {
|
|
752
|
-
this.overflowLogged = true;
|
|
753
|
-
console.warn(
|
|
754
|
-
"[glasstrace] Pending span buffer overflow \u2014 oldest spans evicted. This usually means the API key is taking too long to resolve."
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
if (this.getApiKey() !== API_KEY_PENDING) {
|
|
759
|
-
this.flushPending();
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* Flushes all buffered spans through the delegate exporter.
|
|
764
|
-
* Enriches spans at flush time (not buffer time) so that session IDs
|
|
765
|
-
* are computed with the resolved API key instead of the "pending" sentinel.
|
|
766
|
-
*/
|
|
767
|
-
flushPending() {
|
|
768
|
-
if (this.pendingBatches.length === 0) return;
|
|
769
|
-
const exporter = this.ensureDelegate();
|
|
770
|
-
if (!exporter) {
|
|
771
|
-
let discardedCount = 0;
|
|
772
|
-
for (const batch of this.pendingBatches) {
|
|
773
|
-
discardedCount += batch.spans.length;
|
|
774
|
-
batch.resultCallback({ code: 0 });
|
|
775
|
-
}
|
|
776
|
-
recordSpansDropped(discardedCount);
|
|
777
|
-
this.pendingBatches = [];
|
|
778
|
-
this.pendingSpanCount = 0;
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
const batches = this.pendingBatches;
|
|
782
|
-
this.pendingBatches = [];
|
|
783
|
-
this.pendingSpanCount = 0;
|
|
784
|
-
for (const batch of batches) {
|
|
785
|
-
const enriched = batch.spans.map((span) => this.enrichSpan(span));
|
|
786
|
-
exporter.export(enriched, (result) => {
|
|
787
|
-
if (result.code !== 0) {
|
|
788
|
-
sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
|
|
789
|
-
}
|
|
790
|
-
batch.resultCallback(result);
|
|
791
|
-
});
|
|
792
|
-
recordSpansExported(enriched.length);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
function createEnrichedSpan(span, extra) {
|
|
797
|
-
const enrichedAttributes = { ...span.attributes, ...extra };
|
|
798
|
-
return Object.create(span, {
|
|
799
|
-
attributes: {
|
|
800
|
-
value: enrichedAttributes,
|
|
801
|
-
enumerable: true
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
function hasExceptionEvent(span) {
|
|
806
|
-
return span.events?.some((e) => e.name === "exception") ?? false;
|
|
523
|
+
};
|
|
524
|
+
_signalHandler = handler;
|
|
525
|
+
process.once("SIGTERM", handler);
|
|
526
|
+
process.once("SIGINT", handler);
|
|
807
527
|
}
|
|
808
|
-
function
|
|
809
|
-
|
|
810
|
-
if (
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const message = event.attributes["exception.message"];
|
|
815
|
-
const stacktrace = event.attributes["exception.stacktrace"];
|
|
816
|
-
return {
|
|
817
|
-
type: typeof type === "string" ? type : void 0,
|
|
818
|
-
message: typeof message === "string" ? message : void 0,
|
|
819
|
-
stacktrace: typeof stacktrace === "string" ? stacktrace : void 0
|
|
528
|
+
function registerBeforeExitTrigger() {
|
|
529
|
+
if (_beforeExitRegistered) return;
|
|
530
|
+
if (typeof process === "undefined" || typeof process.once !== "function") return;
|
|
531
|
+
_beforeExitRegistered = true;
|
|
532
|
+
const handler = () => {
|
|
533
|
+
void executeShutdown();
|
|
820
534
|
};
|
|
535
|
+
_beforeExitHandler = handler;
|
|
536
|
+
process.once("beforeExit", handler);
|
|
821
537
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
538
|
+
|
|
539
|
+
// src/error-response-body.ts
|
|
540
|
+
var ERROR_RESPONSE_BODY_MAX_BYTES = 4096;
|
|
541
|
+
var ERROR_RESPONSE_BODY_TRUNCATION_MARKER = "...[truncated]";
|
|
542
|
+
var REDACTED = "[REDACTED]";
|
|
543
|
+
var ERROR_STATUS_MIN = 400;
|
|
544
|
+
var ERROR_STATUS_MAX = 599;
|
|
545
|
+
function coerceHttpStatus(value) {
|
|
546
|
+
let numeric;
|
|
547
|
+
if (typeof value === "number") {
|
|
548
|
+
numeric = value;
|
|
549
|
+
} else if (typeof value === "string") {
|
|
550
|
+
const trimmed = value.trim();
|
|
551
|
+
if (trimmed.length === 0) return void 0;
|
|
552
|
+
numeric = Number(trimmed);
|
|
553
|
+
} else {
|
|
554
|
+
return void 0;
|
|
834
555
|
}
|
|
835
|
-
return void 0;
|
|
556
|
+
return Number.isFinite(numeric) ? numeric : void 0;
|
|
836
557
|
}
|
|
837
|
-
function
|
|
838
|
-
|
|
839
|
-
if (
|
|
840
|
-
return
|
|
558
|
+
function isHttpErrorStatus(status) {
|
|
559
|
+
const numeric = coerceHttpStatus(status);
|
|
560
|
+
if (numeric === void 0) return false;
|
|
561
|
+
return numeric >= ERROR_STATUS_MIN && numeric <= ERROR_STATUS_MAX;
|
|
841
562
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
563
|
+
var REDACTION_PATTERNS = [
|
|
564
|
+
// Order matters: redact specific token shapes BEFORE the generic
|
|
565
|
+
// key=value catcher so a literal `Bearer eyJ…` collapses into a single
|
|
566
|
+
// [REDACTED] and the JWT regex does not separately match the suffix.
|
|
567
|
+
{
|
|
568
|
+
name: "bearer",
|
|
569
|
+
// Case-insensitive on the scheme: HTTP frameworks and proxies
|
|
570
|
+
// round-trip the auth scheme with inconsistent casing
|
|
571
|
+
// (`Bearer`, `bearer`, `BEARER`), and a real token leaks just as
|
|
572
|
+
// badly under any of them.
|
|
573
|
+
pattern: /\bBearer\s+[A-Za-z0-9._\-+/=]+/gi
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
name: "jwt",
|
|
577
|
+
// Three base64url segments separated by dots. Real JWTs encode at
|
|
578
|
+
// minimum a small JSON header in the first segment, which alone is
|
|
579
|
+
// typically ≥10 chars after base64url; a 16-char floor avoids false
|
|
580
|
+
// positives on dotted text like a stack-trace frame
|
|
581
|
+
// (`react.dom.server`) while still catching every real JWT we have
|
|
582
|
+
// seen in the wild. Anchored with word boundaries on both sides so
|
|
583
|
+
// a 3-dot semantic version like "next@15.4.1.2" does not match.
|
|
584
|
+
pattern: /\b[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: "glasstrace-api-key",
|
|
588
|
+
// gt_dev_* and gt_anon_* keys are >=24 chars of [A-Za-z0-9].
|
|
589
|
+
pattern: /\bgt_(?:dev|anon)_[A-Za-z0-9]{16,}\b/g
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
name: "aws-access-key",
|
|
593
|
+
// 20-char prefix-fixed identifier.
|
|
594
|
+
pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
name: "key-value-secret-quoted",
|
|
598
|
+
// Quoted-string variant: (key) [:=] "<value>". The value runs to
|
|
599
|
+
// the next unescaped closing quote so a multi-word secret like
|
|
600
|
+
// `password="my secret phrase"` is fully consumed instead of
|
|
601
|
+
// splitting at the first space and leaving the tail visible.
|
|
602
|
+
// The leading `(?<![A-Za-z0-9_])` prevents matching inside
|
|
603
|
+
// identifiers like `passwordless`. The trailing `"?` after the
|
|
604
|
+
// keyword absorbs the closing quote in JSON-style `"apikey":
|
|
605
|
+
// "value"` so the colon is still seen as the separator.
|
|
606
|
+
pattern: /(?<![A-Za-z0-9_])(?:api[_-]?key|apikey|secret|password|token)"?\s*[:=]\s*"(?:[^"\\]|\\.)*"/gi
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: "key-value-secret-bare",
|
|
610
|
+
// Unquoted variant: (key) [:=] <bare-value>. The bare value
|
|
611
|
+
// capture stops at common JSON/text delimiters so we redact only
|
|
612
|
+
// the value, not surrounding structure. Listed AFTER the quoted
|
|
613
|
+
// variant so a quoted value's surrounding `"` are consumed by
|
|
614
|
+
// the first pattern and we never fall through here for a quoted
|
|
615
|
+
// secret.
|
|
616
|
+
pattern: /(?<![A-Za-z0-9_])(?:api[_-]?key|apikey|secret|password|token)"?\s*[:=]\s*[^\s,;}\]"]+/gi
|
|
864
617
|
}
|
|
865
|
-
|
|
618
|
+
];
|
|
619
|
+
function sanitizeErrorResponseBody(body) {
|
|
620
|
+
let out = body;
|
|
621
|
+
for (const { pattern } of REDACTION_PATTERNS) {
|
|
622
|
+
out = out.replace(pattern, REDACTED);
|
|
623
|
+
}
|
|
624
|
+
return out;
|
|
866
625
|
}
|
|
867
|
-
function
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
626
|
+
function truncateErrorResponseBody(body) {
|
|
627
|
+
const encoder = new TextEncoder();
|
|
628
|
+
const encoded = encoder.encode(body);
|
|
629
|
+
if (encoded.byteLength <= ERROR_RESPONSE_BODY_MAX_BYTES) {
|
|
630
|
+
return body;
|
|
871
631
|
}
|
|
872
|
-
|
|
873
|
-
|
|
632
|
+
let cut = ERROR_RESPONSE_BODY_MAX_BYTES;
|
|
633
|
+
let scan = cut - 1;
|
|
634
|
+
while (scan >= 0 && (encoded[scan] & 192) === 128) {
|
|
635
|
+
scan -= 1;
|
|
874
636
|
}
|
|
875
|
-
|
|
637
|
+
if (scan >= 0) {
|
|
638
|
+
const leading = encoded[scan];
|
|
639
|
+
let expected = 1;
|
|
640
|
+
if ((leading & 128) === 0) {
|
|
641
|
+
expected = 1;
|
|
642
|
+
} else if ((leading & 224) === 192) {
|
|
643
|
+
expected = 2;
|
|
644
|
+
} else if ((leading & 240) === 224) {
|
|
645
|
+
expected = 3;
|
|
646
|
+
} else if ((leading & 248) === 240) {
|
|
647
|
+
expected = 4;
|
|
648
|
+
}
|
|
649
|
+
if (scan + expected > cut) {
|
|
650
|
+
cut = scan;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
654
|
+
const sliced = encoded.subarray(0, cut);
|
|
655
|
+
const decoded = decoder.decode(sliced);
|
|
656
|
+
return decoded + ERROR_RESPONSE_BODY_TRUNCATION_MARKER;
|
|
876
657
|
}
|
|
877
|
-
function
|
|
878
|
-
|
|
879
|
-
if (
|
|
880
|
-
|
|
658
|
+
function prepareErrorResponseBody(body) {
|
|
659
|
+
if (body.length === 0) return null;
|
|
660
|
+
if (body.trim().length === 0) return null;
|
|
661
|
+
const sanitized = sanitizeErrorResponseBody(body);
|
|
662
|
+
return truncateErrorResponseBody(sanitized);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/error-stack.ts
|
|
666
|
+
var ERROR_STACK_MAX_BYTES = 8192;
|
|
667
|
+
var ERROR_STACK_TRUNCATION_MARKER = "...[stack truncated]";
|
|
668
|
+
var PATH_REDACTED = "<path>";
|
|
669
|
+
var PATH_KEEP_MARKERS = [
|
|
670
|
+
"node_modules",
|
|
671
|
+
".next",
|
|
672
|
+
".glasstrace",
|
|
673
|
+
"src",
|
|
674
|
+
"dist",
|
|
675
|
+
"build",
|
|
676
|
+
"lib",
|
|
677
|
+
"app",
|
|
678
|
+
"pages"
|
|
679
|
+
];
|
|
680
|
+
var PATH_TOKEN_RE = /(?<=^|[\s(])(\/[^\s()<>]+|[A-Za-z]:\\[^\s()<>]+|file:\/\/\/[^\s()<>]+|webpack-internal:\/\/[^\s()<>]+|node:[^\s()<>]+)/g;
|
|
681
|
+
var URL_QUERY_FRAGMENT_RE = /(\bhttps?:\/\/[^\s?#()<>]+)([?#][^\s()<>]*)/g;
|
|
682
|
+
function normalizePathToken(token) {
|
|
683
|
+
let work = token;
|
|
684
|
+
if (work.startsWith("file:///")) {
|
|
685
|
+
work = work.slice("file://".length);
|
|
881
686
|
}
|
|
882
|
-
if (
|
|
883
|
-
return
|
|
687
|
+
if (work.startsWith("webpack-internal:") || work.startsWith("node:")) {
|
|
688
|
+
return { token, changed: false };
|
|
884
689
|
}
|
|
885
|
-
|
|
886
|
-
|
|
690
|
+
const isPosixAbs = work.startsWith("/");
|
|
691
|
+
const isWinAbs = /^[A-Za-z]:\\/.test(work);
|
|
692
|
+
if (!isPosixAbs && !isWinAbs) {
|
|
693
|
+
return { token, changed: false };
|
|
887
694
|
}
|
|
888
|
-
|
|
889
|
-
|
|
695
|
+
const sep = isWinAbs ? "\\" : "/";
|
|
696
|
+
let bestIdx = -1;
|
|
697
|
+
for (const marker of PATH_KEEP_MARKERS) {
|
|
698
|
+
const needle = `${sep}${marker}${sep}`;
|
|
699
|
+
const idx = work.lastIndexOf(needle);
|
|
700
|
+
if (idx >= 0) {
|
|
701
|
+
bestIdx = idx;
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
890
704
|
}
|
|
891
|
-
|
|
705
|
+
if (bestIdx >= 0) {
|
|
706
|
+
const kept = work.slice(bestIdx + sep.length);
|
|
707
|
+
const rebuilt2 = `${PATH_REDACTED}/${kept.replace(/\\/g, "/")}`;
|
|
708
|
+
return { token: rebuilt2, changed: true };
|
|
709
|
+
}
|
|
710
|
+
const colonLineRe = /:\d+(?::\d+)?$/;
|
|
711
|
+
const lineMatch = colonLineRe.exec(work);
|
|
712
|
+
const pathBody = lineMatch ? work.slice(0, lineMatch.index) : work;
|
|
713
|
+
const lineSuffix = lineMatch ? work.slice(lineMatch.index) : "";
|
|
714
|
+
const lastSep = Math.max(pathBody.lastIndexOf("/"), pathBody.lastIndexOf("\\"));
|
|
715
|
+
const basename = lastSep >= 0 ? pathBody.slice(lastSep + 1) : pathBody;
|
|
716
|
+
const rebuilt = `${PATH_REDACTED}/${basename}${lineSuffix}`;
|
|
717
|
+
return { token: rebuilt, changed: true };
|
|
892
718
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
719
|
+
function sanitizeStack(stack) {
|
|
720
|
+
let changed = false;
|
|
721
|
+
const pathNormalized = stack.replace(PATH_TOKEN_RE, (token) => {
|
|
722
|
+
const out = normalizePathToken(token);
|
|
723
|
+
if (out.changed) changed = true;
|
|
724
|
+
return out.token;
|
|
725
|
+
});
|
|
726
|
+
const urlStripped = pathNormalized.replace(URL_QUERY_FRAGMENT_RE, (match, prefix) => {
|
|
727
|
+
if (match !== prefix) changed = true;
|
|
728
|
+
return prefix;
|
|
729
|
+
});
|
|
730
|
+
const credentialRedacted = sanitizeErrorResponseBody(urlStripped);
|
|
731
|
+
if (credentialRedacted !== urlStripped) changed = true;
|
|
732
|
+
return { stack: credentialRedacted, redacted: changed };
|
|
901
733
|
}
|
|
902
|
-
function
|
|
903
|
-
|
|
734
|
+
function truncateStack(stack) {
|
|
735
|
+
const encoder = new TextEncoder();
|
|
736
|
+
const encoded = encoder.encode(stack);
|
|
737
|
+
if (encoded.byteLength <= ERROR_STACK_MAX_BYTES) {
|
|
738
|
+
return { stack, truncated: false };
|
|
739
|
+
}
|
|
740
|
+
let cut = ERROR_STACK_MAX_BYTES;
|
|
741
|
+
let scan = cut - 1;
|
|
742
|
+
while (scan >= 0 && (encoded[scan] & 192) === 128) {
|
|
743
|
+
scan -= 1;
|
|
744
|
+
}
|
|
745
|
+
if (scan >= 0) {
|
|
746
|
+
const leading = encoded[scan];
|
|
747
|
+
let expected = 1;
|
|
748
|
+
if ((leading & 128) === 0) {
|
|
749
|
+
expected = 1;
|
|
750
|
+
} else if ((leading & 224) === 192) {
|
|
751
|
+
expected = 2;
|
|
752
|
+
} else if ((leading & 240) === 224) {
|
|
753
|
+
expected = 3;
|
|
754
|
+
} else if ((leading & 248) === 240) {
|
|
755
|
+
expected = 4;
|
|
756
|
+
}
|
|
757
|
+
if (scan + expected > cut) {
|
|
758
|
+
cut = scan;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
762
|
+
const sliced = encoded.subarray(0, cut);
|
|
763
|
+
const decoded = decoder.decode(sliced);
|
|
764
|
+
return { stack: decoded + ERROR_STACK_TRUNCATION_MARKER, truncated: true };
|
|
765
|
+
}
|
|
766
|
+
function prepareStack(stack) {
|
|
767
|
+
if (stack.length === 0) return null;
|
|
768
|
+
if (stack.trim().length === 0) return null;
|
|
769
|
+
const sanitized = sanitizeStack(stack);
|
|
770
|
+
const truncated = truncateStack(sanitized.stack);
|
|
771
|
+
return {
|
|
772
|
+
stack: truncated.stack,
|
|
773
|
+
truncated: truncated.truncated,
|
|
774
|
+
redacted: sanitized.redacted
|
|
775
|
+
};
|
|
904
776
|
}
|
|
905
777
|
|
|
906
|
-
// src/
|
|
907
|
-
var
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
ACTIVE_DEGRADED: "ACTIVE_DEGRADED",
|
|
914
|
-
SHUTTING_DOWN: "SHUTTING_DOWN",
|
|
915
|
-
SHUTDOWN: "SHUTDOWN",
|
|
916
|
-
PRODUCTION_DISABLED: "PRODUCTION_DISABLED",
|
|
917
|
-
REGISTRATION_FAILED: "REGISTRATION_FAILED"
|
|
918
|
-
};
|
|
919
|
-
var AuthState = {
|
|
920
|
-
ANONYMOUS: "ANONYMOUS",
|
|
921
|
-
AUTHENTICATED: "AUTHENTICATED",
|
|
922
|
-
CLAIMING: "CLAIMING",
|
|
923
|
-
CLAIMED: "CLAIMED"
|
|
924
|
-
};
|
|
925
|
-
var OtelState = {
|
|
926
|
-
UNCONFIGURED: "UNCONFIGURED",
|
|
927
|
-
CONFIGURING: "CONFIGURING",
|
|
928
|
-
OWNS_PROVIDER: "OWNS_PROVIDER",
|
|
929
|
-
AUTO_ATTACHED: "AUTO_ATTACHED",
|
|
930
|
-
PROCESSOR_PRESENT: "PROCESSOR_PRESENT",
|
|
931
|
-
COEXISTENCE_FAILED: "COEXISTENCE_FAILED"
|
|
932
|
-
};
|
|
933
|
-
var VALID_CORE_TRANSITIONS = {
|
|
934
|
-
[CoreState.IDLE]: [CoreState.REGISTERING, CoreState.REGISTRATION_FAILED, CoreState.SHUTTING_DOWN],
|
|
935
|
-
[CoreState.REGISTERING]: [
|
|
936
|
-
CoreState.KEY_PENDING,
|
|
937
|
-
CoreState.PRODUCTION_DISABLED,
|
|
938
|
-
CoreState.REGISTRATION_FAILED,
|
|
939
|
-
CoreState.SHUTTING_DOWN
|
|
940
|
-
],
|
|
941
|
-
[CoreState.KEY_PENDING]: [
|
|
942
|
-
CoreState.KEY_RESOLVED,
|
|
943
|
-
CoreState.REGISTRATION_FAILED,
|
|
944
|
-
CoreState.SHUTTING_DOWN
|
|
945
|
-
],
|
|
946
|
-
[CoreState.KEY_RESOLVED]: [
|
|
947
|
-
CoreState.ACTIVE,
|
|
948
|
-
CoreState.ACTIVE_DEGRADED,
|
|
949
|
-
CoreState.SHUTTING_DOWN
|
|
950
|
-
],
|
|
951
|
-
[CoreState.ACTIVE]: [
|
|
952
|
-
CoreState.ACTIVE_DEGRADED,
|
|
953
|
-
CoreState.SHUTTING_DOWN
|
|
954
|
-
],
|
|
955
|
-
[CoreState.ACTIVE_DEGRADED]: [
|
|
956
|
-
CoreState.ACTIVE,
|
|
957
|
-
CoreState.SHUTTING_DOWN
|
|
958
|
-
],
|
|
959
|
-
[CoreState.SHUTTING_DOWN]: [CoreState.SHUTDOWN],
|
|
960
|
-
[CoreState.SHUTDOWN]: [],
|
|
961
|
-
[CoreState.PRODUCTION_DISABLED]: [],
|
|
962
|
-
[CoreState.REGISTRATION_FAILED]: []
|
|
963
|
-
};
|
|
964
|
-
var VALID_AUTH_TRANSITIONS = {
|
|
965
|
-
[AuthState.ANONYMOUS]: [AuthState.CLAIMING],
|
|
966
|
-
[AuthState.AUTHENTICATED]: [AuthState.CLAIMING],
|
|
967
|
-
[AuthState.CLAIMING]: [AuthState.CLAIMED],
|
|
968
|
-
[AuthState.CLAIMED]: [AuthState.CLAIMING]
|
|
969
|
-
};
|
|
970
|
-
var VALID_OTEL_TRANSITIONS = {
|
|
971
|
-
[OtelState.UNCONFIGURED]: [OtelState.CONFIGURING],
|
|
972
|
-
[OtelState.CONFIGURING]: [
|
|
973
|
-
OtelState.OWNS_PROVIDER,
|
|
974
|
-
OtelState.AUTO_ATTACHED,
|
|
975
|
-
OtelState.PROCESSOR_PRESENT,
|
|
976
|
-
OtelState.COEXISTENCE_FAILED
|
|
977
|
-
],
|
|
978
|
-
[OtelState.OWNS_PROVIDER]: [],
|
|
979
|
-
[OtelState.AUTO_ATTACHED]: [],
|
|
980
|
-
[OtelState.PROCESSOR_PRESENT]: [],
|
|
981
|
-
[OtelState.COEXISTENCE_FAILED]: []
|
|
982
|
-
};
|
|
983
|
-
var _coreState = CoreState.IDLE;
|
|
984
|
-
var _authState = AuthState.ANONYMOUS;
|
|
985
|
-
var _otelState = OtelState.UNCONFIGURED;
|
|
986
|
-
var _emitter = new EventEmitter();
|
|
987
|
-
var _logger = null;
|
|
988
|
-
var _initialized = false;
|
|
989
|
-
var _initWarned = false;
|
|
990
|
-
var _coreReadyEmitted = false;
|
|
991
|
-
var _authInitialized = false;
|
|
992
|
-
var _emitting = false;
|
|
993
|
-
function initLifecycle(options) {
|
|
994
|
-
if (_initialized) {
|
|
995
|
-
options.logger("warn", "[glasstrace] initLifecycle() called twice \u2014 ignored.");
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
_logger = options.logger;
|
|
999
|
-
_initialized = true;
|
|
778
|
+
// src/build-info.ts
|
|
779
|
+
var UNSET = "";
|
|
780
|
+
var SHA_SHAPE = /^[0-9a-f]{7,64}$/i;
|
|
781
|
+
function redactBuildHash(value) {
|
|
782
|
+
const sanitize = (s) => s.replace(/[\x00-\x1F\x7F]/g, "?");
|
|
783
|
+
if (value.length <= 12) return sanitize(value.slice(0, 4)) + "...";
|
|
784
|
+
return sanitize(value.slice(0, 8)) + "..." + sanitize(value.slice(-4));
|
|
1000
785
|
}
|
|
1001
|
-
function
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
786
|
+
function readBuildHashFromEnv() {
|
|
787
|
+
const raw = process.env.GLASSTRACE_BUILD_HASH;
|
|
788
|
+
if (typeof raw !== "string") return UNSET;
|
|
789
|
+
const trimmed = raw.trim();
|
|
790
|
+
if (trimmed.length === 0) return UNSET;
|
|
791
|
+
if (!SHA_SHAPE.test(trimmed)) {
|
|
792
|
+
sdkLog(
|
|
793
|
+
"warn",
|
|
794
|
+
`[glasstrace] warning: GLASSTRACE_BUILD_HASH=${redactBuildHash(trimmed)} does not match expected SHA shape (7-64 hex characters); source-map enrichment may not work as expected.`
|
|
1006
795
|
);
|
|
1007
796
|
}
|
|
797
|
+
return trimmed;
|
|
1008
798
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
const valid = VALID_CORE_TRANSITIONS[from];
|
|
1014
|
-
if (!valid.includes(to)) {
|
|
1015
|
-
_logger?.(
|
|
1016
|
-
"warn",
|
|
1017
|
-
`[glasstrace] Invalid core state transition: ${from} \u2192 ${to}. Ignored.`
|
|
1018
|
-
);
|
|
1019
|
-
return;
|
|
799
|
+
var cachedBuildHash = null;
|
|
800
|
+
function getBuildHash() {
|
|
801
|
+
if (cachedBuildHash === null) {
|
|
802
|
+
cachedBuildHash = readBuildHashFromEnv();
|
|
1020
803
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
804
|
+
return cachedBuildHash === UNSET ? void 0 : cachedBuildHash;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/export-circuit-breaker.ts
|
|
808
|
+
var INITIAL_BACKOFF_MS = 3e4;
|
|
809
|
+
var BACKOFF_FACTOR = 2;
|
|
810
|
+
var MAX_BACKOFF_MS = 30 * 60 * 1e3;
|
|
811
|
+
var FAILURE_THRESHOLD = 5;
|
|
812
|
+
function classifyExportFailure(info) {
|
|
813
|
+
const status = readStatus(info);
|
|
814
|
+
if (status === 401 || status === 403) return "auth";
|
|
815
|
+
if (status === 429) return "rate_limit";
|
|
816
|
+
if (typeof status === "number" && status >= 500 && status <= 599) return "server_error";
|
|
817
|
+
if (typeof status === "number" && status >= 400 && status <= 499) return "client_error";
|
|
818
|
+
return "network";
|
|
819
|
+
}
|
|
820
|
+
function readStatus(info) {
|
|
821
|
+
if (typeof info.status === "number") return info.status;
|
|
822
|
+
const err = info.error;
|
|
823
|
+
if (!err || typeof err !== "object") return void 0;
|
|
824
|
+
const record = err;
|
|
825
|
+
const direct = record.status;
|
|
826
|
+
if (typeof direct === "number") return direct;
|
|
827
|
+
if (typeof direct === "string") {
|
|
828
|
+
const parsed = Number.parseInt(direct, 10);
|
|
829
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
830
|
+
}
|
|
831
|
+
const nested = record.response;
|
|
832
|
+
if (nested && typeof nested === "object") {
|
|
833
|
+
const nestedStatus = nested.status;
|
|
834
|
+
if (typeof nestedStatus === "number") return nestedStatus;
|
|
835
|
+
if (typeof nestedStatus === "string") {
|
|
836
|
+
const parsed = Number.parseInt(nestedStatus, 10);
|
|
837
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
1036
838
|
}
|
|
1037
|
-
} finally {
|
|
1038
|
-
_emitting = false;
|
|
1039
839
|
}
|
|
840
|
+
return void 0;
|
|
1040
841
|
}
|
|
1041
|
-
function
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
842
|
+
function buildOpenedMessage(category, count) {
|
|
843
|
+
return `[glasstrace] Export circuit opened after ${count} consecutive failures (category: ${category}). Subsequent spans dropped until probe succeeds.`;
|
|
844
|
+
}
|
|
845
|
+
var _singleton = null;
|
|
846
|
+
function getExportCircuitBreaker(options) {
|
|
847
|
+
if (_singleton === null) {
|
|
848
|
+
_singleton = createExportCircuitBreaker(options);
|
|
1048
849
|
}
|
|
1049
|
-
|
|
1050
|
-
_authState = state;
|
|
850
|
+
return _singleton;
|
|
1051
851
|
}
|
|
1052
|
-
function
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
);
|
|
1062
|
-
|
|
852
|
+
function peekExportCircuitBreaker() {
|
|
853
|
+
return _singleton;
|
|
854
|
+
}
|
|
855
|
+
function createExportCircuitBreaker(options) {
|
|
856
|
+
const events = options.events;
|
|
857
|
+
const recordDropped = options.recordDropped;
|
|
858
|
+
const fsm = options.fsm;
|
|
859
|
+
const now = options.now ?? (() => Date.now());
|
|
860
|
+
const setTimer = options.setTimer ?? ((fn, delayMs) => {
|
|
861
|
+
const handle = setTimeout(fn, delayMs);
|
|
862
|
+
if (typeof handle === "object" && handle && "unref" in handle) {
|
|
863
|
+
handle.unref();
|
|
864
|
+
}
|
|
865
|
+
return handle;
|
|
866
|
+
});
|
|
867
|
+
const clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
868
|
+
let state = "CLOSED";
|
|
869
|
+
let consecutiveFailures = 0;
|
|
870
|
+
let currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
871
|
+
let openedAtMs = null;
|
|
872
|
+
let pendingTimer = null;
|
|
873
|
+
let halfOpenProbeInFlight = false;
|
|
874
|
+
let generation = 0;
|
|
875
|
+
function clearPendingTimer() {
|
|
876
|
+
if (pendingTimer !== null) {
|
|
877
|
+
clearTimer(pendingTimer);
|
|
878
|
+
pendingTimer = null;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function scheduleHalfOpen(delayMs) {
|
|
882
|
+
clearPendingTimer();
|
|
883
|
+
pendingTimer = setTimer(() => {
|
|
884
|
+
pendingTimer = null;
|
|
885
|
+
if (state !== "OPEN") return;
|
|
886
|
+
transitionToHalfOpen(delayMs);
|
|
887
|
+
}, delayMs);
|
|
888
|
+
}
|
|
889
|
+
function transitionToOpen(category) {
|
|
890
|
+
const wasNonOpen = state !== "OPEN";
|
|
891
|
+
state = "OPEN";
|
|
892
|
+
halfOpenProbeInFlight = false;
|
|
893
|
+
if (openedAtMs === null) {
|
|
894
|
+
openedAtMs = now();
|
|
895
|
+
}
|
|
896
|
+
if (wasNonOpen) {
|
|
897
|
+
const timestamp = new Date(now()).toISOString();
|
|
898
|
+
const message = buildOpenedMessage(category, consecutiveFailures);
|
|
899
|
+
try {
|
|
900
|
+
events.emitOpened({
|
|
901
|
+
category,
|
|
902
|
+
message,
|
|
903
|
+
timestamp,
|
|
904
|
+
consecutiveFailures,
|
|
905
|
+
nextProbeMs: currentBackoffMs
|
|
906
|
+
});
|
|
907
|
+
} catch {
|
|
908
|
+
}
|
|
909
|
+
try {
|
|
910
|
+
fsm?.onCircuitOpened();
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
scheduleHalfOpen(currentBackoffMs);
|
|
1063
915
|
}
|
|
1064
|
-
|
|
916
|
+
function transitionToHalfOpen(previousTimerMs) {
|
|
917
|
+
state = "HALF_OPEN";
|
|
918
|
+
halfOpenProbeInFlight = false;
|
|
919
|
+
try {
|
|
920
|
+
events.emitHalfOpen({
|
|
921
|
+
timestamp: new Date(now()).toISOString(),
|
|
922
|
+
previousTimerMs
|
|
923
|
+
});
|
|
924
|
+
} catch {
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function transitionToClosed() {
|
|
928
|
+
const startedAt = openedAtMs;
|
|
929
|
+
state = "CLOSED";
|
|
930
|
+
consecutiveFailures = 0;
|
|
931
|
+
currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
932
|
+
openedAtMs = null;
|
|
933
|
+
halfOpenProbeInFlight = false;
|
|
934
|
+
clearPendingTimer();
|
|
935
|
+
try {
|
|
936
|
+
events.emitClosed({
|
|
937
|
+
timestamp: new Date(now()).toISOString(),
|
|
938
|
+
outageDurationMs: startedAt === null ? 0 : Math.max(0, now() - startedAt)
|
|
939
|
+
});
|
|
940
|
+
} catch {
|
|
941
|
+
}
|
|
942
|
+
try {
|
|
943
|
+
fsm?.onCircuitClosed();
|
|
944
|
+
} catch {
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return {
|
|
948
|
+
shouldExport() {
|
|
949
|
+
if (state === "OPEN") return false;
|
|
950
|
+
if (state === "HALF_OPEN") {
|
|
951
|
+
if (halfOpenProbeInFlight) return false;
|
|
952
|
+
halfOpenProbeInFlight = true;
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
return true;
|
|
956
|
+
},
|
|
957
|
+
recordSuccess() {
|
|
958
|
+
if (state === "HALF_OPEN") {
|
|
959
|
+
halfOpenProbeInFlight = false;
|
|
960
|
+
transitionToClosed();
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
consecutiveFailures = 0;
|
|
964
|
+
},
|
|
965
|
+
recordFailure(info) {
|
|
966
|
+
const category = classifyExportFailure(info);
|
|
967
|
+
if (state === "HALF_OPEN") {
|
|
968
|
+
currentBackoffMs = Math.min(currentBackoffMs * BACKOFF_FACTOR, MAX_BACKOFF_MS);
|
|
969
|
+
halfOpenProbeInFlight = false;
|
|
970
|
+
state = "OPEN";
|
|
971
|
+
scheduleHalfOpen(currentBackoffMs);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
if (state === "CLOSED") {
|
|
975
|
+
consecutiveFailures += 1;
|
|
976
|
+
if (consecutiveFailures >= FAILURE_THRESHOLD) {
|
|
977
|
+
transitionToOpen(category);
|
|
978
|
+
}
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
onSpansDropped(count) {
|
|
983
|
+
if (!Number.isFinite(count) || count <= 0) return;
|
|
984
|
+
try {
|
|
985
|
+
recordDropped(count);
|
|
986
|
+
} catch {
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
getState() {
|
|
990
|
+
return state;
|
|
991
|
+
},
|
|
992
|
+
resetForKeyRotation() {
|
|
993
|
+
generation += 1;
|
|
994
|
+
const wasNonClosed = state !== "CLOSED";
|
|
995
|
+
consecutiveFailures = 0;
|
|
996
|
+
currentBackoffMs = INITIAL_BACKOFF_MS;
|
|
997
|
+
clearPendingTimer();
|
|
998
|
+
if (wasNonClosed) {
|
|
999
|
+
transitionToClosed();
|
|
1000
|
+
}
|
|
1001
|
+
},
|
|
1002
|
+
getGeneration() {
|
|
1003
|
+
return generation;
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1065
1006
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1007
|
+
|
|
1008
|
+
// src/enriching-exporter.ts
|
|
1009
|
+
var ATTR = GLASSTRACE_ATTRIBUTE_NAMES;
|
|
1010
|
+
var API_KEY_PENDING = "pending";
|
|
1011
|
+
var MAX_PENDING_SPANS = 1024;
|
|
1012
|
+
var GlasstraceExporter = class {
|
|
1013
|
+
getApiKey;
|
|
1014
|
+
sessionManager;
|
|
1015
|
+
getConfig;
|
|
1016
|
+
environment;
|
|
1017
|
+
endpointUrl;
|
|
1018
|
+
createDelegateFn;
|
|
1019
|
+
verbose;
|
|
1020
|
+
delegate = null;
|
|
1021
|
+
delegateKey = null;
|
|
1022
|
+
pendingBatches = [];
|
|
1023
|
+
pendingSpanCount = 0;
|
|
1024
|
+
overflowLogged = false;
|
|
1025
|
+
/**
|
|
1026
|
+
* Lazily-bound reference to the export-path circuit breaker
|
|
1027
|
+
* (DISC-1568 / Wave 15C). Resolved on first export so this
|
|
1028
|
+
* constructor stays side-effect-free. The breaker is a module-
|
|
1029
|
+
* singleton — every `GlasstraceExporter` instance shares the same
|
|
1030
|
+
* one so a rotation event observed in `init-client.ts` reaches
|
|
1031
|
+
* every active exporter.
|
|
1032
|
+
*/
|
|
1033
|
+
circuitBreaker = null;
|
|
1034
|
+
constructor(options) {
|
|
1035
|
+
this.getApiKey = options.getApiKey;
|
|
1036
|
+
this.sessionManager = options.sessionManager;
|
|
1037
|
+
this.getConfig = options.getConfig;
|
|
1038
|
+
this.environment = options.environment;
|
|
1039
|
+
this.endpointUrl = options.endpointUrl;
|
|
1040
|
+
this.createDelegateFn = options.createDelegate;
|
|
1041
|
+
this.verbose = options.verbose ?? false;
|
|
1042
|
+
this[/* @__PURE__ */ Symbol.for("glasstrace.exporter")] = true;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Returns the export-path circuit breaker, lazily wiring it on
|
|
1046
|
+
* first call. The breaker is a module-singleton so all exporter
|
|
1047
|
+
* instances share state — a credential rotation observed once
|
|
1048
|
+
* resets the breaker for every active exporter, and a single
|
|
1049
|
+
* outage at the OTLP endpoint trips a single breaker rather than
|
|
1050
|
+
* one per exporter copy.
|
|
1051
|
+
*
|
|
1052
|
+
* The wiring binds:
|
|
1053
|
+
* - the lifecycle event sink to the SDK's lifecycle bus
|
|
1054
|
+
* (`emitLifecycleEvent`) so the `otel:circuit_*` events surface
|
|
1055
|
+
* to runtime-state, the CLI bridge, and any user-installed
|
|
1056
|
+
* subscribers.
|
|
1057
|
+
* - the dropped-span counter to {@link recordSpansDropped} so OPEN-
|
|
1058
|
+
* state drops show up in the existing health surface.
|
|
1059
|
+
* - the FSM hooks to {@link pushDegradationSource} /
|
|
1060
|
+
* {@link clearDegradationSource} keyed on `"export-circuit"`,
|
|
1061
|
+
* which routes the OPEN/CLOSED transitions through the
|
|
1062
|
+
* centralised `recomputeCoreFromDegradationSources()` helper.
|
|
1063
|
+
* That helper guards `ACTIVE ↔ ACTIVE_DEGRADED` so a circuit
|
|
1064
|
+
* recovery never clobbers an unrelated `OtelState.COEXISTENCE_FAILED`
|
|
1065
|
+
* degradation source.
|
|
1066
|
+
*/
|
|
1067
|
+
getCircuitBreaker() {
|
|
1068
|
+
if (this.circuitBreaker !== null) return this.circuitBreaker;
|
|
1069
|
+
this.circuitBreaker = getExportCircuitBreaker({
|
|
1070
|
+
events: {
|
|
1071
|
+
emitOpened: (payload) => emitLifecycleEvent("otel:circuit_opened", payload),
|
|
1072
|
+
emitHalfOpen: (payload) => emitLifecycleEvent("otel:circuit_half_open", payload),
|
|
1073
|
+
emitClosed: (payload) => emitLifecycleEvent("otel:circuit_closed", payload)
|
|
1074
|
+
},
|
|
1075
|
+
recordDropped: (count) => recordSpansDropped(count),
|
|
1076
|
+
fsm: {
|
|
1077
|
+
onCircuitOpened: () => pushDegradationSource("export-circuit"),
|
|
1078
|
+
onCircuitClosed: () => clearDegradationSource("export-circuit")
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
return this.circuitBreaker;
|
|
1082
|
+
}
|
|
1083
|
+
export(spans, resultCallback) {
|
|
1084
|
+
const currentKey = this.getApiKey();
|
|
1085
|
+
if (currentKey === API_KEY_PENDING) {
|
|
1086
|
+
this.bufferSpans(spans, resultCallback);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const breaker = this.getCircuitBreaker();
|
|
1090
|
+
if (!breaker.shouldExport()) {
|
|
1091
|
+
breaker.onSpansDropped(spans.length);
|
|
1092
|
+
resultCallback({ code: 0 });
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
const enrichedSpans = spans.map((span) => this.enrichSpan(span));
|
|
1096
|
+
const exporter = this.ensureDelegate();
|
|
1097
|
+
if (exporter) {
|
|
1098
|
+
const generationAtIssue = breaker.getGeneration();
|
|
1099
|
+
exporter.export(enrichedSpans, (result) => {
|
|
1100
|
+
if (result.code !== 0) {
|
|
1101
|
+
sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
|
|
1102
|
+
}
|
|
1103
|
+
if (breaker.getGeneration() !== generationAtIssue) {
|
|
1104
|
+
resultCallback(result);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (result.code === 0) {
|
|
1108
|
+
breaker.recordSuccess();
|
|
1109
|
+
} else {
|
|
1110
|
+
breaker.recordFailure({ error: result.error });
|
|
1111
|
+
}
|
|
1112
|
+
resultCallback(result);
|
|
1113
|
+
});
|
|
1114
|
+
recordSpansExported(enrichedSpans.length);
|
|
1115
|
+
} else {
|
|
1116
|
+
recordSpansDropped(enrichedSpans.length);
|
|
1117
|
+
resultCallback({ code: 0 });
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Called when the API key transitions from "pending" to a resolved value.
|
|
1122
|
+
* Creates the delegate exporter and flushes all buffered spans.
|
|
1123
|
+
*/
|
|
1124
|
+
notifyKeyResolved() {
|
|
1125
|
+
this.flushPending();
|
|
1126
|
+
}
|
|
1127
|
+
async shutdown() {
|
|
1128
|
+
const currentKey = this.getApiKey();
|
|
1129
|
+
if (currentKey !== API_KEY_PENDING && this.pendingBatches.length > 0) {
|
|
1130
|
+
this.flushPending();
|
|
1131
|
+
} else if (this.pendingBatches.length > 0) {
|
|
1132
|
+
console.warn(
|
|
1133
|
+
`[glasstrace] Shutdown with ${this.pendingSpanCount} buffered spans \u2014 API key never resolved, spans lost.`
|
|
1134
|
+
);
|
|
1135
|
+
recordSpansDropped(this.pendingSpanCount);
|
|
1136
|
+
for (const batch of this.pendingBatches) {
|
|
1137
|
+
batch.resultCallback({ code: 0 });
|
|
1138
|
+
}
|
|
1139
|
+
this.pendingBatches = [];
|
|
1140
|
+
this.pendingSpanCount = 0;
|
|
1141
|
+
}
|
|
1142
|
+
if (this.delegate) {
|
|
1143
|
+
return this.delegate.shutdown();
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Flushes any pending buffered spans (if the API key has resolved) and
|
|
1148
|
+
* delegates to the underlying exporter's forceFlush to drain its queue.
|
|
1149
|
+
*/
|
|
1150
|
+
forceFlush() {
|
|
1151
|
+
if (this.getApiKey() !== API_KEY_PENDING && this.pendingBatches.length > 0) {
|
|
1152
|
+
this.flushPending();
|
|
1153
|
+
}
|
|
1154
|
+
if (this.delegate?.forceFlush) {
|
|
1155
|
+
return this.delegate.forceFlush();
|
|
1156
|
+
}
|
|
1157
|
+
return Promise.resolve();
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Enriches a ReadableSpan with all glasstrace.* attributes.
|
|
1161
|
+
* Returns a new ReadableSpan wrapper; the original span is not mutated.
|
|
1162
|
+
*
|
|
1163
|
+
* Only {@link SessionManager.getSessionId} is individually guarded because
|
|
1164
|
+
* it calls into crypto and schema validation — a session ID failure should
|
|
1165
|
+
* not prevent the rest of enrichment. The other helper calls
|
|
1166
|
+
* ({@link deriveErrorCategory}, {@link deriveOrmProvider},
|
|
1167
|
+
* {@link classifyFetchTarget}) are pure functions on typed string inputs
|
|
1168
|
+
* and rely on the outer catch for any unexpected failure.
|
|
1169
|
+
*
|
|
1170
|
+
* On total failure, returns the original span unchanged.
|
|
1171
|
+
*/
|
|
1172
|
+
enrichSpan(span) {
|
|
1173
|
+
try {
|
|
1174
|
+
const attrs = span.attributes ?? {};
|
|
1175
|
+
const name = span.name ?? "";
|
|
1176
|
+
const extra = {};
|
|
1177
|
+
extra[ATTR.TRACE_TYPE] = "server";
|
|
1178
|
+
try {
|
|
1179
|
+
const sessionId = this.sessionManager.getSessionId(this.getApiKey());
|
|
1180
|
+
extra[ATTR.SESSION_ID] = sessionId;
|
|
1181
|
+
} catch {
|
|
1182
|
+
}
|
|
1183
|
+
const env = this.environment ?? process.env.GLASSTRACE_ENV;
|
|
1184
|
+
if (env) {
|
|
1185
|
+
extra[ATTR.ENVIRONMENT] = env;
|
|
1186
|
+
}
|
|
1187
|
+
const buildHash = getBuildHash();
|
|
1188
|
+
if (buildHash) {
|
|
1189
|
+
extra[ATTR.BUILD_HASH] = buildHash;
|
|
1190
|
+
}
|
|
1191
|
+
const existingCid = attrs["glasstrace.correlation.id"];
|
|
1192
|
+
if (typeof existingCid === "string") {
|
|
1193
|
+
extra[ATTR.CORRELATION_ID] = existingCid;
|
|
1194
|
+
}
|
|
1195
|
+
const rawRoute = attrs["http.route"];
|
|
1196
|
+
const route = typeof rawRoute === "string" ? rawRoute : name;
|
|
1197
|
+
if (route) {
|
|
1198
|
+
extra[ATTR.ROUTE] = route;
|
|
1199
|
+
}
|
|
1200
|
+
const rawUrlAttr = attrs["http.url"] ?? attrs["url.full"] ?? attrs["http.target"];
|
|
1201
|
+
const rawHttpUrl = typeof rawUrlAttr === "string" ? rawUrlAttr : void 0;
|
|
1202
|
+
if (rawHttpUrl) {
|
|
1203
|
+
const trpcMatch = rawHttpUrl.match(/\/api\/trpc\/([^/?#]+)/);
|
|
1204
|
+
if (trpcMatch) {
|
|
1205
|
+
let procedure;
|
|
1206
|
+
try {
|
|
1207
|
+
procedure = decodeURIComponent(trpcMatch[1]);
|
|
1208
|
+
} catch {
|
|
1209
|
+
procedure = trpcMatch[1];
|
|
1210
|
+
}
|
|
1211
|
+
if (procedure) {
|
|
1212
|
+
extra[ATTR.TRPC_PROCEDURE] = procedure;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const method = attrs["http.method"] ?? attrs["http.request.method"];
|
|
1217
|
+
if (method) {
|
|
1218
|
+
extra[ATTR.HTTP_METHOD] = method;
|
|
1219
|
+
}
|
|
1220
|
+
const actionRoute = extractLeadingPath(route);
|
|
1221
|
+
if (method === "POST" && actionRoute) {
|
|
1222
|
+
const isApiRoute = actionRoute === "/api" || actionRoute.startsWith("/api/");
|
|
1223
|
+
const isInternalRoute = actionRoute.startsWith("/_next/");
|
|
1224
|
+
if (!isApiRoute && !isInternalRoute) {
|
|
1225
|
+
extra[ATTR.NEXT_ACTION_DETECTED] = true;
|
|
1226
|
+
if (typeof extra[ATTR.CORRELATION_ID] !== "string") {
|
|
1227
|
+
maybeShowServerActionNudge();
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
const statusCode = coerceHttpStatus(attrs["http.status_code"]) ?? coerceHttpStatus(attrs["http.response.status_code"]);
|
|
1232
|
+
if (statusCode !== void 0) {
|
|
1233
|
+
extra[ATTR.HTTP_STATUS_CODE] = statusCode;
|
|
1234
|
+
}
|
|
1235
|
+
const isErrorByStatus = span.status?.code === SpanStatusCode.ERROR;
|
|
1236
|
+
const isErrorByEvent = hasExceptionEvent(span);
|
|
1237
|
+
const isErrorByAttrs = typeof attrs["exception.type"] === "string" || typeof attrs["exception.message"] === "string";
|
|
1238
|
+
const statusNotExplicitlyOK = span.status?.code !== SpanStatusCode.OK;
|
|
1239
|
+
if (this.verbose && method) {
|
|
1240
|
+
sdkLog(
|
|
1241
|
+
"info",
|
|
1242
|
+
`[glasstrace] enrichSpan "${name}": status.code=${span.status?.code}, http.status_code=${statusCode}, isErrorByStatus=${isErrorByStatus}, isErrorByEvent=${isErrorByEvent}, isErrorByAttrs=${isErrorByAttrs}`
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
if (method && statusNotExplicitlyOK && (isErrorByStatus || isErrorByEvent || isErrorByAttrs)) {
|
|
1246
|
+
if (statusCode === void 0 || statusCode === 0 || statusCode === 200) {
|
|
1247
|
+
const httpErrorType = attrs["error.type"];
|
|
1248
|
+
if (typeof httpErrorType === "string") {
|
|
1249
|
+
const parsed = parseInt(httpErrorType, 10);
|
|
1250
|
+
if (!isNaN(parsed) && parsed >= 400 && parsed <= 599) {
|
|
1251
|
+
extra[ATTR.HTTP_STATUS_CODE] = parsed;
|
|
1252
|
+
} else {
|
|
1253
|
+
extra[ATTR.HTTP_STATUS_CODE] = 500;
|
|
1254
|
+
}
|
|
1255
|
+
} else {
|
|
1256
|
+
extra[ATTR.HTTP_STATUS_CODE] = 500;
|
|
1257
|
+
}
|
|
1258
|
+
if (this.verbose) {
|
|
1259
|
+
sdkLog(
|
|
1260
|
+
"info",
|
|
1261
|
+
`[glasstrace] enrichSpan "${name}": inferred status_code=${extra[ATTR.HTTP_STATUS_CODE]} (was ${statusCode}), error.type=${attrs["error.type"]}`
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (span.startTime && span.endTime) {
|
|
1267
|
+
const [startSec, startNano] = span.startTime;
|
|
1268
|
+
const [endSec, endNano] = span.endTime;
|
|
1269
|
+
const durationMs = (endSec - startSec) * 1e3 + (endNano - startNano) / 1e6;
|
|
1270
|
+
if (durationMs >= 0) {
|
|
1271
|
+
extra[ATTR.HTTP_DURATION_MS] = durationMs;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
const eventDetails = statusNotExplicitlyOK ? getExceptionEventDetails(span) : { type: void 0, message: void 0, stacktrace: void 0 };
|
|
1275
|
+
let errorSource;
|
|
1276
|
+
const attrMessage = attrs["exception.message"];
|
|
1277
|
+
if (eventDetails.message) {
|
|
1278
|
+
extra[ATTR.ERROR_MESSAGE] = eventDetails.message;
|
|
1279
|
+
errorSource = "otel_exception";
|
|
1280
|
+
} else if (typeof attrMessage === "string") {
|
|
1281
|
+
extra[ATTR.ERROR_MESSAGE] = attrMessage;
|
|
1282
|
+
errorSource = "otel_event";
|
|
1283
|
+
}
|
|
1284
|
+
const attrType = attrs["exception.type"];
|
|
1285
|
+
if (eventDetails.type) {
|
|
1286
|
+
extra[ATTR.ERROR_CODE] = eventDetails.type;
|
|
1287
|
+
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(eventDetails.type);
|
|
1288
|
+
errorSource = errorSource ?? "otel_exception";
|
|
1289
|
+
} else if (typeof attrType === "string") {
|
|
1290
|
+
extra[ATTR.ERROR_CODE] = attrType;
|
|
1291
|
+
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(attrType);
|
|
1292
|
+
errorSource = errorSource ?? "otel_event";
|
|
1293
|
+
}
|
|
1294
|
+
if (statusNotExplicitlyOK) {
|
|
1295
|
+
const rawStack = eventDetails.stacktrace ?? (typeof attrs["exception.stacktrace"] === "string" ? attrs["exception.stacktrace"] : void 0);
|
|
1296
|
+
if (rawStack) {
|
|
1297
|
+
const prepared = prepareStack(rawStack);
|
|
1298
|
+
if (prepared !== null) {
|
|
1299
|
+
extra[ATTR.ERROR_STACK] = prepared.stack;
|
|
1300
|
+
extra[ATTR.ERROR_STACK_TRUNCATED] = prepared.truncated;
|
|
1301
|
+
extra[ATTR.ERROR_STACK_REDACTED] = prepared.redacted;
|
|
1302
|
+
errorSource = errorSource ?? (eventDetails.stacktrace ? "otel_exception" : "otel_event");
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const routeIsFallback = route === "/_error" || route === "/_not-found" || route === "/_404" || route === "/_500";
|
|
1307
|
+
if (routeIsFallback && rawHttpUrl) {
|
|
1308
|
+
const originalPath = extractPathOnly(rawHttpUrl);
|
|
1309
|
+
const normOriginal = stripTrailingSlash(originalPath);
|
|
1310
|
+
const normRoute = stripTrailingSlash(route);
|
|
1311
|
+
if (normOriginal && normOriginal !== normRoute) {
|
|
1312
|
+
extra[ATTR.ERROR_ORIGINAL_PATH] = normOriginal;
|
|
1313
|
+
extra[ATTR.ERROR_FALLBACK_ROUTE] = route;
|
|
1314
|
+
extra[ATTR.ERROR_FRAMEWORK_KIND] = "fallback";
|
|
1315
|
+
errorSource = errorSource ?? "framework_fallback";
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
if (errorSource !== void 0) {
|
|
1319
|
+
extra[ATTR.ERROR_SOURCE] = errorSource;
|
|
1320
|
+
}
|
|
1321
|
+
if (this.verbose && (extra[ATTR.ERROR_MESSAGE] || extra[ATTR.ERROR_CODE])) {
|
|
1322
|
+
const msgSource = eventDetails.message ? "event" : typeof attrMessage === "string" ? "attrs" : "none";
|
|
1323
|
+
const typeSource = eventDetails.type ? "event" : typeof attrType === "string" ? "attrs" : "none";
|
|
1324
|
+
sdkLog(
|
|
1325
|
+
"info",
|
|
1326
|
+
`[glasstrace] enrichSpan "${name}": error.message source=${msgSource}, error.code source=${typeSource}`
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
const errorField = attrs["error.field"];
|
|
1330
|
+
if (typeof errorField === "string") {
|
|
1331
|
+
extra[ATTR.ERROR_FIELD] = errorField;
|
|
1332
|
+
}
|
|
1333
|
+
if (this.getConfig().errorResponseBodies) {
|
|
1334
|
+
const responseBody = attrs["glasstrace.internal.response_body"];
|
|
1335
|
+
if (typeof responseBody === "string") {
|
|
1336
|
+
const enrichedStatus = extra[ATTR.HTTP_STATUS_CODE];
|
|
1337
|
+
const effectiveStatus = typeof enrichedStatus === "number" ? enrichedStatus : statusCode;
|
|
1338
|
+
if (isHttpErrorStatus(effectiveStatus)) {
|
|
1339
|
+
const prepared = prepareErrorResponseBody(responseBody);
|
|
1340
|
+
if (prepared !== null) {
|
|
1341
|
+
extra[ATTR.ERROR_RESPONSE_BODY] = prepared;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
const spanAny = span;
|
|
1347
|
+
const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
|
|
1348
|
+
const ormProvider = deriveOrmProvider(instrumentationName);
|
|
1349
|
+
if (ormProvider) {
|
|
1350
|
+
extra[ATTR.ORM_PROVIDER] = ormProvider;
|
|
1351
|
+
const table = attrs["db.sql.table"];
|
|
1352
|
+
const prismaModel = attrs["db.prisma.model"];
|
|
1353
|
+
const model = typeof table === "string" ? table : typeof prismaModel === "string" ? prismaModel : void 0;
|
|
1354
|
+
if (model) {
|
|
1355
|
+
extra[ATTR.ORM_MODEL] = model;
|
|
1356
|
+
}
|
|
1357
|
+
const operation = attrs["db.operation"];
|
|
1358
|
+
if (typeof operation === "string") {
|
|
1359
|
+
extra[ATTR.ORM_OPERATION] = operation;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
const httpUrl = attrs["http.url"];
|
|
1363
|
+
const fullUrl = attrs["url.full"];
|
|
1364
|
+
const url = typeof httpUrl === "string" ? httpUrl : typeof fullUrl === "string" ? fullUrl : void 0;
|
|
1365
|
+
if (url && span.kind === SpanKind.CLIENT) {
|
|
1366
|
+
extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
|
|
1367
|
+
}
|
|
1368
|
+
return createEnrichedSpan(span, extra);
|
|
1369
|
+
} catch {
|
|
1370
|
+
return span;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Lazily creates the delegate OTLP exporter once the API key is resolved.
|
|
1375
|
+
* Recreates the delegate if the key has changed (e.g., after key rotation)
|
|
1376
|
+
* so the Authorization header stays current.
|
|
1377
|
+
*/
|
|
1378
|
+
ensureDelegate() {
|
|
1379
|
+
if (!this.createDelegateFn) return null;
|
|
1380
|
+
const currentKey = this.getApiKey();
|
|
1381
|
+
if (currentKey === API_KEY_PENDING) return null;
|
|
1382
|
+
if (this.delegate && this.delegateKey === currentKey) {
|
|
1383
|
+
return this.delegate;
|
|
1384
|
+
}
|
|
1385
|
+
if (this.delegate) {
|
|
1386
|
+
void this.delegate.shutdown?.().catch(() => {
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
this.delegate = this.createDelegateFn(this.endpointUrl, {
|
|
1390
|
+
Authorization: `Bearer ${currentKey}`
|
|
1391
|
+
});
|
|
1392
|
+
this.delegateKey = currentKey;
|
|
1393
|
+
return this.delegate;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Buffers raw (unenriched) spans while the API key is pending.
|
|
1397
|
+
* Evicts oldest batches if the buffer exceeds MAX_PENDING_SPANS.
|
|
1398
|
+
* Re-checks the key after buffering to close the race window where
|
|
1399
|
+
* the key resolves between the caller's check and this buffer call.
|
|
1400
|
+
*/
|
|
1401
|
+
bufferSpans(spans, resultCallback) {
|
|
1402
|
+
this.pendingBatches.push({ spans, resultCallback });
|
|
1403
|
+
this.pendingSpanCount += spans.length;
|
|
1404
|
+
while (this.pendingSpanCount > MAX_PENDING_SPANS && this.pendingBatches.length > 1) {
|
|
1405
|
+
const evicted = this.pendingBatches.shift();
|
|
1406
|
+
this.pendingSpanCount -= evicted.spans.length;
|
|
1407
|
+
recordSpansDropped(evicted.spans.length);
|
|
1408
|
+
evicted.resultCallback({ code: 0 });
|
|
1409
|
+
if (!this.overflowLogged) {
|
|
1410
|
+
this.overflowLogged = true;
|
|
1411
|
+
console.warn(
|
|
1412
|
+
"[glasstrace] Pending span buffer overflow \u2014 oldest spans evicted. This usually means the API key is taking too long to resolve."
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
if (this.getApiKey() !== API_KEY_PENDING) {
|
|
1417
|
+
this.flushPending();
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Flushes all buffered spans through the delegate exporter.
|
|
1422
|
+
* Enriches spans at flush time (not buffer time) so that session IDs
|
|
1423
|
+
* are computed with the resolved API key instead of the "pending" sentinel.
|
|
1424
|
+
*
|
|
1425
|
+
* Honors the circuit breaker symmetrically with {@link export}: if the
|
|
1426
|
+
* breaker is OPEN at flush time, every buffered batch is dropped via
|
|
1427
|
+
* `recordSpansDropped` and its callback completed with `{ code: 0 }`,
|
|
1428
|
+
* preserving the bounded-memory contract during outages.
|
|
1429
|
+
*/
|
|
1430
|
+
flushPending() {
|
|
1431
|
+
if (this.pendingBatches.length === 0) return;
|
|
1432
|
+
const exporter = this.ensureDelegate();
|
|
1433
|
+
if (!exporter) {
|
|
1434
|
+
let discardedCount = 0;
|
|
1435
|
+
for (const batch of this.pendingBatches) {
|
|
1436
|
+
discardedCount += batch.spans.length;
|
|
1437
|
+
batch.resultCallback({ code: 0 });
|
|
1438
|
+
}
|
|
1439
|
+
recordSpansDropped(discardedCount);
|
|
1440
|
+
this.pendingBatches = [];
|
|
1441
|
+
this.pendingSpanCount = 0;
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
const breaker = this.getCircuitBreaker();
|
|
1445
|
+
const batches = this.pendingBatches;
|
|
1446
|
+
this.pendingBatches = [];
|
|
1447
|
+
this.pendingSpanCount = 0;
|
|
1448
|
+
for (const batch of batches) {
|
|
1449
|
+
if (!breaker.shouldExport()) {
|
|
1450
|
+
breaker.onSpansDropped(batch.spans.length);
|
|
1451
|
+
batch.resultCallback({ code: 0 });
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
const enriched = batch.spans.map((span) => this.enrichSpan(span));
|
|
1455
|
+
const generationAtIssue = breaker.getGeneration();
|
|
1456
|
+
exporter.export(enriched, (result) => {
|
|
1457
|
+
if (result.code !== 0) {
|
|
1458
|
+
sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
|
|
1459
|
+
}
|
|
1460
|
+
if (breaker.getGeneration() !== generationAtIssue) {
|
|
1461
|
+
batch.resultCallback(result);
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
if (result.code === 0) {
|
|
1465
|
+
breaker.recordSuccess();
|
|
1466
|
+
} else {
|
|
1467
|
+
breaker.recordFailure({ error: result.error });
|
|
1468
|
+
}
|
|
1469
|
+
batch.resultCallback(result);
|
|
1470
|
+
});
|
|
1471
|
+
recordSpansExported(enriched.length);
|
|
1472
|
+
}
|
|
1077
1473
|
}
|
|
1078
|
-
|
|
1474
|
+
};
|
|
1475
|
+
function createEnrichedSpan(span, extra) {
|
|
1476
|
+
const enrichedAttributes = { ...span.attributes, ...extra };
|
|
1477
|
+
return Object.create(span, {
|
|
1478
|
+
attributes: {
|
|
1479
|
+
value: enrichedAttributes,
|
|
1480
|
+
enumerable: true
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1079
1483
|
}
|
|
1080
|
-
function
|
|
1081
|
-
return
|
|
1484
|
+
function hasExceptionEvent(span) {
|
|
1485
|
+
return span.events?.some((e) => e.name === "exception") ?? false;
|
|
1082
1486
|
}
|
|
1083
|
-
function
|
|
1487
|
+
function getExceptionEventDetails(span) {
|
|
1488
|
+
const event = span.events?.find((e) => e.name === "exception");
|
|
1489
|
+
if (!event?.attributes) {
|
|
1490
|
+
return { type: void 0, message: void 0, stacktrace: void 0 };
|
|
1491
|
+
}
|
|
1492
|
+
const type = event.attributes["exception.type"];
|
|
1493
|
+
const message = event.attributes["exception.message"];
|
|
1494
|
+
const stacktrace = event.attributes["exception.stacktrace"];
|
|
1084
1495
|
return {
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1496
|
+
type: typeof type === "string" ? type : void 0,
|
|
1497
|
+
message: typeof message === "string" ? message : void 0,
|
|
1498
|
+
stacktrace: typeof stacktrace === "string" ? stacktrace : void 0
|
|
1088
1499
|
};
|
|
1089
1500
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1501
|
+
function extractLeadingPath(raw) {
|
|
1502
|
+
if (!raw) return void 0;
|
|
1503
|
+
const trimmed = raw.trim();
|
|
1504
|
+
if (trimmed.length === 0) return void 0;
|
|
1505
|
+
if (trimmed.startsWith("/")) {
|
|
1506
|
+
const firstSpace = trimmed.indexOf(" ");
|
|
1507
|
+
return firstSpace === -1 ? trimmed : trimmed.slice(0, firstSpace);
|
|
1508
|
+
}
|
|
1509
|
+
for (const token of trimmed.split(/\s+/)) {
|
|
1510
|
+
if (token.startsWith("/")) {
|
|
1511
|
+
return token;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return void 0;
|
|
1095
1515
|
}
|
|
1096
|
-
function
|
|
1097
|
-
|
|
1516
|
+
function stripTrailingSlash(path3) {
|
|
1517
|
+
if (!path3) return path3;
|
|
1518
|
+
if (path3 === "/") return path3;
|
|
1519
|
+
return path3.endsWith("/") ? path3.slice(0, -1) : path3;
|
|
1098
1520
|
}
|
|
1099
|
-
function
|
|
1100
|
-
|
|
1101
|
-
|
|
1521
|
+
function extractPathOnly(raw) {
|
|
1522
|
+
if (!raw) return void 0;
|
|
1523
|
+
const trimmed = raw.trim();
|
|
1524
|
+
if (trimmed.length === 0) return void 0;
|
|
1525
|
+
const isAbsoluteUrl = /^https?:\/\//i.test(trimmed);
|
|
1526
|
+
const isProtocolRelative = trimmed.startsWith("//");
|
|
1527
|
+
if (isAbsoluteUrl || isProtocolRelative) {
|
|
1102
1528
|
try {
|
|
1103
|
-
const
|
|
1104
|
-
if (
|
|
1105
|
-
|
|
1106
|
-
_logger?.(
|
|
1107
|
-
"error",
|
|
1108
|
-
`[glasstrace] Async error in lifecycle event listener for "${event}": ${err instanceof Error ? err.message : String(err)}`
|
|
1109
|
-
);
|
|
1110
|
-
});
|
|
1529
|
+
const parsed = new URL(trimmed, "http://_/");
|
|
1530
|
+
if (parsed.pathname && parsed.pathname.startsWith("/")) {
|
|
1531
|
+
return parsed.pathname;
|
|
1111
1532
|
}
|
|
1112
|
-
} catch
|
|
1113
|
-
_logger?.(
|
|
1114
|
-
"error",
|
|
1115
|
-
`[glasstrace] Error in lifecycle event listener for "${event}": ${err instanceof Error ? err.message : String(err)}`
|
|
1116
|
-
);
|
|
1533
|
+
} catch {
|
|
1117
1534
|
}
|
|
1118
1535
|
}
|
|
1536
|
+
if (trimmed.startsWith("/")) {
|
|
1537
|
+
const queryIdx = trimmed.indexOf("?");
|
|
1538
|
+
const fragIdx = trimmed.indexOf("#");
|
|
1539
|
+
let cut = trimmed.length;
|
|
1540
|
+
if (queryIdx >= 0) cut = Math.min(cut, queryIdx);
|
|
1541
|
+
if (fragIdx >= 0) cut = Math.min(cut, fragIdx);
|
|
1542
|
+
return trimmed.slice(0, cut);
|
|
1543
|
+
}
|
|
1544
|
+
return void 0;
|
|
1119
1545
|
}
|
|
1120
|
-
function
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
if (isReady()) {
|
|
1125
|
-
return Promise.resolve();
|
|
1546
|
+
function deriveOrmProvider(instrumentationName) {
|
|
1547
|
+
const lower = instrumentationName.toLowerCase();
|
|
1548
|
+
if (lower.includes("prisma")) {
|
|
1549
|
+
return "prisma";
|
|
1126
1550
|
}
|
|
1127
|
-
if (
|
|
1128
|
-
return
|
|
1551
|
+
if (lower.includes("drizzle")) {
|
|
1552
|
+
return "drizzle";
|
|
1129
1553
|
}
|
|
1130
|
-
return
|
|
1131
|
-
let settled = false;
|
|
1132
|
-
const listener = ({ to }) => {
|
|
1133
|
-
if (settled) return;
|
|
1134
|
-
if (to === CoreState.ACTIVE || to === CoreState.ACTIVE_DEGRADED) {
|
|
1135
|
-
settled = true;
|
|
1136
|
-
offLifecycleEvent("core:state_changed", listener);
|
|
1137
|
-
resolve2();
|
|
1138
|
-
} else if (to === CoreState.PRODUCTION_DISABLED || to === CoreState.REGISTRATION_FAILED || to === CoreState.SHUTTING_DOWN || to === CoreState.SHUTDOWN) {
|
|
1139
|
-
settled = true;
|
|
1140
|
-
offLifecycleEvent("core:state_changed", listener);
|
|
1141
|
-
reject(new Error(`SDK reached terminal state: ${to}`));
|
|
1142
|
-
}
|
|
1143
|
-
};
|
|
1144
|
-
onLifecycleEvent("core:state_changed", listener);
|
|
1145
|
-
if (timeoutMs > 0) {
|
|
1146
|
-
const timer = setTimeout(() => {
|
|
1147
|
-
if (settled) return;
|
|
1148
|
-
settled = true;
|
|
1149
|
-
offLifecycleEvent("core:state_changed", listener);
|
|
1150
|
-
reject(new Error(`waitForReady timed out after ${timeoutMs}ms (state: ${_coreState})`));
|
|
1151
|
-
}, timeoutMs);
|
|
1152
|
-
if (typeof timer === "object" && "unref" in timer) {
|
|
1153
|
-
timer.unref();
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
});
|
|
1554
|
+
return null;
|
|
1157
1555
|
}
|
|
1158
|
-
function
|
|
1159
|
-
|
|
1160
|
-
if (
|
|
1161
|
-
|
|
1162
|
-
} else if (_authState === AuthState.CLAIMING || _authState === AuthState.CLAIMED) {
|
|
1163
|
-
mode = "claiming";
|
|
1164
|
-
} else if (_authState === AuthState.AUTHENTICATED) {
|
|
1165
|
-
mode = "authenticated";
|
|
1166
|
-
} else {
|
|
1167
|
-
mode = "anonymous";
|
|
1556
|
+
function deriveErrorCategory(errorType) {
|
|
1557
|
+
const lower = errorType.toLowerCase();
|
|
1558
|
+
if (lower.includes("validation") || lower.includes("zod")) {
|
|
1559
|
+
return "validation";
|
|
1168
1560
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
tracing = "not-configured";
|
|
1172
|
-
} else if (_coreState === CoreState.ACTIVE_DEGRADED) {
|
|
1173
|
-
tracing = "degraded";
|
|
1174
|
-
} else if (_otelState === OtelState.AUTO_ATTACHED || _otelState === OtelState.PROCESSOR_PRESENT) {
|
|
1175
|
-
tracing = "coexistence";
|
|
1176
|
-
} else {
|
|
1177
|
-
tracing = "active";
|
|
1561
|
+
if (lower.includes("network") || lower.includes("econnrefused") || lower.includes("fetch") || lower.includes("timeout")) {
|
|
1562
|
+
return "network";
|
|
1178
1563
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
mode,
|
|
1182
|
-
tracing
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
var _shutdownHooks = [];
|
|
1186
|
-
var _signalHandlersRegistered = false;
|
|
1187
|
-
var _signalHandler = null;
|
|
1188
|
-
var _beforeExitRegistered = false;
|
|
1189
|
-
var _beforeExitHandler = null;
|
|
1190
|
-
var _shutdownExecuted = false;
|
|
1191
|
-
function registerShutdownHook(hook) {
|
|
1192
|
-
_shutdownHooks.push(hook);
|
|
1193
|
-
_shutdownHooks.sort((a, b) => a.priority - b.priority);
|
|
1194
|
-
}
|
|
1195
|
-
async function executeShutdown(timeoutMs = 5e3) {
|
|
1196
|
-
if (_shutdownExecuted) return;
|
|
1197
|
-
_shutdownExecuted = true;
|
|
1198
|
-
setCoreState(CoreState.SHUTTING_DOWN);
|
|
1199
|
-
for (const hook of _shutdownHooks) {
|
|
1200
|
-
try {
|
|
1201
|
-
const hookPromise = hook.fn();
|
|
1202
|
-
hookPromise.catch(() => {
|
|
1203
|
-
});
|
|
1204
|
-
await Promise.race([
|
|
1205
|
-
hookPromise,
|
|
1206
|
-
new Promise((_, reject) => {
|
|
1207
|
-
const timer = setTimeout(() => reject(new Error(`Shutdown hook "${hook.name}" timed out`)), timeoutMs);
|
|
1208
|
-
if (typeof timer === "object" && "unref" in timer) {
|
|
1209
|
-
timer.unref();
|
|
1210
|
-
}
|
|
1211
|
-
})
|
|
1212
|
-
]);
|
|
1213
|
-
} catch (err) {
|
|
1214
|
-
_logger?.(
|
|
1215
|
-
"warn",
|
|
1216
|
-
`[glasstrace] Shutdown hook "${hook.name}" failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1217
|
-
);
|
|
1218
|
-
}
|
|
1564
|
+
if (lower.includes("auth") || lower.includes("unauthorized") || lower.includes("forbidden")) {
|
|
1565
|
+
return "auth";
|
|
1219
1566
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
if (typeof process === "undefined" || typeof process.once !== "function") return;
|
|
1225
|
-
_signalHandlersRegistered = true;
|
|
1226
|
-
const otherSigtermListeners = process.listenerCount("SIGTERM");
|
|
1227
|
-
const otherSigintListeners = process.listenerCount("SIGINT");
|
|
1228
|
-
const handler = (signal) => {
|
|
1229
|
-
void executeShutdown().finally(() => {
|
|
1230
|
-
if (_signalHandler) {
|
|
1231
|
-
process.removeListener("SIGTERM", _signalHandler);
|
|
1232
|
-
process.removeListener("SIGINT", _signalHandler);
|
|
1233
|
-
}
|
|
1234
|
-
const otherListeners = signal === "SIGTERM" ? otherSigtermListeners : otherSigintListeners;
|
|
1235
|
-
const otherProviderOwnsSignal = getCoexistenceState() === "coexisting" && otherListeners > 0;
|
|
1236
|
-
if (!otherProviderOwnsSignal) {
|
|
1237
|
-
process.kill(process.pid, signal);
|
|
1238
|
-
}
|
|
1239
|
-
});
|
|
1240
|
-
};
|
|
1241
|
-
_signalHandler = handler;
|
|
1242
|
-
process.once("SIGTERM", handler);
|
|
1243
|
-
process.once("SIGINT", handler);
|
|
1244
|
-
}
|
|
1245
|
-
function registerBeforeExitTrigger() {
|
|
1246
|
-
if (_beforeExitRegistered) return;
|
|
1247
|
-
if (typeof process === "undefined" || typeof process.once !== "function") return;
|
|
1248
|
-
_beforeExitRegistered = true;
|
|
1249
|
-
const handler = () => {
|
|
1250
|
-
void executeShutdown();
|
|
1251
|
-
};
|
|
1252
|
-
_beforeExitHandler = handler;
|
|
1253
|
-
process.once("beforeExit", handler);
|
|
1567
|
+
if (lower.includes("notfound") || lower.includes("not_found")) {
|
|
1568
|
+
return "not-found";
|
|
1569
|
+
}
|
|
1570
|
+
return "internal";
|
|
1254
1571
|
}
|
|
1255
1572
|
|
|
1256
1573
|
// ../../node_modules/@opentelemetry/core/build/esm/trace/suppress-tracing.js
|
|
@@ -3852,6 +4169,31 @@ var OTLPTraceExporter = class extends OTLPExporterBase {
|
|
|
3852
4169
|
}
|
|
3853
4170
|
};
|
|
3854
4171
|
|
|
4172
|
+
// src/api-key-hash.ts
|
|
4173
|
+
var cryptoModule;
|
|
4174
|
+
function loadCrypto() {
|
|
4175
|
+
if (cryptoModule !== void 0) return cryptoModule;
|
|
4176
|
+
try {
|
|
4177
|
+
cryptoModule = __require("node:crypto");
|
|
4178
|
+
} catch {
|
|
4179
|
+
cryptoModule = null;
|
|
4180
|
+
}
|
|
4181
|
+
return cryptoModule;
|
|
4182
|
+
}
|
|
4183
|
+
function hashApiKey(key) {
|
|
4184
|
+
if (typeof key !== "string" || key.length === 0) return "";
|
|
4185
|
+
const crypto = loadCrypto();
|
|
4186
|
+
if (crypto !== null) {
|
|
4187
|
+
return crypto.createHash("sha256").update(key, "utf8").digest("hex").slice(0, 32);
|
|
4188
|
+
}
|
|
4189
|
+
let hash = 2166136261;
|
|
4190
|
+
for (let i = 0; i < key.length; i++) {
|
|
4191
|
+
hash ^= key.charCodeAt(i);
|
|
4192
|
+
hash = hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24)) >>> 0;
|
|
4193
|
+
}
|
|
4194
|
+
return `fnv:${hash.toString(16).padStart(8, "0")}`;
|
|
4195
|
+
}
|
|
4196
|
+
|
|
3855
4197
|
// src/proxy-detection.ts
|
|
3856
4198
|
function isProxyTracerProvider(value) {
|
|
3857
4199
|
if (value === null || value === void 0 || typeof value !== "object") {
|
|
@@ -3878,8 +4220,15 @@ var resolvedApiKey = API_KEY_PENDING;
|
|
|
3878
4220
|
var activeExporter = null;
|
|
3879
4221
|
var additionalExporters = [];
|
|
3880
4222
|
var injectedProcessor = null;
|
|
4223
|
+
var resolvedApiKeyHash = "";
|
|
3881
4224
|
function setResolvedApiKey(key) {
|
|
4225
|
+
const newHash = hashApiKey(key);
|
|
4226
|
+
const isRotation = resolvedApiKeyHash !== "" && resolvedApiKeyHash !== newHash;
|
|
3882
4227
|
resolvedApiKey = key;
|
|
4228
|
+
resolvedApiKeyHash = newHash;
|
|
4229
|
+
if (isRotation) {
|
|
4230
|
+
peekExportCircuitBreaker()?.resetForKeyRotation();
|
|
4231
|
+
}
|
|
3883
4232
|
}
|
|
3884
4233
|
function getResolvedApiKey() {
|
|
3885
4234
|
return resolvedApiKey;
|
|
@@ -3971,10 +4320,7 @@ async function runCoexistencePath(existingProvider, config) {
|
|
|
3971
4320
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3972
4321
|
providerClass: readProviderClass(existingProvider)
|
|
3973
4322
|
});
|
|
3974
|
-
|
|
3975
|
-
if (coreState === CoreState.ACTIVE || coreState === CoreState.KEY_RESOLVED) {
|
|
3976
|
-
setCoreState(CoreState.ACTIVE_DEGRADED);
|
|
3977
|
-
}
|
|
4323
|
+
pushDegradationSource("otel-coexistence-failed");
|
|
3978
4324
|
}
|
|
3979
4325
|
function readProviderClass(tracerProvider) {
|
|
3980
4326
|
try {
|
|
@@ -4404,6 +4750,21 @@ function startRuntimeStateWriter(options) {
|
|
|
4404
4750
|
_lastError = { ...payload };
|
|
4405
4751
|
debouncedWrite();
|
|
4406
4752
|
});
|
|
4753
|
+
onLifecycleEvent("otel:circuit_opened", (payload) => {
|
|
4754
|
+
_lastError = {
|
|
4755
|
+
category: "export-circuit-open",
|
|
4756
|
+
message: payload.message,
|
|
4757
|
+
timestamp: payload.timestamp,
|
|
4758
|
+
exportCircuitCategory: payload.category
|
|
4759
|
+
};
|
|
4760
|
+
debouncedWrite();
|
|
4761
|
+
});
|
|
4762
|
+
onLifecycleEvent("otel:circuit_closed", () => {
|
|
4763
|
+
if (_lastError?.category === "export-circuit-open") {
|
|
4764
|
+
_lastError = void 0;
|
|
4765
|
+
debouncedWrite();
|
|
4766
|
+
}
|
|
4767
|
+
});
|
|
4407
4768
|
onLifecycleEvent("auth:key_resolved", () => debouncedWrite());
|
|
4408
4769
|
onLifecycleEvent("auth:claim_started", () => debouncedWrite());
|
|
4409
4770
|
onLifecycleEvent("auth:claim_completed", () => debouncedWrite());
|
|
@@ -4616,11 +4977,11 @@ function registerGlasstrace(options) {
|
|
|
4616
4977
|
setCoreState(CoreState.REGISTERING);
|
|
4617
4978
|
maybeWarnStaleAgentInstructions({
|
|
4618
4979
|
projectRoot: process.cwd(),
|
|
4619
|
-
sdkVersion: "1.
|
|
4980
|
+
sdkVersion: "1.9.0"
|
|
4620
4981
|
});
|
|
4621
4982
|
startRuntimeStateWriter({
|
|
4622
4983
|
projectRoot: process.cwd(),
|
|
4623
|
-
sdkVersion: "1.
|
|
4984
|
+
sdkVersion: "1.9.0"
|
|
4624
4985
|
});
|
|
4625
4986
|
const config = resolveConfig(options);
|
|
4626
4987
|
if (config.verbose) {
|
|
@@ -4787,8 +5148,8 @@ async function backgroundInit(config, anonKeyForInit, generation) {
|
|
|
4787
5148
|
if (config.verbose) {
|
|
4788
5149
|
console.info("[glasstrace] Background init firing.");
|
|
4789
5150
|
}
|
|
4790
|
-
const healthReport = collectHealthReport("1.
|
|
4791
|
-
const initResult = await performInit(config, anonKeyForInit, "1.
|
|
5151
|
+
const healthReport = collectHealthReport("1.9.0");
|
|
5152
|
+
const initResult = await performInit(config, anonKeyForInit, "1.9.0", healthReport);
|
|
4792
5153
|
if (generation !== registrationGeneration) return;
|
|
4793
5154
|
const currentState = getCoreState();
|
|
4794
5155
|
if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
|
|
@@ -4811,7 +5172,7 @@ async function backgroundInit(config, anonKeyForInit, generation) {
|
|
|
4811
5172
|
}
|
|
4812
5173
|
maybeInstallConsoleCapture();
|
|
4813
5174
|
if (didLastInitSucceed()) {
|
|
4814
|
-
startHeartbeat(config, anonKeyForInit, "1.
|
|
5175
|
+
startHeartbeat(config, anonKeyForInit, "1.9.0", generation, (newApiKey, accountId) => {
|
|
4815
5176
|
setAuthState(AuthState.CLAIMING);
|
|
4816
5177
|
emitLifecycleEvent("auth:claim_started", { accountId });
|
|
4817
5178
|
setResolvedApiKey(newApiKey);
|
|
@@ -5108,7 +5469,7 @@ async function handleSourceMapUpload(distDir) {
|
|
|
5108
5469
|
);
|
|
5109
5470
|
return;
|
|
5110
5471
|
}
|
|
5111
|
-
const { discoverSourceMapFiles, computeBuildHash, uploadSourceMaps } = await import("./source-map-uploader-
|
|
5472
|
+
const { discoverSourceMapFiles, computeBuildHash, uploadSourceMaps } = await import("./source-map-uploader-MMJ2WCL4.js");
|
|
5112
5473
|
const files = await discoverSourceMapFiles(distDir);
|
|
5113
5474
|
if (files.length === 0) {
|
|
5114
5475
|
console.info("[glasstrace] No source map files found. Skipping upload.");
|
|
@@ -5201,14 +5562,14 @@ export {
|
|
|
5201
5562
|
getDateString,
|
|
5202
5563
|
SessionManager,
|
|
5203
5564
|
classifyFetchTarget,
|
|
5204
|
-
GlasstraceExporter,
|
|
5205
5565
|
isReady,
|
|
5206
5566
|
waitForReady,
|
|
5207
5567
|
getStatus,
|
|
5568
|
+
GlasstraceExporter,
|
|
5208
5569
|
createGlasstraceSpanProcessor,
|
|
5209
5570
|
registerGlasstrace,
|
|
5210
5571
|
getDiscoveryHandler,
|
|
5211
5572
|
withGlasstraceConfig,
|
|
5212
5573
|
captureError
|
|
5213
5574
|
};
|
|
5214
|
-
//# sourceMappingURL=chunk-
|
|
5575
|
+
//# sourceMappingURL=chunk-XEPC4NFL.js.map
|