@bitfab/sdk 0.27.2 → 0.28.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.
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-MD4XQGAF.js";
14
14
 
15
15
  // src/version.generated.ts
16
- var __version__ = "0.27.2";
16
+ var __version__ = "0.28.0";
17
17
 
18
18
  // src/constants.ts
19
19
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -168,6 +168,13 @@ var HttpClient = class {
168
168
  this.serviceUrl = config.serviceUrl;
169
169
  this.timeout = config.timeout ?? 12e4;
170
170
  }
171
+ /**
172
+ * Resolve the API key at the moment it is needed (request time), invoking
173
+ * the function form if one was supplied. Never read at construction.
174
+ */
175
+ resolveApiKey() {
176
+ return typeof this.apiKey === "function" ? this.apiKey() : this.apiKey;
177
+ }
171
178
  /**
172
179
  * Make an HTTP request to the Bitfab API. Defaults to POST; pass
173
180
  * `options.method` to use a different verb (e.g. "PATCH").
@@ -198,7 +205,7 @@ var HttpClient = class {
198
205
  method,
199
206
  headers: {
200
207
  "Content-Type": "application/json",
201
- Authorization: `Bearer ${this.apiKey}`
208
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`
202
209
  },
203
210
  body,
204
211
  signal: controller.signal
@@ -359,7 +366,7 @@ var HttpClient = class {
359
366
  try {
360
367
  const response = await fetch(url, {
361
368
  method: "GET",
362
- headers: { Authorization: `Bearer ${this.apiKey}` },
369
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
363
370
  signal: controller.signal
364
371
  });
365
372
  if (!response.ok) {
@@ -395,7 +402,7 @@ var HttpClient = class {
395
402
  try {
396
403
  const response = await fetch(url, {
397
404
  method: "GET",
398
- headers: { Authorization: `Bearer ${this.apiKey}` },
405
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
399
406
  signal: controller.signal
400
407
  });
401
408
  if (!response.ok) {
@@ -2706,6 +2713,12 @@ function getCurrentTrace() {
2706
2713
  }
2707
2714
  };
2708
2715
  }
2716
+ function readEnv(name) {
2717
+ if (typeof process !== "undefined" && process.env) {
2718
+ return process.env[name];
2719
+ }
2720
+ return void 0;
2721
+ }
2709
2722
  var Bitfab = class {
2710
2723
  /**
2711
2724
  * Initialize the Bitfab client.
@@ -2713,30 +2726,75 @@ var Bitfab = class {
2713
2726
  * @param config - Configuration options for the client
2714
2727
  */
2715
2728
  constructor(config) {
2716
- this.apiKey = config.apiKey;
2729
+ /** Gate the empty-key warning to fire at most once. */
2730
+ this.apiKeyWarned = false;
2731
+ this.apiKeyConfig = config.apiKey;
2717
2732
  this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
2718
2733
  this.timeout = config.timeout ?? 12e4;
2719
2734
  this.envVars = config.envVars ?? {};
2720
- const enabled = config.enabled ?? true;
2721
- if (enabled && (!config.apiKey || config.apiKey.trim() === "")) {
2722
- console.warn(
2723
- "Bitfab: apiKey is empty \u2014 tracing is disabled. Provide a valid API key to enable tracing."
2724
- );
2725
- this.enabled = false;
2726
- } else {
2727
- this.enabled = enabled;
2728
- }
2735
+ this.explicitlyEnabled = config.enabled ?? true;
2736
+ this.strict = config.strict ?? false;
2729
2737
  this.bamlClient = config.bamlClient ?? null;
2730
2738
  if (config.dbSnapshot) {
2731
2739
  validateDbSnapshotConfig(config.dbSnapshot);
2732
2740
  }
2733
2741
  this.dbSnapshot = config.dbSnapshot;
2734
2742
  this.httpClient = new HttpClient({
2735
- apiKey: this.apiKey,
2743
+ apiKey: () => this.resolveApiKey(),
2736
2744
  serviceUrl: this.serviceUrl,
2737
2745
  timeout: this.timeout
2738
2746
  });
2739
2747
  }
2748
+ /**
2749
+ * Resolve the API key lazily, the first time a span actually needs it.
2750
+ *
2751
+ * The key is intentionally NOT read at construction. In ESM, a shim that
2752
+ * does `new Bitfab({ apiKey: process.env.BITFAB_API_KEY })` is hoisted and
2753
+ * evaluated before the importing script's body runs `dotenv.config()`, so
2754
+ * the key would be empty at construction even though it is set moments
2755
+ * later. Resolving here (at first `withSpan` call / first request) reads
2756
+ * the key after env loading has run.
2757
+ *
2758
+ * Resolution order: the configured value (string, or function called each
2759
+ * time it is still unresolved), then a fallback read of `BITFAB_API_KEY`
2760
+ * from the environment. Once a non-empty key is found it is cached, so an
2761
+ * early resolve that found nothing never poisons a later one.
2762
+ */
2763
+ resolveApiKey() {
2764
+ if (this.resolvedApiKey !== void 0) {
2765
+ return this.resolvedApiKey;
2766
+ }
2767
+ const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
2768
+ const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv("BITFAB_API_KEY");
2769
+ const key = candidate && candidate.trim() !== "" ? candidate : void 0;
2770
+ if (key) {
2771
+ this.resolvedApiKey = key;
2772
+ return key;
2773
+ }
2774
+ if (this.strict) {
2775
+ throw new BitfabError(
2776
+ "Bitfab: no API key resolved. Set BITFAB_API_KEY or pass apiKey to new Bitfab(). If a script loads env with dotenv, load it before the module that constructs the client is imported (e.g. `node --env-file=.env script.ts`), or pass `apiKey: () => process.env.BITFAB_API_KEY`."
2777
+ );
2778
+ }
2779
+ if (this.explicitlyEnabled && !this.apiKeyWarned) {
2780
+ this.apiKeyWarned = true;
2781
+ console.warn(
2782
+ "Bitfab: apiKey is empty \u2014 tracing is disabled. Provide a valid API key to enable tracing."
2783
+ );
2784
+ }
2785
+ return void 0;
2786
+ }
2787
+ /**
2788
+ * Whether tracing should run, decided lazily at call time (not frozen at
2789
+ * construction). An explicit `enabled: false` short-circuits without ever
2790
+ * touching the key.
2791
+ */
2792
+ isTracingEnabled() {
2793
+ if (!this.explicitlyEnabled) {
2794
+ return false;
2795
+ }
2796
+ return this.resolveApiKey() !== void 0;
2797
+ }
2740
2798
  /**
2741
2799
  * Fetch the function with its current version and BAML prompt from the server.
2742
2800
  *
@@ -2829,7 +2887,9 @@ var Bitfab = class {
2829
2887
  */
2830
2888
  getOpenAiTracingProcessor() {
2831
2889
  return new BitfabOpenAITracingProcessor({
2832
- apiKey: this.apiKey,
2890
+ // Resolved at getter-call time (framework handlers are set up after env
2891
+ // loads); the withSpan path stays lazy via the constructor HttpClient thunk.
2892
+ apiKey: this.resolveApiKey(),
2833
2893
  serviceUrl: this.serviceUrl,
2834
2894
  getActiveSpanContext: () => {
2835
2895
  const stack = getSpanStack();
@@ -2888,7 +2948,7 @@ var Bitfab = class {
2888
2948
  */
2889
2949
  getLangGraphCallbackHandler(traceFunctionKey) {
2890
2950
  return new BitfabLangGraphCallbackHandler({
2891
- apiKey: this.apiKey,
2951
+ apiKey: this.resolveApiKey(),
2892
2952
  traceFunctionKey,
2893
2953
  serviceUrl: this.serviceUrl,
2894
2954
  getActiveSpanContext: () => {
@@ -2940,7 +3000,7 @@ var Bitfab = class {
2940
3000
  */
2941
3001
  getClaudeAgentHandler(traceFunctionKey) {
2942
3002
  return new BitfabClaudeAgentHandler({
2943
- apiKey: this.apiKey,
3003
+ apiKey: this.resolveApiKey(),
2944
3004
  traceFunctionKey,
2945
3005
  serviceUrl: this.serviceUrl,
2946
3006
  getActiveSpanContext: () => {
@@ -3112,9 +3172,8 @@ var Bitfab = class {
3112
3172
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
3113
3173
  */
3114
3174
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
3115
- if (!this.enabled) {
3116
- const fn2 = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
3117
- return fn2;
3175
+ if (!this.explicitlyEnabled) {
3176
+ return typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
3118
3177
  }
3119
3178
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
3120
3179
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
@@ -3129,6 +3188,9 @@ var Bitfab = class {
3129
3188
  }
3130
3189
  })();
3131
3190
  const wrappedFn = function(...args) {
3191
+ if (!self.isTracingEnabled()) {
3192
+ return fn.apply(this, args);
3193
+ }
3132
3194
  if (!asyncLocalStorage && !isAsyncStorageInitDone()) {
3133
3195
  return asyncLocalStorageReady.then(
3134
3196
  () => wrappedFn.apply(this, args)
@@ -3389,7 +3451,7 @@ var Bitfab = class {
3389
3451
  return {
3390
3452
  traceId,
3391
3453
  addContext: (context) => {
3392
- if (!this.enabled) {
3454
+ if (!this.isTracingEnabled()) {
3393
3455
  return Promise.resolve();
3394
3456
  }
3395
3457
  if (typeof context !== "object" || context === null) {
@@ -3400,7 +3462,7 @@ var Bitfab = class {
3400
3462
  });
3401
3463
  },
3402
3464
  setMetadata: (metadata) => {
3403
- if (!this.enabled) {
3465
+ if (!this.isTracingEnabled()) {
3404
3466
  return Promise.resolve();
3405
3467
  }
3406
3468
  if (typeof metadata !== "object" || metadata === null) {
@@ -3409,7 +3471,7 @@ var Bitfab = class {
3409
3471
  return this.httpClient.patchTrace(traceId, { mergeMetadata: metadata });
3410
3472
  },
3411
3473
  setSessionId: (sessionId) => {
3412
- if (!this.enabled) {
3474
+ if (!this.isTracingEnabled()) {
3413
3475
  return Promise.resolve();
3414
3476
  }
3415
3477
  if (typeof sessionId !== "string" || sessionId.length === 0) {
@@ -3798,4 +3860,4 @@ export {
3798
3860
  BitfabFunction,
3799
3861
  finalizers
3800
3862
  };
3801
- //# sourceMappingURL=chunk-DW246LVL.js.map
3863
+ //# sourceMappingURL=chunk-RS5Z6YXY.js.map