@blinkdotnew/sdk 2.6.0 → 2.6.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.mts CHANGED
@@ -941,7 +941,17 @@ declare class HttpClient {
941
941
  private readonly secretKey?;
942
942
  private getToken;
943
943
  private getValidToken?;
944
- constructor(config: BlinkClientConfig, getToken: () => string | null, getValidToken?: () => Promise<string | null>);
944
+ private forceRefreshToken?;
945
+ constructor(config: BlinkClientConfig, getToken: () => string | null, getValidToken?: () => Promise<string | null>, forceRefreshToken?: () => Promise<string | null>);
946
+ /**
947
+ * Whether a 401 on this request is worth recovering from by forcing a
948
+ * user-token refresh and retrying once.
949
+ *
950
+ * Only meaningful for user-JWT auth: server-side `secretKey` 401s are real
951
+ * authorization failures a refresh cannot fix, and publishable-key requests
952
+ * carry no user token to refresh.
953
+ */
954
+ private canAttemptTokenRefresh;
945
955
  private shouldAttachPublishableKey;
946
956
  private shouldSkipSecretKey;
947
957
  private getAuthorizationHeader;
@@ -1285,6 +1295,7 @@ declare class BlinkAuth {
1285
1295
  private initializationPromise;
1286
1296
  private isInitialized;
1287
1297
  private storage;
1298
+ private refreshPromise;
1288
1299
  constructor(config: BlinkClientConfig);
1289
1300
  /**
1290
1301
  * Generate project-scoped storage key
@@ -1672,6 +1683,18 @@ declare class BlinkAuth {
1672
1683
  * Refresh access token using refresh token
1673
1684
  */
1674
1685
  refreshToken(): Promise<boolean>;
1686
+ /**
1687
+ * Force a server-side token refresh and return the resulting access token.
1688
+ *
1689
+ * Used by the HTTP client to recover from a 401 even when the client-side
1690
+ * expiry heuristics believe the access token is still valid (clock skew, a
1691
+ * restored session with a stale `issued_at`, or a refresh race). Delegates to
1692
+ * {@link refreshToken} so it shares the single-flight guard.
1693
+ *
1694
+ * @returns The new access token, or null if refresh was not possible.
1695
+ */
1696
+ forceRefreshAccessToken(): Promise<string | null>;
1697
+ private performTokenRefresh;
1675
1698
  /**
1676
1699
  * Add auth state change listener
1677
1700
  */
package/dist/index.d.ts CHANGED
@@ -941,7 +941,17 @@ declare class HttpClient {
941
941
  private readonly secretKey?;
942
942
  private getToken;
943
943
  private getValidToken?;
944
- constructor(config: BlinkClientConfig, getToken: () => string | null, getValidToken?: () => Promise<string | null>);
944
+ private forceRefreshToken?;
945
+ constructor(config: BlinkClientConfig, getToken: () => string | null, getValidToken?: () => Promise<string | null>, forceRefreshToken?: () => Promise<string | null>);
946
+ /**
947
+ * Whether a 401 on this request is worth recovering from by forcing a
948
+ * user-token refresh and retrying once.
949
+ *
950
+ * Only meaningful for user-JWT auth: server-side `secretKey` 401s are real
951
+ * authorization failures a refresh cannot fix, and publishable-key requests
952
+ * carry no user token to refresh.
953
+ */
954
+ private canAttemptTokenRefresh;
945
955
  private shouldAttachPublishableKey;
946
956
  private shouldSkipSecretKey;
947
957
  private getAuthorizationHeader;
@@ -1285,6 +1295,7 @@ declare class BlinkAuth {
1285
1295
  private initializationPromise;
1286
1296
  private isInitialized;
1287
1297
  private storage;
1298
+ private refreshPromise;
1288
1299
  constructor(config: BlinkClientConfig);
1289
1300
  /**
1290
1301
  * Generate project-scoped storage key
@@ -1672,6 +1683,18 @@ declare class BlinkAuth {
1672
1683
  * Refresh access token using refresh token
1673
1684
  */
1674
1685
  refreshToken(): Promise<boolean>;
1686
+ /**
1687
+ * Force a server-side token refresh and return the resulting access token.
1688
+ *
1689
+ * Used by the HTTP client to recover from a 401 even when the client-side
1690
+ * expiry heuristics believe the access token is still valid (clock skew, a
1691
+ * restored session with a stale `issued_at`, or a refresh race). Delegates to
1692
+ * {@link refreshToken} so it shares the single-flight guard.
1693
+ *
1694
+ * @returns The new access token, or null if refresh was not possible.
1695
+ */
1696
+ forceRefreshAccessToken(): Promise<string | null>;
1697
+ private performTokenRefresh;
1675
1698
  /**
1676
1699
  * Add auth state change listener
1677
1700
  */
package/dist/index.js CHANGED
@@ -421,12 +421,25 @@ var HttpClient = class {
421
421
  // Permanent, non-expiring key (like Stripe's sk_live_...)
422
422
  getToken;
423
423
  getValidToken;
424
- constructor(config, getToken, getValidToken) {
424
+ forceRefreshToken;
425
+ constructor(config, getToken, getValidToken, forceRefreshToken) {
425
426
  this.projectId = config.projectId;
426
427
  this.publishableKey = config.publishableKey;
427
428
  this.secretKey = config.secretKey || config.serviceToken;
428
429
  this.getToken = getToken;
429
430
  this.getValidToken = getValidToken;
431
+ this.forceRefreshToken = forceRefreshToken;
432
+ }
433
+ /**
434
+ * Whether a 401 on this request is worth recovering from by forcing a
435
+ * user-token refresh and retrying once.
436
+ *
437
+ * Only meaningful for user-JWT auth: server-side `secretKey` 401s are real
438
+ * authorization failures a refresh cannot fix, and publishable-key requests
439
+ * carry no user token to refresh.
440
+ */
441
+ canAttemptTokenRefresh(token) {
442
+ return !!this.forceRefreshToken && !this.secretKey && !!token;
430
443
  }
431
444
  shouldAttachPublishableKey(path, method) {
432
445
  if (method !== "GET" && method !== "POST") return false;
@@ -457,28 +470,37 @@ var HttpClient = class {
457
470
  */
458
471
  async request(path, options = {}) {
459
472
  const url = this.buildUrl(path, options.searchParams);
460
- const token = this.getValidToken ? await this.getValidToken() : this.getToken();
461
473
  const method = options.method || "GET";
462
- const headers = {
463
- "Content-Type": "application/json",
464
- ...options.headers
465
- };
466
- const auth = this.getAuthorizationHeader(url, token);
467
- if (auth) {
468
- headers.Authorization = auth;
469
- } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
470
- headers["x-blink-publishable-key"] = this.publishableKey;
471
- }
472
- const requestInit = {
473
- method,
474
- headers,
475
- signal: options.signal
474
+ const sendRequest = (token) => {
475
+ const headers = {
476
+ "Content-Type": "application/json",
477
+ ...options.headers
478
+ };
479
+ const auth = this.getAuthorizationHeader(url, token);
480
+ if (auth) {
481
+ headers.Authorization = auth;
482
+ } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
483
+ headers["x-blink-publishable-key"] = this.publishableKey;
484
+ }
485
+ const requestInit = {
486
+ method,
487
+ headers,
488
+ signal: options.signal
489
+ };
490
+ if (options.body && method !== "GET") {
491
+ requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
492
+ }
493
+ return fetch(url, requestInit);
476
494
  };
477
- if (options.body && method !== "GET") {
478
- requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
479
- }
480
495
  try {
481
- const response = await fetch(url, requestInit);
496
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
497
+ let response = await sendRequest(token);
498
+ if (response.status === 401 && this.canAttemptTokenRefresh(token)) {
499
+ const refreshedToken = await this.forceRefreshToken();
500
+ if (refreshedToken) {
501
+ response = await sendRequest(refreshedToken);
502
+ }
503
+ }
482
504
  if (!response.ok) {
483
505
  await this.handleErrorResponse(response);
484
506
  }
@@ -636,7 +658,6 @@ var HttpClient = class {
636
658
  */
637
659
  async uploadFile(path, file, filePath, options = {}) {
638
660
  const url = this.buildUrl(path);
639
- const token = this.getValidToken ? await this.getValidToken() : this.getToken();
640
661
  const formData = new FormData();
641
662
  if (file instanceof File) {
642
663
  formData.append("file", file);
@@ -650,41 +671,57 @@ var HttpClient = class {
650
671
  throw new BlinkValidationError("Unsupported file type");
651
672
  }
652
673
  formData.append("path", filePath);
653
- const headers = {};
654
- const auth = this.getAuthorizationHeader(url, token);
655
- if (auth) {
656
- headers.Authorization = auth;
657
- } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
658
- headers["x-blink-publishable-key"] = this.publishableKey;
659
- }
660
- try {
661
- if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
662
- return this.uploadWithProgress(url, formData, headers, options.onProgress);
674
+ const attempt = async (allowRetry) => {
675
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
676
+ const headers = {};
677
+ const auth = this.getAuthorizationHeader(url, token);
678
+ if (auth) {
679
+ headers.Authorization = auth;
680
+ } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
681
+ headers["x-blink-publishable-key"] = this.publishableKey;
663
682
  }
664
- const response = await fetch(url, {
665
- method: "POST",
666
- headers,
667
- body: formData
668
- });
669
- if (!response.ok) {
670
- await this.handleErrorResponse(response);
671
- }
672
- const data = await this.parseResponse(response);
673
- return {
674
- data,
675
- status: response.status,
676
- headers: response.headers
677
- };
678
- } catch (error) {
679
- if (error instanceof BlinkError) {
680
- throw error;
683
+ try {
684
+ if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
685
+ return await this.uploadWithProgress(url, formData, headers, options.onProgress);
686
+ }
687
+ const response = await fetch(url, {
688
+ method: "POST",
689
+ headers,
690
+ body: formData
691
+ });
692
+ if (response.status === 401 && allowRetry && this.canAttemptTokenRefresh(token)) {
693
+ const refreshedToken = await this.forceRefreshToken();
694
+ if (refreshedToken) {
695
+ return attempt(false);
696
+ }
697
+ }
698
+ if (!response.ok) {
699
+ await this.handleErrorResponse(response);
700
+ }
701
+ const data = await this.parseResponse(response);
702
+ return {
703
+ data,
704
+ status: response.status,
705
+ headers: response.headers
706
+ };
707
+ } catch (error) {
708
+ if (allowRetry && error instanceof BlinkAuthError && this.canAttemptTokenRefresh(token)) {
709
+ const refreshedToken = await this.forceRefreshToken();
710
+ if (refreshedToken) {
711
+ return attempt(false);
712
+ }
713
+ }
714
+ if (error instanceof BlinkError) {
715
+ throw error;
716
+ }
717
+ throw new BlinkNetworkError(
718
+ `File upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
719
+ 0,
720
+ { originalError: error }
721
+ );
681
722
  }
682
- throw new BlinkNetworkError(
683
- `File upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
684
- 0,
685
- { originalError: error }
686
- );
687
- }
723
+ };
724
+ return attempt(true);
688
725
  }
689
726
  /**
690
727
  * Upload with progress tracking using XMLHttpRequest
@@ -1376,6 +1413,7 @@ var BlinkAuth = class {
1376
1413
  initializationPromise = null;
1377
1414
  isInitialized = false;
1378
1415
  storage;
1416
+ refreshPromise = null;
1379
1417
  constructor(config) {
1380
1418
  this.config = config;
1381
1419
  if (!config.projectId) {
@@ -2823,10 +2861,35 @@ var BlinkAuth = class {
2823
2861
  * Refresh access token using refresh token
2824
2862
  */
2825
2863
  async refreshToken() {
2864
+ if (this.refreshPromise) {
2865
+ return this.refreshPromise;
2866
+ }
2826
2867
  const refreshToken = this.authState.tokens?.refresh_token;
2827
2868
  if (!refreshToken) {
2828
2869
  return false;
2829
2870
  }
2871
+ this.refreshPromise = this.performTokenRefresh(refreshToken);
2872
+ try {
2873
+ return await this.refreshPromise;
2874
+ } finally {
2875
+ this.refreshPromise = null;
2876
+ }
2877
+ }
2878
+ /**
2879
+ * Force a server-side token refresh and return the resulting access token.
2880
+ *
2881
+ * Used by the HTTP client to recover from a 401 even when the client-side
2882
+ * expiry heuristics believe the access token is still valid (clock skew, a
2883
+ * restored session with a stale `issued_at`, or a refresh race). Delegates to
2884
+ * {@link refreshToken} so it shares the single-flight guard.
2885
+ *
2886
+ * @returns The new access token, or null if refresh was not possible.
2887
+ */
2888
+ async forceRefreshAccessToken() {
2889
+ const refreshed = await this.refreshToken();
2890
+ return refreshed ? this.authState.tokens?.access_token || null : null;
2891
+ }
2892
+ async performTokenRefresh(refreshToken) {
2830
2893
  try {
2831
2894
  const response = await fetch(`${this.authUrl}/api/auth/refresh`, {
2832
2895
  method: "POST",
@@ -5136,6 +5199,9 @@ var BlinkAIImpl = class {
5136
5199
  * ```
5137
5200
  */
5138
5201
  createAgent(options) {
5202
+ if (!options) {
5203
+ throw new BlinkAIError("createAgent(options) requires an options object with at least a `model`.");
5204
+ }
5139
5205
  const agent = new Agent(options);
5140
5206
  agent._setHttpClient(this.httpClient);
5141
5207
  return agent;
@@ -5150,6 +5216,11 @@ var BlinkAIImpl = class {
5150
5216
  * @returns The same Agent instance (with httpClient set)
5151
5217
  */
5152
5218
  bindAgent(agent) {
5219
+ if (!agent) {
5220
+ throw new BlinkAIError(
5221
+ "bindAgent(agent) requires an Agent instance but received null/undefined. Create one first with `new Agent({...})` or `blink.ai.createAgent({...})`. (The useAgent() hook stays idle while its `agent` is null, so this only fires on a direct bindAgent() call with a missing agent.)"
5222
+ );
5223
+ }
5153
5224
  agent._setHttpClient(this.httpClient);
5154
5225
  return agent;
5155
5226
  }
@@ -6956,7 +7027,8 @@ var BlinkClientImpl = class {
6956
7027
  this._httpClient = new HttpClient(
6957
7028
  config,
6958
7029
  () => this.auth.getToken(),
6959
- () => this.auth.getValidToken()
7030
+ () => this.auth.getValidToken(),
7031
+ () => this.auth.forceRefreshAccessToken()
6960
7032
  );
6961
7033
  this.db = new BlinkDatabase(this._httpClient);
6962
7034
  this.storage = new BlinkStorageImpl(this._httpClient);
package/dist/index.mjs CHANGED
@@ -419,12 +419,25 @@ var HttpClient = class {
419
419
  // Permanent, non-expiring key (like Stripe's sk_live_...)
420
420
  getToken;
421
421
  getValidToken;
422
- constructor(config, getToken, getValidToken) {
422
+ forceRefreshToken;
423
+ constructor(config, getToken, getValidToken, forceRefreshToken) {
423
424
  this.projectId = config.projectId;
424
425
  this.publishableKey = config.publishableKey;
425
426
  this.secretKey = config.secretKey || config.serviceToken;
426
427
  this.getToken = getToken;
427
428
  this.getValidToken = getValidToken;
429
+ this.forceRefreshToken = forceRefreshToken;
430
+ }
431
+ /**
432
+ * Whether a 401 on this request is worth recovering from by forcing a
433
+ * user-token refresh and retrying once.
434
+ *
435
+ * Only meaningful for user-JWT auth: server-side `secretKey` 401s are real
436
+ * authorization failures a refresh cannot fix, and publishable-key requests
437
+ * carry no user token to refresh.
438
+ */
439
+ canAttemptTokenRefresh(token) {
440
+ return !!this.forceRefreshToken && !this.secretKey && !!token;
428
441
  }
429
442
  shouldAttachPublishableKey(path, method) {
430
443
  if (method !== "GET" && method !== "POST") return false;
@@ -455,28 +468,37 @@ var HttpClient = class {
455
468
  */
456
469
  async request(path, options = {}) {
457
470
  const url = this.buildUrl(path, options.searchParams);
458
- const token = this.getValidToken ? await this.getValidToken() : this.getToken();
459
471
  const method = options.method || "GET";
460
- const headers = {
461
- "Content-Type": "application/json",
462
- ...options.headers
463
- };
464
- const auth = this.getAuthorizationHeader(url, token);
465
- if (auth) {
466
- headers.Authorization = auth;
467
- } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
468
- headers["x-blink-publishable-key"] = this.publishableKey;
469
- }
470
- const requestInit = {
471
- method,
472
- headers,
473
- signal: options.signal
472
+ const sendRequest = (token) => {
473
+ const headers = {
474
+ "Content-Type": "application/json",
475
+ ...options.headers
476
+ };
477
+ const auth = this.getAuthorizationHeader(url, token);
478
+ if (auth) {
479
+ headers.Authorization = auth;
480
+ } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
481
+ headers["x-blink-publishable-key"] = this.publishableKey;
482
+ }
483
+ const requestInit = {
484
+ method,
485
+ headers,
486
+ signal: options.signal
487
+ };
488
+ if (options.body && method !== "GET") {
489
+ requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
490
+ }
491
+ return fetch(url, requestInit);
474
492
  };
475
- if (options.body && method !== "GET") {
476
- requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
477
- }
478
493
  try {
479
- const response = await fetch(url, requestInit);
494
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
495
+ let response = await sendRequest(token);
496
+ if (response.status === 401 && this.canAttemptTokenRefresh(token)) {
497
+ const refreshedToken = await this.forceRefreshToken();
498
+ if (refreshedToken) {
499
+ response = await sendRequest(refreshedToken);
500
+ }
501
+ }
480
502
  if (!response.ok) {
481
503
  await this.handleErrorResponse(response);
482
504
  }
@@ -634,7 +656,6 @@ var HttpClient = class {
634
656
  */
635
657
  async uploadFile(path, file, filePath, options = {}) {
636
658
  const url = this.buildUrl(path);
637
- const token = this.getValidToken ? await this.getValidToken() : this.getToken();
638
659
  const formData = new FormData();
639
660
  if (file instanceof File) {
640
661
  formData.append("file", file);
@@ -648,41 +669,57 @@ var HttpClient = class {
648
669
  throw new BlinkValidationError("Unsupported file type");
649
670
  }
650
671
  formData.append("path", filePath);
651
- const headers = {};
652
- const auth = this.getAuthorizationHeader(url, token);
653
- if (auth) {
654
- headers.Authorization = auth;
655
- } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
656
- headers["x-blink-publishable-key"] = this.publishableKey;
657
- }
658
- try {
659
- if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
660
- return this.uploadWithProgress(url, formData, headers, options.onProgress);
672
+ const attempt = async (allowRetry) => {
673
+ const token = this.getValidToken ? await this.getValidToken() : this.getToken();
674
+ const headers = {};
675
+ const auth = this.getAuthorizationHeader(url, token);
676
+ if (auth) {
677
+ headers.Authorization = auth;
678
+ } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
679
+ headers["x-blink-publishable-key"] = this.publishableKey;
661
680
  }
662
- const response = await fetch(url, {
663
- method: "POST",
664
- headers,
665
- body: formData
666
- });
667
- if (!response.ok) {
668
- await this.handleErrorResponse(response);
669
- }
670
- const data = await this.parseResponse(response);
671
- return {
672
- data,
673
- status: response.status,
674
- headers: response.headers
675
- };
676
- } catch (error) {
677
- if (error instanceof BlinkError) {
678
- throw error;
681
+ try {
682
+ if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
683
+ return await this.uploadWithProgress(url, formData, headers, options.onProgress);
684
+ }
685
+ const response = await fetch(url, {
686
+ method: "POST",
687
+ headers,
688
+ body: formData
689
+ });
690
+ if (response.status === 401 && allowRetry && this.canAttemptTokenRefresh(token)) {
691
+ const refreshedToken = await this.forceRefreshToken();
692
+ if (refreshedToken) {
693
+ return attempt(false);
694
+ }
695
+ }
696
+ if (!response.ok) {
697
+ await this.handleErrorResponse(response);
698
+ }
699
+ const data = await this.parseResponse(response);
700
+ return {
701
+ data,
702
+ status: response.status,
703
+ headers: response.headers
704
+ };
705
+ } catch (error) {
706
+ if (allowRetry && error instanceof BlinkAuthError && this.canAttemptTokenRefresh(token)) {
707
+ const refreshedToken = await this.forceRefreshToken();
708
+ if (refreshedToken) {
709
+ return attempt(false);
710
+ }
711
+ }
712
+ if (error instanceof BlinkError) {
713
+ throw error;
714
+ }
715
+ throw new BlinkNetworkError(
716
+ `File upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
717
+ 0,
718
+ { originalError: error }
719
+ );
679
720
  }
680
- throw new BlinkNetworkError(
681
- `File upload failed: ${error instanceof Error ? error.message : "Unknown error"}`,
682
- 0,
683
- { originalError: error }
684
- );
685
- }
721
+ };
722
+ return attempt(true);
686
723
  }
687
724
  /**
688
725
  * Upload with progress tracking using XMLHttpRequest
@@ -1374,6 +1411,7 @@ var BlinkAuth = class {
1374
1411
  initializationPromise = null;
1375
1412
  isInitialized = false;
1376
1413
  storage;
1414
+ refreshPromise = null;
1377
1415
  constructor(config) {
1378
1416
  this.config = config;
1379
1417
  if (!config.projectId) {
@@ -2821,10 +2859,35 @@ var BlinkAuth = class {
2821
2859
  * Refresh access token using refresh token
2822
2860
  */
2823
2861
  async refreshToken() {
2862
+ if (this.refreshPromise) {
2863
+ return this.refreshPromise;
2864
+ }
2824
2865
  const refreshToken = this.authState.tokens?.refresh_token;
2825
2866
  if (!refreshToken) {
2826
2867
  return false;
2827
2868
  }
2869
+ this.refreshPromise = this.performTokenRefresh(refreshToken);
2870
+ try {
2871
+ return await this.refreshPromise;
2872
+ } finally {
2873
+ this.refreshPromise = null;
2874
+ }
2875
+ }
2876
+ /**
2877
+ * Force a server-side token refresh and return the resulting access token.
2878
+ *
2879
+ * Used by the HTTP client to recover from a 401 even when the client-side
2880
+ * expiry heuristics believe the access token is still valid (clock skew, a
2881
+ * restored session with a stale `issued_at`, or a refresh race). Delegates to
2882
+ * {@link refreshToken} so it shares the single-flight guard.
2883
+ *
2884
+ * @returns The new access token, or null if refresh was not possible.
2885
+ */
2886
+ async forceRefreshAccessToken() {
2887
+ const refreshed = await this.refreshToken();
2888
+ return refreshed ? this.authState.tokens?.access_token || null : null;
2889
+ }
2890
+ async performTokenRefresh(refreshToken) {
2828
2891
  try {
2829
2892
  const response = await fetch(`${this.authUrl}/api/auth/refresh`, {
2830
2893
  method: "POST",
@@ -5134,6 +5197,9 @@ var BlinkAIImpl = class {
5134
5197
  * ```
5135
5198
  */
5136
5199
  createAgent(options) {
5200
+ if (!options) {
5201
+ throw new BlinkAIError("createAgent(options) requires an options object with at least a `model`.");
5202
+ }
5137
5203
  const agent = new Agent(options);
5138
5204
  agent._setHttpClient(this.httpClient);
5139
5205
  return agent;
@@ -5148,6 +5214,11 @@ var BlinkAIImpl = class {
5148
5214
  * @returns The same Agent instance (with httpClient set)
5149
5215
  */
5150
5216
  bindAgent(agent) {
5217
+ if (!agent) {
5218
+ throw new BlinkAIError(
5219
+ "bindAgent(agent) requires an Agent instance but received null/undefined. Create one first with `new Agent({...})` or `blink.ai.createAgent({...})`. (The useAgent() hook stays idle while its `agent` is null, so this only fires on a direct bindAgent() call with a missing agent.)"
5220
+ );
5221
+ }
5151
5222
  agent._setHttpClient(this.httpClient);
5152
5223
  return agent;
5153
5224
  }
@@ -6954,7 +7025,8 @@ var BlinkClientImpl = class {
6954
7025
  this._httpClient = new HttpClient(
6955
7026
  config,
6956
7027
  () => this.auth.getToken(),
6957
- () => this.auth.getValidToken()
7028
+ () => this.auth.getValidToken(),
7029
+ () => this.auth.forceRefreshAccessToken()
6958
7030
  );
6959
7031
  this.db = new BlinkDatabase(this._httpClient);
6960
7032
  this.storage = new BlinkStorageImpl(this._httpClient);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkdotnew/sdk",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "Blink TypeScript SDK for client-side applications - Zero-boilerplate CRUD + auth + AI + analytics + notifications for modern SaaS/AI apps",
5
5
  "keywords": [
6
6
  "blink",