@glasstrace/sdk 0.7.0 → 0.7.2

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
@@ -306,8 +306,12 @@ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null,
306
306
  /**
307
307
  * Returns the current capture config from the three-tier fallback chain:
308
308
  * 1. In-memory config from latest init response
309
- * 2. File cache
309
+ * 2. File cache (read at most once per process lifetime)
310
310
  * 3. DEFAULT_CAPTURE_CONFIG
311
+ *
312
+ * The disk read is cached via `configCacheChecked` to avoid repeated
313
+ * synchronous I/O on the hot path (called by GlasstraceExporter on
314
+ * every span export batch).
311
315
  */
312
316
  declare function getActiveConfig(): CaptureConfig;
313
317
 
package/dist/index.d.ts CHANGED
@@ -306,8 +306,12 @@ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null,
306
306
  /**
307
307
  * Returns the current capture config from the three-tier fallback chain:
308
308
  * 1. In-memory config from latest init response
309
- * 2. File cache
309
+ * 2. File cache (read at most once per process lifetime)
310
310
  * 3. DEFAULT_CAPTURE_CONFIG
311
+ *
312
+ * The disk read is cached via `configCacheChecked` to avoid repeated
313
+ * synchronous I/O on the hot path (called by GlasstraceExporter on
314
+ * every span export batch).
311
315
  */
312
316
  declare function getActiveConfig(): CaptureConfig;
313
317
 
package/dist/index.js CHANGED
@@ -186,13 +186,14 @@ function classifyFetchTarget(url) {
186
186
 
187
187
  // src/init-client.ts
188
188
  import { readFileSync } from "fs";
189
- import { writeFile, mkdir } from "fs/promises";
189
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
190
190
  import { join } from "path";
191
191
  var GLASSTRACE_DIR = ".glasstrace";
192
192
  var CONFIG_FILE = "config";
193
193
  var TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1e3;
194
194
  var INIT_TIMEOUT_MS = 1e4;
195
195
  var currentConfig = null;
196
+ var configCacheChecked = false;
196
197
  var rateLimitBackoff = false;
197
198
  function loadCachedConfig(projectRoot) {
198
199
  const root = projectRoot ?? process.cwd();
@@ -222,12 +223,14 @@ async function saveCachedConfig(response, projectRoot) {
222
223
  const dirPath = join(root, GLASSTRACE_DIR);
223
224
  const configPath = join(dirPath, CONFIG_FILE);
224
225
  try {
225
- await mkdir(dirPath, { recursive: true });
226
+ await mkdir(dirPath, { recursive: true, mode: 448 });
227
+ await chmod(dirPath, 448);
226
228
  const cached = {
227
229
  response,
228
230
  cachedAt: Date.now()
229
231
  };
230
- await writeFile(configPath, JSON.stringify(cached), "utf-8");
232
+ await writeFile(configPath, JSON.stringify(cached), { encoding: "utf-8", mode: 384 });
233
+ await chmod(configPath, 384);
231
234
  } catch (err) {
232
235
  console.warn(
233
236
  `[glasstrace] Failed to cache config to ${configPath}: ${err instanceof Error ? err.message : String(err)}`
@@ -280,6 +283,78 @@ async function sendInitRequest(config, anonKey, sdkVersion, importGraph, healthR
280
283
  const body = await response.json();
281
284
  return SdkInitResponseSchema.parse(body);
282
285
  }
286
+ async function writeClaimedKey(newApiKey, projectRoot) {
287
+ const root = projectRoot ?? process.cwd();
288
+ const envLocalPath = join(root, ".env.local");
289
+ let envLocalWritten = false;
290
+ try {
291
+ let content;
292
+ try {
293
+ content = await readFile(envLocalPath, "utf-8");
294
+ if (/^GLASSTRACE_API_KEY=.*/m.test(content)) {
295
+ content = content.replace(
296
+ /^GLASSTRACE_API_KEY=.*$/gm,
297
+ `GLASSTRACE_API_KEY=${newApiKey}`
298
+ );
299
+ } else {
300
+ if (content.length > 0 && !content.endsWith("\n")) {
301
+ content += "\n";
302
+ }
303
+ content += `GLASSTRACE_API_KEY=${newApiKey}
304
+ `;
305
+ }
306
+ } catch (readErr) {
307
+ const code = readErr instanceof Error ? readErr.code : void 0;
308
+ if (code !== "ENOENT") {
309
+ throw readErr;
310
+ }
311
+ content = `GLASSTRACE_API_KEY=${newApiKey}
312
+ `;
313
+ }
314
+ await writeFile(envLocalPath, content, { encoding: "utf-8", mode: 384 });
315
+ await chmod(envLocalPath, 384);
316
+ envLocalWritten = true;
317
+ } catch {
318
+ }
319
+ if (envLocalWritten) {
320
+ try {
321
+ process.stderr.write(
322
+ "[glasstrace] Account claimed! API key written to .env.local. Restart your dev server to use it.\n"
323
+ );
324
+ } catch {
325
+ }
326
+ return;
327
+ }
328
+ let claimedKeyWritten = false;
329
+ try {
330
+ const dirPath = join(root, GLASSTRACE_DIR);
331
+ await mkdir(dirPath, { recursive: true, mode: 448 });
332
+ await chmod(dirPath, 448);
333
+ const claimedKeyPath = join(dirPath, "claimed-key");
334
+ await writeFile(claimedKeyPath, newApiKey, {
335
+ encoding: "utf-8",
336
+ mode: 384
337
+ });
338
+ await chmod(claimedKeyPath, 384);
339
+ claimedKeyWritten = true;
340
+ } catch {
341
+ }
342
+ if (claimedKeyWritten) {
343
+ try {
344
+ process.stderr.write(
345
+ "[glasstrace] Account claimed! API key written to .glasstrace/claimed-key. Copy it to your .env.local file.\n"
346
+ );
347
+ } catch {
348
+ }
349
+ return;
350
+ }
351
+ try {
352
+ process.stderr.write(
353
+ "[glasstrace] Account claimed but could not write key to disk. Visit your dashboard settings to rotate and retrieve a new API key.\n"
354
+ );
355
+ } catch {
356
+ }
357
+ }
283
358
  async function performInit(config, anonKey, sdkVersion) {
284
359
  if (rateLimitBackoff) {
285
360
  rateLimitBackoff = false;
@@ -308,14 +383,8 @@ async function performInit(config, anonKey, sdkVersion) {
308
383
  await saveCachedConfig(result);
309
384
  if (result.claimResult) {
310
385
  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
- );
386
+ await writeClaimedKey(result.claimResult.newApiKey);
387
+ } catch {
319
388
  }
320
389
  return { claimResult: result.claimResult };
321
390
  }
@@ -366,9 +435,13 @@ function getActiveConfig() {
366
435
  if (currentConfig) {
367
436
  return currentConfig.config;
368
437
  }
369
- const cached = loadCachedConfig();
370
- if (cached) {
371
- return cached.config;
438
+ if (!configCacheChecked) {
439
+ configCacheChecked = true;
440
+ const cached = loadCachedConfig();
441
+ if (cached) {
442
+ currentConfig = cached;
443
+ return cached.config;
444
+ }
372
445
  }
373
446
  return { ...DEFAULT_CAPTURE_CONFIG };
374
447
  }
@@ -3564,15 +3637,7 @@ function registerGlasstrace(options) {
3564
3637
  () => Promise.resolve(anonKey),
3565
3638
  () => sessionManager.getSessionId(getResolvedApiKey())
3566
3639
  );
3567
- if (config.verbose) {
3568
- console.info("[glasstrace] Background init firing.");
3569
- }
3570
- const initResult = await performInit(config, anonKey, "0.7.0");
3571
- if (initResult?.claimResult) {
3572
- setResolvedApiKey(initResult.claimResult.newApiKey);
3573
- notifyApiKeyResolved();
3574
- }
3575
- maybeInstallConsoleCapture();
3640
+ await backgroundInit(config, anonKey, currentGeneration);
3576
3641
  } catch (err) {
3577
3642
  console.warn(
3578
3643
  `[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
@@ -3588,15 +3653,7 @@ function registerGlasstrace(options) {
3588
3653
  notifyApiKeyResolved();
3589
3654
  effectiveKey = anonKey;
3590
3655
  if (currentGeneration !== registrationGeneration) return;
3591
- if (config.verbose) {
3592
- console.info("[glasstrace] Background init firing.");
3593
- }
3594
- const initResult = await performInit(config, anonKey, "0.7.0");
3595
- if (initResult?.claimResult) {
3596
- setResolvedApiKey(initResult.claimResult.newApiKey);
3597
- notifyApiKeyResolved();
3598
- }
3599
- maybeInstallConsoleCapture();
3656
+ await backgroundInit(config, anonKey, currentGeneration);
3600
3657
  } catch (err) {
3601
3658
  console.warn(
3602
3659
  `[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
@@ -3614,11 +3671,7 @@ function registerGlasstrace(options) {
3614
3671
  } catch {
3615
3672
  }
3616
3673
  if (currentGeneration !== registrationGeneration) return;
3617
- if (config.verbose) {
3618
- console.info("[glasstrace] Background init firing.");
3619
- }
3620
- await performInit(config, anonKeyForInit, "0.7.0");
3621
- maybeInstallConsoleCapture();
3674
+ await backgroundInit(config, anonKeyForInit, currentGeneration);
3622
3675
  } catch (err) {
3623
3676
  console.warn(
3624
3677
  `[glasstrace] Background init failed: ${err instanceof Error ? err.message : String(err)}`
@@ -3635,6 +3688,18 @@ function registerGlasstrace(options) {
3635
3688
  );
3636
3689
  }
3637
3690
  }
3691
+ async function backgroundInit(config, anonKeyForInit, generation) {
3692
+ if (config.verbose) {
3693
+ console.info("[glasstrace] Background init firing.");
3694
+ }
3695
+ const initResult = await performInit(config, anonKeyForInit, "0.7.2");
3696
+ if (generation !== registrationGeneration) return;
3697
+ if (initResult?.claimResult) {
3698
+ setResolvedApiKey(initResult.claimResult.newApiKey);
3699
+ notifyApiKeyResolved();
3700
+ }
3701
+ maybeInstallConsoleCapture();
3702
+ }
3638
3703
  function getDiscoveryHandler() {
3639
3704
  return discoveryHandler;
3640
3705
  }
@@ -3658,7 +3723,7 @@ function isDiscoveryEnabled(config) {
3658
3723
  import * as fs2 from "fs/promises";
3659
3724
  import * as path2 from "path";
3660
3725
  import * as crypto from "crypto";
3661
- import { execSync } from "child_process";
3726
+ import { execFileSync } from "child_process";
3662
3727
  async function collectSourceMaps(buildDir) {
3663
3728
  const results = [];
3664
3729
  try {
@@ -3692,7 +3757,7 @@ async function walkDir(baseDir, currentDir, results) {
3692
3757
  }
3693
3758
  async function computeBuildHash(maps) {
3694
3759
  try {
3695
- const sha = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
3760
+ const sha = execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf-8" }).trim();
3696
3761
  if (sha) {
3697
3762
  return sha;
3698
3763
  }
@@ -3994,6 +4059,9 @@ function captureError(error) {
3994
4059
  };
3995
4060
  if (error instanceof Error) {
3996
4061
  attributes["error.type"] = error.constructor.name;
4062
+ if (error.stack) {
4063
+ attributes["error.stack"] = error.stack;
4064
+ }
3997
4065
  }
3998
4066
  span.addEvent("glasstrace.error", attributes);
3999
4067
  maybeShowMcpNudge(String(error));