@glasstrace/sdk 0.9.1 → 0.11.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/chunk-2JUH3VGJ.js +77 -0
- package/dist/chunk-2JUH3VGJ.js.map +1 -0
- package/dist/{chunk-PQWAKVQ5.js → chunk-D3JC3LAK.js} +36 -103
- package/dist/chunk-D3JC3LAK.js.map +1 -0
- package/dist/{chunk-Y6V7BTF3.js → chunk-M7RDFOFR.js} +2 -2
- package/dist/chunk-SLSWEQCC.js +417 -0
- package/dist/chunk-SLSWEQCC.js.map +1 -0
- package/dist/cli/init.cjs +58 -19
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +3 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs +3 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +2 -1
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/uninit.cjs +57 -18
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +34 -2
- package/dist/cli/uninit.d.ts +34 -2
- package/dist/cli/uninit.js +55 -18
- package/dist/cli/uninit.js.map +1 -1
- package/dist/index.cjs +15731 -15009
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +64 -408
- package/dist/index.js.map +1 -1
- package/dist/source-map-uploader-OFEM54UE.js +25 -0
- package/dist/source-map-uploader-OFEM54UE.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-PQWAKVQ5.js.map +0 -1
- /package/dist/{chunk-Y6V7BTF3.js.map → chunk-M7RDFOFR.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -314,6 +314,14 @@ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null,
|
|
|
314
314
|
* every span export batch).
|
|
315
315
|
*/
|
|
316
316
|
declare function getActiveConfig(): CaptureConfig;
|
|
317
|
+
/**
|
|
318
|
+
* Returns the `linkedAccountId` from the current in-memory init response,
|
|
319
|
+
* or `undefined` if no init response is available or no account is linked.
|
|
320
|
+
*
|
|
321
|
+
* Used by the discovery endpoint to determine whether `claimed: true`
|
|
322
|
+
* should be included in the response.
|
|
323
|
+
*/
|
|
324
|
+
declare function getLinkedAccountId(): string | undefined;
|
|
317
325
|
|
|
318
326
|
/**
|
|
319
327
|
* Lightweight SpanProcessor that delegates to a wrapped processor.
|
|
@@ -417,6 +425,17 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
417
425
|
private flushPending;
|
|
418
426
|
}
|
|
419
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Claim state returned by the `getClaimState` callback.
|
|
430
|
+
*
|
|
431
|
+
* - `claimed` — `true` when the anonymous key has been linked to an account.
|
|
432
|
+
* - `accountHint` — optional masked identifier (e.g. `"er***@example.com"`)
|
|
433
|
+
* for the browser extension to display to the user.
|
|
434
|
+
*/
|
|
435
|
+
interface ClaimState {
|
|
436
|
+
claimed: boolean;
|
|
437
|
+
accountHint?: string;
|
|
438
|
+
}
|
|
420
439
|
/**
|
|
421
440
|
* Creates a request handler for the `/__glasstrace/config` discovery endpoint.
|
|
422
441
|
*
|
|
@@ -424,10 +443,14 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
424
443
|
* If not, returns `null` (pass-through). If it matches, returns a `DiscoveryResponse`
|
|
425
444
|
* with the anonymous key and current session ID.
|
|
426
445
|
*
|
|
446
|
+
* When `getClaimState` returns a non-null value with `claimed: true`, the
|
|
447
|
+
* response includes `claimed` and (optionally) `accountHint` so the browser
|
|
448
|
+
* extension can prompt the user to sign in.
|
|
449
|
+
*
|
|
427
450
|
* The triple guard (anonymous + dev + active) is enforced by the caller,
|
|
428
451
|
* not by this module. If the handler is registered, it serves.
|
|
429
452
|
*/
|
|
430
|
-
declare function createDiscoveryHandler(getAnonKey: () => Promise<AnonApiKey | null>, getSessionId: () => SessionId): (request: Request) => Promise<Response | null>;
|
|
453
|
+
declare function createDiscoveryHandler(getAnonKey: () => Promise<AnonApiKey | null>, getSessionId: () => SessionId, getClaimState?: () => ClaimState | null): (request: Request) => Promise<Response | null>;
|
|
431
454
|
|
|
432
455
|
/**
|
|
433
456
|
* The primary SDK entry point called by developers in their `instrumentation.ts`.
|
|
@@ -588,4 +611,4 @@ declare function extractImports(fileContent: string): string[];
|
|
|
588
611
|
*/
|
|
589
612
|
declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
|
|
590
613
|
|
|
591
|
-
export { type AutoUploadOptions, type BlobUploader, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, deriveSessionId, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getOrCreateAnonKey, getOrigin, isAnonymousMode, isProductionDisabled, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, withGlasstraceConfig };
|
|
614
|
+
export { type AutoUploadOptions, type BlobUploader, type ClaimState, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, deriveSessionId, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, isAnonymousMode, isProductionDisabled, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, withGlasstraceConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -314,6 +314,14 @@ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null,
|
|
|
314
314
|
* every span export batch).
|
|
315
315
|
*/
|
|
316
316
|
declare function getActiveConfig(): CaptureConfig;
|
|
317
|
+
/**
|
|
318
|
+
* Returns the `linkedAccountId` from the current in-memory init response,
|
|
319
|
+
* or `undefined` if no init response is available or no account is linked.
|
|
320
|
+
*
|
|
321
|
+
* Used by the discovery endpoint to determine whether `claimed: true`
|
|
322
|
+
* should be included in the response.
|
|
323
|
+
*/
|
|
324
|
+
declare function getLinkedAccountId(): string | undefined;
|
|
317
325
|
|
|
318
326
|
/**
|
|
319
327
|
* Lightweight SpanProcessor that delegates to a wrapped processor.
|
|
@@ -417,6 +425,17 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
417
425
|
private flushPending;
|
|
418
426
|
}
|
|
419
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Claim state returned by the `getClaimState` callback.
|
|
430
|
+
*
|
|
431
|
+
* - `claimed` — `true` when the anonymous key has been linked to an account.
|
|
432
|
+
* - `accountHint` — optional masked identifier (e.g. `"er***@example.com"`)
|
|
433
|
+
* for the browser extension to display to the user.
|
|
434
|
+
*/
|
|
435
|
+
interface ClaimState {
|
|
436
|
+
claimed: boolean;
|
|
437
|
+
accountHint?: string;
|
|
438
|
+
}
|
|
420
439
|
/**
|
|
421
440
|
* Creates a request handler for the `/__glasstrace/config` discovery endpoint.
|
|
422
441
|
*
|
|
@@ -424,10 +443,14 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
424
443
|
* If not, returns `null` (pass-through). If it matches, returns a `DiscoveryResponse`
|
|
425
444
|
* with the anonymous key and current session ID.
|
|
426
445
|
*
|
|
446
|
+
* When `getClaimState` returns a non-null value with `claimed: true`, the
|
|
447
|
+
* response includes `claimed` and (optionally) `accountHint` so the browser
|
|
448
|
+
* extension can prompt the user to sign in.
|
|
449
|
+
*
|
|
427
450
|
* The triple guard (anonymous + dev + active) is enforced by the caller,
|
|
428
451
|
* not by this module. If the handler is registered, it serves.
|
|
429
452
|
*/
|
|
430
|
-
declare function createDiscoveryHandler(getAnonKey: () => Promise<AnonApiKey | null>, getSessionId: () => SessionId): (request: Request) => Promise<Response | null>;
|
|
453
|
+
declare function createDiscoveryHandler(getAnonKey: () => Promise<AnonApiKey | null>, getSessionId: () => SessionId, getClaimState?: () => ClaimState | null): (request: Request) => Promise<Response | null>;
|
|
431
454
|
|
|
432
455
|
/**
|
|
433
456
|
* The primary SDK entry point called by developers in their `instrumentation.ts`.
|
|
@@ -588,4 +611,4 @@ declare function extractImports(fileContent: string): string[];
|
|
|
588
611
|
*/
|
|
589
612
|
declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
|
|
590
613
|
|
|
591
|
-
export { type AutoUploadOptions, type BlobUploader, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, deriveSessionId, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getOrCreateAnonKey, getOrigin, isAnonymousMode, isProductionDisabled, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, withGlasstraceConfig };
|
|
614
|
+
export { type AutoUploadOptions, type BlobUploader, type ClaimState, type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, type InitClaimResult, PRESIGNED_THRESHOLD_BYTES, type ResolvedConfig, SdkError, SessionManager, type SourceMapEntry, buildImportGraph, captureError, classifyFetchTarget, collectSourceMaps, computeBuildHash, createDiscoveryHandler, deriveSessionId, discoverTestFiles, extractImports, getActiveConfig, getDateString, getDiscoveryHandler, getLinkedAccountId, getOrCreateAnonKey, getOrigin, isAnonymousMode, isProductionDisabled, loadCachedConfig, performInit, readAnonKey, readEnvVars, registerGlasstrace, resolveConfig, saveCachedConfig, sendInitRequest, uploadSourceMaps, uploadSourceMapsAuto, uploadSourceMapsPresigned, withGlasstraceConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PRESIGNED_THRESHOLD_BYTES,
|
|
3
|
+
collectSourceMaps,
|
|
4
|
+
computeBuildHash,
|
|
5
|
+
installConsoleCapture,
|
|
6
|
+
isAnonymousMode,
|
|
7
|
+
isProductionDisabled,
|
|
8
|
+
maybeShowMcpNudge,
|
|
9
|
+
readEnvVars,
|
|
10
|
+
resolveConfig,
|
|
11
|
+
uploadSourceMaps,
|
|
12
|
+
uploadSourceMapsAuto,
|
|
13
|
+
uploadSourceMapsPresigned
|
|
14
|
+
} from "./chunk-SLSWEQCC.js";
|
|
1
15
|
import {
|
|
2
16
|
buildImportGraph,
|
|
3
17
|
discoverTestFiles,
|
|
4
18
|
extractImports
|
|
5
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-M7RDFOFR.js";
|
|
20
|
+
import {
|
|
21
|
+
getOrCreateAnonKey,
|
|
22
|
+
readAnonKey
|
|
23
|
+
} from "./chunk-2JUH3VGJ.js";
|
|
6
24
|
import {
|
|
7
25
|
DEFAULT_CAPTURE_CONFIG,
|
|
8
26
|
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
9
|
-
PresignedUploadResponseSchema,
|
|
10
27
|
SdkCachedConfigSchema,
|
|
11
28
|
SdkInitResponseSchema,
|
|
12
|
-
SessionIdSchema
|
|
13
|
-
|
|
14
|
-
SourceMapUploadResponseSchema,
|
|
15
|
-
getOrCreateAnonKey,
|
|
16
|
-
readAnonKey
|
|
17
|
-
} from "./chunk-PQWAKVQ5.js";
|
|
29
|
+
SessionIdSchema
|
|
30
|
+
} from "./chunk-D3JC3LAK.js";
|
|
18
31
|
import {
|
|
19
32
|
INVALID_SPAN_CONTEXT,
|
|
20
33
|
SamplingDecision,
|
|
@@ -42,56 +55,6 @@ var SdkError = class extends Error {
|
|
|
42
55
|
}
|
|
43
56
|
};
|
|
44
57
|
|
|
45
|
-
// src/env-detection.ts
|
|
46
|
-
var DEFAULT_ENDPOINT = "https://api.glasstrace.dev";
|
|
47
|
-
function readEnvVars() {
|
|
48
|
-
return {
|
|
49
|
-
GLASSTRACE_API_KEY: process.env.GLASSTRACE_API_KEY?.trim() || void 0,
|
|
50
|
-
GLASSTRACE_FORCE_ENABLE: process.env.GLASSTRACE_FORCE_ENABLE,
|
|
51
|
-
GLASSTRACE_ENV: process.env.GLASSTRACE_ENV,
|
|
52
|
-
GLASSTRACE_COVERAGE_MAP: process.env.GLASSTRACE_COVERAGE_MAP,
|
|
53
|
-
NODE_ENV: process.env.NODE_ENV,
|
|
54
|
-
VERCEL_ENV: process.env.VERCEL_ENV
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function resolveConfig(options) {
|
|
58
|
-
const env = readEnvVars();
|
|
59
|
-
return {
|
|
60
|
-
apiKey: options?.apiKey ?? env.GLASSTRACE_API_KEY,
|
|
61
|
-
endpoint: options?.endpoint ?? DEFAULT_ENDPOINT,
|
|
62
|
-
forceEnable: options?.forceEnable ?? env.GLASSTRACE_FORCE_ENABLE === "true",
|
|
63
|
-
verbose: options?.verbose ?? false,
|
|
64
|
-
environment: env.GLASSTRACE_ENV,
|
|
65
|
-
coverageMapEnabled: env.GLASSTRACE_COVERAGE_MAP === "true",
|
|
66
|
-
nodeEnv: env.NODE_ENV,
|
|
67
|
-
vercelEnv: env.VERCEL_ENV
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function isProductionDisabled(config) {
|
|
71
|
-
if (config.forceEnable) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
if (config.nodeEnv === "production") {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
if (config.vercelEnv === "production") {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
function isAnonymousMode(config) {
|
|
83
|
-
if (config.apiKey === void 0) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
if (config.apiKey.trim() === "") {
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
if (config.apiKey.startsWith("gt_anon_")) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
58
|
// src/session.ts
|
|
96
59
|
import { createHash } from "crypto";
|
|
97
60
|
var FOUR_HOURS_MS = 4 * 60 * 60 * 1e3;
|
|
@@ -445,6 +408,12 @@ function getActiveConfig() {
|
|
|
445
408
|
}
|
|
446
409
|
return { ...DEFAULT_CAPTURE_CONFIG };
|
|
447
410
|
}
|
|
411
|
+
function getLinkedAccountId() {
|
|
412
|
+
return currentConfig?.linkedAccountId;
|
|
413
|
+
}
|
|
414
|
+
function getClaimResult() {
|
|
415
|
+
return currentConfig?.claimResult;
|
|
416
|
+
}
|
|
448
417
|
function _setCurrentConfig(config) {
|
|
449
418
|
currentConfig = config;
|
|
450
419
|
}
|
|
@@ -756,7 +725,7 @@ function buildCorsHeaders(origin) {
|
|
|
756
725
|
}
|
|
757
726
|
return headers;
|
|
758
727
|
}
|
|
759
|
-
function createDiscoveryHandler(getAnonKey, getSessionId) {
|
|
728
|
+
function createDiscoveryHandler(getAnonKey, getSessionId, getClaimState) {
|
|
760
729
|
return async (request) => {
|
|
761
730
|
let url;
|
|
762
731
|
try {
|
|
@@ -800,8 +769,16 @@ function createDiscoveryHandler(getAnonKey, getSessionId) {
|
|
|
800
769
|
);
|
|
801
770
|
}
|
|
802
771
|
const sessionId = getSessionId();
|
|
772
|
+
const responseBody = { key: anonKey, sessionId };
|
|
773
|
+
const claimState = getClaimState?.();
|
|
774
|
+
if (claimState?.claimed) {
|
|
775
|
+
responseBody.claimed = true;
|
|
776
|
+
if (claimState.accountHint) {
|
|
777
|
+
responseBody.accountHint = claimState.accountHint;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
803
780
|
return new Response(
|
|
804
|
-
JSON.stringify(
|
|
781
|
+
JSON.stringify(responseBody),
|
|
805
782
|
{
|
|
806
783
|
status: 200,
|
|
807
784
|
headers: corsHeaders
|
|
@@ -2230,7 +2207,7 @@ function appendRootPathToUrlIfNeeded(url) {
|
|
|
2230
2207
|
return void 0;
|
|
2231
2208
|
}
|
|
2232
2209
|
}
|
|
2233
|
-
function appendResourcePathToUrl(url,
|
|
2210
|
+
function appendResourcePathToUrl(url, path2) {
|
|
2234
2211
|
try {
|
|
2235
2212
|
new URL(url);
|
|
2236
2213
|
} catch {
|
|
@@ -2240,11 +2217,11 @@ function appendResourcePathToUrl(url, path3) {
|
|
|
2240
2217
|
if (!url.endsWith("/")) {
|
|
2241
2218
|
url = url + "/";
|
|
2242
2219
|
}
|
|
2243
|
-
url +=
|
|
2220
|
+
url += path2;
|
|
2244
2221
|
try {
|
|
2245
2222
|
new URL(url);
|
|
2246
2223
|
} catch {
|
|
2247
|
-
diag.warn(`Configuration: Provided URL appended with '${
|
|
2224
|
+
diag.warn(`Configuration: Provided URL appended with '${path2}' is not a valid URL, using 'undefined' instead of '${url}'`);
|
|
2248
2225
|
return void 0;
|
|
2249
2226
|
}
|
|
2250
2227
|
return url;
|
|
@@ -3488,112 +3465,6 @@ async function configureOtel(config, sessionManager) {
|
|
|
3488
3465
|
registerShutdownHooks(provider);
|
|
3489
3466
|
}
|
|
3490
3467
|
|
|
3491
|
-
// src/nudge/error-nudge.ts
|
|
3492
|
-
import { existsSync } from "fs";
|
|
3493
|
-
import { join as join2 } from "path";
|
|
3494
|
-
var hasFired = false;
|
|
3495
|
-
function sanitize(input) {
|
|
3496
|
-
return input.replace(/[\x00-\x1f\x7f]/g, "");
|
|
3497
|
-
}
|
|
3498
|
-
function maybeShowMcpNudge(errorSummary) {
|
|
3499
|
-
if (hasFired) {
|
|
3500
|
-
return;
|
|
3501
|
-
}
|
|
3502
|
-
const config = resolveConfig();
|
|
3503
|
-
if (isProductionDisabled(config)) {
|
|
3504
|
-
hasFired = true;
|
|
3505
|
-
return;
|
|
3506
|
-
}
|
|
3507
|
-
let markerExists = false;
|
|
3508
|
-
try {
|
|
3509
|
-
const markerPath = join2(process.cwd(), ".glasstrace", "mcp-connected");
|
|
3510
|
-
markerExists = existsSync(markerPath);
|
|
3511
|
-
} catch {
|
|
3512
|
-
markerExists = false;
|
|
3513
|
-
}
|
|
3514
|
-
if (markerExists) {
|
|
3515
|
-
hasFired = true;
|
|
3516
|
-
return;
|
|
3517
|
-
}
|
|
3518
|
-
hasFired = true;
|
|
3519
|
-
const safe = sanitize(errorSummary);
|
|
3520
|
-
process.stderr.write(
|
|
3521
|
-
`[glasstrace] Error captured: ${safe}
|
|
3522
|
-
Debug with AI: ask your agent "What's the latest Glasstrace error?"
|
|
3523
|
-
Not connected? Run: npx glasstrace mcp add
|
|
3524
|
-
`
|
|
3525
|
-
);
|
|
3526
|
-
}
|
|
3527
|
-
|
|
3528
|
-
// src/console-capture.ts
|
|
3529
|
-
var isGlasstraceLog = false;
|
|
3530
|
-
var originalError = null;
|
|
3531
|
-
var originalWarn = null;
|
|
3532
|
-
var installed = false;
|
|
3533
|
-
var otelApi = null;
|
|
3534
|
-
function formatArgs(args) {
|
|
3535
|
-
return args.map((arg) => {
|
|
3536
|
-
if (typeof arg === "string") return arg;
|
|
3537
|
-
if (arg instanceof Error) return arg.stack ?? arg.message;
|
|
3538
|
-
try {
|
|
3539
|
-
return JSON.stringify(arg);
|
|
3540
|
-
} catch {
|
|
3541
|
-
return String(arg);
|
|
3542
|
-
}
|
|
3543
|
-
}).join(" ");
|
|
3544
|
-
}
|
|
3545
|
-
function isSdkMessage(args) {
|
|
3546
|
-
return typeof args[0] === "string" && args[0].startsWith("[glasstrace]");
|
|
3547
|
-
}
|
|
3548
|
-
async function installConsoleCapture() {
|
|
3549
|
-
if (installed) return;
|
|
3550
|
-
try {
|
|
3551
|
-
otelApi = await import("./esm-POMEQPKL.js");
|
|
3552
|
-
} catch {
|
|
3553
|
-
otelApi = null;
|
|
3554
|
-
}
|
|
3555
|
-
originalError = console.error;
|
|
3556
|
-
originalWarn = console.warn;
|
|
3557
|
-
installed = true;
|
|
3558
|
-
console.error = (...args) => {
|
|
3559
|
-
originalError.apply(console, args);
|
|
3560
|
-
if (isGlasstraceLog || isSdkMessage(args)) return;
|
|
3561
|
-
if (otelApi) {
|
|
3562
|
-
const span = otelApi.trace.getSpan(otelApi.context.active());
|
|
3563
|
-
if (span) {
|
|
3564
|
-
const formattedMessage = formatArgs(args);
|
|
3565
|
-
span.addEvent("console.error", {
|
|
3566
|
-
"console.message": formattedMessage
|
|
3567
|
-
});
|
|
3568
|
-
try {
|
|
3569
|
-
maybeShowMcpNudge(formattedMessage);
|
|
3570
|
-
} catch {
|
|
3571
|
-
}
|
|
3572
|
-
}
|
|
3573
|
-
}
|
|
3574
|
-
};
|
|
3575
|
-
console.warn = (...args) => {
|
|
3576
|
-
originalWarn.apply(console, args);
|
|
3577
|
-
if (isGlasstraceLog || isSdkMessage(args)) return;
|
|
3578
|
-
if (otelApi) {
|
|
3579
|
-
const span = otelApi.trace.getSpan(otelApi.context.active());
|
|
3580
|
-
if (span) {
|
|
3581
|
-
span.addEvent("console.warn", {
|
|
3582
|
-
"console.message": formatArgs(args)
|
|
3583
|
-
});
|
|
3584
|
-
}
|
|
3585
|
-
}
|
|
3586
|
-
};
|
|
3587
|
-
}
|
|
3588
|
-
function sdkLog(level, message) {
|
|
3589
|
-
isGlasstraceLog = true;
|
|
3590
|
-
try {
|
|
3591
|
-
console[level](message);
|
|
3592
|
-
} finally {
|
|
3593
|
-
isGlasstraceLog = false;
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
3468
|
// src/register.ts
|
|
3598
3469
|
var consoleCaptureInstalled = false;
|
|
3599
3470
|
var discoveryHandler = null;
|
|
@@ -3604,6 +3475,12 @@ function registerGlasstrace(options) {
|
|
|
3604
3475
|
if (isRegistered) {
|
|
3605
3476
|
return;
|
|
3606
3477
|
}
|
|
3478
|
+
if (typeof process === "undefined" || typeof process.versions?.node !== "string") {
|
|
3479
|
+
console.warn(
|
|
3480
|
+
"[glasstrace] SDK requires a Node.js runtime. Edge Runtime, browser, and Deno without Node compat are not supported. Glasstrace is disabled in this environment."
|
|
3481
|
+
);
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3607
3484
|
const config = resolveConfig(options);
|
|
3608
3485
|
if (config.verbose) {
|
|
3609
3486
|
console.info("[glasstrace] Config resolved.");
|
|
@@ -3659,9 +3536,15 @@ function registerGlasstrace(options) {
|
|
|
3659
3536
|
if (isDiscoveryEnabled(config)) {
|
|
3660
3537
|
let resolvedAnonKey = null;
|
|
3661
3538
|
const anonKeyPromise = getOrCreateAnonKey();
|
|
3539
|
+
const getClaimState = () => {
|
|
3540
|
+
if (getLinkedAccountId()) return { claimed: true };
|
|
3541
|
+
if (getClaimResult()) return { claimed: true };
|
|
3542
|
+
return null;
|
|
3543
|
+
};
|
|
3662
3544
|
discoveryHandler = createDiscoveryHandler(
|
|
3663
3545
|
async () => resolvedAnonKey,
|
|
3664
|
-
() => sessionManager.getSessionId(getResolvedApiKey())
|
|
3546
|
+
() => sessionManager.getSessionId(getResolvedApiKey()),
|
|
3547
|
+
getClaimState
|
|
3665
3548
|
);
|
|
3666
3549
|
if (config.verbose) {
|
|
3667
3550
|
console.info("[glasstrace] Discovery endpoint registered (key pending).");
|
|
@@ -3677,7 +3560,8 @@ function registerGlasstrace(options) {
|
|
|
3677
3560
|
if (currentGeneration !== registrationGeneration) return;
|
|
3678
3561
|
discoveryHandler = createDiscoveryHandler(
|
|
3679
3562
|
() => Promise.resolve(anonKey),
|
|
3680
|
-
() => sessionManager.getSessionId(getResolvedApiKey())
|
|
3563
|
+
() => sessionManager.getSessionId(getResolvedApiKey()),
|
|
3564
|
+
getClaimState
|
|
3681
3565
|
);
|
|
3682
3566
|
await backgroundInit(config, anonKey, currentGeneration);
|
|
3683
3567
|
} catch (err) {
|
|
@@ -3734,7 +3618,7 @@ async function backgroundInit(config, anonKeyForInit, generation) {
|
|
|
3734
3618
|
if (config.verbose) {
|
|
3735
3619
|
console.info("[glasstrace] Background init firing.");
|
|
3736
3620
|
}
|
|
3737
|
-
const initResult = await performInit(config, anonKeyForInit, "0.
|
|
3621
|
+
const initResult = await performInit(config, anonKeyForInit, "0.11.0");
|
|
3738
3622
|
if (generation !== registrationGeneration) return;
|
|
3739
3623
|
if (initResult?.claimResult) {
|
|
3740
3624
|
setResolvedApiKey(initResult.claimResult.newApiKey);
|
|
@@ -3761,241 +3645,11 @@ function isDiscoveryEnabled(config) {
|
|
|
3761
3645
|
return false;
|
|
3762
3646
|
}
|
|
3763
3647
|
|
|
3764
|
-
// src/source-map-uploader.ts
|
|
3765
|
-
import * as fs2 from "fs/promises";
|
|
3766
|
-
import * as path2 from "path";
|
|
3767
|
-
import * as crypto from "crypto";
|
|
3768
|
-
import { execFileSync } from "child_process";
|
|
3769
|
-
async function collectSourceMaps(buildDir) {
|
|
3770
|
-
const results = [];
|
|
3771
|
-
try {
|
|
3772
|
-
await walkDir(buildDir, buildDir, results);
|
|
3773
|
-
} catch {
|
|
3774
|
-
return [];
|
|
3775
|
-
}
|
|
3776
|
-
return results;
|
|
3777
|
-
}
|
|
3778
|
-
async function walkDir(baseDir, currentDir, results) {
|
|
3779
|
-
let entries;
|
|
3780
|
-
try {
|
|
3781
|
-
entries = await fs2.readdir(currentDir, { withFileTypes: true });
|
|
3782
|
-
} catch {
|
|
3783
|
-
return;
|
|
3784
|
-
}
|
|
3785
|
-
for (const entry of entries) {
|
|
3786
|
-
const fullPath = path2.join(currentDir, entry.name);
|
|
3787
|
-
if (entry.isDirectory()) {
|
|
3788
|
-
await walkDir(baseDir, fullPath, results);
|
|
3789
|
-
} else if (entry.isFile() && entry.name.endsWith(".map")) {
|
|
3790
|
-
try {
|
|
3791
|
-
const content = await fs2.readFile(fullPath, "utf-8");
|
|
3792
|
-
const relativePath = path2.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
3793
|
-
const compiledPath = relativePath.replace(/\.map$/, "");
|
|
3794
|
-
results.push({ filePath: compiledPath, content });
|
|
3795
|
-
} catch {
|
|
3796
|
-
}
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
}
|
|
3800
|
-
async function computeBuildHash(maps) {
|
|
3801
|
-
try {
|
|
3802
|
-
const sha = execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf-8" }).trim();
|
|
3803
|
-
if (sha) {
|
|
3804
|
-
return sha;
|
|
3805
|
-
}
|
|
3806
|
-
} catch {
|
|
3807
|
-
}
|
|
3808
|
-
const sortedMaps = [...maps ?? []].sort(
|
|
3809
|
-
(a, b) => a.filePath.localeCompare(b.filePath)
|
|
3810
|
-
);
|
|
3811
|
-
const hashInput = sortedMaps.map((m) => `${m.filePath}
|
|
3812
|
-
${m.content.length}
|
|
3813
|
-
${m.content}`).join("");
|
|
3814
|
-
const hash = crypto.createHash("sha256").update(hashInput).digest("hex");
|
|
3815
|
-
return hash;
|
|
3816
|
-
}
|
|
3817
|
-
async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
|
|
3818
|
-
const body = {
|
|
3819
|
-
apiKey,
|
|
3820
|
-
buildHash,
|
|
3821
|
-
files: maps.map((m) => ({
|
|
3822
|
-
filePath: m.filePath,
|
|
3823
|
-
sourceMap: m.content
|
|
3824
|
-
}))
|
|
3825
|
-
};
|
|
3826
|
-
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3827
|
-
const response = await fetch(`${baseUrl}/v1/source-maps`, {
|
|
3828
|
-
method: "POST",
|
|
3829
|
-
headers: {
|
|
3830
|
-
"Content-Type": "application/json",
|
|
3831
|
-
Authorization: `Bearer ${apiKey}`
|
|
3832
|
-
},
|
|
3833
|
-
body: JSON.stringify(body)
|
|
3834
|
-
});
|
|
3835
|
-
if (!response.ok) {
|
|
3836
|
-
try {
|
|
3837
|
-
await response.text();
|
|
3838
|
-
} catch {
|
|
3839
|
-
}
|
|
3840
|
-
throw new Error(
|
|
3841
|
-
`Source map upload failed: ${String(response.status)} ${response.statusText}`
|
|
3842
|
-
);
|
|
3843
|
-
}
|
|
3844
|
-
const json = await response.json();
|
|
3845
|
-
return SourceMapUploadResponseSchema.parse(json);
|
|
3846
|
-
}
|
|
3847
|
-
var PRESIGNED_THRESHOLD_BYTES = 45e5;
|
|
3848
|
-
function stripTrailingSlashes(url) {
|
|
3849
|
-
let result = url;
|
|
3850
|
-
while (result.endsWith("/")) {
|
|
3851
|
-
result = result.slice(0, -1);
|
|
3852
|
-
}
|
|
3853
|
-
return result;
|
|
3854
|
-
}
|
|
3855
|
-
async function requestPresignedTokens(apiKey, endpoint, buildHash, files) {
|
|
3856
|
-
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3857
|
-
const response = await fetch(`${baseUrl}/v1/source-maps/presign`, {
|
|
3858
|
-
method: "POST",
|
|
3859
|
-
headers: {
|
|
3860
|
-
"Content-Type": "application/json",
|
|
3861
|
-
Authorization: `Bearer ${apiKey}`
|
|
3862
|
-
},
|
|
3863
|
-
body: JSON.stringify({ buildHash, files })
|
|
3864
|
-
});
|
|
3865
|
-
if (!response.ok) {
|
|
3866
|
-
try {
|
|
3867
|
-
await response.text();
|
|
3868
|
-
} catch {
|
|
3869
|
-
}
|
|
3870
|
-
throw new Error(
|
|
3871
|
-
`Presigned token request failed: ${String(response.status)} ${response.statusText}`
|
|
3872
|
-
);
|
|
3873
|
-
}
|
|
3874
|
-
const json = await response.json();
|
|
3875
|
-
return PresignedUploadResponseSchema.parse(json);
|
|
3876
|
-
}
|
|
3877
|
-
async function uploadToBlob(clientToken, pathname, content) {
|
|
3878
|
-
let mod;
|
|
3879
|
-
try {
|
|
3880
|
-
mod = await import("@vercel/blob/client");
|
|
3881
|
-
} catch (err) {
|
|
3882
|
-
const code = err.code;
|
|
3883
|
-
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
3884
|
-
throw new Error(
|
|
3885
|
-
"Presigned upload requires @vercel/blob. Install it: npm install @vercel/blob"
|
|
3886
|
-
);
|
|
3887
|
-
}
|
|
3888
|
-
throw err;
|
|
3889
|
-
}
|
|
3890
|
-
const result = await mod.put(pathname, new Blob([content]), {
|
|
3891
|
-
access: "public",
|
|
3892
|
-
token: clientToken
|
|
3893
|
-
});
|
|
3894
|
-
return { url: result.url, size: Buffer.byteLength(content, "utf-8") };
|
|
3895
|
-
}
|
|
3896
|
-
async function submitManifest(apiKey, endpoint, uploadId, buildHash, files) {
|
|
3897
|
-
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3898
|
-
const response = await fetch(`${baseUrl}/v1/source-maps/manifest`, {
|
|
3899
|
-
method: "POST",
|
|
3900
|
-
headers: {
|
|
3901
|
-
"Content-Type": "application/json",
|
|
3902
|
-
Authorization: `Bearer ${apiKey}`
|
|
3903
|
-
},
|
|
3904
|
-
body: JSON.stringify({ uploadId, buildHash, files })
|
|
3905
|
-
});
|
|
3906
|
-
if (!response.ok) {
|
|
3907
|
-
try {
|
|
3908
|
-
await response.text();
|
|
3909
|
-
} catch {
|
|
3910
|
-
}
|
|
3911
|
-
throw new Error(
|
|
3912
|
-
`Source map manifest submission failed: ${String(response.status)} ${response.statusText}`
|
|
3913
|
-
);
|
|
3914
|
-
}
|
|
3915
|
-
const json = await response.json();
|
|
3916
|
-
return SourceMapManifestResponseSchema.parse(json);
|
|
3917
|
-
}
|
|
3918
|
-
async function uploadSourceMapsPresigned(apiKey, endpoint, buildHash, maps, blobUploader = uploadToBlob) {
|
|
3919
|
-
if (maps.length === 0) {
|
|
3920
|
-
throw new Error("No source maps to upload");
|
|
3921
|
-
}
|
|
3922
|
-
const presigned = await requestPresignedTokens(
|
|
3923
|
-
apiKey,
|
|
3924
|
-
endpoint,
|
|
3925
|
-
buildHash,
|
|
3926
|
-
maps.map((m) => ({
|
|
3927
|
-
filePath: m.filePath,
|
|
3928
|
-
sizeBytes: Buffer.byteLength(m.content, "utf-8")
|
|
3929
|
-
}))
|
|
3930
|
-
);
|
|
3931
|
-
const mapsByPath = new Map(maps.map((m) => [m.filePath, m]));
|
|
3932
|
-
if (mapsByPath.size !== maps.length) {
|
|
3933
|
-
throw new Error("Duplicate filePath entries in source maps");
|
|
3934
|
-
}
|
|
3935
|
-
for (const token of presigned.files) {
|
|
3936
|
-
if (!mapsByPath.has(token.filePath)) {
|
|
3937
|
-
throw new Error(
|
|
3938
|
-
`Presigned token for "${token.filePath}" has no matching source map entry`
|
|
3939
|
-
);
|
|
3940
|
-
}
|
|
3941
|
-
}
|
|
3942
|
-
const CONCURRENCY = 5;
|
|
3943
|
-
const uploadResults = [];
|
|
3944
|
-
for (let i = 0; i < presigned.files.length; i += CONCURRENCY) {
|
|
3945
|
-
const chunk = presigned.files.slice(i, i + CONCURRENCY);
|
|
3946
|
-
const chunkResults = await Promise.all(
|
|
3947
|
-
chunk.map(async (token) => {
|
|
3948
|
-
const entry = mapsByPath.get(token.filePath);
|
|
3949
|
-
const result = await blobUploader(token.clientToken, token.pathname, entry.content);
|
|
3950
|
-
return {
|
|
3951
|
-
filePath: token.filePath,
|
|
3952
|
-
sizeBytes: result.size,
|
|
3953
|
-
blobUrl: result.url
|
|
3954
|
-
};
|
|
3955
|
-
})
|
|
3956
|
-
);
|
|
3957
|
-
uploadResults.push(...chunkResults);
|
|
3958
|
-
}
|
|
3959
|
-
return submitManifest(apiKey, endpoint, presigned.uploadId, buildHash, uploadResults);
|
|
3960
|
-
}
|
|
3961
|
-
async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options) {
|
|
3962
|
-
if (maps.length === 0) {
|
|
3963
|
-
throw new Error("No source maps to upload");
|
|
3964
|
-
}
|
|
3965
|
-
const totalBytes = maps.reduce(
|
|
3966
|
-
(sum, m) => sum + Buffer.byteLength(m.content, "utf-8"),
|
|
3967
|
-
0
|
|
3968
|
-
);
|
|
3969
|
-
if (totalBytes < PRESIGNED_THRESHOLD_BYTES) {
|
|
3970
|
-
return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
|
|
3971
|
-
}
|
|
3972
|
-
const checkAvailable = options?.checkBlobAvailable ?? (async () => {
|
|
3973
|
-
try {
|
|
3974
|
-
await import("@vercel/blob/client");
|
|
3975
|
-
return true;
|
|
3976
|
-
} catch {
|
|
3977
|
-
return false;
|
|
3978
|
-
}
|
|
3979
|
-
});
|
|
3980
|
-
const blobAvailable = await checkAvailable();
|
|
3981
|
-
if (blobAvailable) {
|
|
3982
|
-
return uploadSourceMapsPresigned(
|
|
3983
|
-
apiKey,
|
|
3984
|
-
endpoint,
|
|
3985
|
-
buildHash,
|
|
3986
|
-
maps,
|
|
3987
|
-
options?.blobUploader
|
|
3988
|
-
);
|
|
3989
|
-
}
|
|
3990
|
-
sdkLog(
|
|
3991
|
-
"warn",
|
|
3992
|
-
`[glasstrace] Build exceeds 4.5MB (${totalBytes} bytes). Install @vercel/blob for presigned uploads to avoid serverless body size limits. Falling back to legacy upload.`
|
|
3993
|
-
);
|
|
3994
|
-
return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
|
|
3995
|
-
}
|
|
3996
|
-
|
|
3997
3648
|
// src/config-wrapper.ts
|
|
3998
3649
|
function withGlasstraceConfig(nextConfig) {
|
|
3650
|
+
if (typeof process === "undefined" || typeof process.versions?.node !== "string") {
|
|
3651
|
+
return nextConfig != null ? { ...nextConfig } : {};
|
|
3652
|
+
}
|
|
3999
3653
|
const config = nextConfig != null ? { ...nextConfig } : {};
|
|
4000
3654
|
const existingExperimental = config.experimental ?? {};
|
|
4001
3655
|
config.experimental = { ...existingExperimental, serverSourceMaps: true };
|
|
@@ -4038,13 +3692,14 @@ async function handleSourceMapUpload(distDir) {
|
|
|
4038
3692
|
);
|
|
4039
3693
|
return;
|
|
4040
3694
|
}
|
|
4041
|
-
const
|
|
3695
|
+
const { collectSourceMaps: collectSourceMaps2, computeBuildHash: computeBuildHash2, uploadSourceMaps: uploadSourceMaps2 } = await import("./source-map-uploader-OFEM54UE.js");
|
|
3696
|
+
const maps = await collectSourceMaps2(distDir);
|
|
4042
3697
|
if (maps.length === 0) {
|
|
4043
3698
|
console.info("[glasstrace] No source map files found. Skipping upload.");
|
|
4044
3699
|
return;
|
|
4045
3700
|
}
|
|
4046
|
-
const buildHash = await
|
|
4047
|
-
await
|
|
3701
|
+
const buildHash = await computeBuildHash2(maps);
|
|
3702
|
+
await uploadSourceMaps2(apiKey, endpoint, buildHash, maps);
|
|
4048
3703
|
console.info(
|
|
4049
3704
|
`[glasstrace] Uploaded ${String(maps.length)} source map(s) for build ${buildHash}.`
|
|
4050
3705
|
);
|
|
@@ -4093,6 +3748,7 @@ export {
|
|
|
4093
3748
|
getActiveConfig,
|
|
4094
3749
|
getDateString,
|
|
4095
3750
|
getDiscoveryHandler,
|
|
3751
|
+
getLinkedAccountId,
|
|
4096
3752
|
getOrCreateAnonKey,
|
|
4097
3753
|
getOrigin,
|
|
4098
3754
|
isAnonymousMode,
|