@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/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-Y6V7BTF3.js";
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
- SourceMapManifestResponseSchema,
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({ key: anonKey, sessionId }),
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, path3) {
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 += path3;
2220
+ url += path2;
2244
2221
  try {
2245
2222
  new URL(url);
2246
2223
  } catch {
2247
- diag.warn(`Configuration: Provided URL appended with '${path3}' is not a valid URL, using 'undefined' instead of '${url}'`);
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.9.1");
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 maps = await collectSourceMaps(distDir);
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 computeBuildHash(maps);
4047
- await uploadSourceMaps(apiKey, endpoint, buildHash, maps);
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,