@glasstrace/sdk 0.4.1 → 0.7.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-STECO33B.js → chunk-PLJVIWHN.js} +1 -1
- package/dist/chunk-PLJVIWHN.js.map +1 -0
- package/dist/{chunk-EC5IINUT.js → chunk-QW6W4CSA.js} +64 -5
- package/dist/chunk-QW6W4CSA.js.map +1 -0
- package/dist/{chunk-LAMTBURS.js → chunk-SALPGSWK.js} +2 -2
- package/dist/cli/init.cjs +46 -2
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +3 -3
- package/dist/cli/mcp-add.cjs +45 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +2 -2
- package/dist/index.cjs +291 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -5
- package/dist/index.d.ts +74 -5
- package/dist/index.js +231 -49
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/dist/chunk-EC5IINUT.js.map +0 -1
- package/dist/chunk-STECO33B.js.map +0 -1
- /package/dist/{chunk-LAMTBURS.js.map → chunk-SALPGSWK.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -67,6 +67,7 @@ type GlasstraceEnvVars = z.infer<typeof GlasstraceEnvVarsSchema>;
|
|
|
67
67
|
* - SDK health diagnostics (embedded in init requests)
|
|
68
68
|
* - Discovery endpoint (GET /__glasstrace/config)
|
|
69
69
|
* - Source map upload (POST /v1/source-maps)
|
|
70
|
+
* - Presigned source map upload (POST /v1/source-maps/presign, POST /v1/source-maps/manifest)
|
|
70
71
|
*/
|
|
71
72
|
|
|
72
73
|
/** Test file import relationships, embedded in SDK init request. */
|
|
@@ -112,6 +113,11 @@ declare const SdkInitResponseSchema: z.ZodObject<{
|
|
|
112
113
|
maxTraceSizeBytes: z.ZodNumber;
|
|
113
114
|
maxConcurrentSessions: z.ZodNumber;
|
|
114
115
|
}, z.core.$strip>;
|
|
116
|
+
claimResult: z.ZodOptional<z.ZodObject<{
|
|
117
|
+
newApiKey: z.core.$ZodBranded<z.ZodString, "DevApiKey", "out">;
|
|
118
|
+
accountId: z.ZodString;
|
|
119
|
+
graceExpiresAt: z.ZodNumber;
|
|
120
|
+
}, z.core.$strip>>;
|
|
115
121
|
}, z.core.$strip>;
|
|
116
122
|
type SdkInitResponse = z.infer<typeof SdkInitResponseSchema>;
|
|
117
123
|
/** Response from POST /v1/source-maps. */
|
|
@@ -122,6 +128,15 @@ declare const SourceMapUploadResponseSchema: z.ZodObject<{
|
|
|
122
128
|
totalSizeBytes: z.ZodNumber;
|
|
123
129
|
}, z.core.$strip>;
|
|
124
130
|
type SourceMapUploadResponse = z.infer<typeof SourceMapUploadResponseSchema>;
|
|
131
|
+
/** Response confirming source map manifest activation. */
|
|
132
|
+
declare const SourceMapManifestResponseSchema: z.ZodObject<{
|
|
133
|
+
success: z.ZodLiteral<true>;
|
|
134
|
+
buildHash: z.core.$ZodBranded<z.ZodString, "BuildHash", "out">;
|
|
135
|
+
fileCount: z.ZodNumber;
|
|
136
|
+
totalSizeBytes: z.ZodNumber;
|
|
137
|
+
activatedAt: z.ZodNumber;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
type SourceMapManifestResponse = z.infer<typeof SourceMapManifestResponseSchema>;
|
|
125
140
|
|
|
126
141
|
/**
|
|
127
142
|
* Internal SDK error class with a typed diagnostic code.
|
|
@@ -270,11 +285,24 @@ declare function sendInitRequest(config: ResolvedConfig, anonKey: AnonApiKey | n
|
|
|
270
285
|
message: string;
|
|
271
286
|
timestamp: number;
|
|
272
287
|
}>, signal?: AbortSignal): Promise<SdkInitResponse>;
|
|
288
|
+
/**
|
|
289
|
+
* Result returned by {@link performInit} when the backend reports an
|
|
290
|
+
* account claim transition. `null` means no claim was present.
|
|
291
|
+
*/
|
|
292
|
+
interface InitClaimResult {
|
|
293
|
+
claimResult: NonNullable<SdkInitResponse["claimResult"]>;
|
|
294
|
+
}
|
|
273
295
|
/**
|
|
274
296
|
* Orchestrates the full init flow: send request, update config, cache result.
|
|
275
297
|
* This function MUST NOT throw.
|
|
298
|
+
*
|
|
299
|
+
* Returns the claim result when the backend reports an account claim
|
|
300
|
+
* transition, or `null` when no claim result is available (including
|
|
301
|
+
* when init is skipped due to rate-limit backoff, missing API key,
|
|
302
|
+
* or request failure). Callers that do not need claim information
|
|
303
|
+
* can safely ignore the return value.
|
|
276
304
|
*/
|
|
277
|
-
declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<
|
|
305
|
+
declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<InitClaimResult | null>;
|
|
278
306
|
/**
|
|
279
307
|
* Returns the current capture config from the three-tier fallback chain:
|
|
280
308
|
* 1. In-memory config from latest init response
|
|
@@ -354,9 +382,13 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
354
382
|
* Enriches a ReadableSpan with all glasstrace.* attributes.
|
|
355
383
|
* Returns a new ReadableSpan wrapper; the original span is not mutated.
|
|
356
384
|
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
385
|
+
* Only {@link SessionManager.getSessionId} is individually guarded because
|
|
386
|
+
* it calls into crypto and schema validation — a session ID failure should
|
|
387
|
+
* not prevent the rest of enrichment. The other helper calls
|
|
388
|
+
* ({@link deriveErrorCategory}, {@link deriveOrmProvider},
|
|
389
|
+
* {@link classifyFetchTarget}) are pure functions on typed string inputs
|
|
390
|
+
* and rely on the outer catch for any unexpected failure.
|
|
391
|
+
*
|
|
360
392
|
* On total failure, returns the original span unchanged.
|
|
361
393
|
*/
|
|
362
394
|
private enrichSpan;
|
|
@@ -453,6 +485,43 @@ declare function computeBuildHash(maps?: SourceMapEntry[]): Promise<string>;
|
|
|
453
485
|
* and file entries. Validates the response against SourceMapUploadResponseSchema.
|
|
454
486
|
*/
|
|
455
487
|
declare function uploadSourceMaps(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[]): Promise<SourceMapUploadResponse>;
|
|
488
|
+
/** Builds at or above this byte size route to the presigned upload flow. */
|
|
489
|
+
declare const PRESIGNED_THRESHOLD_BYTES = 4500000;
|
|
490
|
+
/** Signature for the blob upload function, injectable for testing. */
|
|
491
|
+
type BlobUploader = (clientToken: string, pathname: string, content: string) => Promise<{
|
|
492
|
+
url: string;
|
|
493
|
+
size: number;
|
|
494
|
+
}>;
|
|
495
|
+
/**
|
|
496
|
+
* Orchestrates the 3-phase presigned upload flow.
|
|
497
|
+
*
|
|
498
|
+
* 1. Requests presigned tokens for all source map files
|
|
499
|
+
* 2. Uploads each file to blob storage with a concurrency limit of 5
|
|
500
|
+
* 3. Submits the manifest to finalize the upload
|
|
501
|
+
*
|
|
502
|
+
* Accepts an optional `blobUploader` for test injection; defaults to
|
|
503
|
+
* {@link uploadToBlob}.
|
|
504
|
+
*/
|
|
505
|
+
declare function uploadSourceMapsPresigned(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[], blobUploader?: BlobUploader): Promise<SourceMapManifestResponse>;
|
|
506
|
+
/**
|
|
507
|
+
* Options for {@link uploadSourceMapsAuto}, primarily used for test injection.
|
|
508
|
+
*/
|
|
509
|
+
interface AutoUploadOptions {
|
|
510
|
+
/** Override blob availability check (for testing). */
|
|
511
|
+
checkBlobAvailable?: () => Promise<boolean>;
|
|
512
|
+
/** Override blob uploader (for testing). */
|
|
513
|
+
blobUploader?: BlobUploader;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Automatically routes source map uploads based on total build size.
|
|
517
|
+
*
|
|
518
|
+
* - Below {@link PRESIGNED_THRESHOLD_BYTES}: uses the legacy single-request
|
|
519
|
+
* {@link uploadSourceMaps} endpoint.
|
|
520
|
+
* - At or above the threshold: checks if `@vercel/blob` is available and
|
|
521
|
+
* uses the presigned 3-phase flow. Falls back to legacy with a warning
|
|
522
|
+
* if the package is not installed.
|
|
523
|
+
*/
|
|
524
|
+
declare function uploadSourceMapsAuto(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[], options?: AutoUploadOptions): Promise<SourceMapUploadResponse | SourceMapManifestResponse>;
|
|
456
525
|
|
|
457
526
|
/**
|
|
458
527
|
* Manual error capture API.
|
|
@@ -515,4 +584,4 @@ declare function extractImports(fileContent: string): string[];
|
|
|
515
584
|
*/
|
|
516
585
|
declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
|
|
517
586
|
|
|
518
|
-
export { type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, 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, withGlasstraceConfig };
|
|
587
|
+
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ type GlasstraceEnvVars = z.infer<typeof GlasstraceEnvVarsSchema>;
|
|
|
67
67
|
* - SDK health diagnostics (embedded in init requests)
|
|
68
68
|
* - Discovery endpoint (GET /__glasstrace/config)
|
|
69
69
|
* - Source map upload (POST /v1/source-maps)
|
|
70
|
+
* - Presigned source map upload (POST /v1/source-maps/presign, POST /v1/source-maps/manifest)
|
|
70
71
|
*/
|
|
71
72
|
|
|
72
73
|
/** Test file import relationships, embedded in SDK init request. */
|
|
@@ -112,6 +113,11 @@ declare const SdkInitResponseSchema: z.ZodObject<{
|
|
|
112
113
|
maxTraceSizeBytes: z.ZodNumber;
|
|
113
114
|
maxConcurrentSessions: z.ZodNumber;
|
|
114
115
|
}, z.core.$strip>;
|
|
116
|
+
claimResult: z.ZodOptional<z.ZodObject<{
|
|
117
|
+
newApiKey: z.core.$ZodBranded<z.ZodString, "DevApiKey", "out">;
|
|
118
|
+
accountId: z.ZodString;
|
|
119
|
+
graceExpiresAt: z.ZodNumber;
|
|
120
|
+
}, z.core.$strip>>;
|
|
115
121
|
}, z.core.$strip>;
|
|
116
122
|
type SdkInitResponse = z.infer<typeof SdkInitResponseSchema>;
|
|
117
123
|
/** Response from POST /v1/source-maps. */
|
|
@@ -122,6 +128,15 @@ declare const SourceMapUploadResponseSchema: z.ZodObject<{
|
|
|
122
128
|
totalSizeBytes: z.ZodNumber;
|
|
123
129
|
}, z.core.$strip>;
|
|
124
130
|
type SourceMapUploadResponse = z.infer<typeof SourceMapUploadResponseSchema>;
|
|
131
|
+
/** Response confirming source map manifest activation. */
|
|
132
|
+
declare const SourceMapManifestResponseSchema: z.ZodObject<{
|
|
133
|
+
success: z.ZodLiteral<true>;
|
|
134
|
+
buildHash: z.core.$ZodBranded<z.ZodString, "BuildHash", "out">;
|
|
135
|
+
fileCount: z.ZodNumber;
|
|
136
|
+
totalSizeBytes: z.ZodNumber;
|
|
137
|
+
activatedAt: z.ZodNumber;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
type SourceMapManifestResponse = z.infer<typeof SourceMapManifestResponseSchema>;
|
|
125
140
|
|
|
126
141
|
/**
|
|
127
142
|
* Internal SDK error class with a typed diagnostic code.
|
|
@@ -270,11 +285,24 @@ declare function sendInitRequest(config: ResolvedConfig, anonKey: AnonApiKey | n
|
|
|
270
285
|
message: string;
|
|
271
286
|
timestamp: number;
|
|
272
287
|
}>, signal?: AbortSignal): Promise<SdkInitResponse>;
|
|
288
|
+
/**
|
|
289
|
+
* Result returned by {@link performInit} when the backend reports an
|
|
290
|
+
* account claim transition. `null` means no claim was present.
|
|
291
|
+
*/
|
|
292
|
+
interface InitClaimResult {
|
|
293
|
+
claimResult: NonNullable<SdkInitResponse["claimResult"]>;
|
|
294
|
+
}
|
|
273
295
|
/**
|
|
274
296
|
* Orchestrates the full init flow: send request, update config, cache result.
|
|
275
297
|
* This function MUST NOT throw.
|
|
298
|
+
*
|
|
299
|
+
* Returns the claim result when the backend reports an account claim
|
|
300
|
+
* transition, or `null` when no claim result is available (including
|
|
301
|
+
* when init is skipped due to rate-limit backoff, missing API key,
|
|
302
|
+
* or request failure). Callers that do not need claim information
|
|
303
|
+
* can safely ignore the return value.
|
|
276
304
|
*/
|
|
277
|
-
declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<
|
|
305
|
+
declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<InitClaimResult | null>;
|
|
278
306
|
/**
|
|
279
307
|
* Returns the current capture config from the three-tier fallback chain:
|
|
280
308
|
* 1. In-memory config from latest init response
|
|
@@ -354,9 +382,13 @@ declare class GlasstraceExporter implements SpanExporter {
|
|
|
354
382
|
* Enriches a ReadableSpan with all glasstrace.* attributes.
|
|
355
383
|
* Returns a new ReadableSpan wrapper; the original span is not mutated.
|
|
356
384
|
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
385
|
+
* Only {@link SessionManager.getSessionId} is individually guarded because
|
|
386
|
+
* it calls into crypto and schema validation — a session ID failure should
|
|
387
|
+
* not prevent the rest of enrichment. The other helper calls
|
|
388
|
+
* ({@link deriveErrorCategory}, {@link deriveOrmProvider},
|
|
389
|
+
* {@link classifyFetchTarget}) are pure functions on typed string inputs
|
|
390
|
+
* and rely on the outer catch for any unexpected failure.
|
|
391
|
+
*
|
|
360
392
|
* On total failure, returns the original span unchanged.
|
|
361
393
|
*/
|
|
362
394
|
private enrichSpan;
|
|
@@ -453,6 +485,43 @@ declare function computeBuildHash(maps?: SourceMapEntry[]): Promise<string>;
|
|
|
453
485
|
* and file entries. Validates the response against SourceMapUploadResponseSchema.
|
|
454
486
|
*/
|
|
455
487
|
declare function uploadSourceMaps(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[]): Promise<SourceMapUploadResponse>;
|
|
488
|
+
/** Builds at or above this byte size route to the presigned upload flow. */
|
|
489
|
+
declare const PRESIGNED_THRESHOLD_BYTES = 4500000;
|
|
490
|
+
/** Signature for the blob upload function, injectable for testing. */
|
|
491
|
+
type BlobUploader = (clientToken: string, pathname: string, content: string) => Promise<{
|
|
492
|
+
url: string;
|
|
493
|
+
size: number;
|
|
494
|
+
}>;
|
|
495
|
+
/**
|
|
496
|
+
* Orchestrates the 3-phase presigned upload flow.
|
|
497
|
+
*
|
|
498
|
+
* 1. Requests presigned tokens for all source map files
|
|
499
|
+
* 2. Uploads each file to blob storage with a concurrency limit of 5
|
|
500
|
+
* 3. Submits the manifest to finalize the upload
|
|
501
|
+
*
|
|
502
|
+
* Accepts an optional `blobUploader` for test injection; defaults to
|
|
503
|
+
* {@link uploadToBlob}.
|
|
504
|
+
*/
|
|
505
|
+
declare function uploadSourceMapsPresigned(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[], blobUploader?: BlobUploader): Promise<SourceMapManifestResponse>;
|
|
506
|
+
/**
|
|
507
|
+
* Options for {@link uploadSourceMapsAuto}, primarily used for test injection.
|
|
508
|
+
*/
|
|
509
|
+
interface AutoUploadOptions {
|
|
510
|
+
/** Override blob availability check (for testing). */
|
|
511
|
+
checkBlobAvailable?: () => Promise<boolean>;
|
|
512
|
+
/** Override blob uploader (for testing). */
|
|
513
|
+
blobUploader?: BlobUploader;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Automatically routes source map uploads based on total build size.
|
|
517
|
+
*
|
|
518
|
+
* - Below {@link PRESIGNED_THRESHOLD_BYTES}: uses the legacy single-request
|
|
519
|
+
* {@link uploadSourceMaps} endpoint.
|
|
520
|
+
* - At or above the threshold: checks if `@vercel/blob` is available and
|
|
521
|
+
* uses the presigned 3-phase flow. Falls back to legacy with a warning
|
|
522
|
+
* if the package is not installed.
|
|
523
|
+
*/
|
|
524
|
+
declare function uploadSourceMapsAuto(apiKey: string, endpoint: string, buildHash: string, maps: SourceMapEntry[], options?: AutoUploadOptions): Promise<SourceMapUploadResponse | SourceMapManifestResponse>;
|
|
456
525
|
|
|
457
526
|
/**
|
|
458
527
|
* Manual error capture API.
|
|
@@ -515,4 +584,4 @@ declare function extractImports(fileContent: string): string[];
|
|
|
515
584
|
*/
|
|
516
585
|
declare function buildImportGraph(projectRoot: string): Promise<ImportGraphPayload>;
|
|
517
586
|
|
|
518
|
-
export { type FetchTarget, GlasstraceExporter, type GlasstraceExporterOptions, GlasstraceSpanProcessor, 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, withGlasstraceConfig };
|
|
587
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -2,17 +2,19 @@ import {
|
|
|
2
2
|
buildImportGraph,
|
|
3
3
|
discoverTestFiles,
|
|
4
4
|
extractImports
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SALPGSWK.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_CAPTURE_CONFIG,
|
|
8
8
|
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
9
|
+
PresignedUploadResponseSchema,
|
|
9
10
|
SdkCachedConfigSchema,
|
|
10
11
|
SdkInitResponseSchema,
|
|
11
12
|
SessionIdSchema,
|
|
13
|
+
SourceMapManifestResponseSchema,
|
|
12
14
|
SourceMapUploadResponseSchema,
|
|
13
15
|
getOrCreateAnonKey,
|
|
14
16
|
readAnonKey
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-QW6W4CSA.js";
|
|
16
18
|
import {
|
|
17
19
|
INVALID_SPAN_CONTEXT,
|
|
18
20
|
SamplingDecision,
|
|
@@ -281,13 +283,13 @@ async function sendInitRequest(config, anonKey, sdkVersion, importGraph, healthR
|
|
|
281
283
|
async function performInit(config, anonKey, sdkVersion) {
|
|
282
284
|
if (rateLimitBackoff) {
|
|
283
285
|
rateLimitBackoff = false;
|
|
284
|
-
return;
|
|
286
|
+
return null;
|
|
285
287
|
}
|
|
286
288
|
try {
|
|
287
289
|
const effectiveKey = config.apiKey || anonKey;
|
|
288
290
|
if (!effectiveKey) {
|
|
289
291
|
console.warn("[glasstrace] No API key available for init request.");
|
|
290
|
-
return;
|
|
292
|
+
return null;
|
|
291
293
|
}
|
|
292
294
|
const controller = new AbortController();
|
|
293
295
|
const timeoutId = setTimeout(() => controller.abort(), INIT_TIMEOUT_MS);
|
|
@@ -304,45 +306,61 @@ async function performInit(config, anonKey, sdkVersion) {
|
|
|
304
306
|
clearTimeout(timeoutId);
|
|
305
307
|
currentConfig = result;
|
|
306
308
|
await saveCachedConfig(result);
|
|
309
|
+
if (result.claimResult) {
|
|
310
|
+
try {
|
|
311
|
+
process.stderr.write(
|
|
312
|
+
`[glasstrace] Account claimed! Update GLASSTRACE_API_KEY=${result.claimResult.newApiKey} in your .env file.
|
|
313
|
+
`
|
|
314
|
+
);
|
|
315
|
+
} catch (logErr) {
|
|
316
|
+
console.warn(
|
|
317
|
+
`[glasstrace] Failed to write claim migration message: ${logErr instanceof Error ? logErr.message : String(logErr)}`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return { claimResult: result.claimResult };
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
307
323
|
} catch (err) {
|
|
308
324
|
clearTimeout(timeoutId);
|
|
309
325
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
310
326
|
console.warn("[glasstrace] ingestion_unreachable: Init request timed out.");
|
|
311
|
-
return;
|
|
327
|
+
return null;
|
|
312
328
|
}
|
|
313
329
|
const status = err.status;
|
|
314
330
|
if (status === 401) {
|
|
315
331
|
console.warn(
|
|
316
332
|
"[glasstrace] ingestion_auth_failed: Check your GLASSTRACE_API_KEY."
|
|
317
333
|
);
|
|
318
|
-
return;
|
|
334
|
+
return null;
|
|
319
335
|
}
|
|
320
336
|
if (status === 429) {
|
|
321
337
|
console.warn("[glasstrace] ingestion_rate_limited: Backing off.");
|
|
322
338
|
rateLimitBackoff = true;
|
|
323
|
-
return;
|
|
339
|
+
return null;
|
|
324
340
|
}
|
|
325
341
|
if (typeof status === "number" && status >= 400) {
|
|
326
342
|
console.warn(
|
|
327
343
|
`[glasstrace] Init request failed with status ${status}. Using cached config.`
|
|
328
344
|
);
|
|
329
|
-
return;
|
|
345
|
+
return null;
|
|
330
346
|
}
|
|
331
347
|
if (err instanceof Error && err.name === "ZodError") {
|
|
332
348
|
console.warn(
|
|
333
349
|
"[glasstrace] Init response failed validation (schema version mismatch?). Using cached config."
|
|
334
350
|
);
|
|
335
|
-
return;
|
|
351
|
+
return null;
|
|
336
352
|
}
|
|
337
353
|
console.warn(
|
|
338
354
|
`[glasstrace] ingestion_unreachable: ${err instanceof Error ? err.message : String(err)}`
|
|
339
355
|
);
|
|
356
|
+
return null;
|
|
340
357
|
}
|
|
341
358
|
} catch (err) {
|
|
342
359
|
console.warn(
|
|
343
360
|
`[glasstrace] Unexpected init error: ${err instanceof Error ? err.message : String(err)}`
|
|
344
361
|
);
|
|
345
362
|
}
|
|
363
|
+
return null;
|
|
346
364
|
}
|
|
347
365
|
function getActiveConfig() {
|
|
348
366
|
if (currentConfig) {
|
|
@@ -452,9 +470,13 @@ var GlasstraceExporter = class {
|
|
|
452
470
|
* Enriches a ReadableSpan with all glasstrace.* attributes.
|
|
453
471
|
* Returns a new ReadableSpan wrapper; the original span is not mutated.
|
|
454
472
|
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
473
|
+
* Only {@link SessionManager.getSessionId} is individually guarded because
|
|
474
|
+
* it calls into crypto and schema validation — a session ID failure should
|
|
475
|
+
* not prevent the rest of enrichment. The other helper calls
|
|
476
|
+
* ({@link deriveErrorCategory}, {@link deriveOrmProvider},
|
|
477
|
+
* {@link classifyFetchTarget}) are pure functions on typed string inputs
|
|
478
|
+
* and rely on the outer catch for any unexpected failure.
|
|
479
|
+
*
|
|
458
480
|
* On total failure, returns the original span unchanged.
|
|
459
481
|
*/
|
|
460
482
|
enrichSpan(span) {
|
|
@@ -497,44 +519,39 @@ var GlasstraceExporter = class {
|
|
|
497
519
|
}
|
|
498
520
|
}
|
|
499
521
|
const errorMessage = attrs["exception.message"];
|
|
500
|
-
if (errorMessage) {
|
|
522
|
+
if (typeof errorMessage === "string") {
|
|
501
523
|
extra[ATTR.ERROR_MESSAGE] = errorMessage;
|
|
502
524
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
|
|
508
|
-
}
|
|
509
|
-
} catch {
|
|
525
|
+
const errorType = attrs["exception.type"];
|
|
526
|
+
if (typeof errorType === "string") {
|
|
527
|
+
extra[ATTR.ERROR_CODE] = errorType;
|
|
528
|
+
extra[ATTR.ERROR_CATEGORY] = deriveErrorCategory(errorType);
|
|
510
529
|
}
|
|
511
530
|
const errorField = attrs["error.field"];
|
|
512
|
-
if (errorField) {
|
|
531
|
+
if (typeof errorField === "string") {
|
|
513
532
|
extra[ATTR.ERROR_FIELD] = errorField;
|
|
514
533
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const operation = attrs["db.operation"];
|
|
526
|
-
if (operation) {
|
|
527
|
-
extra[ATTR.ORM_OPERATION] = operation;
|
|
528
|
-
}
|
|
534
|
+
const spanAny = span;
|
|
535
|
+
const instrumentationName = spanAny.instrumentationScope?.name ?? spanAny.instrumentationLibrary?.name ?? "";
|
|
536
|
+
const ormProvider = deriveOrmProvider(instrumentationName);
|
|
537
|
+
if (ormProvider) {
|
|
538
|
+
extra[ATTR.ORM_PROVIDER] = ormProvider;
|
|
539
|
+
const table = attrs["db.sql.table"];
|
|
540
|
+
const prismaModel = attrs["db.prisma.model"];
|
|
541
|
+
const model = typeof table === "string" ? table : typeof prismaModel === "string" ? prismaModel : void 0;
|
|
542
|
+
if (model) {
|
|
543
|
+
extra[ATTR.ORM_MODEL] = model;
|
|
529
544
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const url = attrs["http.url"] ?? attrs["url.full"];
|
|
534
|
-
if (url && span.kind === SpanKind.CLIENT) {
|
|
535
|
-
extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
|
|
545
|
+
const operation = attrs["db.operation"];
|
|
546
|
+
if (typeof operation === "string") {
|
|
547
|
+
extra[ATTR.ORM_OPERATION] = operation;
|
|
536
548
|
}
|
|
537
|
-
}
|
|
549
|
+
}
|
|
550
|
+
const httpUrl = attrs["http.url"];
|
|
551
|
+
const fullUrl = attrs["url.full"];
|
|
552
|
+
const url = typeof httpUrl === "string" ? httpUrl : typeof fullUrl === "string" ? fullUrl : void 0;
|
|
553
|
+
if (url && span.kind === SpanKind.CLIENT) {
|
|
554
|
+
extra[ATTR.FETCH_TARGET] = classifyFetchTarget(url);
|
|
538
555
|
}
|
|
539
556
|
return createEnrichedSpan(span, extra);
|
|
540
557
|
} catch {
|
|
@@ -3453,6 +3470,14 @@ async function installConsoleCapture() {
|
|
|
3453
3470
|
}
|
|
3454
3471
|
};
|
|
3455
3472
|
}
|
|
3473
|
+
function sdkLog(level, message) {
|
|
3474
|
+
isGlasstraceLog = true;
|
|
3475
|
+
try {
|
|
3476
|
+
console[level](message);
|
|
3477
|
+
} finally {
|
|
3478
|
+
isGlasstraceLog = false;
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3456
3481
|
|
|
3457
3482
|
// src/register.ts
|
|
3458
3483
|
var consoleCaptureInstalled = false;
|
|
@@ -3542,7 +3567,11 @@ function registerGlasstrace(options) {
|
|
|
3542
3567
|
if (config.verbose) {
|
|
3543
3568
|
console.info("[glasstrace] Background init firing.");
|
|
3544
3569
|
}
|
|
3545
|
-
await performInit(config, anonKey, "0.
|
|
3570
|
+
const initResult = await performInit(config, anonKey, "0.7.0");
|
|
3571
|
+
if (initResult?.claimResult) {
|
|
3572
|
+
setResolvedApiKey(initResult.claimResult.newApiKey);
|
|
3573
|
+
notifyApiKeyResolved();
|
|
3574
|
+
}
|
|
3546
3575
|
maybeInstallConsoleCapture();
|
|
3547
3576
|
} catch (err) {
|
|
3548
3577
|
console.warn(
|
|
@@ -3562,7 +3591,11 @@ function registerGlasstrace(options) {
|
|
|
3562
3591
|
if (config.verbose) {
|
|
3563
3592
|
console.info("[glasstrace] Background init firing.");
|
|
3564
3593
|
}
|
|
3565
|
-
await performInit(config, anonKey, "0.
|
|
3594
|
+
const initResult = await performInit(config, anonKey, "0.7.0");
|
|
3595
|
+
if (initResult?.claimResult) {
|
|
3596
|
+
setResolvedApiKey(initResult.claimResult.newApiKey);
|
|
3597
|
+
notifyApiKeyResolved();
|
|
3598
|
+
}
|
|
3566
3599
|
maybeInstallConsoleCapture();
|
|
3567
3600
|
} catch (err) {
|
|
3568
3601
|
console.warn(
|
|
@@ -3584,7 +3617,7 @@ function registerGlasstrace(options) {
|
|
|
3584
3617
|
if (config.verbose) {
|
|
3585
3618
|
console.info("[glasstrace] Background init firing.");
|
|
3586
3619
|
}
|
|
3587
|
-
await performInit(config, anonKeyForInit, "0.
|
|
3620
|
+
await performInit(config, anonKeyForInit, "0.7.0");
|
|
3588
3621
|
maybeInstallConsoleCapture();
|
|
3589
3622
|
} catch (err) {
|
|
3590
3623
|
console.warn(
|
|
@@ -3683,10 +3716,7 @@ async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
|
|
|
3683
3716
|
sourceMap: m.content
|
|
3684
3717
|
}))
|
|
3685
3718
|
};
|
|
3686
|
-
|
|
3687
|
-
while (baseUrl.endsWith("/")) {
|
|
3688
|
-
baseUrl = baseUrl.slice(0, -1);
|
|
3689
|
-
}
|
|
3719
|
+
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3690
3720
|
const response = await fetch(`${baseUrl}/v1/source-maps`, {
|
|
3691
3721
|
method: "POST",
|
|
3692
3722
|
headers: {
|
|
@@ -3707,6 +3737,155 @@ async function uploadSourceMaps(apiKey, endpoint, buildHash, maps) {
|
|
|
3707
3737
|
const json = await response.json();
|
|
3708
3738
|
return SourceMapUploadResponseSchema.parse(json);
|
|
3709
3739
|
}
|
|
3740
|
+
var PRESIGNED_THRESHOLD_BYTES = 45e5;
|
|
3741
|
+
function stripTrailingSlashes(url) {
|
|
3742
|
+
let result = url;
|
|
3743
|
+
while (result.endsWith("/")) {
|
|
3744
|
+
result = result.slice(0, -1);
|
|
3745
|
+
}
|
|
3746
|
+
return result;
|
|
3747
|
+
}
|
|
3748
|
+
async function requestPresignedTokens(apiKey, endpoint, buildHash, files) {
|
|
3749
|
+
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3750
|
+
const response = await fetch(`${baseUrl}/v1/source-maps/presign`, {
|
|
3751
|
+
method: "POST",
|
|
3752
|
+
headers: {
|
|
3753
|
+
"Content-Type": "application/json",
|
|
3754
|
+
Authorization: `Bearer ${apiKey}`
|
|
3755
|
+
},
|
|
3756
|
+
body: JSON.stringify({ buildHash, files })
|
|
3757
|
+
});
|
|
3758
|
+
if (!response.ok) {
|
|
3759
|
+
try {
|
|
3760
|
+
await response.text();
|
|
3761
|
+
} catch {
|
|
3762
|
+
}
|
|
3763
|
+
throw new Error(
|
|
3764
|
+
`Presigned token request failed: ${String(response.status)} ${response.statusText}`
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3767
|
+
const json = await response.json();
|
|
3768
|
+
return PresignedUploadResponseSchema.parse(json);
|
|
3769
|
+
}
|
|
3770
|
+
async function uploadToBlob(clientToken, pathname, content) {
|
|
3771
|
+
let mod;
|
|
3772
|
+
try {
|
|
3773
|
+
mod = await import("@vercel/blob/client");
|
|
3774
|
+
} catch (err) {
|
|
3775
|
+
const code = err.code;
|
|
3776
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
3777
|
+
throw new Error(
|
|
3778
|
+
"Presigned upload requires @vercel/blob. Install it: npm install @vercel/blob"
|
|
3779
|
+
);
|
|
3780
|
+
}
|
|
3781
|
+
throw err;
|
|
3782
|
+
}
|
|
3783
|
+
const result = await mod.put(pathname, new Blob([content]), {
|
|
3784
|
+
access: "public",
|
|
3785
|
+
token: clientToken
|
|
3786
|
+
});
|
|
3787
|
+
return { url: result.url, size: Buffer.byteLength(content, "utf-8") };
|
|
3788
|
+
}
|
|
3789
|
+
async function submitManifest(apiKey, endpoint, uploadId, buildHash, files) {
|
|
3790
|
+
const baseUrl = stripTrailingSlashes(endpoint);
|
|
3791
|
+
const response = await fetch(`${baseUrl}/v1/source-maps/manifest`, {
|
|
3792
|
+
method: "POST",
|
|
3793
|
+
headers: {
|
|
3794
|
+
"Content-Type": "application/json",
|
|
3795
|
+
Authorization: `Bearer ${apiKey}`
|
|
3796
|
+
},
|
|
3797
|
+
body: JSON.stringify({ uploadId, buildHash, files })
|
|
3798
|
+
});
|
|
3799
|
+
if (!response.ok) {
|
|
3800
|
+
try {
|
|
3801
|
+
await response.text();
|
|
3802
|
+
} catch {
|
|
3803
|
+
}
|
|
3804
|
+
throw new Error(
|
|
3805
|
+
`Source map manifest submission failed: ${String(response.status)} ${response.statusText}`
|
|
3806
|
+
);
|
|
3807
|
+
}
|
|
3808
|
+
const json = await response.json();
|
|
3809
|
+
return SourceMapManifestResponseSchema.parse(json);
|
|
3810
|
+
}
|
|
3811
|
+
async function uploadSourceMapsPresigned(apiKey, endpoint, buildHash, maps, blobUploader = uploadToBlob) {
|
|
3812
|
+
if (maps.length === 0) {
|
|
3813
|
+
throw new Error("No source maps to upload");
|
|
3814
|
+
}
|
|
3815
|
+
const presigned = await requestPresignedTokens(
|
|
3816
|
+
apiKey,
|
|
3817
|
+
endpoint,
|
|
3818
|
+
buildHash,
|
|
3819
|
+
maps.map((m) => ({
|
|
3820
|
+
filePath: m.filePath,
|
|
3821
|
+
sizeBytes: Buffer.byteLength(m.content, "utf-8")
|
|
3822
|
+
}))
|
|
3823
|
+
);
|
|
3824
|
+
const mapsByPath = new Map(maps.map((m) => [m.filePath, m]));
|
|
3825
|
+
if (mapsByPath.size !== maps.length) {
|
|
3826
|
+
throw new Error("Duplicate filePath entries in source maps");
|
|
3827
|
+
}
|
|
3828
|
+
for (const token of presigned.files) {
|
|
3829
|
+
if (!mapsByPath.has(token.filePath)) {
|
|
3830
|
+
throw new Error(
|
|
3831
|
+
`Presigned token for "${token.filePath}" has no matching source map entry`
|
|
3832
|
+
);
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
const CONCURRENCY = 5;
|
|
3836
|
+
const uploadResults = [];
|
|
3837
|
+
for (let i = 0; i < presigned.files.length; i += CONCURRENCY) {
|
|
3838
|
+
const chunk = presigned.files.slice(i, i + CONCURRENCY);
|
|
3839
|
+
const chunkResults = await Promise.all(
|
|
3840
|
+
chunk.map(async (token) => {
|
|
3841
|
+
const entry = mapsByPath.get(token.filePath);
|
|
3842
|
+
const result = await blobUploader(token.clientToken, token.pathname, entry.content);
|
|
3843
|
+
return {
|
|
3844
|
+
filePath: token.filePath,
|
|
3845
|
+
sizeBytes: result.size,
|
|
3846
|
+
blobUrl: result.url
|
|
3847
|
+
};
|
|
3848
|
+
})
|
|
3849
|
+
);
|
|
3850
|
+
uploadResults.push(...chunkResults);
|
|
3851
|
+
}
|
|
3852
|
+
return submitManifest(apiKey, endpoint, presigned.uploadId, buildHash, uploadResults);
|
|
3853
|
+
}
|
|
3854
|
+
async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options) {
|
|
3855
|
+
if (maps.length === 0) {
|
|
3856
|
+
throw new Error("No source maps to upload");
|
|
3857
|
+
}
|
|
3858
|
+
const totalBytes = maps.reduce(
|
|
3859
|
+
(sum, m) => sum + Buffer.byteLength(m.content, "utf-8"),
|
|
3860
|
+
0
|
|
3861
|
+
);
|
|
3862
|
+
if (totalBytes < PRESIGNED_THRESHOLD_BYTES) {
|
|
3863
|
+
return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
|
|
3864
|
+
}
|
|
3865
|
+
const checkAvailable = options?.checkBlobAvailable ?? (async () => {
|
|
3866
|
+
try {
|
|
3867
|
+
await import("@vercel/blob/client");
|
|
3868
|
+
return true;
|
|
3869
|
+
} catch {
|
|
3870
|
+
return false;
|
|
3871
|
+
}
|
|
3872
|
+
});
|
|
3873
|
+
const blobAvailable = await checkAvailable();
|
|
3874
|
+
if (blobAvailable) {
|
|
3875
|
+
return uploadSourceMapsPresigned(
|
|
3876
|
+
apiKey,
|
|
3877
|
+
endpoint,
|
|
3878
|
+
buildHash,
|
|
3879
|
+
maps,
|
|
3880
|
+
options?.blobUploader
|
|
3881
|
+
);
|
|
3882
|
+
}
|
|
3883
|
+
sdkLog(
|
|
3884
|
+
"warn",
|
|
3885
|
+
`[glasstrace] Build exceeds 4.5MB (${totalBytes} bytes). Install @vercel/blob for presigned uploads to avoid serverless body size limits. Falling back to legacy upload.`
|
|
3886
|
+
);
|
|
3887
|
+
return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
|
|
3888
|
+
}
|
|
3710
3889
|
|
|
3711
3890
|
// src/config-wrapper.ts
|
|
3712
3891
|
function withGlasstraceConfig(nextConfig) {
|
|
@@ -3824,6 +4003,7 @@ function captureError(error) {
|
|
|
3824
4003
|
export {
|
|
3825
4004
|
GlasstraceExporter,
|
|
3826
4005
|
GlasstraceSpanProcessor,
|
|
4006
|
+
PRESIGNED_THRESHOLD_BYTES,
|
|
3827
4007
|
SdkError,
|
|
3828
4008
|
SessionManager,
|
|
3829
4009
|
buildImportGraph,
|
|
@@ -3851,6 +4031,8 @@ export {
|
|
|
3851
4031
|
saveCachedConfig,
|
|
3852
4032
|
sendInitRequest,
|
|
3853
4033
|
uploadSourceMaps,
|
|
4034
|
+
uploadSourceMapsAuto,
|
|
4035
|
+
uploadSourceMapsPresigned,
|
|
3854
4036
|
withGlasstraceConfig
|
|
3855
4037
|
};
|
|
3856
4038
|
//# sourceMappingURL=index.js.map
|