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