@blinkdotnew/dev-sdk 0.0.1 → 2.1.1

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.mjs CHANGED
@@ -5,6 +5,33 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
5
5
  throw Error('Dynamic require of "' + x + '" is not supported');
6
6
  });
7
7
 
8
+ // ../core/src/platform.ts
9
+ function detectPlatform() {
10
+ if (typeof Deno !== "undefined") {
11
+ return "deno";
12
+ }
13
+ if (typeof process !== "undefined" && process.versions?.node) {
14
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
15
+ return "react-native";
16
+ }
17
+ return "node";
18
+ }
19
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
20
+ return "react-native";
21
+ }
22
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
23
+ return "web";
24
+ }
25
+ return "node";
26
+ }
27
+ var platform = detectPlatform();
28
+ var isWeb = platform === "web";
29
+ var isReactNative = platform === "react-native";
30
+ var isNode = platform === "node";
31
+ var isDeno = platform === "deno";
32
+ var isBrowser = isWeb || isReactNative;
33
+ var isServer = isNode || isDeno;
34
+
8
35
  // ../core/src/storage-adapter.ts
9
36
  var WebStorageAdapter = class {
10
37
  getItem(key) {
@@ -90,6 +117,9 @@ var NoOpStorageAdapter = class {
90
117
  }
91
118
  };
92
119
  function getDefaultStorageAdapter() {
120
+ if (isDeno) {
121
+ return new NoOpStorageAdapter();
122
+ }
93
123
  if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
94
124
  try {
95
125
  localStorage.setItem("__test__", "test");
@@ -101,28 +131,6 @@ function getDefaultStorageAdapter() {
101
131
  return new NoOpStorageAdapter();
102
132
  }
103
133
 
104
- // ../core/src/platform.ts
105
- function detectPlatform() {
106
- if (typeof process !== "undefined" && process.versions?.node) {
107
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
108
- return "react-native";
109
- }
110
- return "node";
111
- }
112
- if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
113
- return "react-native";
114
- }
115
- if (typeof window !== "undefined" && typeof document !== "undefined") {
116
- return "web";
117
- }
118
- return "node";
119
- }
120
- var platform = detectPlatform();
121
- var isWeb = platform === "web";
122
- var isReactNative = platform === "react-native";
123
- var isNode = platform === "node";
124
- var isBrowser = isWeb || isReactNative;
125
-
126
134
  // ../core/src/types.ts
127
135
  var BlinkError = class extends Error {
128
136
  constructor(message, code, status, details) {
@@ -389,32 +397,65 @@ var HttpClient = class {
389
397
  authUrl = "https://blink.new";
390
398
  coreUrl = "https://core.blink.new";
391
399
  projectId;
400
+ publishableKey;
401
+ secretKey;
402
+ // Permanent, non-expiring key (like Stripe's sk_live_...)
392
403
  getToken;
393
404
  getValidToken;
394
405
  constructor(config, getToken, getValidToken) {
395
406
  this.projectId = config.projectId;
407
+ this.publishableKey = config.publishableKey;
408
+ this.secretKey = config.secretKey || config.serviceToken;
396
409
  this.getToken = getToken;
397
410
  this.getValidToken = getValidToken;
398
411
  }
412
+ shouldAttachPublishableKey(path, method) {
413
+ if (method !== "GET" && method !== "POST") return false;
414
+ if (path.includes("/api/analytics/")) return true;
415
+ if (path.includes("/api/storage/")) return true;
416
+ if (path.includes("/api/db/") && path.includes("/rest/v1/")) return method === "GET";
417
+ return false;
418
+ }
419
+ shouldSkipSecretKey(url) {
420
+ try {
421
+ const parsed = new URL(url);
422
+ return parsed.hostname.endsWith(".functions.blink.new");
423
+ } catch {
424
+ return false;
425
+ }
426
+ }
427
+ getAuthorizationHeader(url, token) {
428
+ if (this.secretKey && !this.shouldSkipSecretKey(url)) {
429
+ return `Bearer ${this.secretKey}`;
430
+ }
431
+ if (token) {
432
+ return `Bearer ${token}`;
433
+ }
434
+ return null;
435
+ }
399
436
  /**
400
437
  * Make an authenticated request to the Blink API
401
438
  */
402
439
  async request(path, options = {}) {
403
440
  const url = this.buildUrl(path, options.searchParams);
404
441
  const token = this.getValidToken ? await this.getValidToken() : this.getToken();
442
+ const method = options.method || "GET";
405
443
  const headers = {
406
444
  "Content-Type": "application/json",
407
445
  ...options.headers
408
446
  };
409
- if (token) {
410
- headers.Authorization = `Bearer ${token}`;
447
+ const auth = this.getAuthorizationHeader(url, token);
448
+ if (auth) {
449
+ headers.Authorization = auth;
450
+ } else if (this.publishableKey && !headers["x-blink-publishable-key"] && this.shouldAttachPublishableKey(path, method)) {
451
+ headers["x-blink-publishable-key"] = this.publishableKey;
411
452
  }
412
453
  const requestInit = {
413
- method: options.method || "GET",
454
+ method,
414
455
  headers,
415
456
  signal: options.signal
416
457
  };
417
- if (options.body && options.method !== "GET") {
458
+ if (options.body && method !== "GET") {
418
459
  requestInit.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
419
460
  }
420
461
  try {
@@ -568,12 +609,12 @@ var HttpClient = class {
568
609
  throw new BlinkValidationError("Unsupported file type");
569
610
  }
570
611
  formData.append("path", filePath);
571
- if (options.upsert !== void 0) {
572
- formData.append("options", JSON.stringify({ upsert: options.upsert }));
573
- }
574
612
  const headers = {};
575
- if (token) {
576
- headers.Authorization = `Bearer ${token}`;
613
+ const auth = this.getAuthorizationHeader(url, token);
614
+ if (auth) {
615
+ headers.Authorization = auth;
616
+ } else if (this.publishableKey && path.includes("/api/storage/") && !headers["x-blink-publishable-key"]) {
617
+ headers["x-blink-publishable-key"] = this.publishableKey;
577
618
  }
578
619
  try {
579
620
  if (typeof XMLHttpRequest !== "undefined" && options.onProgress) {
@@ -674,7 +715,7 @@ var HttpClient = class {
674
715
  });
675
716
  }
676
717
  /**
677
- * Stream AI text generation with Vercel AI SDK data stream format
718
+ * Stream AI text generation - uses Vercel AI SDK's pipeUIMessageStreamToResponse (Data Stream Protocol)
678
719
  */
679
720
  async streamAiText(prompt, options = {}, onChunk) {
680
721
  const url = this.buildUrl(`/api/ai/${this.projectId}/text`);
@@ -682,9 +723,8 @@ var HttpClient = class {
682
723
  const headers = {
683
724
  "Content-Type": "application/json"
684
725
  };
685
- if (token) {
686
- headers.Authorization = `Bearer ${token}`;
687
- }
726
+ const auth = this.getAuthorizationHeader(url, token);
727
+ if (auth) headers.Authorization = auth;
688
728
  const body = {
689
729
  prompt,
690
730
  stream: true,
@@ -704,7 +744,7 @@ var HttpClient = class {
704
744
  if (!response.body) {
705
745
  throw new BlinkNetworkError("No response body for streaming");
706
746
  }
707
- return this.parseDataStream(response.body, onChunk);
747
+ return this.parseDataStreamProtocol(response.body, onChunk);
708
748
  } catch (error) {
709
749
  if (error instanceof BlinkError) {
710
750
  throw error;
@@ -729,7 +769,7 @@ var HttpClient = class {
729
769
  });
730
770
  }
731
771
  /**
732
- * Stream AI object generation with Vercel AI SDK data stream format
772
+ * Stream AI object generation - uses Vercel AI SDK's pipeTextStreamToResponse
733
773
  */
734
774
  async streamAiObject(prompt, options = {}, onPartial) {
735
775
  const url = this.buildUrl(`/api/ai/${this.projectId}/object`);
@@ -737,9 +777,8 @@ var HttpClient = class {
737
777
  const headers = {
738
778
  "Content-Type": "application/json"
739
779
  };
740
- if (token) {
741
- headers.Authorization = `Bearer ${token}`;
742
- }
780
+ const auth = this.getAuthorizationHeader(url, token);
781
+ if (auth) headers.Authorization = auth;
743
782
  const body = {
744
783
  prompt,
745
784
  stream: true,
@@ -759,7 +798,35 @@ var HttpClient = class {
759
798
  if (!response.body) {
760
799
  throw new BlinkNetworkError("No response body for streaming");
761
800
  }
762
- return this.parseDataStream(response.body, void 0, onPartial);
801
+ const reader = response.body.getReader();
802
+ const decoder = new TextDecoder();
803
+ let buffer = "";
804
+ let latestObject = {};
805
+ try {
806
+ while (true) {
807
+ const { done, value } = await reader.read();
808
+ if (done) break;
809
+ const chunk = decoder.decode(value, { stream: true });
810
+ buffer += chunk;
811
+ try {
812
+ const parsed = JSON.parse(buffer);
813
+ latestObject = parsed;
814
+ if (onPartial) {
815
+ onPartial(parsed);
816
+ }
817
+ } catch {
818
+ }
819
+ }
820
+ if (buffer) {
821
+ try {
822
+ latestObject = JSON.parse(buffer);
823
+ } catch {
824
+ }
825
+ }
826
+ return { object: latestObject };
827
+ } finally {
828
+ reader.releaseLock();
829
+ }
763
830
  } catch (error) {
764
831
  if (error instanceof BlinkError) {
765
832
  throw error;
@@ -954,93 +1021,94 @@ var HttpClient = class {
954
1021
  }
955
1022
  }
956
1023
  /**
957
- * Parse Vercel AI SDK data stream format
958
- * Handles text chunks (0:"text"), partial objects (2:[...]), and metadata (d:, e:)
1024
+ * Parse Vercel AI SDK v5 Data Stream Protocol (Server-Sent Events)
1025
+ * Supports all event types from the UI Message Stream protocol
959
1026
  */
960
- async parseDataStream(body, onChunk, onPartial) {
1027
+ async parseDataStreamProtocol(body, onChunk) {
961
1028
  const reader = body.getReader();
962
1029
  const decoder = new TextDecoder();
1030
+ const finalResult = {
1031
+ text: "",
1032
+ toolCalls: [],
1033
+ toolResults: [],
1034
+ sources: [],
1035
+ files: [],
1036
+ reasoning: []
1037
+ };
963
1038
  let buffer = "";
964
- let finalResult = {};
965
1039
  try {
966
1040
  while (true) {
967
1041
  const { done, value } = await reader.read();
968
1042
  if (done) break;
969
1043
  buffer += decoder.decode(value, { stream: true });
970
- const lines = buffer.split(/\r?\n/);
1044
+ const lines = buffer.split("\n");
971
1045
  buffer = lines.pop() || "";
972
1046
  for (const line of lines) {
973
1047
  if (!line.trim()) continue;
1048
+ if (line === "[DONE]") {
1049
+ continue;
1050
+ }
1051
+ if (!line.startsWith("data: ")) continue;
974
1052
  try {
975
- if (line.startsWith("f:")) {
976
- const metadata = JSON.parse(line.slice(2));
977
- finalResult.messageId = metadata.messageId;
978
- } else if (line.startsWith("0:")) {
979
- const textChunk = JSON.parse(line.slice(2));
980
- if (onChunk) {
981
- onChunk(textChunk);
982
- }
983
- finalResult.text = (finalResult.text || "") + textChunk;
984
- } else if (line.startsWith("2:")) {
985
- const data = JSON.parse(line.slice(2));
986
- if (Array.isArray(data) && data.length > 0) {
987
- const item = data[0];
988
- if (typeof item === "string") {
989
- finalResult.status = item;
990
- } else if (typeof item === "object") {
991
- if (onPartial) {
992
- onPartial(item);
993
- }
994
- finalResult.object = item;
1053
+ const jsonStr = line.slice(6);
1054
+ const part = JSON.parse(jsonStr);
1055
+ switch (part.type) {
1056
+ case "text-start":
1057
+ break;
1058
+ case "text-delta":
1059
+ if (part.delta) {
1060
+ finalResult.text += part.delta;
1061
+ if (onChunk) onChunk(part.delta);
995
1062
  }
996
- }
997
- } else if (line.startsWith("d:")) {
998
- const metadata = JSON.parse(line.slice(2));
999
- if (metadata.usage) {
1000
- finalResult.usage = metadata.usage;
1001
- }
1002
- if (metadata.finishReason) {
1003
- finalResult.finishReason = metadata.finishReason;
1004
- }
1005
- } else if (line.startsWith("e:")) {
1006
- const errorData = JSON.parse(line.slice(2));
1007
- finalResult.error = errorData;
1008
- }
1009
- } catch (error) {
1010
- console.warn("Failed to parse stream line:", line, error);
1011
- }
1012
- }
1013
- }
1014
- if (buffer.trim()) {
1015
- try {
1016
- if (buffer.startsWith("0:")) {
1017
- const textChunk = JSON.parse(buffer.slice(2));
1018
- if (onChunk) {
1019
- onChunk(textChunk);
1020
- }
1021
- finalResult.text = (finalResult.text || "") + textChunk;
1022
- } else if (buffer.startsWith("2:")) {
1023
- const data = JSON.parse(buffer.slice(2));
1024
- if (Array.isArray(data) && data.length > 0) {
1025
- const item = data[0];
1026
- if (typeof item === "object") {
1027
- if (onPartial) {
1028
- onPartial(item);
1063
+ if (part.textDelta) {
1064
+ finalResult.text += part.textDelta;
1065
+ if (onChunk) onChunk(part.textDelta);
1029
1066
  }
1030
- finalResult.object = item;
1031
- }
1032
- }
1033
- } else if (buffer.startsWith("d:")) {
1034
- const metadata = JSON.parse(buffer.slice(2));
1035
- if (metadata.usage) {
1036
- finalResult.usage = metadata.usage;
1037
- }
1038
- if (metadata.finishReason) {
1039
- finalResult.finishReason = metadata.finishReason;
1067
+ break;
1068
+ case "text-end":
1069
+ break;
1070
+ case "tool-call":
1071
+ finalResult.toolCalls.push({
1072
+ toolCallId: part.toolCallId,
1073
+ toolName: part.toolName,
1074
+ args: part.args
1075
+ });
1076
+ break;
1077
+ case "tool-result":
1078
+ finalResult.toolResults.push({
1079
+ toolCallId: part.toolCallId,
1080
+ toolName: part.toolName,
1081
+ result: part.result
1082
+ });
1083
+ break;
1084
+ case "source-url":
1085
+ finalResult.sources.push({
1086
+ id: part.id,
1087
+ url: part.url,
1088
+ title: part.title
1089
+ });
1090
+ break;
1091
+ case "file":
1092
+ finalResult.files.push(part.file);
1093
+ break;
1094
+ case "reasoning":
1095
+ finalResult.reasoning.push(part.content);
1096
+ break;
1097
+ case "finish":
1098
+ finalResult.finishReason = part.finishReason;
1099
+ finalResult.usage = part.usage;
1100
+ if (part.response) finalResult.response = part.response;
1101
+ break;
1102
+ case "error":
1103
+ finalResult.error = part.error;
1104
+ throw new Error(part.error);
1105
+ case "data":
1106
+ if (!finalResult.customData) finalResult.customData = [];
1107
+ finalResult.customData.push(part.value);
1108
+ break;
1040
1109
  }
1110
+ } catch (e) {
1041
1111
  }
1042
- } catch (error) {
1043
- console.warn("Failed to parse final buffer:", buffer, error);
1044
1112
  }
1045
1113
  }
1046
1114
  return finalResult;
@@ -1931,22 +1999,33 @@ var BlinkAuth = class {
1931
1999
  if (!hasWindow()) {
1932
2000
  throw new BlinkAuthError("NETWORK_ERROR" /* NETWORK_ERROR */, "signInWithProvider requires a browser environment");
1933
2001
  }
2002
+ const shouldPreferRedirect = isWeb && this.isIframe || typeof window !== "undefined" && window.crossOriginIsolated === true;
2003
+ const state = this.generateState();
2004
+ try {
2005
+ const sessionStorage = getSessionStorage();
2006
+ if (sessionStorage) {
2007
+ sessionStorage.setItem("blink_oauth_state", state);
2008
+ }
2009
+ } catch {
2010
+ }
2011
+ const redirectUrl = options?.redirectUrl || getLocationOrigin() || "";
2012
+ const buildAuthUrl = (mode) => {
2013
+ const url = new URL("/auth", this.authUrl);
2014
+ url.searchParams.set("provider", provider);
2015
+ url.searchParams.set("project_id", this.config.projectId);
2016
+ url.searchParams.set("state", state);
2017
+ url.searchParams.set("mode", mode);
2018
+ url.searchParams.set("redirect_url", redirectUrl);
2019
+ url.searchParams.set("opener_origin", getLocationOrigin() || "");
2020
+ return url;
2021
+ };
2022
+ if (shouldPreferRedirect) {
2023
+ window.location.href = buildAuthUrl("redirect").toString();
2024
+ return new Promise(() => {
2025
+ });
2026
+ }
1934
2027
  return new Promise((resolve, reject) => {
1935
- const state = this.generateState();
1936
- try {
1937
- const sessionStorage = getSessionStorage();
1938
- if (sessionStorage) {
1939
- sessionStorage.setItem("blink_oauth_state", state);
1940
- }
1941
- } catch {
1942
- }
1943
- const redirectUrl = options?.redirectUrl || getLocationOrigin() || "";
1944
- const popupUrl = new URL("/auth", this.authUrl);
1945
- popupUrl.searchParams.set("provider", provider);
1946
- popupUrl.searchParams.set("project_id", this.config.projectId);
1947
- popupUrl.searchParams.set("state", state);
1948
- popupUrl.searchParams.set("mode", "popup");
1949
- popupUrl.searchParams.set("redirect_url", redirectUrl);
2028
+ const popupUrl = buildAuthUrl("popup");
1950
2029
  const popup = window.open(
1951
2030
  popupUrl.toString(),
1952
2031
  "blink-auth",
@@ -1957,6 +2036,15 @@ var BlinkAuth = class {
1957
2036
  return;
1958
2037
  }
1959
2038
  let timeoutId;
2039
+ let closedIntervalId;
2040
+ let cleanedUp = false;
2041
+ const cleanup = () => {
2042
+ if (cleanedUp) return;
2043
+ cleanedUp = true;
2044
+ clearTimeout(timeoutId);
2045
+ if (closedIntervalId) clearInterval(closedIntervalId);
2046
+ window.removeEventListener("message", messageListener);
2047
+ };
1960
2048
  const messageListener = (event) => {
1961
2049
  let allowed = false;
1962
2050
  try {
@@ -1993,29 +2081,34 @@ var BlinkAuth = class {
1993
2081
  }, true).then(() => {
1994
2082
  resolve(this.authState.user);
1995
2083
  }).catch(reject);
1996
- clearTimeout(timeoutId);
1997
- window.removeEventListener("message", messageListener);
2084
+ cleanup();
1998
2085
  popup.close();
1999
2086
  } else if (event.data?.type === "BLINK_AUTH_ERROR") {
2000
2087
  const errorCode = this.mapErrorCodeFromResponse(event.data.code);
2001
2088
  reject(new BlinkAuthError(errorCode, event.data.message || "Authentication failed"));
2002
- clearTimeout(timeoutId);
2003
- window.removeEventListener("message", messageListener);
2089
+ cleanup();
2004
2090
  popup.close();
2005
2091
  }
2006
2092
  };
2093
+ if (popup.opener === null) {
2094
+ try {
2095
+ popup.close();
2096
+ } catch {
2097
+ }
2098
+ cleanup();
2099
+ window.location.href = buildAuthUrl("redirect").toString();
2100
+ return;
2101
+ }
2007
2102
  timeoutId = setTimeout(() => {
2008
- window.removeEventListener("message", messageListener);
2103
+ cleanup();
2009
2104
  if (!popup.closed) {
2010
2105
  popup.close();
2011
2106
  }
2012
2107
  reject(new BlinkAuthError("AUTH_TIMEOUT" /* AUTH_TIMEOUT */, "Authentication timed out"));
2013
2108
  }, 3e5);
2014
- const checkClosed = setInterval(() => {
2109
+ closedIntervalId = setInterval(() => {
2015
2110
  if (popup.closed) {
2016
- clearInterval(checkClosed);
2017
- clearTimeout(timeoutId);
2018
- window.removeEventListener("message", messageListener);
2111
+ cleanup();
2019
2112
  reject(new BlinkAuthError("POPUP_CANCELED" /* POPUP_CANCELED */, "Authentication was canceled"));
2020
2113
  }
2021
2114
  }, 1e3);
@@ -2942,6 +3035,11 @@ var BlinkAuth = class {
2942
3035
  };
2943
3036
 
2944
3037
  // src/database.ts
3038
+ function assertServerOnly(methodName) {
3039
+ if (typeof window !== "undefined") {
3040
+ throw new Error(`${methodName} is server-only. Use Blink CRUD methods (blink.db.<table>.*) instead.`);
3041
+ }
3042
+ }
2945
3043
  function camelToSnake3(str) {
2946
3044
  return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
2947
3045
  }
@@ -3160,6 +3258,7 @@ var BlinkTable = class {
3160
3258
  * Raw SQL query on this table (for advanced use cases)
3161
3259
  */
3162
3260
  async sql(query, params) {
3261
+ assertServerOnly("blink.db.<table>.sql");
3163
3262
  const response = await this.httpClient.dbSql(query, params);
3164
3263
  return response.data;
3165
3264
  }
@@ -3208,6 +3307,7 @@ var BlinkDatabase = class {
3208
3307
  * Execute raw SQL query
3209
3308
  */
3210
3309
  async sql(query, params) {
3310
+ assertServerOnly("blink.db.sql");
3211
3311
  const response = await this.httpClient.dbSql(query, params);
3212
3312
  return response.data;
3213
3313
  }
@@ -3215,6 +3315,7 @@ var BlinkDatabase = class {
3215
3315
  * Execute batch SQL operations
3216
3316
  */
3217
3317
  async batch(statements, mode = "write") {
3318
+ assertServerOnly("blink.db.batch");
3218
3319
  const response = await this.httpClient.dbBatch(statements, mode);
3219
3320
  return response.data;
3220
3321
  }
@@ -3277,7 +3378,6 @@ var BlinkStorageImpl = class {
3277
3378
  correctedPath,
3278
3379
  // Use corrected path with proper extension
3279
3380
  {
3280
- upsert: options.upsert,
3281
3381
  onProgress: options.onProgress,
3282
3382
  contentType: detectedContentType
3283
3383
  // Pass detected content type
@@ -3297,7 +3397,7 @@ var BlinkStorageImpl = class {
3297
3397
  if (error instanceof Error && "status" in error) {
3298
3398
  const status = error.status;
3299
3399
  if (status === 409) {
3300
- throw new BlinkStorageError("File already exists. Set upsert: true to overwrite.", 409);
3400
+ throw new BlinkStorageError("File already exists.", 409);
3301
3401
  }
3302
3402
  if (status === 400) {
3303
3403
  throw new BlinkStorageError("Invalid request parameters", 400);
@@ -3342,7 +3442,6 @@ var BlinkStorageImpl = class {
3342
3442
  detectedContentType
3343
3443
  };
3344
3444
  } catch (error) {
3345
- console.warn("File type detection failed, using original path:", error);
3346
3445
  return {
3347
3446
  correctedPath: originalPath,
3348
3447
  detectedContentType: "application/octet-stream"
@@ -3732,13 +3831,7 @@ var BlinkAIImpl = class {
3732
3831
  options.prompt || "",
3733
3832
  requestBody
3734
3833
  );
3735
- if (response.data?.result) {
3736
- return response.data.result;
3737
- } else if (response.data?.text) {
3738
- return response.data;
3739
- } else {
3740
- throw new BlinkAIError("Invalid response format: missing text");
3741
- }
3834
+ return response.data;
3742
3835
  } catch (error) {
3743
3836
  if (error instanceof BlinkAIError) {
3744
3837
  throw error;
@@ -3807,9 +3900,14 @@ var BlinkAIImpl = class {
3807
3900
  );
3808
3901
  return {
3809
3902
  text: result.text || "",
3810
- finishReason: "stop",
3903
+ finishReason: result.finishReason || "stop",
3811
3904
  usage: result.usage,
3812
- ...result
3905
+ toolCalls: result.toolCalls,
3906
+ toolResults: result.toolResults,
3907
+ sources: result.sources,
3908
+ files: result.files,
3909
+ reasoningDetails: result.reasoning,
3910
+ response: result.response
3813
3911
  };
3814
3912
  } catch (error) {
3815
3913
  if (error instanceof BlinkAIError) {
@@ -3888,13 +3986,7 @@ var BlinkAIImpl = class {
3888
3986
  signal: options.signal
3889
3987
  }
3890
3988
  );
3891
- if (response.data?.result) {
3892
- return response.data.result;
3893
- } else if (response.data?.object) {
3894
- return response.data;
3895
- } else {
3896
- throw new BlinkAIError("Invalid response format: missing object");
3897
- }
3989
+ return response.data;
3898
3990
  } catch (error) {
3899
3991
  if (error instanceof BlinkAIError) {
3900
3992
  throw error;
@@ -3956,8 +4048,7 @@ var BlinkAIImpl = class {
3956
4048
  return {
3957
4049
  object: result.object || {},
3958
4050
  finishReason: "stop",
3959
- usage: result.usage,
3960
- ...result
4051
+ usage: result.usage
3961
4052
  };
3962
4053
  } catch (error) {
3963
4054
  if (error instanceof BlinkAIError) {
@@ -5276,7 +5367,6 @@ var BlinkAnalyticsImpl = class {
5276
5367
  } catch (error) {
5277
5368
  this.queue = [...events, ...this.queue];
5278
5369
  this.persistQueue();
5279
- console.error("Failed to send analytics events:", error);
5280
5370
  }
5281
5371
  if (this.queue.length > 0) {
5282
5372
  this.timer = setTimeout(() => this.flush(), BATCH_TIMEOUT);
@@ -5486,6 +5576,45 @@ var BlinkConnectorsImpl = class {
5486
5576
  }
5487
5577
  };
5488
5578
 
5579
+ // src/functions.ts
5580
+ var BlinkFunctionsImpl = class {
5581
+ httpClient;
5582
+ projectId;
5583
+ constructor(httpClient, projectId, _getToken) {
5584
+ this.httpClient = httpClient;
5585
+ this.projectId = projectId;
5586
+ }
5587
+ /**
5588
+ * Get the project suffix from the full project ID.
5589
+ * Project IDs are formatted as: prj_xxxxx
5590
+ * The suffix is the last 8 characters used in function URLs.
5591
+ */
5592
+ getProjectSuffix() {
5593
+ return this.projectId.slice(-8);
5594
+ }
5595
+ /**
5596
+ * Build the full function URL
5597
+ */
5598
+ buildFunctionUrl(functionSlug, searchParams) {
5599
+ const suffix = this.getProjectSuffix();
5600
+ const baseUrl = `https://${suffix}--${functionSlug}.functions.blink.new`;
5601
+ if (!searchParams || Object.keys(searchParams).length === 0) {
5602
+ return baseUrl;
5603
+ }
5604
+ const url = new URL(baseUrl);
5605
+ Object.entries(searchParams).forEach(([key, value]) => {
5606
+ url.searchParams.set(key, value);
5607
+ });
5608
+ return url.toString();
5609
+ }
5610
+ async invoke(functionSlug, options = {}) {
5611
+ const { method = "POST", body, headers = {}, searchParams } = options;
5612
+ const url = this.buildFunctionUrl(functionSlug, searchParams);
5613
+ const res = await this.httpClient.request(url, { method, body, headers });
5614
+ return { data: res.data, status: res.status, headers: res.headers };
5615
+ }
5616
+ };
5617
+
5489
5618
  // src/client.ts
5490
5619
  var BlinkClientImpl = class {
5491
5620
  auth;
@@ -5497,8 +5626,12 @@ var BlinkClientImpl = class {
5497
5626
  notifications;
5498
5627
  analytics;
5499
5628
  connectors;
5629
+ functions;
5500
5630
  httpClient;
5501
5631
  constructor(config) {
5632
+ if ((config.secretKey || config.serviceToken) && isBrowser) {
5633
+ throw new Error("secretKey/serviceToken is server-only. Do not provide it in browser/React Native clients.");
5634
+ }
5502
5635
  this.auth = new BlinkAuth(config);
5503
5636
  this.httpClient = new HttpClient(
5504
5637
  config,
@@ -5513,6 +5646,11 @@ var BlinkClientImpl = class {
5513
5646
  this.notifications = new BlinkNotificationsImpl(this.httpClient);
5514
5647
  this.analytics = new BlinkAnalyticsImpl(this.httpClient, config.projectId);
5515
5648
  this.connectors = new BlinkConnectorsImpl(this.httpClient);
5649
+ this.functions = new BlinkFunctionsImpl(
5650
+ this.httpClient,
5651
+ config.projectId,
5652
+ () => this.auth.getValidToken()
5653
+ );
5516
5654
  this.auth.onAuthStateChanged((state) => {
5517
5655
  if (state.isAuthenticated && state.user) {
5518
5656
  this.analytics.setUserId(state.user.id);
@@ -5531,6 +5669,6 @@ function createClient(config) {
5531
5669
  return new BlinkClientImpl(config);
5532
5670
  }
5533
5671
 
5534
- export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkConnectorsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isNode, isReactNative, isWeb, platform };
5672
+ export { AsyncStorageAdapter, BlinkAIImpl, BlinkAnalyticsImpl, BlinkConnectorsImpl, BlinkDataImpl, BlinkDatabase, BlinkRealtimeChannel, BlinkRealtimeImpl, BlinkStorageImpl, BlinkTable, NoOpStorageAdapter, WebStorageAdapter, createClient, getDefaultStorageAdapter, isBrowser, isDeno, isNode, isReactNative, isServer, isWeb, platform };
5535
5673
  //# sourceMappingURL=index.mjs.map
5536
5674
  //# sourceMappingURL=index.mjs.map